Quellcode durchsuchen

Merge pull request #1 from BabylonJS/master - update fork

update fork

never test to pull request just to update (as seen here https://www.youtube.com/watch?v=bTaFAqJ6bjE ), sorry if i do something wrong...
V!nc3r vor 8 Jahren
Ursprung
Commit
d3dbeab11e
100 geänderte Dateien mit 74189 neuen und 619 gelöschten Zeilen
  1. 39 1
      .gitignore
  2. 1 1
      .travis.yml
  3. 122 0
      .vscode/launch.json
  4. 39 0
      .vscode/settings.json
  5. 44 0
      .vscode/tasks.json
  6. 572 0
      Babylon.csproj
  7. 34 0
      Babylon.sln
  8. 132 0
      Exporters/3ds Max/ActionsBuilder/ActionsBuilder.csproj
  9. 87 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.actionNode.js
  10. 119 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.contextMenu.js
  11. 352 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.js
  12. 256 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.list.js
  13. 108 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.main.js
  14. 2627 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.max.js
  15. 562 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.parameters.js
  16. 86 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.toolbar.js
  17. 434 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.utils.js
  18. 632 0
      Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.viewer.js
  19. 60548 0
      Exporters/3ds Max/ActionsBuilder/Sources/babylon.max.js
  20. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts.css
  21. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.eot
  22. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.otf
  23. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.svg
  24. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.ttf
  25. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.woff
  26. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.eot
  27. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.otf
  28. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.svg
  29. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.ttf
  30. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.woff
  31. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.eot
  32. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.otf
  33. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.svg
  34. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.ttf
  35. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.woff
  36. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.eot
  37. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.otf
  38. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.svg
  39. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.ttf
  40. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.woff
  41. 97 0
      Exporters/3ds Max/ActionsBuilder/Sources/index-debug.html
  42. 191 0
      Exporters/3ds Max/ActionsBuilder/Sources/index.css
  43. 72 0
      Exporters/3ds Max/ActionsBuilder/Sources/index.html
  44. 0 0
      Exporters/3ds Max/ActionsBuilder/Sources/raphael.js
  45. 62 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.actionNode.js
  46. 101 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.actionNode.ts
  47. 104 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.contextMenu.js
  48. 149 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.contextMenu.ts
  49. 316 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.js
  50. 209 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.list.js
  51. 306 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.list.ts
  52. 96 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.main.js
  53. 118 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.main.ts
  54. 440 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.parameters.js
  55. 650 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.parameters.ts
  56. 80 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.toolbar.js
  57. 94 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.toolbar.ts
  58. 326 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.ts
  59. 370 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.utils.js
  60. 527 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.utils.ts
  61. 539 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.viewer.js
  62. 773 0
      Exporters/3ds Max/ActionsBuilder/actionsbuilder.viewer.ts
  63. 42 0
      Exporters/3ds Max/ActionsBuilder/gulpfile.js
  64. 10 0
      Exporters/3ds Max/ActionsBuilder/package.json
  65. 69 0
      Exporters/3ds Max/ActionsBuilder/raphaeljs.d.ts
  66. 31 0
      Exporters/3ds Max/ActionsBuilder/web.Debug.config
  67. 32 0
      Exporters/3ds Max/ActionsBuilder/web.Release.config
  68. 11 0
      Exporters/3ds Max/ActionsBuilder/web.config
  69. 6 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonAbstractMesh.cs
  70. 12 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonAnimation.cs
  71. 36 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonCamera.cs
  72. 16 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonDirectionalLight.cs
  73. 17 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonExport.Entities.csproj
  74. 37 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonHDRCubeTexture.cs
  75. 6 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonLight.cs
  76. 4 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonMaterial.cs
  77. 8 6
      Exporters/3ds Max/BabylonExport.Entities/BabylonMesh.cs
  78. 52 53
      Exporters/3ds Max/BabylonExport.Entities/BabylonPBRMaterial.cs
  79. 24 4
      Exporters/3ds Max/BabylonExport.Entities/BabylonParticleSystem.cs
  80. 12 3
      Exporters/3ds Max/BabylonExport.Entities/BabylonScene.cs
  81. 99 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonShaderMaterial.cs
  82. 17 2
      Exporters/3ds Max/BabylonExport.Entities/BabylonShadowGenerator.cs
  83. 11 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonStandardMaterial.cs
  84. 8 1
      Exporters/3ds Max/BabylonExport.Entities/BabylonTexture.cs
  85. 12 0
      Exporters/3ds Max/BabylonExport.Entities/BabylonUniversalCamera.cs
  86. 3 3
      Exporters/3ds Max/BabylonExport.Entities/packages.config
  87. 66 0
      Exporters/3ds Max/BabylonFileConverter/BabylonFileConverter.csproj
  88. 374 0
      Exporters/3ds Max/BabylonFileConverter/BinaryConverter.cs
  89. 36 0
      Exporters/3ds Max/BabylonFileConverter/Properties/AssemblyInfo.cs
  90. 4 0
      Exporters/3ds Max/BabylonFileConverter/packages.config
  91. BIN
      Exporters/3ds Max/Max2Babylon-0.5.0.zip
  92. BIN
      Exporters/3ds Max/Max2Babylon-0.8.0.zip
  93. BIN
      Exporters/3ds Max/Max2Babylon-0.9.0.zip
  94. 223 34
      Exporters/3ds Max/Max2Babylon.sln
  95. 273 293
      Exporters/3ds Max/Max2Babylon/2015/Max2Babylon2015.csproj
  96. 36 36
      Exporters/3ds Max/Max2Babylon/2015/Properties/AssemblyInfo.cs
  97. 63 63
      Exporters/3ds Max/Max2Babylon/2015/Properties/Resources.Designer.cs
  98. 119 119
      Exporters/3ds Max/Max2Babylon/2015/Properties/Resources.resx
  99. 6 0
      Exporters/3ds Max/Max2Babylon/2015/packages.config
  100. 0 0
      Exporters/3ds Max/Max2Babylon/2017/Max2Babylon2017.csproj

+ 39 - 1
.gitignore

@@ -14,6 +14,17 @@ TestResults
 *.sln.docstates
 *.map
 
+# javascript files
+src/**/*.js
+src/**/*.d.ts
+loaders/src/**/*.js
+materialsLibrary/src/**/*.js
+proceduralTexturesLibrary/src/**/*.js
+postProcessLibrary/src/**/*.js
+inspector/src/**/*.js
+serializers/src/**/*.js
+gui/src/**/*.js
+
 # Build results
 [Dd]ebug/
 [Rr]elease/
@@ -104,6 +115,19 @@ ClientBin
 *.dbmdl
 Generated_Code #added for RIA/Silverlight projects
 
+# OS or Editor folders
+*.DS_Store
+._*
+Thumbs.db
+.cache
+.tmproj
+nbproject
+*.sublime-project
+*.sublime-workspace
+.directory
+build
+.history
+
 # Backup & report files from converting an old project file to a newer
 # Visual Studio version. Backup files are not needed, because we have git ;-)
 _UpgradeReport_Files/
@@ -121,10 +145,24 @@ UpgradeLog*.XML
 
 # Do not ignore loaders/obj
 !loaders/*
+**/node_modules
 node_modules
 
 # for JetBrains IDE
 .idea
 
 # for VSCode
-.vscode
+.vs
+.tempChromeProfileForDebug
+.temp
+*.js.map
+*.js.fx
+*.d.ts
+!dist/**/*.d.ts
+!lib.d.ts
+
+# local dev
+localDev/src/*
+/dist/preview release/babylon.custom.js
+/dist/preview release/babylon.custom.max.js
+/localDev/src/index.js

+ 1 - 1
.travis.yml

@@ -1,6 +1,6 @@
 language: node_js
 node_js:
-  - "4.1"
+  - "6"
 before_script:
   - npm install -g gulp
   - cd ./Tools/Gulp

+ 122 - 0
.vscode/launch.json

@@ -0,0 +1,122 @@
+{
+    "version": "0.1.0",
+    "configurations": [
+        {
+            "name": "Launch sandbox (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/sandbox/index-local.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        },         
+        {
+            "name": "Launch playground (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/Playground/index-local.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,            
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        },        
+        {
+            "name": "Launch playground (Chrome+WebGL 1.0 forced)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/Playground/index-local.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--disable-es3-apis" 
+            ]
+        },
+        {
+            "name": "Launch Materials Library (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/materialsLibrary/index.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        },
+        {
+            "name": "Launch Post Processes Library (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/postProcessLibrary/index.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        },
+        {
+            "name": "Launch Procedural Textures Library (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/proceduralTexturesLibrary/index.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        },
+        {
+            "name": "Launch Inspector (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/inspector/index.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        },
+        {
+            "name": "Launch Local Dev (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/localDev/index.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        },
+        {
+            "name": "Launch Build Validation (Chrome)",
+            "type": "chrome",
+            "request": "launch",
+            "url": "http://localhost:1338/tests/validation/index.html",
+            "webRoot": "${workspaceRoot}/",
+            "sourceMaps": true,
+            "preLaunchTask": "run",
+            "userDataDir": "${workspaceRoot}/.tempChromeProfileForDebug",
+            "runtimeArgs": [
+                "--enable-unsafe-es3-apis" 
+            ]
+        }
+    ]
+}

+ 39 - 0
.vscode/settings.json

@@ -0,0 +1,39 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+    // Configure glob patterns for excluding files and folders.
+    "files.exclude": {
+        "**/.git": true,
+        "**/.svn": true,
+        "**/.hg": true,
+        "**/.DS_Store": true,
+        "**/.vs": true,        
+        "**/.tempChromeProfileForDebug": true,
+        "**/node_modules": true,
+        "**/temp": true,
+        "**/.temp": true,
+        "**/*.d.ts": true,
+        "**/*.js.map": true,
+        "**/*.js.fx": true,
+        "**/*.js": { 
+            "when":"$(basename).ts"
+        }
+    },
+    "files.associations": {
+        "*.fx": "glsl"
+    },
+    "search.exclude": {
+        "**/node_modules": true,
+        "**/bower_components": true,
+        "**/.vs": true,
+        "**/.tempChromeProfileForDebug": true,
+        "**/.temp": true,
+        "**/dist": true,
+        "**/*.map": true,
+        "**/*.js": { 
+             "when":"$(basename).ts"
+         },
+        "**/*.d.ts": true,
+        "assets":true
+    },
+    "typescript.check.workspaceVersion": false
+}

+ 44 - 0
.vscode/tasks.json

@@ -0,0 +1,44 @@
+{
+    // See https://go.microsoft.com/fwlink/?LinkId=733558
+    // for the documentation about the tasks.json format
+    "version": "0.1.0",
+    "command": "gulp",
+    "isShellCommand": true,
+    "args": [],
+    "showOutput": "always",
+    "options": {
+        "cwd": "${workspaceRoot}/Tools/Gulp"
+    },
+    "tasks": [
+        { 
+            "taskName": "run",
+            // Hijack Test Command for easy shortcut
+            "isTestCommand": true,
+            "isBackground": true,
+            "problemMatcher": {
+                "owner": "typescript",
+                "fileLocation": "relative",
+                "pattern": {
+                    "regexp": "^([^\\s].*)\\((\\d+|\\,\\d+|\\d+,\\d+,\\d+,\\d+)\\):\\s+(error|warning|info)\\s+(TS\\d+)\\s*:\\s*(.*)$",
+                    "file": 1,
+                    "location": 2,
+                    "severity": 3,
+                    "code": 4,
+                    "message": 5
+                },
+                "watching": {
+                    "activeOnStart": true,
+                    "beginsPattern": "Starting \\'watch\\'",
+                    "endsPattern": "Finished \\'run\\'"
+                }
+            }
+        },
+        { 
+            "taskName": "typescript-all",
+            "isBuildCommand": true,
+            "problemMatcher": [
+                "$tsc"
+            ]
+        }
+    ]
+}

+ 572 - 0
Babylon.csproj

@@ -0,0 +1,572 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props')" />
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>
+    </ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}</ProjectGuid>
+    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>BabylonExport</RootNamespace>
+    <AssemblyName>Babylon</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <UseIISExpress>true</UseIISExpress>
+    <IISExpressSSLPort />
+    <IISExpressAnonymousAuthentication />
+    <IISExpressWindowsAuthentication />
+    <IISExpressUseClassicPipelineMode />
+    <SccProjectName>SAK</SccProjectName>
+    <SccLocalPath>SAK</SccLocalPath>
+    <SccAuxPath>SAK</SccAuxPath>
+    <SccProvider>SAK</SccProvider>
+    <TypeScriptToolsVersion>2.1</TypeScriptToolsVersion>
+    <UseGlobalApplicationHostFile />
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Web.DynamicData" />
+    <Reference Include="System.Web.Entity" />
+    <Reference Include="System.Web.ApplicationServices" />
+    <Reference Include="System.ComponentModel.DataAnnotations" />
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="System.Web.Extensions" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Web" />
+    <Reference Include="System.Xml" />
+    <Reference Include="System.Configuration" />
+    <Reference Include="System.Web.Services" />
+    <Reference Include="System.EnterpriseServices" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Playground\babylon.d.txt" />
+    <Content Include="Playground\debug.html" />
+    <Content Include="Playground\index-local.html" />
+    <Content Include="Playground\sounds\gunshot.wav" />
+    <Content Include="Playground\sounds\violons11.wav" />
+    <Content Include="Playground\textures\albedo.png" />
+    <Content Include="Playground\textures\reflectivity.png" />
+    <Content Include="Playground\textures\room.hdr" />
+    <Content Include="Playground\package.json" />
+    <Content Include="src\Shaders\glowBlurPostProcess.fragment.fx" />
+    <Content Include="src\Shaders\glowMapGeneration.fragment.fx" />
+    <Content Include="src\Shaders\glowMapGeneration.vertex.fx" />
+    <Content Include="src\Shaders\glowMapMerge.fragment.fx" />
+    <Content Include="src\Shaders\glowMapMerge.vertex.fx" />
+    <None Include="Web.Debug.config">
+      <DependentUpon>Web.config</DependentUpon>
+    </None>
+    <None Include="Web.Release.config">
+      <DependentUpon>Web.config</DependentUpon>
+    </None>
+    <Content Include="Playground\frame.css" />
+    <Content Include="Playground\frame.js" />
+    <Content Include="Playground\scripts\pbr.js" />
+    <Content Include="Playground\scripts\refraction and reflection.js" />
+    <Content Include="Playground\frame.html" />
+    <Content Include="Playground\textures\mask.png" />
+    <Content Include="Playground\hand.minified-1.2.js" />
+    <Content Include="Playground\textures\equirectangular.jpg" />
+    <Content Include="Playground\textures\sphereMap.png" />
+    <Content Include="Playground\textures\waterbump.png" />
+    <TypeScriptCompile Include="src\Actions\babylon.action.ts" />
+    <TypeScriptCompile Include="src\Actions\babylon.actionManager.ts" />
+    <TypeScriptCompile Include="src\Actions\babylon.condition.ts" />
+    <TypeScriptCompile Include="src\Actions\babylon.directActions.ts" />
+    <TypeScriptCompile Include="src\Actions\babylon.interpolateValueAction.ts" />
+    <TypeScriptCompile Include="src\Animations\babylon.easing.ts" />
+    <TypeScriptCompile Include="src\Audio\babylon.analyser.ts" />
+    <TypeScriptCompile Include="src\Audio\babylon.audioEngine.ts" />
+    <TypeScriptCompile Include="src\Audio\babylon.sound.ts" />
+    <TypeScriptCompile Include="src\Audio\babylon.soundtrack.ts" />
+    <TypeScriptCompile Include="src\Bones\babylon.bone.ts" />
+    <TypeScriptCompile Include="src\Bones\babylon.skeleton.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.arcRotateCameraInputsManager.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.cameraInputsManager.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.followCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.freeCameraInputsManager.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.gamepadCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.stereoscopicCameras.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.targetCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.universalCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.touchCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.deviceOrientationCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.virtualJoysticksCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.arcrotatecamera.input.gamepad.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.arcrotatecamera.input.keyboard.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.arcrotatecamera.input.mousewheel.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.arcrotatecamera.input.pointers.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.freecamera.input.deviceorientation.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.freecamera.input.gamepad.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.freecamera.input.keyboard.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.freecamera.input.mouse.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.freecamera.input.touch.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.freecamera.input.virtualjoystick.ts" />
+    <TypeScriptCompile Include="src\Cameras\Inputs\babylon.arcrotatecamera.input.vrdeviceorientation.ts" />
+    <TypeScriptCompile Include="src\Cameras\VR\babylon.vrCameraMetrics.ts" />
+    <TypeScriptCompile Include="src\Cameras\VR\babylon.vrDeviceOrientationCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\VR\babylon.webVRCamera.ts" />
+    <TypeScriptCompile Include="src\Collisions\babylon.collisionCoordinator.ts" />
+    <TypeScriptCompile Include="src\Collisions\babylon.collisionWorker.ts" />
+    <TypeScriptCompile Include="src\Culling\babylon.ray.ts" />
+    <TypeScriptCompile Include="src\Debug\babylon.axesViewer.ts" />
+    <TypeScriptCompile Include="src\Debug\babylon.boneAxesViewer.ts" />
+    <TypeScriptCompile Include="src\Debug\babylon.skeletonViewer.ts" />
+    <TypeScriptCompile Include="src\Debug\babylon.debugLayer.ts" />
+    <TypeScriptCompile Include="src\Layer\babylon.highlightlayer.ts" />
+    <TypeScriptCompile Include="src\Loading\babylon.sceneLoader.ts" />
+    <TypeScriptCompile Include="src\Loading\Plugins\babylon.babylonFileLoader.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.colorCurves.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.effect.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.fresnelParameters.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.material.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.materialHelper.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.multiMaterial.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.pbrMaterial.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.shaderMaterial.ts" />
+    <TypeScriptCompile Include="src\Materials\babylon.standardMaterial.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.baseTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.colorGradingTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.cubeTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.dynamicTexture.ts" />
+    <TypeScriptCompile Include="canvas2D\src\Engine\babylon.fontTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.mapTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.refractionTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.mirrorTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.rawTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.renderTargetTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.texture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.videoTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\babylon.hdrCubeTexture.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.buffer.ts" />
+    <TypeScriptCompile Include="src\Physics\babylon.physicsImpostor.ts" />
+    <TypeScriptCompile Include="src\Physics\babylon.physicsJoint.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.standardRenderingPipeline.ts" />
+    <TypeScriptCompile Include="src\States\babylon.alphaCullingState.ts" />
+    <TypeScriptCompile Include="src\States\babylon.depthCullingState.ts" />
+    <TypeScriptCompile Include="src\States\babylon.stencilState.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.decorators.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.dynamicFloatArray.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.earcut.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.khronosTextureContainer.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.observable.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.rectPackingMap.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.stringDictionary.ts" />
+    <TypeScriptCompile Include="src\Tools\HDR\babylon.tools.cubemapToSphericalPolynomial.ts" />
+    <TypeScriptCompile Include="src\Tools\HDR\babylon.tools.hdr.ts" />
+    <TypeScriptCompile Include="src\Tools\HDR\babylon.tools.panoramaToCubemap.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\Procedurals\babylon.customProceduralTexture.ts" />
+    <TypeScriptCompile Include="src\Materials\Textures\Procedurals\babylon.proceduralTexture.ts" />
+    <TypeScriptCompile Include="src\Math\babylon.math.SIMD.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.abstractMesh.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.geometry.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.groundMesh.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.instancedMesh.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.linesMesh.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.meshBuilder.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.meshLODLevel.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.meshSimplification.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.polygonMesh.ts" />
+    <TypeScriptCompile Include="src\Particles\babylon.solidParticle.ts" />
+    <TypeScriptCompile Include="src\Particles\babylon.solidParticleSystem.ts" />
+    <TypeScriptCompile Include="src\Physics\Plugins\babylon.oimoJSPlugin.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.colorCorrectionPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.displayPassPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.filterPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.convolutionPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.fxaaPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.hdrRenderingPipeline.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.lensRenderingPipeline.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.ssaoRenderingPipeline.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.stereoscopicInterlacePostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.tonemapPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.volumetricLightScatteringPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.vrDistortionCorrectionPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.refractionPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\RenderPipeline\babylon.postProcessRenderEffect.ts" />
+    <TypeScriptCompile Include="src\PostProcess\RenderPipeline\babylon.postProcessRenderPass.ts" />
+    <Content Include="Playground\splitbox.css" />
+    <Content Include="Playground\splitbox.js" />
+    <Content Include="Playground\textures\amiga.jpg" />
+    <Content Include="Playground\textures\grass.dds" />
+    <Content Include="Playground\textures\specmap.png" />
+    <Content Include="Playground\textures\specularglossymap.png" />
+    <Content Include="Playground\xtag.min.js" />
+    <Content Include="src\Shaders\anaglyph.fragment.fx" />
+    <Content Include="src\Shaders\chromaticAberration.fragment.fx" />
+    <Content Include="src\Shaders\ShadersInclude\colorCurves.fx" />
+    <Content Include="src\Shaders\ShadersInclude\colorCurvesDefinition.fx" />
+    <Content Include="src\Shaders\ShadersInclude\colorGrading.fx" />
+    <Content Include="src\Shaders\ShadersInclude\colorGradingDefinition.fx" />
+    <Content Include="src\Shaders\standard.fragment.fx" />
+    <Content Include="src\Shaders\legacypbr.fragment.fx" />
+    <Content Include="src\Shaders\legacypbr.vertex.fx" />
+    <Content Include="src\Shaders\line.fragment.fx" />
+    <Content Include="src\Shaders\line.vertex.fx" />
+    <Content Include="src\Shaders\colorCorrection.fragment.fx" />
+    <Content Include="src\Shaders\depthBoxBlur.fragment.fx" />
+    <Content Include="src\Shaders\depthOfField.fragment.fx" />
+    <Content Include="src\Shaders\hdr.fragment.fx" />
+    <Content Include="src\Shaders\lensHighlights.fragment.fx" />
+    <Content Include="src\Shaders\pbr.fragment.fx" />
+    <Content Include="src\Shaders\pbr.vertex.fx" />
+    <Content Include="src\Shaders\procedural.vertex.fx" />
+    <Content Include="src\Shaders\outline.fragment.fx" />
+    <Content Include="src\Shaders\color.fragment.fx" />
+    <Content Include="src\Shaders\outline.vertex.fx" />
+    <Content Include="src\Shaders\color.vertex.fx" />
+    <Content Include="src\Shaders\displayPass.fragment.fx" />
+    <Content Include="src\Shaders\filter.fragment.fx" />
+    <Content Include="src\Shaders\convolution.fragment.fx" />
+    <Content Include="src\Shaders\blackAndWhite.fragment.fx" />
+    <Content Include="src\Shaders\fxaa.fragment.fx" />
+    <Content Include="src\Shaders\lensFlare.fragment.fx" />
+    <Content Include="src\Shaders\lensFlare.vertex.fx" />
+    <Content Include="src\Shaders\ShadersInclude\bumpFragment.fx" />
+    <Content Include="src\Shaders\ShadersInclude\clipPlaneFragment.fx" />
+    <Content Include="src\Shaders\ShadersInclude\clipPlaneVertex.fx" />
+    <Content Include="src\Shaders\ShadersInclude\clipPlaneFragmentDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\clipPlaneVertexDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\harmonicsFunctions.fx" />
+    <Content Include="src\Shaders\ShadersInclude\helperFunctions.fx" />
+    <Content Include="src\Shaders\ShadersInclude\lightFragment.fx" />
+    <Content Include="src\Shaders\ShadersInclude\pbrFunctions.fx" />
+    <Content Include="src\Shaders\ShadersInclude\pbrLightFunctions.fx" />
+    <Content Include="src\Shaders\ShadersInclude\pbrLightFunctionsCall.fx" />
+    <Content Include="src\Shaders\ShadersInclude\pbrShadowFunctions.fx" />
+    <Content Include="src\Shaders\ShadersInclude\reflectionFunction.fx" />
+    <Content Include="src\Shaders\ShadersInclude\fresnelFunction.fx" />
+    <Content Include="src\Shaders\ShadersInclude\pointCloudVertexDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\pointCloudVertex.fx" />
+    <Content Include="src\Shaders\ShadersInclude\logDepthDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\logDepthFragment.fx" />
+    <Content Include="src\Shaders\ShadersInclude\logDepthVertex.fx" />
+    <Content Include="src\Shaders\ShadersInclude\lightsFragmentFunctions.fx" />
+    <Content Include="src\Shaders\ShadersInclude\lightFragmentDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\bumpFragmentFunctions.fx" />
+    <Content Include="src\Shaders\ShadersInclude\shadowsVertexDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\shadowsVertex.fx" />
+    <Content Include="src\Shaders\ShadersInclude\shadowsFragmentFunctions.fx" />
+    <Content Include="src\Shaders\ShadersInclude\fogVertex.fx" />
+    <Content Include="src\Shaders\ShadersInclude\fogVertexDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\fogFragment.fx" />
+    <Content Include="src\Shaders\ShadersInclude\fogFragmentDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\instancesVertex.fx" />
+    <Content Include="src\Shaders\ShadersInclude\instancesDeclaration.fx" />
+    <Content Include="src\Shaders\ShadersInclude\bonesVertex.fx" />
+    <Content Include="src\Shaders\ShadersInclude\bonesDeclaration.fx" />
+    <Content Include="src\Shaders\stereoscopicInterlace.fragment.fx" />
+    <Content Include="src\Shaders\tonemap.fragment.fx" />
+    <Content Include="src\Shaders\vrDistortionCorrection.fragment.fx" />
+    <Content Include="src\Shaders\pass.fragment.fx" />
+    <Content Include="src\Shaders\refraction.fragment.fx" />
+    <TypeScriptCompile Include="src\PostProcess\RenderPipeline\babylon.postProcessRenderPipeline.ts" />
+    <TypeScriptCompile Include="src\PostProcess\RenderPipeline\babylon.postProcessRenderPipelineManager.ts" />
+    <TypeScriptCompile Include="src\Probes\babylon.reflectionProbe.ts" />
+    <TypeScriptCompile Include="src\Rendering\babylon.boundingBoxRenderer.ts" />
+    <TypeScriptCompile Include="src\Rendering\babylon.depthRenderer.ts" />
+    <TypeScriptCompile Include="src\Rendering\babylon.edgesRenderer.ts" />
+    <TypeScriptCompile Include="src\Rendering\babylon.outlineRenderer.ts" />
+    <Content Include="src\Shaders\depth.fragment.fx" />
+    <Content Include="src\Shaders\shadowMap.vertex.fx" />
+    <Content Include="src\Shaders\ssao.fragment.fx" />
+    <Content Include="src\Shaders\ssaoCombine.fragment.fx" />
+    <Content Include="src\Shaders\volumetricLightScattering.fragment.fx" />
+    <Content Include="src\Shaders\volumetricLightScatteringPass.fragment.fx" />
+    <TypeScriptCompile Include="src\Tools\babylon.assetsManager.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.filesInput.ts" />
+    <Content Include="Playground\scripts\hdr rendering pipeline.js" />
+    <Content Include="Playground\bootstrap\css\bootstrap-responsive.css" />
+    <Content Include="Playground\bootstrap\css\bootstrap-responsive.min.css" />
+    <Content Include="Playground\bootstrap\css\bootstrap.css" />
+    <Content Include="Playground\bootstrap\css\bootstrap.min.css" />
+    <Content Include="Playground\bootstrap\img\glyphicons-halflings-white.png" />
+    <Content Include="Playground\bootstrap\img\glyphicons-halflings.png" />
+    <Content Include="Playground\bootstrap\js\bootstrap.js" />
+    <Content Include="Playground\bootstrap\js\bootstrap.min.js" />
+    <Content Include="Playground\fileSaver.js" />
+    <Content Include="Playground\index.css" />
+    <Content Include="Playground\index.html" />
+    <Content Include="Playground\scenes\0.jpg" />
+    <Content Include="Playground\scenes\Dude\0.jpg" />
+    <Content Include="Playground\scenes\Dude\1.jpg" />
+    <Content Include="Playground\scenes\Dude\2.jpg" />
+    <Content Include="Playground\scenes\Dude\3.jpg" />
+    <Content Include="Playground\scripts\Easing functions.js" />
+    <Content Include="Playground\scripts\proceduralTexture.js" />
+    <Content Include="Playground\scripts\ssao rendering pipeline.js" />
+    <Content Include="Playground\scripts\volumetric light scattering.js" />
+    <Content Include="Playground\index.js" />
+    <Content Include="Playground\jszip.min.js" />
+    <Content Include="Playground\scripts\drag and drop.js" />
+    <Content Include="Playground\scripts\actions.js" />
+    <Content Include="Playground\scripts\Environment.js" />
+    <Content Include="Playground\scripts\cameras.js" />
+    <Content Include="Playground\scripts\collisions.js" />
+    <Content Include="Playground\scripts\height Map.js" />
+    <Content Include="Playground\scripts\import meshes.js" />
+    <Content Include="Playground\scripts\Intersections.js" />
+    <Content Include="Playground\scripts\fresnel.js" />
+    <Content Include="Playground\scripts\lights.js" />
+    <Content Include="Playground\scripts\particles.js" />
+    <Content Include="Playground\scripts\rotation and scaling.js" />
+    <Content Include="Playground\scripts\basic elements.js" />
+    <Content Include="Playground\scripts\basic scene.js" />
+    <Content Include="Playground\scripts\materials.js" />
+    <Content Include="Playground\scripts\animations.js" />
+    <Content Include="Playground\scripts\scripts.txt" />
+    <Content Include="Playground\scripts\shadows.js" />
+    <Content Include="Playground\scripts\sprites.js" />
+    <Content Include="Playground\textures\babylonjs.mp4" />
+    <Content Include="Playground\textures\bloc.jpg" />
+    <Content Include="Playground\textures\cloud.png" />
+    <Content Include="Playground\textures\crate.png" />
+    <Content Include="Playground\textures\customProceduralTextures\land\custom.fragment.fx" />
+    <Content Include="Playground\textures\customProceduralTextures\land\textures\dirt.jpg" />
+    <Content Include="Playground\textures\customProceduralTextures\land\textures\grass.png" />
+    <Content Include="Playground\textures\earth.jpg" />
+    <Content Include="Playground\textures\flare.png" />
+    <Content Include="Playground\textures\grass.jpg" />
+    <Content Include="Playground\textures\ground.jpg" />
+    <Content Include="Playground\textures\heightMap.png" />
+    <Content Include="Playground\textures\impact.png" />
+    <Content Include="Playground\textures\normalMap.jpg" />
+    <Content Include="Playground\textures\orient.jpg" />
+    <Content Include="Playground\textures\palm.png" />
+    <Content Include="Playground\textures\player.png" />
+    <Content Include="Playground\textures\skybox_nx.jpg" />
+    <Content Include="Playground\textures\skybox_ny.jpg" />
+    <Content Include="Playground\textures\skybox_nz.jpg" />
+    <Content Include="Playground\textures\skybox_px.jpg" />
+    <Content Include="Playground\textures\skybox_py.jpg" />
+    <Content Include="Playground\textures\skybox_pz.jpg" />
+    <Content Include="Playground\textures\sun.png" />
+    <Content Include="Playground\textures\tree.png" />
+    <Content Include="Playground\textures\misc.jpg" />
+    <Content Include="Playground\textures\TropicalSunnyDay_ny.jpg" />
+    <Content Include="Playground\textures\TropicalSunnyDay_nx.jpg" />
+    <Content Include="Playground\textures\TropicalSunnyDay_nz.jpg" />
+    <Content Include="Playground\textures\TropicalSunnyDay_px.jpg" />
+    <Content Include="Playground\textures\TropicalSunnyDay_py.jpg" />
+    <Content Include="Playground\textures\TropicalSunnyDay_pz.jpg" />
+    <Content Include="Playground\textures\worldHeightMap.jpg" />
+    <Content Include="Playground\textures\xStrip.jpg" />
+    <Content Include="Playground\textures\yStrip.jpg" />
+    <Content Include="Playground\textures\zStrip.jpg" />
+    <Content Include="Playground\zipContent\index.html" />
+    <Content Include="src\Shaders\blur.fragment.fx" />
+    <Content Include="src\Shaders\postprocess.vertex.fx" />
+    <Content Include="src\Shaders\shadowMap.fragment.fx" />
+    <Content Include="src\Shaders\depth.vertex.fx" />
+    <Content Include="src\Shaders\layer.fragment.fx" />
+    <Content Include="src\Shaders\layer.vertex.fx" />
+    <Content Include="src\Shaders\particles.fragment.fx" />
+    <Content Include="src\Shaders\particles.vertex.fx" />
+    <Content Include="src\Shaders\sprites.fragment.fx" />
+    <Content Include="src\Shaders\sprites.vertex.fx" />
+    <Content Include="Playground\scripts\picking.js" />
+    <Content Include="Web.config">
+      <SubType>Designer</SubType>
+    </Content>
+    <TypeScriptCompile Include="src\Tools\babylon.gamepads.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.database.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.loadingScreen.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.sceneOptimizer.ts" />
+    <TypeScriptCompile Include="src\Tools\HDR\babylon.tools.pmremGenerator.ts" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="src\Shaders\default.fragment.fx" />
+    <Content Include="src\Shaders\default.vertex.fx" />
+    <Content Include="Playground\textures\babylonjs.webm" />
+    <Content Include="Playground\scenes\skull.babylon" />
+    <Content Include="Playground\textures\customProceduralTextures\land\config.json" />
+    <Content Include="Playground\scenes\SSAOcat.babylon" />
+    <Content Include="Playground\scenes\Rabbit.babylon" />
+    <Content Include="Playground\scenes\Dude\dude.babylon" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Properties\" />
+  </ItemGroup>
+  <ItemGroup>
+    <TypeScriptCompile Include="src\Animations\babylon.animatable.ts" />
+    <TypeScriptCompile Include="src\Animations\babylon.animation.ts" />
+    <TypeScriptCompile Include="src\babylon.engine.ts" />
+    <TypeScriptCompile Include="src\babylon.mixins.ts" />
+    <TypeScriptCompile Include="src\babylon.scene.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.arcRotateCamera.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.camera.ts" />
+    <TypeScriptCompile Include="src\Cameras\babylon.freeCamera.ts" />
+    <TypeScriptCompile Include="src\Collisions\babylon.collider.ts" />
+    <TypeScriptCompile Include="src\Collisions\babylon.pickingInfo.ts" />
+    <TypeScriptCompile Include="src\Culling\babylon.boundingBox.ts" />
+    <TypeScriptCompile Include="src\Culling\babylon.boundingInfo.ts" />
+    <TypeScriptCompile Include="src\Culling\babylon.boundingSphere.ts" />
+    <TypeScriptCompile Include="src\Culling\Octrees\babylon.octree.ts" />
+    <TypeScriptCompile Include="src\Culling\Octrees\babylon.octreeBlock.ts" />
+    <TypeScriptCompile Include="src\Layer\babylon.layer.ts" />
+    <TypeScriptCompile Include="src\LensFlare\babylon.lensFlare.ts" />
+    <TypeScriptCompile Include="src\LensFlare\babylon.lensFlareSystem.ts" />
+    <TypeScriptCompile Include="src\Lights\babylon.directionalLight.ts" />
+    <TypeScriptCompile Include="src\Lights\babylon.hemisphericLight.ts" />
+    <TypeScriptCompile Include="src\Lights\babylon.light.ts" />
+    <TypeScriptCompile Include="src\Lights\babylon.pointLight.ts" />
+    <TypeScriptCompile Include="src\Lights\babylon.spotLight.ts" />
+    <TypeScriptCompile Include="src\Lights\Shadows\babylon.shadowGenerator.ts" />
+    <TypeScriptCompile Include="src\Math\babylon.math.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.csg.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.mesh.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.mesh.vertexData.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.subMesh.ts" />
+    <TypeScriptCompile Include="src\Mesh\babylon.vertexBuffer.ts" />
+    <TypeScriptCompile Include="src\babylon.node.ts" />
+    <TypeScriptCompile Include="src\Particles\babylon.particle.ts" />
+    <TypeScriptCompile Include="src\Particles\babylon.particleSystem.ts" />
+    <TypeScriptCompile Include="src\Physics\babylon.physicsEngine.ts" />
+    <TypeScriptCompile Include="src\Physics\Plugins\babylon.cannonJSPlugin.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.anaglyphPostProcess.ts">
+      <SubType>Code</SubType>
+    </TypeScriptCompile>
+    <TypeScriptCompile Include="src\PostProcess\babylon.blackAndWhitePostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.blurPostProcess.ts" />
+    <TypeScriptCompile Include="src\PostProcess\babylon.passPostProcess.ts">
+      <SubType>Code</SubType>
+    </TypeScriptCompile>
+    <TypeScriptCompile Include="src\PostProcess\babylon.postProcess.ts">
+      <SubType>Code</SubType>
+    </TypeScriptCompile>
+    <TypeScriptCompile Include="src\PostProcess\babylon.postProcessManager.ts" />
+    <TypeScriptCompile Include="src\Rendering\babylon.renderingGroup.ts" />
+    <TypeScriptCompile Include="src\Rendering\babylon.renderingManager.ts" />
+    <TypeScriptCompile Include="src\Sprites\babylon.sprite.ts" />
+    <TypeScriptCompile Include="src\Sprites\babylon.spriteManager.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.andOrNotEvaluator.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.sceneSerializer.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.smartArray.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.tags.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.tools.dds.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.tools.tga.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.tools.ts" />
+    <TypeScriptCompile Include="src\Tools\babylon.virtualJoystick.ts" />
+  </ItemGroup>
+  <PropertyGroup>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
+    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
+    <OutputPath>bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x64</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
+    <DebugSymbols>true</DebugSymbols>
+    <OutputPath>bin\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <DebugType>full</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
+    <OutputPath>bin\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <Optimize>true</Optimize>
+    <DebugType>pdbonly</DebugType>
+    <PlatformTarget>x86</PlatformTarget>
+    <ErrorReport>prompt</ErrorReport>
+    <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
+  </PropertyGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
+    <TypeScriptGeneratesDeclarations>False</TypeScriptGeneratesDeclarations>
+    <TypeScriptModuleKind>none</TypeScriptModuleKind>
+    <TypeScriptNoImplicitAny>False</TypeScriptNoImplicitAny>
+    <TypeScriptTarget>ES5</TypeScriptTarget>
+    <TypeScriptCompileOnSaveEnabled>False</TypeScriptCompileOnSaveEnabled>
+    <TypeScriptRemoveComments>False</TypeScriptRemoveComments>
+    <TypeScriptOutFile />
+    <TypeScriptOutDir />
+    <TypeScriptNoEmitOnError>True</TypeScriptNoEmitOnError>
+    <TypeScriptSourceMap>True</TypeScriptSourceMap>
+    <TypeScriptMapRoot />
+    <TypeScriptSourceRoot />
+    <TypeScriptExperimentalDecorators>true</TypeScriptExperimentalDecorators>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)' == 'Release'">
+    <TypeScriptTarget>ES5</TypeScriptTarget>
+  </PropertyGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets')" />
+  <Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
+        <WebProjectProperties>
+          <UseIIS>True</UseIIS>
+          <AutoAssignPort>True</AutoAssignPort>
+          <DevelopmentServerPort>0</DevelopmentServerPort>
+          <DevelopmentServerVPath>/</DevelopmentServerVPath>
+          <IISUrl>http://localhost:10683/</IISUrl>
+          <NTLMAuthentication>False</NTLMAuthentication>
+          <UseCustomServer>False</UseCustomServer>
+          <CustomServerUrl>
+          </CustomServerUrl>
+          <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
+        </WebProjectProperties>
+      </FlavorProperties>
+      <UserProperties node__modules_2grunt-contrib-uglify_2package_1json__JSONSchema="http://json.schemastore.org/package" />
+    </VisualStudio>
+  </ProjectExtensions>
+  <PropertyGroup>
+    <PreBuildEvent>
+    </PreBuildEvent>
+  </PropertyGroup>
+  <PropertyGroup>
+    <PostBuildEvent>
+    </PostBuildEvent>
+  </PropertyGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 34 - 0
Babylon.sln

@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.25914.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Babylon", "Babylon.csproj", "{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Release|Any CPU = Release|Any CPU
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Debug|x64.ActiveCfg = Debug|x64
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Debug|x64.Build.0 = Debug|x64
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Debug|x86.ActiveCfg = Debug|x86
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Debug|x86.Build.0 = Debug|x86
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Release|Any CPU.Build.0 = Release|Any CPU
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Release|x64.ActiveCfg = Release|x64
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Release|x64.Build.0 = Release|x64
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Release|x86.ActiveCfg = Release|x86
+		{BA85B096-DDB8-4324-BB47-5F4EC2C5DB6A}.Release|x86.Build.0 = Release|x86
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal

+ 132 - 0
Exporters/3ds Max/ActionsBuilder/ActionsBuilder.csproj

@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <ProjectGuid>{293A754C-3D80-42FB-99D8-7C9386CD297E}</ProjectGuid>
+    <ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+    <OutputType>Library</OutputType>
+    <OutputPath>bin</OutputPath>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <DebugType>full</DebugType>
+    <DebugSymbols>true</DebugSymbols>
+    <TypeScriptToolsVersion>2.1</TypeScriptToolsVersion>
+    <UseIISExpress>true</UseIISExpress>
+    <IISExpressSSLPort />
+    <IISExpressAnonymousAuthentication />
+    <IISExpressWindowsAuthentication />
+    <IISExpressUseClassicPipelineMode />
+    <SccProjectName>SAK</SccProjectName>
+    <SccLocalPath>SAK</SccLocalPath>
+    <SccAuxPath>SAK</SccAuxPath>
+    <SccProvider>SAK</SccProvider>
+    <UseGlobalApplicationHostFile />
+  </PropertyGroup>
+  <ItemGroup>
+    <Content Include="gulpfile.js" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300Light.svg" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300LightItalic.svg" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-500Medium.svg" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-600SemiBold.svg" />
+    <Content Include="Sources\index-debug.html" />
+    <Content Include="Sources\index.css" />
+    <Content Include="Sources\fonts.css" />
+    <Content Include="Sources\index.html" />
+    <Content Include="Sources\raphael.js" />
+    <Content Include="web.config" />
+  </ItemGroup>
+  <ItemGroup>
+    <TypeScriptCompile Include="..\..\..\dist\preview release\babylon.d.ts">
+      <Link>babylon.d.ts</Link>
+    </TypeScriptCompile>
+    <TypeScriptCompile Include="actionsbuilder.actionNode.ts" />
+    <TypeScriptCompile Include="actionsbuilder.contextMenu.ts" />
+    <TypeScriptCompile Include="actionsbuilder.list.ts" />
+    <TypeScriptCompile Include="actionsbuilder.main.ts" />
+    <TypeScriptCompile Include="actionsbuilder.parameters.ts" />
+    <TypeScriptCompile Include="actionsbuilder.toolbar.ts" />
+    <TypeScriptCompile Include="actionsbuilder.ts" />
+    <TypeScriptCompile Include="actionsbuilder.utils.ts" />
+    <TypeScriptCompile Include="actionsbuilder.viewer.ts" />
+    <TypeScriptCompile Include="raphaeljs.d.ts" />
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="package.json" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300Light.eot" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300Light.otf" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300Light.ttf" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300Light.woff" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300LightItalic.eot" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300LightItalic.otf" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300LightItalic.ttf" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-300LightItalic.woff" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-500Medium.eot" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-500Medium.otf" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-500Medium.ttf" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-500Medium.woff" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-600SemiBold.eot" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-600SemiBold.otf" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-600SemiBold.ttf" />
+    <Content Include="Sources\fonts\SinkinSans\SinkinSans-600SemiBold.woff" />
+    <None Include="web.Debug.config">
+      <DependentUpon>web.config</DependentUpon>
+    </None>
+    <None Include="web.Release.config">
+      <DependentUpon>web.config</DependentUpon>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Properties\" />
+  </ItemGroup>
+  <PropertyGroup>
+    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
+  </PropertyGroup>
+  <PropertyGroup>
+    <RootNamespace>ActionsBuilder</RootNamespace>
+  </PropertyGroup>
+  <PropertyGroup>
+    <RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
+  </PropertyGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\WebApplications\Microsoft.WebApplication.targets" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\WebApplications\Microsoft.WebApplication.targets')" />
+  <ProjectExtensions>
+    <VisualStudio>
+      <FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
+        <WebProjectProperties>
+          <UseIIS>False</UseIIS>
+          <AutoAssignPort>True</AutoAssignPort>
+          <DevelopmentServerPort>51472</DevelopmentServerPort>
+          <DevelopmentServerVPath>/</DevelopmentServerVPath>
+          <IISUrl>http://localhost:51472/</IISUrl>
+          <NTLMAuthentication>False</NTLMAuthentication>
+          <UseCustomServer>False</UseCustomServer>
+          <CustomServerUrl>
+          </CustomServerUrl>
+          <SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
+        </WebProjectProperties>
+      </FlavorProperties>
+    </VisualStudio>
+  </ProjectExtensions>
+  <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
+    <TypeScriptRemoveComments>false</TypeScriptRemoveComments>
+    <TypeScriptSourceMap>true</TypeScriptSourceMap>
+    <TypeScriptCompileOnSaveEnabled>True</TypeScriptCompileOnSaveEnabled>
+    <OutputPath>..\..\..\..\..\..\..\Program Files\Autodesk\3ds Max 2013\bin\assemblies\BabylonActionsBuilder\</OutputPath>
+    <TypeScriptOutDir>Sources\</TypeScriptOutDir>
+    <TypeScriptGeneratesDeclarations>False</TypeScriptGeneratesDeclarations>
+    <TypeScriptTarget>ES5</TypeScriptTarget>
+    <TypeScriptSourceRoot>$(SolutionDir)ActionsBuilder\</TypeScriptSourceRoot>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)' == 'Release'">
+    <TypeScriptRemoveComments>true</TypeScriptRemoveComments>
+    <TypeScriptSourceMap>false</TypeScriptSourceMap>
+  </PropertyGroup>
+  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets')" />
+  <PropertyGroup>
+    <PostBuildEvent>copy "$(SolutionDir)..\..\dist\preview release\babylon.max.js" "$(SolutionDir)ActionsBuilder\Sources\babylon.max.js"
+if $(ConfigurationName) == Release gulp</PostBuildEvent>
+  </PropertyGroup>
+</Project>

+ 87 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.actionNode.js

@@ -0,0 +1,87 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Node = (function () {
+        function Node() {
+            this.rect = null;
+            this.text = null;
+            this.line = null;
+            this.detached = false;
+            this.minimized = false;
+        }
+        /**
+        * Returns if the point (x, y) is inside the text or rect
+        * @param x: the x position of the point
+        * @param y: the y position of the point
+        */
+        Node.prototype.isPointInside = function (x, y) {
+            return this.rect.isPointInside(x, y) || this.text.isPointInside(x, y);
+        };
+        return Node;
+    }());
+    ActionsBuilder.Node = Node;
+    var Action = (function () {
+        /**
+        * Constructor
+        * @param node: The associated node to draw in the viewer
+        */
+        function Action(node) {
+            this.parent = null;
+            this.children = new Array();
+            this.name = "";
+            this.type = ActionsBuilder.Type.OBJECT;
+            this.properties = new Array();
+            this.propertiesResults = new Array();
+            this.combineArray = null;
+            this.hub = null;
+            this.combineAction = null;
+            this.node = node;
+        }
+        /*
+        * Removes a combined action from the combine array
+        * @param action: the action to remove
+        */
+        Action.prototype.removeCombinedAction = function (action) {
+            if (action === null || this.combineArray === null) {
+                return false;
+            }
+            var index = this.combineArray.indexOf(action);
+            if (index !== -1) {
+                this.combineArray.splice(index, 1);
+            }
+            return false;
+        };
+        /*
+        * Adds a child
+        * @param child: the action to add as child
+        */
+        Action.prototype.addChild = function (child) {
+            if (child === null) {
+                return false;
+            }
+            this.children.push(child);
+            child.parent = this;
+            return true;
+        };
+        /*
+        * Removes the given action to children
+        * @param child: the child to remove
+        */
+        Action.prototype.removeChild = function (child) {
+            var indice = this.children.indexOf(child);
+            if (indice !== -1) {
+                this.children.splice(indice, 1);
+                return true;
+            }
+            return false;
+        };
+        /*
+        * Clears the children's array
+        */
+        Action.prototype.clearChildren = function () {
+            this.children = new Array();
+        };
+        return Action;
+    }());
+    ActionsBuilder.Action = Action;
+})(ActionsBuilder || (ActionsBuilder = {}));
+//# sourceMappingURL=actionsbuilder.actionNode.js.map

+ 119 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.contextMenu.js

@@ -0,0 +1,119 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var ContextMenu = (function () {
+        /*
+        * Constructor
+        * @param viewer: the graph viewer
+        */
+        function ContextMenu(viewer) {
+            this.showing = false;
+            this.savedColor = Raphael.rgb(255, 255, 255);
+            this.overColor = Raphael.rgb(140, 200, 230);
+            this._viewer = null;
+            this.elements = [
+                { text: "Reduce", node: null, action: "onReduce" },
+                { text: "Delete", node: null, action: "onRemoveNode" },
+                { text: "Delete branch", node: null, action: "onRemoveBranch" },
+                { text: "Connect / Disconnect", node: null, action: "onDetachAction" },
+                { text: "Copy", node: null, action: "onCopyStructure" },
+                { text: "Paste", node: null, action: "onPasteStructure" },
+                // Add other elements here
+                { text: "", node: null, action: null } // Color separator (top)
+            ];
+            // Members
+            this._viewer = viewer;
+            // Configure
+            this.attachControl(this._viewer.paper.canvas);
+        }
+        ContextMenu.prototype.attachControl = function (element) {
+            var _this = this;
+            var onClick = function (event) {
+                var x = _this._viewer.mousex;
+                var y = _this._viewer.mousey;
+                // Remove all context menu nodes, and run action if selected
+                if (_this.showing) {
+                    for (var i = 0; i < _this.elements.length; i++) {
+                        var element = _this.elements[i];
+                        if (element.action && element.node.rect.isPointInside(x, y)) {
+                            _this._viewer.utils[element.action]();
+                            _this._viewer.update();
+                        }
+                        element.node.rect.remove();
+                        element.node.text.remove();
+                    }
+                }
+                _this.showing = false;
+            };
+            var onMouseMove = function (event) {
+                // Override context menu's node color if mouse is inside
+                if (_this.showing) {
+                    for (var i = 0; i < _this.elements.length; i++) {
+                        var element = _this.elements[i];
+                        if (element.text === "")
+                            continue;
+                        var x = _this._viewer.mousex;
+                        var y = _this._viewer.mousey;
+                        if (element.node.rect.isPointInside(x, y)) {
+                            element.node.rect.attr("fill", _this.overColor);
+                        }
+                        else {
+                            element.node.rect.attr("fill", _this.savedColor);
+                        }
+                    }
+                }
+            };
+            var onRightClick = function (event) {
+                var x = _this._viewer.mousex;
+                var y = _this._viewer.mousey;
+                _this._viewer.onClick(event);
+                // Set selected node
+                var result = _this._viewer.traverseGraph(null, x, y, true);
+                if (result.hit) {
+                }
+                // Properly draw the context menu on the screen
+                if (y + (ActionsBuilder.Viewer.NODE_HEIGHT * _this.elements.length) > _this._viewer.viewerElement.offsetHeight + _this._viewer.viewerElement.scrollTop) {
+                    y = (ActionsBuilder.Viewer.NODE_HEIGHT * _this.elements.length);
+                }
+                if (x + ActionsBuilder.Viewer.NODE_WIDTH > _this._viewer.viewerElement.offsetWidth + _this._viewer.viewerElement.scrollLeft) {
+                    x -= ActionsBuilder.Viewer.NODE_WIDTH;
+                }
+                if (!_this.showing) {
+                    if (_this._viewer.selectedNode === null)
+                        return;
+                    // Create elements
+                    var yOffset = 10;
+                    for (var i = 0; i < _this.elements.length - 1; i++) {
+                        var element = _this.elements[i];
+                        element.node = _this._viewer._createNode(element.text, ActionsBuilder.Type.OBJECT, true);
+                        element.node.rect.attr("fill", Raphael.rgb(216, 216, 216));
+                        element.node.rect.attr("x", x);
+                        element.node.rect.attr("y", y + yOffset);
+                        element.node.text.attr("x", x + 5);
+                        element.node.text.attr("y", y + yOffset + element.node.rect.attr("height") / 2);
+                        yOffset += ActionsBuilder.Viewer.NODE_HEIGHT;
+                    }
+                    // Color separator
+                    var separator = _this.elements[_this.elements.length - 1];
+                    separator.node = _this._viewer._createNode("", ActionsBuilder.Type.OBJECT, true);
+                    separator.node.rect.attr("fill", _this._viewer.getNodeColor(_this._viewer.selectedNode.type, _this._viewer.selectedNode.node.detached));
+                    separator.node.rect.attr("x", x);
+                    separator.node.rect.attr("y", y);
+                    separator.node.rect.attr("height", 10);
+                    // Finish
+                    _this.showing = true;
+                }
+                else {
+                    onClick(event);
+                    onRightClick(event);
+                }
+                window.event.returnValue = false;
+            };
+            document.addEventListener("click", onClick);
+            document.addEventListener("mousemove", onMouseMove);
+            element.addEventListener("contextmenu", onRightClick);
+        };
+        return ContextMenu;
+    }());
+    ActionsBuilder.ContextMenu = ContextMenu;
+})(ActionsBuilder || (ActionsBuilder = {}));
+//# sourceMappingURL=actionsbuilder.contextMenu.js.map

+ 352 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.js

@@ -0,0 +1,352 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    /**
+    * Defines static types
+    */
+    var Type = (function () {
+        function Type() {
+        }
+        Object.defineProperty(Type, "TRIGGER", {
+            get: function () {
+                return Type._TRIGGER;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Type, "ACTION", {
+            get: function () {
+                return Type._ACTION;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Type, "FLOW_CONTROL", {
+            get: function () {
+                return Type._FLOW_CONTROL;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Type, "OBJECT", {
+            get: function () {
+                return Type._OBJECT;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Type, "SCENE", {
+            get: function () {
+                return Type._SCENE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        return Type;
+    }());
+    Type._TRIGGER = 0;
+    Type._ACTION = 1;
+    Type._FLOW_CONTROL = 2;
+    Type._OBJECT = 3;
+    Type._SCENE = 4;
+    ActionsBuilder.Type = Type;
+    /*
+    * Defines the BABYLON.JS elements
+    */
+    var SceneElements = (function () {
+        function SceneElements() {
+        }
+        Object.defineProperty(SceneElements, "ENGINE", {
+            get: function () {
+                return SceneElements._ENGINE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "SCENE", {
+            get: function () {
+                return SceneElements._SCENE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "MESH", {
+            get: function () {
+                return SceneElements._MESH;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "LIGHT", {
+            get: function () {
+                return SceneElements._LIGHT;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "CAMERA", {
+            get: function () {
+                return SceneElements._CAMERA;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "MESHES", {
+            get: function () {
+                return SceneElements._MESHES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "LIGHTS", {
+            get: function () {
+                return SceneElements._LIGHTS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "CAMERAS", {
+            get: function () {
+                return SceneElements._CAMERAS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "SOUNDS", {
+            get: function () {
+                return SceneElements._SOUNDS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "MESH_PROPERTIES", {
+            get: function () {
+                return SceneElements._MESH_PROPERTIES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "LIGHT_PROPERTIES", {
+            get: function () {
+                return SceneElements._LIGHT_PROPERTIES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "CAMERA_PROPERTIES", {
+            get: function () {
+                return SceneElements._CAMERA_PROPERTIES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "SCENE_PROPERTIES", {
+            get: function () {
+                return SceneElements._SCENE_PROPERTIES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "TYPES", {
+            get: function () {
+                return SceneElements._TYPES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "OPERATORS", {
+            get: function () {
+                return SceneElements._OPERATORS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /*
+        * Methods
+        */
+        SceneElements.GetInstanceOf = function (object) {
+            if (object === null || object === undefined) {
+                return "";
+            }
+            return object.constructor.toString().match(/function (\w*)/)[1];
+        };
+        SceneElements.TestInstanceOf = function (object, propertyName) {
+            if (object === null || object.constructor === null) {
+                return false;
+            }
+            if (propertyName.length > 0 && propertyName[0] === "_")
+                return false;
+            var name = SceneElements.GetInstanceOf(object);
+            for (var i = 0; i < SceneElements.TYPES.length; i++) {
+                if (name === SceneElements.TYPES[i]) {
+                    return true;
+                }
+            }
+            return false;
+        };
+        return SceneElements;
+    }());
+    /*
+    * BabylonJS objects
+    */
+    SceneElements._ENGINE = new BABYLON.Engine(document.getElementById("RenderCanvasID"));
+    SceneElements._SCENE = new BABYLON.Scene(SceneElements.ENGINE);
+    SceneElements._MESH = new BABYLON.Mesh("mesh", SceneElements._SCENE);
+    SceneElements._LIGHT = new BABYLON.Light("light", SceneElements._SCENE);
+    SceneElements._CAMERA = new BABYLON.Camera("camera", BABYLON.Vector3.Zero(), SceneElements._SCENE);
+    /*
+    * Objects names
+    */
+    SceneElements._MESHES = new Array();
+    SceneElements._LIGHTS = new Array();
+    SceneElements._CAMERAS = new Array();
+    SceneElements._SOUNDS = new Array();
+    /*
+    * Properties
+    */
+    SceneElements._MESH_PROPERTIES = new Array();
+    SceneElements._LIGHT_PROPERTIES = new Array();
+    SceneElements._CAMERA_PROPERTIES = new Array();
+    SceneElements._SCENE_PROPERTIES = new Array();
+    /*
+    * Types
+    */
+    SceneElements._TYPES = new Array();
+    /*
+    * Operators
+    */
+    SceneElements._OPERATORS = new Array();
+    ActionsBuilder.SceneElements = SceneElements;
+    // Functions
+    var specialTypes = [
+        "StandardMaterial"
+    ];
+    SceneElements.MESH.material = new BABYLON.StandardMaterial("material", SceneElements.SCENE);
+    var addSpecialType = function (object, properties, thing) {
+        for (var specialThing in object[thing]) {
+            if (object[thing].hasOwnProperty(specialThing) && SceneElements.TestInstanceOf(object[thing][specialThing], specialThing)) {
+                properties.push(thing + "." + specialThing);
+            }
+        }
+    };
+    // Configure types
+    SceneElements.TYPES.push("Color3");
+    SceneElements.TYPES.push("Boolean");
+    SceneElements.TYPES.push("Number");
+    SceneElements.TYPES.push("Vector2");
+    SceneElements.TYPES.push("Vector3");
+    SceneElements.TYPES.push("String");
+    // Configure operators
+    SceneElements.OPERATORS.push("IsEqual");
+    SceneElements.OPERATORS.push("IsDifferent");
+    SceneElements.OPERATORS.push("IsGreater");
+    SceneElements.OPERATORS.push("IsLesser");
+    // Configure properties
+    for (var thing in SceneElements.MESH) {
+        var instance = SceneElements.GetInstanceOf(SceneElements.MESH[thing]);
+        if (SceneElements.MESH.hasOwnProperty(thing)) {
+            if (specialTypes.indexOf(instance) !== -1) {
+                addSpecialType(SceneElements.MESH, SceneElements.MESH_PROPERTIES, thing);
+            }
+            else if (SceneElements.TestInstanceOf(SceneElements.MESH[thing], thing)) {
+                SceneElements.MESH_PROPERTIES.push(thing);
+            }
+        }
+    }
+    for (var thing in SceneElements.LIGHT) {
+        if (SceneElements.LIGHT.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.LIGHT[thing], thing)) {
+            SceneElements.LIGHT_PROPERTIES.push(thing);
+        }
+    }
+    for (var thing in SceneElements.CAMERA) {
+        if (SceneElements.CAMERA.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.CAMERA[thing], thing)) {
+            SceneElements.CAMERA_PROPERTIES.push(thing);
+        }
+    }
+    for (var thing in SceneElements.SCENE) {
+        if (SceneElements.SCENE.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.SCENE[thing], thing)) {
+            SceneElements.SCENE_PROPERTIES.push(thing);
+        }
+    }
+    /**
+    * Actions Builder elements (triggers, actions & flow controls) that are
+    * arrays of Element
+    */
+    var Elements = (function () {
+        function Elements() {
+        }
+        Object.defineProperty(Elements, "TRIGGERS", {
+            get: function () {
+                return Elements._TRIGGERS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Elements, "ACTIONS", {
+            get: function () {
+                return Elements._ACTIONS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Elements, "FLOW_CONTROLS", {
+            get: function () {
+                return Elements._FLOW_CONTROLS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Elements.GetElementFromName = function (name) {
+            for (var i = 0; i < Elements.TRIGGERS.length; i++) {
+                if (Elements.TRIGGERS[i].name === name) {
+                    return Elements._TRIGGERS[i];
+                }
+            }
+            for (var i = 0; i < Elements.ACTIONS.length; i++) {
+                if (Elements.ACTIONS[i].name === name) {
+                    return Elements._ACTIONS[i];
+                }
+            }
+            for (var i = 0; i < Elements.FLOW_CONTROLS.length; i++) {
+                if (Elements.FLOW_CONTROLS[i].name === name) {
+                    return Elements._FLOW_CONTROLS[i];
+                }
+            }
+            return null;
+        };
+        return Elements;
+    }());
+    Elements._TRIGGERS = new Array();
+    Elements._ACTIONS = new Array();
+    Elements._FLOW_CONTROLS = new Array();
+    ActionsBuilder.Elements = Elements;
+    // Configure triggers
+    Elements.TRIGGERS.push({ name: "OnPickTrigger", text: "pick", properties: [], description: "When the user picks the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnLeftPickTrigger", text: "left pick", properties: [], description: "When the user picks the edited mesh using the left click" });
+    Elements.TRIGGERS.push({ name: "OnRightPickTrigger", text: "right pick", properties: [], description: "When the user picks the edited mesh using the right click" });
+    Elements.TRIGGERS.push({ name: "OnCenterPickTrigger", text: "center pick", properties: [], description: "When the user picks the edited mesh using the click of the mouse wheel" });
+    Elements.TRIGGERS.push({ name: "OnPointerOverTrigger", text: "pointer over", properties: [], description: "When the user's mouse is over the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnPointerOutTrigger", text: "pointer out", properties: [], description: "When the user's mouse is out of the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnEveryFrameTrigger", text: "every frame", properties: [], description: "This trigger is called each frame (only on scene)" });
+    Elements.TRIGGERS.push({ name: "OnIntersectionEnterTrigger", text: "intersection enter", properties: [{ targetType: "MeshProperties", text: "parameter", value: "Object name?" }], description: "When the edited mesh intersects the another mesh predefined in the options" });
+    Elements.TRIGGERS.push({ name: "OnIntersectionExitTrigger", text: "intersection exit", properties: [{ targetType: "MeshProperties", text: "parameter", value: "Object name?" }], description: "When the edited mesh exits intersection with the another mesh predefined in the options" });
+    Elements.TRIGGERS.push({ name: "OnKeyDownTrigger", text: "key down", properties: [{ targetType: null, text: "parameter", value: "a" }], description: "When the user pressed a key (enter the key character, example: \"r\")" });
+    Elements.TRIGGERS.push({ name: "OnKeyUpTrigger", text: "key up", properties: [{ targetType: null, text: "parameter", value: "a" }], description: "When the user unpressed a key (enter the key character, example: \"p\")" });
+    // Configure actions
+    Elements.ACTIONS.push({ name: "SwitchBooleanAction", text: "switch boolean", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }], description: "Switches the boolean value of a given parameter of the target object: true to false, or false to true" });
+    Elements.ACTIONS.push({ name: "SetStateAction", text: "set state", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "value", value: "" }], description: "Sets a new state value for the target object (example: \"off\" or \"on\")" });
+    Elements.ACTIONS.push({ name: "SetValueAction", text: "set value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }], description: "Sets a new value to the specified parameter of the target object (example: position.x to 0.0)" });
+    Elements.ACTIONS.push({ name: "SetParentAction", text: "set parent", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "parent", value: "" }], description: "Sets the new parent of the target object (example: a mesh or a light)" });
+    Elements.ACTIONS.push({ name: "IncrementValueAction", text: "increment value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }], description: "Increments the value of the given parameter of the target object. The value can be negative. (example: increment position.x of 5.0)" });
+    Elements.ACTIONS.push({ name: "PlayAnimationAction", text: "play animation", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "from", value: "0" }, { text: "to", value: "150" }, { text: "loop", value: "false" }], description: "Plays an animation of the target object. Specify the start frame, the end frame and if the animation should loop." });
+    Elements.ACTIONS.push({ name: "StopAnimationAction", text: "stop animation", properties: [{ targetType: "MeshProperties", text: "target", value: "" }], description: "Stops the animations of the target object." });
+    Elements.ACTIONS.push({ name: "DoNothingAction", text: "do nothing", properties: [], description: "Does nothing, can be used to balance/equilibrate the actions graph." });
+    Elements.ACTIONS.push({ name: "InterpolateValueAction", text: "interpolate value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "0" }, { text: "duration", value: "1000" }, { text: "stopOtherAnimations", value: "false" }], description: "Creates an animation (key frames) that animates the target object by interpolating the given parameter of the target value." });
+    Elements.ACTIONS.push({ name: "PlaySoundAction", text: "play sound", properties: [{ text: "sound", value: "" }], description: "Plays the specified sound." });
+    Elements.ACTIONS.push({ name: "StopSoundAction", text: "stop sound", properties: [{ text: "sound", value: "" }], description: "Stops the specified sound." });
+    Elements.ACTIONS.push({ name: "CombineAction", text: "combine", properties: [], description: "Special action that combines multiple actions. The combined actions are executed at the same time. Drag'n'drop the new actions inside to combine actions." });
+    // Configure flow control
+    Elements.FLOW_CONTROLS.push({ name: "ValueCondition", text: "value condition", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }, { text: "operator", value: SceneElements.OPERATORS[0] }], description: "A condition checking if a given value is equal, different, lesser or greater than the given parameter of the target object" });
+    Elements.FLOW_CONTROLS.push({ name: "StateCondition", text: "state condition", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "value", value: "" }], description: "A condition checking if the target object's state is equal to the given state. See \"set state\" action to set a state to an object." });
+    Elements.FLOW_CONTROLS.push({ name: "Hub", text: "hub", properties: [], description: "The hub is internally used by the Combine Action. It allows to add children to the Combine Action" });
+})(ActionsBuilder || (ActionsBuilder = {}));
+//# sourceMappingURL=actionsbuilder.js.map

+ 256 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.list.js

@@ -0,0 +1,256 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var ListElement = (function () {
+        function ListElement() {
+            this.rect = null;
+            this.text = null;
+            this.name = "";
+            this.type = ActionsBuilder.Type.TRIGGER;
+            this.element = null;
+        }
+        return ListElement;
+    }());
+    ActionsBuilder.ListElement = ListElement;
+    var List = (function () {
+        /**
+        * Constructor
+        */
+        function List(viewer) {
+            var _this = this;
+            this._listElements = new Array();
+            // Get HTML elements
+            this.listElement = document.getElementById("ListsElementID");
+            this.triggersElement = document.getElementById("TriggersListID");
+            this.actionsElement = document.getElementById("ActionsListID");
+            this.flowControlsElement = document.getElementById("FlowActionsListID");
+            this._parentContainer = document.getElementById("ParentContainerID");
+            // Configure this
+            this._viewer = viewer;
+            // Create elements (lists)
+            this.triggersList = Raphael("TriggersListID", (25 * screen.width) / 100, 400);
+            this.actionsList = Raphael("ActionsListID", (25 * screen.width) / 100, 400);
+            this.flowControlsList = Raphael("FlowActionsListID", (25 * screen.width) / 100, 400);
+            // Manage events
+            window.addEventListener("resize", function (event) {
+                _this.onResize(event);
+            });
+        }
+        Object.defineProperty(List, "ELEMENT_HEIGHT", {
+            get: function () {
+                return 25;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+        * Resize event that resizes the list element dynamically
+        * @param event: the resize event
+        */
+        List.prototype.onResize = function (event) {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.listElement.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 + "px";
+            var listElementWidth = this.listElement.getBoundingClientRect().width;
+            for (var i = 0; i < this._listElements.length; i++) {
+                var rect = this._listElements[i].rect;
+                rect.attr("width", listElementWidth - 40);
+            }
+            this.triggersList.setSize(listElementWidth, this.triggersList.height);
+            this.actionsList.setSize(listElementWidth, this.triggersList.height);
+            this.flowControlsList.setSize(listElementWidth, this.triggersList.height);
+        };
+        List.prototype.createListsElements = function () {
+            var excludedTriggers = [6, 9, 10];
+            var yPosition = 10;
+            var textColor = Raphael.rgb(61, 72, 76);
+            var whiteColor = Raphael.rgb(255, 255, 255);
+            var configureTitle = function (listElement, rectColor) {
+                listElement.text.attr("x", 15);
+                listElement.rect.attr("fill", rectColor);
+                listElement.text.attr("font-family", "Sinkin Sans Medium");
+                listElement.text.attr("font-size", "11");
+            };
+            // Create triggers
+            var triggers = this._createListElement(this.triggersList, yPosition, "TRIGGERS", ActionsBuilder.Type.TRIGGER, whiteColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(triggers, Raphael.rgb(41, 129, 255));
+            for (var i = 0; i < ActionsBuilder.Elements.TRIGGERS.length; i++) {
+                var element = ActionsBuilder.Elements.TRIGGERS[i];
+                if (this._viewer.root.type === ActionsBuilder.Type.OBJECT && excludedTriggers.indexOf(i) !== -1) {
+                    continue;
+                }
+                else if (this._viewer.root.type === ActionsBuilder.Type.SCENE && excludedTriggers.indexOf(i) === -1) {
+                    continue;
+                }
+                var trigger = this._createListElement(this.triggersList, yPosition, element.text, ActionsBuilder.Type.TRIGGER, textColor, true, element);
+                trigger.rect.attr("fill", Raphael.rgb(133, 154, 185));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+            yPosition += List.ELEMENT_HEIGHT;
+            this.triggersElement.style.height = this.triggersList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.triggersList, this.triggersElement, triggers, yPosition);
+            // Create actions
+            yPosition = 10;
+            var actions = this._createListElement(this.actionsList, yPosition, "ACTIONS", ActionsBuilder.Type.ACTION, textColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(actions, Raphael.rgb(255, 220, 42));
+            for (var i = 0; i < ActionsBuilder.Elements.ACTIONS.length; i++) {
+                var element = ActionsBuilder.Elements.ACTIONS[i];
+                var action = this._createListElement(this.actionsList, yPosition, element.text, ActionsBuilder.Type.ACTION, textColor, true, element);
+                action.rect.attr("fill", Raphael.rgb(182, 185, 132));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+            yPosition += List.ELEMENT_HEIGHT;
+            this.actionsElement.style.height = this.actionsList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.actionsList, this.actionsElement, actions, yPosition);
+            // Create flow controls
+            yPosition = 10;
+            var flowControls = this._createListElement(this.flowControlsList, yPosition, "FLOW CONTROLS", ActionsBuilder.Type.FLOW_CONTROL, whiteColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(flowControls, Raphael.rgb(255, 41, 53));
+            for (var i = 0; i < ActionsBuilder.Elements.FLOW_CONTROLS.length - 1; i++) {
+                var element = ActionsBuilder.Elements.FLOW_CONTROLS[i];
+                var flowControl = this._createListElement(this.flowControlsList, yPosition, element.text, ActionsBuilder.Type.FLOW_CONTROL, textColor, true, element);
+                flowControl.rect.attr("fill", Raphael.rgb(185, 132, 140));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+            yPosition += List.ELEMENT_HEIGHT;
+            this.flowControlsElement.style.height = this.flowControlsList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.flowControlsList, this.flowControlsElement, flowControls, yPosition);
+        };
+        /**
+        * Clears the list of elements and removes the elements
+        */
+        List.prototype.clearLists = function () {
+            for (var i = 0; i < this._listElements.length; i++) {
+                this._removeListElement(this._listElements[i]);
+            }
+            this._listElements.splice(0, this._listElements.length - 1);
+        };
+        /**
+        * Sets the color theme of the lists
+        * @param color: the theme color
+        */
+        List.prototype.setColorTheme = function (color) {
+            this.triggersList.canvas.style.backgroundColor = color;
+            this.actionsList.canvas.style.backgroundColor = color;
+            this.flowControlsList.canvas.style.backgroundColor = color;
+        };
+        /**
+        * Creates a list element
+        * @param paper: the Raphael.js paper
+        * @param yPosition: the y position of the element
+        * @param text: the element text
+        * @param type: the element type (trigger, action, flow control)
+        * @param textColor: the text color
+        * @param drag: if the element should be drag'n'dropped
+        */
+        List.prototype._createListElement = function (paper, yPosition, text, type, textColor, drag, element) {
+            var object = new ListElement();
+            object.rect = paper.rect(10, yPosition, 300, List.ELEMENT_HEIGHT);
+            object.text = paper.text(30, yPosition + object.rect.attr("height") / 2, text);
+            object.text.attr("fill", textColor);
+            object.text.attr("text-anchor", "start");
+            object.text.attr("font-size", "12");
+            object.text.attr("text-anchor", "start");
+            object.text.attr("font-family", "Sinkin Sans Light");
+            if (drag) {
+                this._createListElementAnimation(object);
+            }
+            object.type = type;
+            object.element = element;
+            this._listElements.push(object);
+            return object;
+        };
+        /**
+        * Removes a list element
+        * @param element: the element to remove
+        */
+        List.prototype._removeListElement = function (element) {
+            element.rect.remove();
+            element.text.remove();
+        };
+        /*
+        * Creates the collapse animation of a list
+        * @param paper: the list paper
+        * @param htmlElement: the list div container
+        * @param element: the list element to click on
+        * @param expandedHeight: the height when the list is expanded
+        */
+        List.prototype._createCollapseAnimation = function (paper, htmlElement, element, expandedHeight) {
+            var onClick = function (event) {
+                var height = htmlElement.style.height;
+                if (height === expandedHeight + "px") {
+                    htmlElement.style.height = paper.canvas.style.height = 35 + "px";
+                }
+                else {
+                    htmlElement.style.height = paper.canvas.style.height = expandedHeight + "px";
+                }
+            };
+            element.rect.click(onClick);
+        };
+        /*
+        * Creates the animation of a list element
+        * @param element: the list element to animate
+        */
+        List.prototype._createListElementAnimation = function (element) {
+            var _this = this;
+            var onMove = function (dx, dy, x, y) { };
+            var onStart = function (x, y, event) {
+                _this._parentContainer.style.cursor = "copy";
+                element.rect.animate({
+                    x: -10,
+                    opacity: 0.25
+                }, 500, ">");
+                element.text.animate({
+                    x: 10,
+                    opacity: 0.25
+                }, 500, ">");
+            };
+            var onEnd = function (event) {
+                _this._parentContainer.style.cursor = "default";
+                element.rect.animate({
+                    x: 10,
+                    opacity: 1.0
+                }, 500, "<");
+                element.text.animate({
+                    x: 30,
+                    opacity: 1.0
+                }, 500, "<");
+                var dragResult = _this._viewer.traverseGraph(null, _this._viewer.mousex, _this._viewer.mousey, false);
+                if (dragResult.hit) {
+                    if (element.type === ActionsBuilder.Type.TRIGGER && dragResult.action !== _this._viewer.root) {
+                        alert("Triggers can be dragged only on the root node (the mesh)");
+                        return;
+                    }
+                    if (element.type === ActionsBuilder.Type.ACTION && dragResult.action === _this._viewer.root) {
+                        alert("Please add a trigger before.");
+                        return;
+                    }
+                    //if (element.type === Type.FLOW_CONTROL && (dragResult.action === this._viewer.root || (dragResult.action.type === Type.FLOW_CONTROL && dragResult.action.parent.hub === null))) {
+                    if (element.type === ActionsBuilder.Type.FLOW_CONTROL && dragResult.action === _this._viewer.root) {
+                        return;
+                    }
+                    if (element.type === ActionsBuilder.Type.FLOW_CONTROL && dragResult.action.combineArray !== null) {
+                        alert("A condition cannot be handled by a Combine Action.");
+                        return;
+                    }
+                    if ((element.type === ActionsBuilder.Type.FLOW_CONTROL || element.type === ActionsBuilder.Type.ACTION) && dragResult.action.type === ActionsBuilder.Type.TRIGGER && dragResult.action.children.length > 0) {
+                        alert("Triggers can have only one child. Please add another trigger of same type.");
+                        return;
+                    }
+                    if (!(dragResult.action.combineArray !== null) && dragResult.action.children.length > 0 && dragResult.action.type !== ActionsBuilder.Type.TRIGGER && dragResult.action !== _this._viewer.root) {
+                        alert("An action can have only one child.");
+                        return;
+                    }
+                    _this._viewer.addAction(dragResult.action, element.type, element.element);
+                    _this._viewer.update();
+                }
+            };
+            element.rect.drag(onMove, onStart, onEnd);
+            element.text.drag(onMove, onStart, onEnd);
+        };
+        return List;
+    }());
+    ActionsBuilder.List = List;
+})(ActionsBuilder || (ActionsBuilder = {}));
+//# sourceMappingURL=actionsbuilder.list.js.map

+ 108 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.main.js

@@ -0,0 +1,108 @@
+/*
+Global functions called by the plugins (3ds Max, etc.)
+*/
+// Elements
+var list = null;
+var viewer = null;
+var actionsBuilderJsonInput = document.getElementById("ActionsBuilderJSON");
+this.getList = function () {
+    return list;
+};
+this.getViewer = function () {
+    return viewer;
+};
+this.createJSON = function () {
+    var structure = viewer.utils.createJSON(viewer.root);
+    var asText = JSON.stringify(structure);
+    actionsBuilderJsonInput.value = asText;
+    console.log(asText);
+};
+this.loadFromJSON = function () {
+    var json = actionsBuilderJsonInput.value;
+    if (json !== "") {
+        var structure = JSON.parse(json);
+        viewer.utils.loadFromJSON(structure, null);
+    }
+};
+this.updateObjectName = function () {
+    var element = document.getElementById("ActionsBuilderObjectName");
+    var name = element.value;
+    viewer.objectName = name;
+    if (viewer.root.type === ActionsBuilder.Type.OBJECT) {
+        name += " - Mesh";
+    }
+    else {
+        name += " - Scene";
+    }
+    viewer.root.node.text.attr("text", name);
+};
+this.resetList = function () {
+    list.clearLists();
+    list.createListsElements();
+};
+this.setMeshesNames = function () {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.MESHES.push(args[i]);
+    }
+};
+this.setLightsNames = function () {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.LIGHTS.push(args[i]);
+    }
+};
+this.setCamerasNames = function () {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.CAMERAS.push(args[i]);
+    }
+};
+this.setSoundsNames = function () {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        var sound = args[i];
+        if (sound !== "" && ActionsBuilder.SceneElements.SOUNDS.indexOf(sound) === -1) {
+            ActionsBuilder.SceneElements.SOUNDS.push(args[i]);
+        }
+    }
+};
+this.hideButtons = function () {
+    // Empty
+};
+this.setIsObject = function () {
+    viewer.root.type = ActionsBuilder.Type.OBJECT;
+};
+this.setIsScene = function () {
+    viewer.root.type = ActionsBuilder.Type.SCENE;
+};
+this.run = function () {
+    // Configure viewer
+    viewer = new ActionsBuilder.Viewer(ActionsBuilder.Type.OBJECT);
+    viewer.setColorTheme("-ms-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("-webkit-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("-o-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.onResize();
+    viewer.update();
+    // Configure list
+    list = new ActionsBuilder.List(viewer);
+    list.setColorTheme("rgb(64, 64, 64)");
+    list.createListsElements();
+    list.onResize();
+    // 3ds Max fix
+    viewer.onResize();
+};
+//# sourceMappingURL=actionsbuilder.main.js.map

Datei-Diff unterdrückt, da er zu groß ist
+ 2627 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.max.js


+ 562 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.parameters.js

@@ -0,0 +1,562 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Parameters = (function () {
+        /*
+        * Constructor
+        */
+        function Parameters(viewer) {
+            var _this = this;
+            this._action = null;
+            // Get HTML elements
+            this.parametersContainer = document.getElementById("ParametersElementID");
+            this.parametersHelpElement = document.getElementById("ParametersHelpElementID");
+            // Configure this
+            this._viewer = viewer;
+            // Configure events
+            window.addEventListener("resize", function (event) {
+                _this.onResize(event);
+            });
+        }
+        /*
+        * Clears the parameters fileds in the parameters view
+        */
+        Parameters.prototype.clearParameters = function () {
+            if (this.parametersContainer.children === null) {
+                return;
+            }
+            while (this.parametersContainer.children.length > 0) {
+                this.parametersContainer.removeChild(this.parametersContainer.firstChild);
+            }
+        };
+        /*
+        * Creates parameters fields
+        * @param action: the action to configure
+        */
+        Parameters.prototype.createParameters = function (action) {
+            // Clear parameters fields and draw help description
+            this._action = action;
+            this.clearParameters();
+            if (action === null) {
+                return;
+            }
+            this._createHelpSection(action);
+            this._createNodeSection(action);
+            // Get properties
+            var properties = action.properties;
+            var propertiesResults = action.propertiesResults;
+            var targetParameterSelect = null;
+            var targetParameterNameSelect = null;
+            var propertyPathSelect = null;
+            var propertyPathOptionalSelect = null;
+            var booleanSelect = null;
+            var propertyInput = null;
+            var propertyPathIndice = -1;
+            if (properties.length === 0) {
+                return;
+            }
+            // Draw properties
+            for (var i = 0; i < properties.length; i++) {
+                // Create separator
+                var separator = document.createElement("hr");
+                separator.noShade = true;
+                separator.className = "ParametersElementSeparatorClass";
+                this.parametersContainer.appendChild(separator);
+                // Create parameter text
+                var parameterName = document.createElement("a");
+                parameterName.text = properties[i].text;
+                parameterName.className = "ParametersElementTitleClass";
+                this.parametersContainer.appendChild(parameterName);
+                if (properties[i].text === "parameter" || properties[i].text === "target" || properties[i].text === "parent") {
+                    if (properties[i].targetType === null) {
+                        var parameterInput = document.createElement("input");
+                        parameterInput.value = propertiesResults[i].value;
+                        parameterInput.className = "ParametersElementInputClass";
+                        this.parametersContainer.appendChild(parameterInput);
+                        // Configure event
+                        parameterInput.onkeyup = this._propertyInputChanged(parameterInput, i);
+                    }
+                    else {
+                        // Create target select element
+                        targetParameterSelect = document.createElement("select");
+                        targetParameterSelect.className = "ParametersElementSelectClass";
+                        this.parametersContainer.appendChild(targetParameterSelect);
+                        // Create target name select element
+                        targetParameterNameSelect = document.createElement("select");
+                        targetParameterNameSelect.className = "ParametersElementSelectClass";
+                        this.parametersContainer.appendChild(targetParameterNameSelect);
+                        // Events and configure
+                        (this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i))(null);
+                        targetParameterSelect.value = propertiesResults[i].targetType;
+                        targetParameterNameSelect.value = propertiesResults[i].value;
+                        targetParameterSelect.onchange = this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i);
+                        targetParameterNameSelect.onchange = this._parameterTargetNameChanged(targetParameterSelect, targetParameterNameSelect, i);
+                    }
+                }
+                else if (properties[i].text === "propertyPath") {
+                    propertyPathIndice = i;
+                    // Create property path select
+                    propertyPathSelect = document.createElement("select");
+                    propertyPathSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(propertyPathSelect);
+                    // Create additional select
+                    propertyPathOptionalSelect = document.createElement("select");
+                    propertyPathOptionalSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(propertyPathOptionalSelect);
+                    // Events and configure
+                    (this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, null, null, i))(null);
+                    var property = this._action.propertiesResults[i].value.split(".");
+                    if (property.length > 0) {
+                        if (property.length === 1) {
+                            propertyPathSelect.value = property[0];
+                        }
+                        else {
+                            var completePropertyPath = "";
+                            for (var j = 0; j < property.length - 1; j++) {
+                                completePropertyPath += property[j];
+                                completePropertyPath += (j === property.length - 2) ? "" : ".";
+                            }
+                            propertyPathSelect.value = completePropertyPath;
+                            this._viewer.utils.setElementVisible(propertyPathOptionalSelect, true);
+                        }
+                        this._fillAdditionalPropertyPath(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect);
+                        propertyPathOptionalSelect.value = property[property.length - 1];
+                        if (propertyPathOptionalSelect.options.length === 0 || propertyPathOptionalSelect.options[0].textContent === "") {
+                            this._viewer.utils.setElementVisible(propertyPathOptionalSelect, false);
+                        }
+                    }
+                    targetParameterSelect.onchange = this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i - 1);
+                    propertyPathSelect.onchange = this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, null, null, i);
+                    propertyPathOptionalSelect.onchange = this._additionalPropertyPathSelectChanged(propertyPathSelect, propertyPathOptionalSelect, i);
+                }
+                else if (properties[i].text === "operator") {
+                    var conditionOperatorSelect = document.createElement("select");
+                    conditionOperatorSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(conditionOperatorSelect);
+                    // Configure event
+                    (this._conditionOperatorSelectChanged(conditionOperatorSelect, i))(null);
+                    conditionOperatorSelect.value = propertiesResults[i].value;
+                    conditionOperatorSelect.onchange = this._conditionOperatorSelectChanged(conditionOperatorSelect, i);
+                }
+                else if (properties[i].text === "sound") {
+                    var soundSelect = document.createElement("select");
+                    soundSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(soundSelect);
+                    // Configure event
+                    (this._soundSelectChanged(soundSelect, i))(null);
+                    soundSelect.value = propertiesResults[i].value;
+                    soundSelect.onchange = this._soundSelectChanged(soundSelect, i);
+                }
+                else {
+                    var isBoolean = propertiesResults[i].value === "true" || propertiesResults[i].value === "false";
+                    var object = this._getObjectFromType(targetParameterSelect.value);
+                    if (object !== null) {
+                        var property = this._action.propertiesResults[i - 1].value.split(".");
+                        for (var j = 0; j < property.length && object !== undefined; j++) {
+                            object = object[property[j]];
+                            if (j === property.length - 1) {
+                                isBoolean = isBoolean || typeof object === "boolean";
+                            }
+                        }
+                    }
+                    booleanSelect = document.createElement("select");
+                    booleanSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(booleanSelect);
+                    // Configure event
+                    (this._booleanSelectChanged(booleanSelect, i))(null);
+                    booleanSelect.value = propertiesResults[i].value;
+                    booleanSelect.onchange = this._booleanSelectChanged(booleanSelect, i);
+                    propertyInput = document.createElement("input");
+                    propertyInput.value = propertiesResults[i].value;
+                    propertyInput.className = "ParametersElementInputClass";
+                    this.parametersContainer.appendChild(propertyInput);
+                    // Configure event
+                    propertyInput.onkeyup = this._propertyInputChanged(propertyInput, i);
+                    if (propertyPathIndice !== -1 && properties[i].text === "value") {
+                        propertyPathSelect.onchange = this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, booleanSelect, propertyInput, propertyPathIndice);
+                    }
+                    if (isBoolean) {
+                        this._viewer.utils.setElementVisible(booleanSelect, true);
+                        this._viewer.utils.setElementVisible(propertyInput, false);
+                    }
+                    else {
+                        this._viewer.utils.setElementVisible(booleanSelect, false);
+                        this._viewer.utils.setElementVisible(propertyInput, true);
+                    }
+                }
+            }
+        };
+        /*
+        * Resizes the parameters view
+        * @param: the resize event
+        */
+        Parameters.prototype.onResize = function (event) {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.parametersContainer.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 200 + "px";
+            this.parametersHelpElement.style.height = 200 + "px";
+        };
+        /*
+        * Returns the boolean select change event
+        * @param booleanSelect: the boolean select element
+        * @param indice: the properties result indice
+        */
+        Parameters.prototype._booleanSelectChanged = function (booleanSelect, indice) {
+            var _this = this;
+            return function (ev) {
+                if (booleanSelect.options.length === 0) {
+                    var values = ["true", "false"];
+                    for (var i = 0; i < values.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = values[i];
+                        booleanSelect.add(option);
+                    }
+                }
+                else {
+                    _this._action.propertiesResults[indice].value = booleanSelect.value;
+                }
+            };
+        };
+        /*
+        * Returns the sound select change event
+        * @param soundSelect: the sound select element
+        * @param indice: the properties result indice
+        */
+        Parameters.prototype._soundSelectChanged = function (soundSelect, indice) {
+            var _this = this;
+            return function (ev) {
+                if (soundSelect.options.length === 0) {
+                    for (var i = 0; i < ActionsBuilder.SceneElements.SOUNDS.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = ActionsBuilder.SceneElements.SOUNDS[i];
+                        soundSelect.add(option);
+                    }
+                    _this._sortList(soundSelect);
+                }
+                else {
+                    _this._action.propertiesResults[indice].value = soundSelect.value;
+                }
+            };
+        };
+        /*
+        * Returns the condition opeator select changed event
+        * @param conditionOperatorSelect: the condition operator select element
+        * @param indice: the properties result indice
+        */
+        Parameters.prototype._conditionOperatorSelectChanged = function (conditionOperatorSelect, indice) {
+            var _this = this;
+            return function (ev) {
+                if (conditionOperatorSelect.options.length === 0) {
+                    for (var i = 0; i < ActionsBuilder.SceneElements.OPERATORS.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = ActionsBuilder.SceneElements.OPERATORS[i];
+                        //conditionOperatorSelect.options.add(option);
+                        conditionOperatorSelect.add(option);
+                    }
+                }
+                else {
+                    _this._action.propertiesResults[indice].value = conditionOperatorSelect.value;
+                }
+            };
+        };
+        /*
+        * Returns the property input changed event
+        * @param propertyInput: the property input
+        * @param indice: the properties result indice
+        */
+        Parameters.prototype._propertyInputChanged = function (propertyInput, indice) {
+            var _this = this;
+            return function (ev) {
+                _this._action.propertiesResults[indice].value = propertyInput.value;
+            };
+        };
+        /*
+        * Returns the propertyPath select changed event
+        * @param targetParameterSelect: the target/parameter select element
+        * @param propertyPathSelect: the propertyPath select element
+        * @param additionalPropertyPathSelect: the additional propertyPath select element
+        * @param indice: the properties indice in action.properties
+        */
+        Parameters.prototype._propertyPathSelectChanged = function (targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect, booleanSelect, propertyInput, indice) {
+            var _this = this;
+            return function (event) {
+                if (propertyPathSelect.options.length === 0) {
+                    // Configure start values
+                    var properties = _this._getPropertiesFromType(targetParameterSelect.value);
+                    if (properties !== null) {
+                        for (var i = 0; i < properties.length; i++) {
+                            var option = document.createElement("option");
+                            option.value = option.text = properties[i];
+                            propertyPathSelect.add(option);
+                        }
+                    }
+                }
+                else {
+                    // Set property
+                    _this._action.propertiesResults[indice].value = propertyPathSelect.value;
+                    if (booleanSelect !== null && propertyInput !== null) {
+                        var object = _this._getObjectFromType(targetParameterSelect.value);
+                        var isBoolean = false;
+                        if (object !== null) {
+                            var property = _this._action.propertiesResults[indice].value.split(".");
+                            for (var j = 0; j < property.length; j++) {
+                                object = object[property[j]];
+                                if (j === property.length - 1) {
+                                    isBoolean = isBoolean || typeof object === "boolean";
+                                }
+                            }
+                        }
+                        if (isBoolean) {
+                            _this._viewer.utils.setElementVisible(booleanSelect, true);
+                            _this._viewer.utils.setElementVisible(propertyInput, false);
+                        }
+                        else {
+                            _this._viewer.utils.setElementVisible(booleanSelect, false);
+                            _this._viewer.utils.setElementVisible(propertyInput, true);
+                        }
+                    }
+                }
+                // Configure addition property
+                _this._fillAdditionalPropertyPath(targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect);
+                // Sort
+                _this._sortList(propertyPathSelect);
+            };
+        };
+        Parameters.prototype._fillAdditionalPropertyPath = function (targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect) {
+            additionalPropertyPathSelect.options.length = 0;
+            var object = this._getObjectFromType(targetParameterSelect.value);
+            if (object !== null) {
+                var propertyPath = propertyPathSelect.value.split(".");
+                for (var i = 0; i < propertyPath.length; i++) {
+                    object = object[propertyPath[i]];
+                }
+            }
+            if (object === null || object === undefined || (typeof (object)).toLowerCase() === "string") {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, false);
+                return;
+            }
+            // Add options
+            var emptyOption = document.createElement("option");
+            emptyOption.value = emptyOption.text = "";
+            additionalPropertyPathSelect.add(emptyOption);
+            for (var thing in object) {
+                var type = ActionsBuilder.SceneElements.GetInstanceOf(object[thing]);
+                var index = ActionsBuilder.SceneElements.TYPES.indexOf(type);
+                if (index !== -1) {
+                    var option = document.createElement("option");
+                    option.value = option.text = thing;
+                    additionalPropertyPathSelect.add(option);
+                    emptyOption.text += thing + ", ";
+                }
+            }
+            if (additionalPropertyPathSelect.options.length === 0 || additionalPropertyPathSelect.options[0].textContent === "") {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, false);
+            }
+            else {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, true);
+            }
+        };
+        /*
+        * Returns the additional propertyPath select changed event
+        * @param propertyPathSelect: the propertyPath select element
+        * @param additionalPropertyPathSelect: the additional propertyPath select element
+        * @param indice: the properties indice in action.properties
+        */
+        Parameters.prototype._additionalPropertyPathSelectChanged = function (propertyPathSelect, additionalPropertyPathSelect, indice) {
+            var _this = this;
+            return function (event) {
+                var property = propertyPathSelect.value;
+                var additionalProperty = additionalPropertyPathSelect.value;
+                if (additionalProperty !== "") {
+                    property += ".";
+                    property += additionalPropertyPathSelect.value;
+                }
+                _this._action.propertiesResults[indice].value = property;
+            };
+        };
+        /*
+        * Returns the parameter/target select changed event
+        * @param targetParameterSelect: the target/parameter select element
+        * @param targetParameterNameSelect: the target/parameter name select element
+        * @param propertyPathSelect: the propertyPath select element
+        * @param additionalPropertyPathSelect: the additional propertyPath select element
+        * @param indice: the properties indice in action.properties
+        */
+        Parameters.prototype._parameterTargetChanged = function (targetParameterSelect, targetParameterNameSelect, propertyPathSelect, additionalPropertyPathSelect, indice) {
+            var _this = this;
+            return function (event) {
+                if (targetParameterSelect.options.length === 0) {
+                    // Configure start values
+                    var options = [
+                        { text: "Mesh", targetType: "MeshProperties" },
+                        { text: "Light", targetType: "LightProperties" },
+                        { text: "Camera", targetType: "CameraProperties" },
+                        { text: "Scene", targetType: "SceneProperties" }
+                    ];
+                    targetParameterSelect.options.length = 0;
+                    for (var i = 0; i < options.length; i++) {
+                        var option = document.createElement("option");
+                        option.text = options[i].text;
+                        option.value = options[i].targetType;
+                        targetParameterSelect.add(option);
+                    }
+                    targetParameterSelect.value = _this._action.propertiesResults[indice].targetType;
+                }
+                else {
+                    _this._action.propertiesResults[indice].targetType = targetParameterSelect.value;
+                    var names = _this._getListFromType(targetParameterSelect.value);
+                    if (names !== null && names.length > 0) {
+                        _this._action.propertiesResults[indice].value = names[0];
+                    }
+                    else {
+                        _this._action.propertiesResults[indice].value = "";
+                    }
+                    if (propertyPathSelect !== null) {
+                        _this._action.propertiesResults[indice + 1].value = ""; // propertyPath
+                    }
+                }
+                // Configure target names
+                var targetParameterProperties = _this._getTargetFromType(targetParameterSelect.value);
+                targetParameterNameSelect.options.length = 0;
+                if (targetParameterProperties !== null) {
+                    for (var i = 0; i < targetParameterProperties.length; i++) {
+                        var option = document.createElement("option");
+                        option.text = option.value = targetParameterProperties[i];
+                        targetParameterNameSelect.add(option);
+                    }
+                }
+                targetParameterNameSelect.value = _this._action.propertiesResults[indice].value;
+                // Clear property path
+                if (propertyPathSelect !== null) {
+                    propertyPathSelect.options.length = 0;
+                    additionalPropertyPathSelect.options.length = 0;
+                    _this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect, null, null, indice + 1)(null);
+                }
+                _this._sortList(targetParameterNameSelect);
+                _this._sortList(targetParameterSelect);
+            };
+        };
+        /*
+        * Returns the parameter/target name select changed
+        * @param indice: the properties indice to change
+        */
+        Parameters.prototype._parameterTargetNameChanged = function (targetParameterSelect, targetParameterNameSelect, indice) {
+            var _this = this;
+            return function (event) {
+                _this._action.propertiesResults[indice].value = targetParameterNameSelect.value;
+            };
+        };
+        /*
+        * Returns the array of objects names in function of its type
+        * @param type: the target type
+        */
+        Parameters.prototype._getTargetFromType = function (type) {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return ActionsBuilder.SceneElements.MESHES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return ActionsBuilder.SceneElements.LIGHTS;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return ActionsBuilder.SceneElements.CAMERAS;
+            }
+            return null;
+        };
+        /*
+        * Returns the properties in function of its type
+        * @param type: the target type
+        */
+        Parameters.prototype._getPropertiesFromType = function (type) {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return ActionsBuilder.SceneElements.MESH_PROPERTIES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return ActionsBuilder.SceneElements.LIGHT_PROPERTIES;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return ActionsBuilder.SceneElements.CAMERA_PROPERTIES;
+            }
+            if (type === "SceneProperties" || type === "Scene") {
+                return ActionsBuilder.SceneElements.SCENE_PROPERTIES;
+            }
+            return null;
+        };
+        Parameters.prototype._getListFromType = function (type) {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return ActionsBuilder.SceneElements.MESHES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return ActionsBuilder.SceneElements.LIGHTS;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return ActionsBuilder.SceneElements.CAMERAS;
+            }
+            return null;
+        };
+        /*
+        * Returns the object in function of the given type
+        * @param type: the target type
+        */
+        Parameters.prototype._getObjectFromType = function (type) {
+            if (type === "MeshProperties" || type === "Mesh") {
+                this._currentObject = ActionsBuilder.SceneElements.MESH;
+                return ActionsBuilder.SceneElements.MESH;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                this._currentObject = ActionsBuilder.SceneElements.LIGHT;
+                return ActionsBuilder.SceneElements.LIGHT;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                this._currentObject = ActionsBuilder.SceneElements.CAMERA;
+                return ActionsBuilder.SceneElements.CAMERA;
+            }
+            if (type === "SceneProperties" || type === "Scene") {
+                this._currentObject = ActionsBuilder.SceneElements.SCENE;
+                return ActionsBuilder.SceneElements.SCENE;
+            }
+            return null;
+        };
+        /*
+        * Creates the node section (top of parameters)
+        * @param action: the action element to get color, text, name etc.
+        */
+        Parameters.prototype._createNodeSection = function (action) {
+            var element = document.createElement("div");
+            element.style.background = this._viewer.getSelectedNodeColor(action.type, action.node.detached);
+            element.className = "ParametersElementNodeClass";
+            var text = document.createElement("a");
+            text.text = action.name;
+            text.className = "ParametersElementNodeTextClass";
+            element.appendChild(text);
+            this.parametersContainer.appendChild(element);
+        };
+        /*
+        * Creates the help section
+        * @param action : the action containing the description
+        */
+        Parameters.prototype._createHelpSection = function (action) {
+            // Get description
+            var element = ActionsBuilder.Elements.GetElementFromName(action.name);
+            if (element !== null) {
+                this.parametersHelpElement.textContent = element.description;
+            }
+        };
+        /*
+        * Alphabetically sorts a HTML select element options
+        * @param element : the HTML select element to sort
+        */
+        Parameters.prototype._sortList = function (element) {
+            var options = [];
+            for (var i = element.options.length - 1; i >= 0; i--) {
+                options.push(element.removeChild(element.options[i]));
+            }
+            options.sort(function (a, b) {
+                return a.innerHTML.localeCompare(b.innerHTML);
+            });
+            for (var i = 0; i < options.length; i++) {
+                element.add(options[i]);
+            }
+        };
+        return Parameters;
+    }());
+    ActionsBuilder.Parameters = Parameters;
+})(ActionsBuilder || (ActionsBuilder = {}));
+//# sourceMappingURL=actionsbuilder.parameters.js.map

+ 86 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.toolbar.js

@@ -0,0 +1,86 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Toolbar = (function () {
+        function Toolbar(viewer) {
+            var _this = this;
+            // Get HTML elements
+            this.toolbarElement = document.getElementById("ToolbarElementID");
+            // Configure this
+            this._viewer = viewer;
+            // Manage events
+            window.addEventListener("resize", function (event) {
+                _this.onResize();
+            });
+            // Bottom toolbar
+            document.getElementById("ViewerDeZoomID").addEventListener("click", function (event) {
+                if (_this._viewer.zoom > 0.1) {
+                    _this._viewer.zoom -= 0.1;
+                }
+                _this._viewer.update();
+            });
+            document.getElementById("ViewerZoomID").addEventListener("click", function (event) {
+                if (_this._viewer.zoom < 1.0) {
+                    _this._viewer.zoom += 0.1;
+                }
+                _this._viewer.update();
+            });
+            document.getElementById("ViewerReconnectAll").addEventListener("click", function (event) {
+                for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                    _this._viewer.selectedNode = _this._viewer.root.children[i];
+                    _this._viewer.utils.onDetachAction(false, true);
+                }
+                _this._viewer.update();
+                _this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerDisconnectAll").addEventListener("click", function (event) {
+                for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                    _this._viewer.selectedNode = _this._viewer.root.children[i];
+                    _this._viewer.utils.onDetachAction(true, false);
+                }
+                _this._viewer.update();
+                _this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerReduceAll").addEventListener("click", function (event) {
+                for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                    _this._viewer.selectedNode = _this._viewer.root.children[i];
+                    _this._viewer.utils.onReduceAll(false);
+                }
+                _this._viewer.update();
+                _this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerExpandAll").addEventListener("click", function (event) {
+                for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                    _this._viewer.selectedNode = _this._viewer.root.children[i];
+                    _this._viewer.utils.onReduceAll(true);
+                }
+                _this._viewer.update();
+                _this._viewer.selectedNode = null;
+            });
+            // Top toolbar
+            this.saveActionGraphElement = document.getElementById("ToolsButtonIDSaveActionGraph");
+            this.drawSaveActionGraphButton(false);
+            document.getElementById("ResetActionGraphID").addEventListener("click", function (event) {
+                if (confirm("Are you sure?")) {
+                    for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                        _this._viewer.selectedNode = _this._viewer.root.children[i];
+                        _this._viewer.utils.onRemoveBranch();
+                    }
+                    _this._viewer.update();
+                    _this._viewer.selectedNode = null;
+                }
+            });
+            document.getElementById("TestActionGraphID").addEventListener("click", function (event) {
+                _this._viewer.utils.onTestGraph();
+            });
+        }
+        Toolbar.prototype.onResize = function () {
+            this.toolbarElement.style.top = this._viewer.viewerElement.clientHeight + 20 + "px";
+        };
+        Toolbar.prototype.drawSaveActionGraphButton = function (draw) {
+            this.saveActionGraphElement.style.display = draw ? "block" : "none";
+        };
+        return Toolbar;
+    }());
+    ActionsBuilder.Toolbar = Toolbar;
+})(ActionsBuilder || (ActionsBuilder = {}));
+//# sourceMappingURL=actionsbuilder.toolbar.js.map

+ 434 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.utils.js

@@ -0,0 +1,434 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Utils = (function () {
+        /*
+        * Constructor
+        * @param viewer: the viewer instance
+        */
+        function Utils(viewer) {
+            // Members
+            this.copiedStructure = null;
+            // Configure this
+            this._viewer = viewer;
+        }
+        /*
+        * Tests the graph and reports errors
+        */
+        Utils.prototype.onTestGraph = function () {
+            var _this = this;
+            if (this._viewer.root.children.length === 0) {
+                alert("Please add at least a Trigger and an Action to test the graph");
+            }
+            var onTestTarget = function (targetType, target) {
+                var targetExists = false;
+                var array = _this._viewer.parameters._getTargetFromType(targetType);
+                if (array === null) {
+                    return targetExists;
+                }
+                for (var i = 0; i < array.length; i++) {
+                    if (array[i] === target) {
+                        targetExists = true;
+                        break;
+                    }
+                }
+                return targetExists;
+            };
+            var onNodeError = function (action) {
+                var node = action.node;
+                node.rect.attr("fill", Raphael.rgb(255, 0, 0));
+                return false;
+            };
+            var onTestAction = function (action) {
+                console.log("Testing " + action.name);
+                if (action.combineArray !== null) {
+                    var foundError = false;
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        if (!onTestAction(action.combineArray[i])) {
+                            foundError = true;
+                        }
+                    }
+                    if (foundError) {
+                        return false;
+                    }
+                }
+                else {
+                    // Test properties
+                    var properties = action.properties;
+                    var propertiesResults = action.propertiesResults;
+                    if (properties !== null) {
+                        var object = null;
+                        var propertyPath = null;
+                        for (var i = 0; i < properties.length; i++) {
+                            // Target
+                            if (properties[i].text === "target" || properties[i].text === "parent") {
+                                object = _this._viewer.parameters._getObjectFromType(properties[i].targetType);
+                                var targetExists = onTestTarget(propertiesResults[i].targetType, propertiesResults[i].value);
+                                if (!targetExists) {
+                                    return onNodeError(action);
+                                }
+                            }
+                            else if (properties[i].text === "propertyPath") {
+                                var property = propertiesResults[i].value;
+                                var effectiveProperty = object;
+                                var p = property.split(".");
+                                for (var j = 0; j < p.length && effectiveProperty !== undefined; j++) {
+                                    effectiveProperty = effectiveProperty[p[j]];
+                                }
+                                if (effectiveProperty === undefined) {
+                                    return onNodeError(action);
+                                }
+                                else {
+                                    propertyPath = effectiveProperty;
+                                }
+                            }
+                            else if (properties[i].text == "value" && propertyPath != null) {
+                                var value = propertiesResults[i].value;
+                                if (!isNaN(propertyPath)) {
+                                    var num = parseFloat(value);
+                                    if (isNaN(num) || value === "") {
+                                        return onNodeError(action);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    var foundError = false;
+                    for (var i = 0; i < action.children.length; i++) {
+                        if (!onTestAction(action.children[i])) {
+                            foundError = true;
+                        }
+                    }
+                    return !foundError;
+                }
+            };
+            var root = this._viewer.root;
+            var foundError = false;
+            for (var i = 0; i < root.children.length; i++) {
+                var trigger = root.children[i];
+                var properties = trigger.properties;
+                // Test properties of trigger (parameter)
+                if (properties !== null && properties.length > 0) {
+                    // Only one property
+                    var parameter = trigger.propertiesResults[0].value;
+                    if (properties[0].targetType !== null) {
+                        // Intersection trigger
+                        if (!onTestTarget("MeshProperties", parameter)) {
+                            foundError = onNodeError(trigger);
+                        }
+                    }
+                    else {
+                        // Key trigger
+                        if (!parameter.match(/[a-z]/)) {
+                            foundError = onNodeError(trigger);
+                        }
+                    }
+                }
+                for (var j = 0; j < trigger.children.length; j++) {
+                    var child = trigger.children[j];
+                    var result = onTestAction(child);
+                    if (!result) {
+                        foundError = true;
+                    }
+                }
+            }
+            if (foundError) {
+                alert("Found error(s). the red nodes contain the error.");
+            }
+            else {
+                alert("No error found.");
+            }
+        };
+        /*
+        * Recursively reduce/expand nodes
+        */
+        Utils.prototype.onReduceAll = function (forceExpand) {
+            if (forceExpand === void 0) { forceExpand = false; }
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var action = this._viewer.selectedNode;
+            if (action.combineArray !== null) {
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    this._viewer.selectedNode = action.combineArray[i];
+                    this.onReduce(forceExpand, !forceExpand);
+                }
+            }
+            else {
+                this.onReduce(forceExpand, !forceExpand);
+            }
+            for (var i = 0; i < action.children.length; i++) {
+                this._viewer.selectedNode = action.children[i];
+                this.onReduceAll(forceExpand);
+            }
+        };
+        /*
+        * Reduces the selected node
+        */
+        Utils.prototype.onReduce = function (forceExpand, forceReduce) {
+            if (forceExpand === void 0) { forceExpand = false; }
+            if (forceReduce === void 0) { forceReduce = false; }
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var node = this._viewer.selectedNode.node;
+            node.rect.stop(node.rect.animation);
+            // Set minimized
+            if (forceExpand === true) {
+                node.minimized = false;
+            }
+            else if (forceReduce === true) {
+                node.minimized = true;
+            }
+            else {
+                node.minimized = !node.minimized;
+            }
+            // Set size
+            if (node.minimized) {
+                node.text.hide();
+                node.rect.attr("width", ActionsBuilder.Viewer.NODE_MINIMIZED_WIDTH * this._viewer.zoom);
+            }
+            else {
+                node.text.show();
+                node.rect.attr("width", ActionsBuilder.Viewer.NODE_WIDTH * this._viewer.zoom);
+            }
+        };
+        /*
+        * Detaches the selected action
+        */
+        Utils.prototype.onDetachAction = function (forceDetach, forceAttach) {
+            var _this = this;
+            if (forceDetach === void 0) { forceDetach = false; }
+            if (forceAttach === void 0) { forceAttach = false; }
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var action = this._viewer.selectedNode;
+            if (forceDetach === true) {
+                action.node.detached = true;
+            }
+            else if (forceAttach === true) {
+                action.node.detached = false;
+            }
+            else {
+                action.node.detached = !action.node.detached;
+            }
+            var onSetColor = function (root, detached) {
+                var rootNode = root.node;
+                rootNode.rect.attr("fill", _this._viewer.getNodeColor(root.type, detached));
+                if (root.combineArray !== null) {
+                    for (var i = 0; i < root.combineArray.length; i++) {
+                        var combineNode = root.combineArray[i].node;
+                        combineNode.rect.attr("fill", _this._viewer.getNodeColor(root.combineArray[i].type, detached));
+                    }
+                }
+                for (var i = 0; i < root.children.length; i++) {
+                    onSetColor(root.children[i], detached);
+                }
+            };
+            onSetColor(action, action.node.detached);
+        };
+        /*
+        * Removes the selected node
+        */
+        Utils.prototype.onRemoveNode = function () {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var action = this._viewer.selectedNode;
+            var parent = action.parent;
+            // If trigger, remove branch
+            if (action.type === ActionsBuilder.Type.TRIGGER) {
+                this.onRemoveBranch();
+                return;
+            }
+            // If it is a combine hub
+            if (action.type === ActionsBuilder.Type.FLOW_CONTROL && parent !== null && parent.combineArray !== null) {
+                action = parent;
+                parent = action.parent;
+            }
+            // Remove
+            if (parent !== null && parent.combineArray !== null) {
+                parent.removeCombinedAction(action);
+                if (parent.combineArray.length === 0) {
+                    parent.node.text.attr("text", "combine");
+                }
+            }
+            else {
+                if (action.combineArray !== null) {
+                    action.removeChild(action.hub);
+                }
+                action.parent.removeChild(action);
+            }
+            if (action.combineArray !== null) {
+                this._viewer.removeAction(action.hub, false);
+            }
+            this._viewer.removeAction(action, false);
+            // Finish
+            this._viewer.update();
+            this._viewer.parameters.clearParameters();
+            this._viewer.selectedNode = null;
+        };
+        /*
+        * Removes a branch starting from the selected node
+        */
+        Utils.prototype.onRemoveBranch = function () {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            if (this._viewer.selectedNode === this._viewer.root) {
+                alert("Cannot remove the root node");
+                return;
+            }
+            var action = this._viewer.selectedNode;
+            var parent = action.parent;
+            // If combine
+            if (action.parent !== null && action.parent.combineArray !== null) {
+                action = parent;
+                parent = action.parent;
+            }
+            // Remove
+            if (action.combineArray !== null) {
+                action.removeChild(action.hub);
+            }
+            action.parent.removeChild(action);
+            this._viewer.removeAction(action, true);
+            // Finish
+            this._viewer.update();
+            this._viewer.parameters.clearParameters();
+            this._viewer.selectedNode = null;
+        };
+        /*
+        * Copies the selected structure
+        */
+        Utils.prototype.onCopyStructure = function () {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var structure = this.createJSON(this._viewer.selectedNode);
+            var asText = JSON.stringify(structure);
+            if (window.clipboardData !== undefined) {
+                window.clipboardData.setData("text", asText);
+            }
+            else {
+                this.copiedStructure = asText;
+            }
+        };
+        /*
+        * Pastes the graph structure previously copied
+        */
+        Utils.prototype.onPasteStructure = function () {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var asText = (window.clipboardData !== undefined) ? window.clipboardData.getData("text") : this.copiedStructure;
+            var isJson = asText.length > 0 && asText[0] == "{" && asText[asText.length - 1] == "}";
+            var structure = JSON.parse(asText);
+            var action = this._viewer.selectedNode;
+            if (structure.type === ActionsBuilder.Type.TRIGGER && action !== this._viewer.root) {
+                alert("You can't paste a trigger if the selected node isn't the root object");
+                return;
+            }
+            if (structure.type !== ActionsBuilder.Type.TRIGGER && action === this._viewer.root) {
+                alert("You can't paste an action or condition if the selected node is the root object");
+                return;
+            }
+            this.loadFromJSON(structure, action);
+            this._viewer.update();
+        };
+        /*
+        * Loads a graph from JSON
+        * @pram graph: the graph structure
+        * @param startAction: the action to start load
+        */
+        Utils.prototype.loadFromJSON = function (graph, startAction) {
+            var _this = this;
+            // If startNode is null, means it replaces all the graph
+            // If not, it comes from a copy/paste
+            if (startAction === null) {
+                for (var i = 0; i < this._viewer.root.children.length; i++) {
+                    this._viewer.removeAction(this._viewer.root.children[i], true);
+                }
+                this._viewer.root.clearChildren();
+            }
+            var load = function (root, parent, detached, combine) {
+                if (parent === null) {
+                    parent = _this._viewer.root;
+                }
+                var newAction = null;
+                if (root.type !== ActionsBuilder.Type.OBJECT && root.type !== ActionsBuilder.Type.SCENE) {
+                    var action = _this._viewer.addAction(parent, root.type, ActionsBuilder.Elements.GetElementFromName(root.name));
+                    for (var i = 0; i < root.properties.length; i++) {
+                        var targetType = root.properties[i].targetType;
+                        if (targetType === undefined) {
+                            targetType = "MeshProperties"; // Default is mesh properties
+                        }
+                        action.propertiesResults[i] = { value: root.properties[i].value, targetType: targetType };
+                    }
+                    var node = action.node;
+                    node.detached = root.detached;
+                    if (detached) {
+                        node.rect.attr("fill", _this._viewer.getNodeColor(action.type, detached));
+                    }
+                    // If combine array
+                    if (root.combine !== undefined) {
+                        for (var i = 0; i < root.combine.length; i++) {
+                            load(root.combine[i], action, detached, true);
+                        }
+                    }
+                    if (!combine) {
+                        parent = parent.children[parent.children.length - 1];
+                    }
+                }
+                for (var i = 0; i < root.children.length; i++) {
+                    load(root.children[i], newAction !== null && newAction.combineArray !== null ? newAction.hub : parent, root.detached, false);
+                }
+            };
+            // Finish
+            load(graph, startAction, false, false);
+            this._viewer.update();
+        };
+        /*
+        * Creates a JSON object starting from a root action
+        * @param root: the root action
+        */
+        Utils.prototype.createJSON = function (root) {
+            var action = {
+                type: root.type,
+                name: root.name,
+                detached: root.node.detached,
+                children: new Array(),
+                combine: new Array(),
+                properties: new Array()
+            };
+            // Set properties
+            for (var i = 0; i < root.properties.length; i++) {
+                action.properties.push({
+                    name: root.properties[i].text,
+                    value: root.propertiesResults[i].value,
+                    targetType: root.propertiesResults[i].targetType
+                });
+            }
+            // If combine
+            if (root.combineArray !== null) {
+                for (var i = 0; i < root.combineArray.length; i++) {
+                    var combinedAction = root.combineArray[i];
+                    action.combine.push(this.createJSON(combinedAction));
+                }
+                root = root.children[0]; // Hub
+            }
+            for (var i = 0; i < root.children.length; i++) {
+                action.children.push(this.createJSON(root.children[i]));
+            }
+            return action;
+        };
+        /*
+        *
+        */
+        Utils.prototype.setElementVisible = function (element, visible) {
+            element.style.display = visible ? "block" : "none";
+        };
+        return Utils;
+    }());
+    ActionsBuilder.Utils = Utils;
+})(ActionsBuilder || (ActionsBuilder = {}));
+//# sourceMappingURL=actionsbuilder.utils.js.map

+ 632 - 0
Exporters/3ds Max/ActionsBuilder/Sources/actionsbuilder.viewer.js

@@ -0,0 +1,632 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Viewer = (function () {
+        /*
+        * Constructor
+        * @param type: the root type object (OBJECT or SCENE)
+        */
+        function Viewer(type) {
+            var _this = this;
+            this.objectName = "Unnamed Object";
+            this.zoom = 1.0;
+            this._firstUpdate = true;
+            // Get HTML elements
+            this.viewerContainer = document.getElementById("GraphContainerID");
+            this.viewerElement = document.getElementById("GraphElementID");
+            // Create element
+            this.paper = Raphael("GraphElementID", screen.width, screen.height);
+            // Configure this
+            //var name = type === Type.OBJECT ? "Unnamed object" : "Scene";
+            this.root = this.addAction(null, type, { name: this.objectName, text: this.objectName, properties: [], description: "" });
+            this.selectedNode = null;
+            // Configure events
+            window.addEventListener("resize", function (event) {
+                _this.onResize(event);
+            });
+            window.addEventListener("mousemove", function (event) {
+                _this.onMove(event);
+            });
+            this.paper.canvas.addEventListener("click", function (event) {
+                _this.onClick(event);
+            });
+            // Load modules
+            this._toolbar = new ActionsBuilder.Toolbar(this);
+            this._contextMenu = new ActionsBuilder.ContextMenu(this);
+            this.parameters = new ActionsBuilder.Parameters(this);
+            this.utils = new ActionsBuilder.Utils(this);
+            // Finish
+            this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
+            this.onResize(null);
+        }
+        Object.defineProperty(Viewer, "NODE_WIDTH", {
+            get: function () {
+                return Viewer._NODE_WIDTH;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Viewer, "NODE_HEIGHT", {
+            get: function () {
+                return Viewer._NODE_HEIGHT;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Viewer, "NODE_MINIMIZED_WIDTH", {
+            get: function () {
+                return Viewer._NODE_MINIMIZE_WIDTH;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Viewer, "VERTICAL_OFFSET", {
+            get: function () {
+                return Viewer._VERTICAL_OFFSET;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /*
+        * Resize event
+        * @param event: the resize event
+        */
+        Viewer.prototype.onResize = function (event) {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.viewerContainer.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
+            this.viewerElement.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
+            this.parameters.onResize();
+            this._toolbar.onResize();
+            if (this.paper.height < window.innerHeight) {
+                this.paper.setSize(this.paper.width, window.innerHeight);
+            }
+            if (this._firstUpdate) {
+                this.viewerElement.scrollLeft = ((this.viewerElement.scrollWidth / 2) - (this.viewerElement.getBoundingClientRect().width / 2));
+                this._firstUpdate = false;
+            }
+        };
+        /*
+        * Handles the onMove event
+        * @param event: the onMove mouse event
+        */
+        Viewer.prototype.onMove = function (event) {
+            this.mousex = event.clientX - this.paper.canvas.getBoundingClientRect().left;
+            this.mousey = event.clientY - this.paper.canvas.getBoundingClientRect().top;
+        };
+        /*
+        * Handles the onClick event to get selected node
+        * @param event: the onClick mouse event
+        */
+        Viewer.prototype.onClick = function (event) {
+            if (this._contextMenu.showing) {
+                return;
+            }
+            // Reset selected node
+            if (this.selectedNode !== null) {
+                var node = this.selectedNode.node;
+                node.rect.attr("fill", this.getNodeColor(this.selectedNode.type, node.detached));
+            }
+            // Configure new selected node
+            var result = this.traverseGraph(null, this.mousex, this.mousey, true);
+            if (result.hit) {
+                this.selectedNode = result.action;
+                var node = this.selectedNode.node;
+                node.rect.attr("fill", this.getSelectedNodeColor(this.selectedNode.type, node.detached));
+            }
+            else {
+                this.selectedNode = null;
+                this.parameters.clearParameters();
+                this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
+            }
+        };
+        /*
+        * Set the color theme of the viewer
+        * @param color: the color theme ( ex: "rgb(64, 64, 64)" )
+        */
+        Viewer.prototype.setColorTheme = function (color) {
+            this.paper.canvas.style.background = color;
+        };
+        /*
+        * Returns the color according to the given parameters
+        * @param action: the action used to select the color
+        * @param detached: if the node is attached to its parent or not
+        */
+        Viewer.prototype.getNodeColor = function (type, detached) {
+            if (detached) {
+                return Raphael.rgb(96, 122, 14);
+            }
+            switch (type) {
+                case ActionsBuilder.Type.TRIGGER: return Raphael.rgb(133, 154, 185);
+                case ActionsBuilder.Type.ACTION: return Raphael.rgb(182, 185, 132);
+                case ActionsBuilder.Type.FLOW_CONTROL: return Raphael.rgb(185, 132, 140);
+                case ActionsBuilder.Type.OBJECT:
+                case ActionsBuilder.Type.SCENE: return Raphael.rgb(255, 255, 255);
+                default: break;
+            }
+            return null;
+        };
+        /*
+        * Returns the selected node color according to the given parameters
+        * @param action: the action used to select the color
+        * @param detached: if the node is attached to its parent or not
+        */
+        Viewer.prototype.getSelectedNodeColor = function (type, detached) {
+            if (detached) {
+                return Raphael.rgb(96, 122, 14);
+            }
+            switch (type) {
+                case ActionsBuilder.Type.TRIGGER: return Raphael.rgb(41, 129, 255);
+                case ActionsBuilder.Type.ACTION: return Raphael.rgb(255, 220, 42);
+                case ActionsBuilder.Type.FLOW_CONTROL: return Raphael.rgb(255, 41, 53);
+                case ActionsBuilder.Type.OBJECT:
+                case ActionsBuilder.Type.SCENE: return Raphael.rgb(255, 255, 255);
+                default: break;
+            }
+            return null;
+        };
+        /*
+        * Removes the given action from the graph
+        * @param action: the action to remove
+        * @param removeChildren: if remove the branch or not
+        */
+        Viewer.prototype.removeAction = function (action, removeChildren) {
+            // If selected node is combine
+            if (action.parent !== null && action.parent.hub === action) {
+                this.removeAction(action.parent, false);
+                return;
+            }
+            // Basic suppress
+            this.removeNode(action.node);
+            if (action.combineArray !== null) {
+                this.removeNode(action.hub.node);
+                // Remove combine array
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    this.removeNode(action.combineArray[i].node);
+                }
+                action.combineArray.length = 0;
+            }
+            if (removeChildren) {
+                for (var i = 0; i < action.children.length; i++) {
+                    this.removeAction(action.children[i], removeChildren);
+                }
+                action.clearChildren();
+            }
+            else {
+                for (var i = 0; i < action.children.length; i++) {
+                    action.parent.addChild(action.children[i]);
+                    action.children[i].parent = action.parent;
+                }
+            }
+        };
+        /*
+        * Removes the given node (not the action)
+        * @param node: the node to remove
+        */
+        Viewer.prototype.removeNode = function (node) {
+            node.rect.remove();
+            node.text.remove();
+            if (node.line !== null) {
+                node.line.remove();
+            }
+        };
+        /*
+        * Updates the graph viewer
+        */
+        Viewer.prototype.update = function () {
+            var _this = this;
+            // Set root position
+            this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
+            // Sets node size
+            var onSetNodeSize = function (node) {
+                node.rect.attr("width", node.minimized ? Viewer.NODE_MINIMIZED_WIDTH : Viewer.NODE_WIDTH * _this.zoom);
+                node.rect.attr("height", Viewer.NODE_HEIGHT * _this.zoom);
+                node.text.attr("font-size", 11 * _this.zoom);
+            };
+            // First pass: set actions positions according to parents
+            var onSetPositionPass = function (action, yPosition) {
+                var node = action.node;
+                var parent = action.parent !== null ? action.parent : null;
+                // Set node properties (size, text size, etc.)
+                if (action.combineArray !== null) {
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        var combinedNode = action.combineArray[i].node;
+                        onSetNodeSize(combinedNode);
+                    }
+                }
+                onSetNodeSize(node);
+                // Set position from parent
+                if (parent) {
+                    var parentx = parent.node.rect.attr("x");
+                    if (parent.combineArray !== null && parent.combineArray.length > 1) {
+                        parentx += parent.node.rect.attr("width") / 2;
+                    }
+                    _this._setActionPosition(action, parentx, yPosition);
+                    _this._setActionLine(action);
+                }
+                // Calculate total width for current action
+                var totalSize = 0;
+                for (var i = 0; i < action.children.length; i++) {
+                    var childNode = action.children[i].node;
+                    totalSize += childNode.rect.attr("width");
+                }
+                // Get values to place nodes according to the parent position
+                var nodeWidth = node.rect.attr("width");
+                var startingPositionX = node.rect.attr("x");
+                // Set children positions
+                for (var i = 0; i < action.children.length; i++) {
+                    var childAction = action.children[i];
+                    var childNode = childAction.node;
+                    var newPositionX = startingPositionX;
+                    if (childAction.combineArray !== null && childAction.combineArray.length > 1) {
+                        newPositionX -= (childNode.rect.attr("width") / 2) - nodeWidth / 2;
+                    }
+                    var newPositionY = yPosition + Viewer.VERTICAL_OFFSET * _this.zoom;
+                    onSetPositionPass(childAction, newPositionY);
+                    _this._setActionPosition(childAction, newPositionX, newPositionY);
+                    _this._setActionLine(childAction);
+                }
+            };
+            onSetPositionPass(this.root, 10 * this.zoom);
+            // Seconds pass, get sizes of groups
+            var onGetSizePass = function (action, maxSize) {
+                var mySize = 0;
+                if (action.combineArray !== null) {
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        mySize += action.combineArray[i].node.rect.attr("width");
+                    }
+                }
+                else {
+                    mySize = action.node.rect.attr("width");
+                }
+                if (mySize > maxSize) {
+                    maxSize = mySize;
+                }
+                for (var i = 0; i < action.children.length; i++) {
+                    maxSize = onGetSizePass(action.children[i], maxSize);
+                }
+                return maxSize;
+            };
+            // Resize canvas
+            var onResizeCanvas = function (action) {
+                var node = action.node;
+                var nodex = node.rect.attr("x");
+                var nodey = node.rect.attr("y");
+                if (nodex < 0 || nodex > _this.paper.width) {
+                    _this.paper.setSize(_this.paper.width + 1000, _this.paper.height);
+                    _this._setActionPosition(_this.root, (_this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * _this.zoom, 10);
+                }
+                if (nodey > _this.paper.height) {
+                    _this.paper.setSize(_this.paper.width, _this.paper.height + 1000);
+                    _this._setActionPosition(_this.root, (_this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * _this.zoom, 10);
+                }
+            };
+            var widths = new Array();
+            for (var i = 0; i < this.root.children.length; i++) {
+                var trigger = this.root.children[i];
+                var triggerResult = { triggerWidth: onGetSizePass(trigger, 0), childrenWidths: new Array() };
+                if (trigger.children.length > 0) {
+                    triggerResult.triggerWidth = 0;
+                }
+                for (var j = 0; j < trigger.children.length; j++) {
+                    var actionWidth = onGetSizePass(trigger.children[j], 0);
+                    triggerResult.triggerWidth += actionWidth + 15;
+                    triggerResult.childrenWidths.push({
+                        triggerWidth: actionWidth,
+                        childrenWidths: null
+                    });
+                }
+                widths.push(triggerResult);
+            }
+            // Third pass, set positions of nodes
+            var onSetNodePosition = function (action, widthArray, isChild) {
+                var actionsCount = action.children.length;
+                var actionsMiddle = actionsCount % 2;
+                var actionsHasMiddle = actionsMiddle !== 0;
+                var actionsLeftOffset = 0;
+                var actionsRightOffset = 0;
+                var actionWidth = action.node.rect.attr("width");
+                if (actionsHasMiddle && actionsCount > 1) {
+                    var middle = Math.floor(actionsCount / 2);
+                    actionsLeftOffset += widthArray[middle].triggerWidth / 2;
+                    actionsRightOffset += widthArray[middle].triggerWidth / 2;
+                }
+                // Move left
+                var leftStart = actionsHasMiddle ? Math.floor(actionsCount / 2) - 1 : (actionsCount / 2) - 1;
+                for (var i = leftStart; i >= 0; i--) {
+                    var child = action.children[i];
+                    var node = child.node;
+                    var width = (widthArray[i].triggerWidth) + 15;
+                    _this._setActionPosition(action.children[i], node.rect.attr("x") - actionsLeftOffset - (width / 2), node.rect.attr("y"));
+                    _this._setActionLine(child);
+                    onResizeCanvas(child);
+                    actionsLeftOffset += width;
+                }
+                // Move right
+                var rightStart = actionsHasMiddle ? Math.round(actionsCount / 2) : actionsCount / 2;
+                for (var i = rightStart; i < actionsCount; i++) {
+                    var child = action.children[i];
+                    var node = child.node;
+                    var width = (widthArray[i].triggerWidth) + 15;
+                    _this._setActionPosition(action.children[i], node.rect.attr("x") + actionsRightOffset + (width / 2), node.rect.attr("y"));
+                    _this._setActionLine(child);
+                    onResizeCanvas(child);
+                    actionsRightOffset += width;
+                }
+            };
+            onSetNodePosition(this.root, widths, false);
+            for (var i = 0; i < this.root.children.length; i++) {
+                onSetNodePosition(this.root.children[i], widths[i].childrenWidths, true);
+            }
+        };
+        /*
+        * Adds an action to the graph viewer and returns it
+        * @param parent: the parent action
+        * @param type: the action type
+        * @param element: the Actions Builder type (TRIGGERS, ACTIONS, FLOW_CONTROLS)
+        */
+        Viewer.prototype.addAction = function (parent, type, element) {
+            var node = this._createNode(element.text, type, parent === null);
+            var action = new ActionsBuilder.Action(node);
+            if (element.name === "CombineAction") {
+                action.combineArray = new Array();
+                var hubElement = ActionsBuilder.Elements.FLOW_CONTROLS[ActionsBuilder.Elements.FLOW_CONTROLS.length - 1];
+                var hub = this.addAction(action, ActionsBuilder.Type.FLOW_CONTROL, hubElement);
+                action.hub = hub;
+                action.addChild(hub);
+                this._createActionAnimation(hub);
+            }
+            action.name = element.name;
+            action.properties = element.properties;
+            action.type = type;
+            // Configure properties
+            for (var i = 0; i < action.properties.length; i++) {
+                action.propertiesResults.push({ targetType: action.properties[i].targetType, value: action.properties[i].value });
+            }
+            if (action.properties !== null && action.properties.length > 0) {
+                if (action.properties[0].text === "target") {
+                    action.propertiesResults[0].value = this.objectName;
+                }
+            }
+            if (parent !== null) {
+                if (parent.combineArray === null) {
+                    parent.addChild(action);
+                }
+                else if (parent.combineArray !== null && action.name !== "Hub") {
+                    parent.combineArray.push(action);
+                    action.parent = parent;
+                    action.combineAction = parent;
+                    parent.node.text.attr("text", "");
+                }
+            }
+            // Create animation
+            this._createActionAnimation(action);
+            return action;
+        };
+        /*
+        * Traverses the graph viewer and returns if an action
+        * is selected at coordinates (x, y)
+        * @param start: the start node. Can be null
+        * @param x: the x coordinate
+        * @param y: the y coordinate
+        * @param traverseCombine: if we traverse combine actions children
+        */
+        Viewer.prototype.traverseGraph = function (start, x, y, traverseCombine) {
+            if (start === null)
+                start = this.root;
+            var result = { action: start, hit: true };
+            if (start.node.isPointInside(x, y)) {
+                return result;
+            }
+            for (var i = 0; i < start.children.length; i++) {
+                var action = start.children[i];
+                if (action.node.isPointInside(x, y)) {
+                    result.hit = true;
+                    result.action = start.children[i];
+                    if (traverseCombine && action.combineArray !== null) {
+                        for (var j = 0; j < action.combineArray.length; j++) {
+                            if (action.combineArray[j].node.isPointInside(x, y)) {
+                                result.action = action.combineArray[j];
+                                break;
+                            }
+                        }
+                    }
+                    return result;
+                }
+                result = this.traverseGraph(action, x, y, traverseCombine);
+                if (result.hit) {
+                    return result;
+                }
+            }
+            result.hit = false;
+            result.action = null;
+            return result;
+        };
+        /*
+        * Sets the action's position (node)
+        * @param action: the action to place
+        * @param x: the x position of the action
+        * @param y: the y position of the action
+        */
+        Viewer.prototype._setActionPosition = function (action, x, y) {
+            var node = action.node;
+            var offsetx = node.rect.attr("x") - x;
+            var parent = action.parent;
+            if (parent !== null && parent.combineArray !== null && parent.combineArray.length > 1) {
+                var parentNode = parent.node;
+                x = parentNode.rect.attr("x") + (parent.node.rect.attr("width") / 2) - (node.rect.attr("width") / 2);
+            }
+            node.rect.attr("x", x);
+            node.rect.attr("y", y);
+            var textBBox = node.text.getBBox();
+            var textWidth = 0;
+            if (textBBox !== null && textBBox !== undefined) {
+                textWidth = textBBox.width;
+            }
+            node.text.attr("x", x + node.rect.attr("width") / 2 - textWidth / 2);
+            node.text.attr("y", y + node.rect.attr("height") / 2);
+            if (action.combineArray !== null && action.combineArray.length > 0) {
+                var length = 0;
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    var combinedAction = action.combineArray[i];
+                    var combinedNode = combinedAction.node;
+                    combinedNode.rect.attr("x", node.rect.attr("x") + length);
+                    combinedNode.rect.attr("y", node.rect.attr("y"));
+                    textBBox = combinedNode.text.getBBox();
+                    if (textBBox !== null) {
+                        textWidth = textBBox.width;
+                    }
+                    combinedNode.text.attr("x", combinedNode.rect.attr("x") + combinedNode.rect.attr("width") / 2 - textWidth / 2);
+                    combinedNode.text.attr("y", y + combinedNode.rect.attr("height") / 2);
+                    length += combinedNode.rect.attr("width");
+                }
+                node.rect.attr("width", length);
+            }
+            for (var i = 0; i < action.children.length; i++) {
+                var child = action.children[i];
+                this._setActionPosition(child, child.node.rect.attr("x") - offsetx, y + Viewer.VERTICAL_OFFSET * this.zoom);
+                this._setActionLine(child);
+            }
+        };
+        /*
+        * Configures the line (link) between the action and its parent
+        * @param action: the action to configure
+        */
+        Viewer.prototype._setActionLine = function (action) {
+            if (action.node.line === null) {
+                return;
+            }
+            var node = action.node;
+            var nodex = node.rect.attr("x");
+            var nodey = node.rect.attr("y");
+            var nodeWidth = node.rect.attr("width");
+            var nodeHeight = node.rect.attr("height");
+            var parent = action.parent.node;
+            var parentx = parent.rect.attr("x");
+            var parenty = parent.rect.attr("y");
+            var parentWidth = parent.rect.attr("width");
+            var parentHeight = parent.rect.attr("height");
+            if (node.detached) {
+                node.line.attr("path", ["M", nodex, nodey, "L", nodex, nodey]);
+                return;
+            }
+            var line1x = nodex + (nodeWidth / 2);
+            var line1y = nodey;
+            var line2y = line1y - (line1y - parenty - parentHeight) / 2;
+            var line3x = parentx + (parentWidth / 2);
+            var line4y = parenty + parentHeight;
+            node.line.attr("path", ["M", line1x, line1y, "L", line1x, line2y, "L", line3x, line2y, "L", line3x, line4y]);
+        };
+        /*
+        * Creates and returns a node
+        * @param text: the text to draw in the nde
+        * @param color: the node's color
+        * @param noLine: if draw a line to the parent or not
+        */
+        Viewer.prototype._createNode = function (text, type, noLine) {
+            var node = new ActionsBuilder.Node();
+            var color = this.getNodeColor(type, false);
+            node.rect = this.paper.rect(20, 20, Viewer.NODE_WIDTH, Viewer.NODE_HEIGHT, 0);
+            node.rect.attr("fill", color);
+            node.text = this.paper.text(20, 20, text);
+            node.text.attr("font-size", 11);
+            node.text.attr("text-anchor", "start");
+            node.text.attr("font-family", "Sinkin Sans Light");
+            if (!noLine) {
+                node.line = this.paper.path("");
+                node.line.attr("stroke", color);
+            }
+            return node;
+        };
+        /*
+        * Creates the drag animation
+        * @param action: the action to animate
+        */
+        Viewer.prototype._createActionAnimation = function (action) {
+            var _this = this;
+            var node = action.node;
+            var finished = true;
+            var nodex = 0;
+            var nodey = 0;
+            var onMove = function (dx, dy, x, y) { };
+            var onStart = function (x, y, event) {
+                if (node.minimized) {
+                    return;
+                }
+                if (finished) {
+                    nodex = node.rect.attr("x");
+                    nodey = node.rect.attr("y");
+                }
+                finished = false;
+                node.rect.animate({
+                    x: node.rect.attr("x") - 10,
+                    y: node.rect.attr("y"),
+                    width: (Viewer.NODE_WIDTH + 20) * _this.zoom,
+                    height: (Viewer.NODE_HEIGHT + 10) * _this.zoom,
+                    opacity: 0.25
+                }, 500, ">");
+            };
+            var onEnd = function (event) {
+                if (!node.minimized) {
+                    node.rect.animate({
+                        x: nodex,
+                        y: nodey,
+                        width: Viewer.NODE_WIDTH * _this.zoom,
+                        height: Viewer.NODE_HEIGHT * _this.zoom,
+                        opacity: 1.0
+                    }, 500, ">", function () { finished = true; });
+                }
+                var dragResult = _this.traverseGraph(null, _this.mousex, _this.mousey, true);
+                if (dragResult.hit && dragResult.action === action || !dragResult.hit) {
+                    // Create parameters. Action can be null
+                    _this.parameters.createParameters(action);
+                }
+                else {
+                    // Manage drag'n'drop
+                    if (dragResult.action.children.length > 0 && action.type !== ActionsBuilder.Type.TRIGGER) {
+                        return;
+                    }
+                    if (action.type === ActionsBuilder.Type.TRIGGER && dragResult.action !== _this.root) {
+                        return;
+                    }
+                    if (action.type === ActionsBuilder.Type.ACTION && dragResult.action === _this.root) {
+                        return;
+                    }
+                    if (action.type === ActionsBuilder.Type.FLOW_CONTROL && (dragResult.action === _this.root || dragResult.action.type === ActionsBuilder.Type.FLOW_CONTROL)) {
+                        return;
+                    }
+                    if (action === dragResult.action.parent) {
+                        return;
+                    }
+                    if (action.parent !== null && action.parent.combineArray !== null) {
+                        return;
+                    }
+                    // Reset node
+                    node.rect.stop(node.rect.animation);
+                    node.text.stop(node.text.animation);
+                    node.rect.undrag();
+                    node.text.undrag();
+                    node.rect.attr("opacity", 1.0);
+                    node.rect.attr("width", Viewer.NODE_WIDTH);
+                    node.rect.attr("height", Viewer.NODE_HEIGHT);
+                    if (action.parent !== null) {
+                        // Configure drag'n'drop
+                        action.parent.removeChild(action);
+                        dragResult.action.addChild(action);
+                        _this.update();
+                        _this._createActionAnimation(action);
+                    }
+                }
+            };
+            node.rect.drag(onMove, onStart, onEnd);
+            node.text.drag(onMove, onStart, onEnd);
+        };
+        return Viewer;
+    }());
+    // Statics
+    Viewer._NODE_WIDTH = 150;
+    Viewer._NODE_HEIGHT = 25;
+    Viewer._NODE_MINIMIZE_WIDTH = 50;
+    Viewer._VERTICAL_OFFSET = 70;
+    Viewer._DEFAULT_INFO_MESSAGE = "Select or add a node to customize actions";
+    ActionsBuilder.Viewer = Viewer;
+})(ActionsBuilder || (ActionsBuilder = {}));
+//# sourceMappingURL=actionsbuilder.viewer.js.map

Datei-Diff unterdrückt, da er zu groß ist
+ 60548 - 0
Exporters/3ds Max/ActionsBuilder/Sources/babylon.max.js


Tools/ActionsBuilder/Sources/fonts.css → Exporters/3ds Max/ActionsBuilder/Sources/fonts.css


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.eot → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.eot


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.otf → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.otf


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.svg → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.svg


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.ttf → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.ttf


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.woff → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300Light.woff


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.eot → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.eot


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.otf → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.otf


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.svg → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.svg


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.ttf → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.ttf


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.woff → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-300LightItalic.woff


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.eot → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.eot


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.otf → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.otf


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.svg → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.svg


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.ttf → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.ttf


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.woff → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-500Medium.woff


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.eot → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.eot


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.otf → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.otf


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.svg → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.svg


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.ttf → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.ttf


Tools/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.woff → Exporters/3ds Max/ActionsBuilder/Sources/fonts/SinkinSans/SinkinSans-600SemiBold.woff


Datei-Diff unterdrückt, da er zu groß ist
+ 97 - 0
Exporters/3ds Max/ActionsBuilder/Sources/index-debug.html


+ 191 - 0
Exporters/3ds Max/ActionsBuilder/Sources/index.css

@@ -0,0 +1,191 @@
+html, body {
+  width: 100%;
+  height: 100%;
+
+  max-width: 100%;
+  max-height: 100%;
+
+  /*background: rgb(64, 64, 64);*/
+
+  overflow: hidden;
+  font-family: "Sinkin Sans Light";
+}
+
+* {
+  position: relative;
+}
+
+#ParentContainerID {
+    height: 100%;
+    width: 100%;
+
+    max-height: 100%;
+    max-width: 100%;
+
+    display: inline-block;
+}
+
+#ListsElementID {
+    width: 25%;
+    height: 100%;
+    
+    max-width: 25%;
+    max-height: 100%;
+
+    background: rgb(64, 64, 64);
+
+    float: left;
+    overflow-y: scroll;
+    overflow-x: hidden;
+}
+
+#GraphContainerID {
+    width: 50%;
+    max-width: 50%;
+
+    float: left;
+    position: relative;
+}
+
+#GraphElementID {
+    position: absolute;
+
+    width: 100%;
+    height: 100%;
+
+    overflow-y: auto;
+    overflow-x: auto;
+
+    max-width: 100%;
+    max-height: 100%;
+}
+
+#ToolbarElementID {
+    position: absolute;
+
+    width: 100%;
+    height: 45px;
+
+    max-width: 100%;
+    max-height: 42px;
+
+    float: left;
+    background: rgb(0, 0, 0);
+
+    vertical-align: middle;
+}
+
+#ToolbarElementID li {
+    color: white;
+    margin-left: 2em;
+    margin-top: 1em;
+}
+
+#ParametersContainerID {
+    width: 25%;
+    height: 100%;
+
+    max-width: 25%;
+    max-height: 100%;
+
+    float: right;
+    overflow-y: auto;
+}
+
+#ParametersElementID {
+    width: 100%;
+    max-width: 100%;
+
+    background: rgb(32, 32, 32);
+    overflow: auto;
+}
+
+#ParametersHelpElementID {
+    background: rgb(0, 0, 0);
+
+    font-family: "Sinkin Sans Light";
+    font-size: 12px;
+    font-style: italic;
+
+    text-align: center;
+    text-anchor: middle;
+    color: white;
+}
+
+.ParametersElementNodeClass {
+    font-size: 11px;
+    height: 25px;
+    margin-top: 10px;
+    border: 1px solid white;
+
+    text-align: center;
+
+    margin-left: auto;
+    margin-right: auto;
+    width: 90%;
+}
+.ParametersElementNodeTextClass {
+    width: 100%;
+    height: 100%;
+
+    vertical-align: middle;
+    line-height: 25px;
+}
+.ParametersElementSeparatorClass {
+    width: 90%;
+}
+.ParametersElementTitleClass {
+    margin-left: 3em;
+    display: block;
+    color: white;
+    font-size: 11px;
+}
+.ParametersElementSelectClass {
+    margin-top: 15px;
+
+    width: 80%;
+    height: 25px;
+
+    display: block;
+    font-size: 11px;
+    margin-left: 3em;
+}
+.ParametersElementInputClass {
+    margin-top: 15px;
+
+    width: 80%;
+    height: 15px;
+
+    display: block;
+    font-size: 11px;
+    margin-left: 3em;
+}
+
+/*
+UL & LI (toolbar & top buttons)
+*/
+#ToolsButtonsID {
+    position: relative;
+
+    width: 100%;
+    height: 25px;
+
+    max-width: 100%;
+    max-height: 25px;
+}
+
+ul {
+  padding-left: 1em;
+  margin-bottom: 0.5em;
+  margin-top: 0.0em;
+}
+ul li {
+  list-style-type: none;
+  display: inline-block;
+  margin-right: 3em;
+  cursor: pointer;
+  font-size: 12px;
+}
+ul li:last-child {
+  margin-right: 0;
+}

+ 72 - 0
Exporters/3ds Max/ActionsBuilder/Sources/index.html

@@ -0,0 +1,72 @@
+<!-- saved from url=(0014)about:internet -->
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+    <title>Actions Builder</title>
+
+    <!-- CSS FILES -->
+    <link href="./index.css" rel="stylesheet" />
+    <link href="./fonts.css" rel="stylesheet" />
+
+</head>
+
+<body>
+
+    <!-- PARENT CONTAINER -->
+    <div id="ParentContainerID">
+
+        <ul id="ToolsButtonsID">
+            <li id="ToolsButtonIDSaveActionGraph">save action graph</li>
+            <li id="ResetActionGraphID">reset action graph</li>
+            <li id="TestActionGraphID">test action graph</li>
+        </ul>
+
+        <!-- LIST CONTAINER -->
+        <div id="ListsElementID">
+            <div id="TriggersListID"></div>
+            <div id="ActionsListID"></div>
+            <div id="FlowActionsListID"></div>
+        </div>
+
+        <!-- GRAPH CONTAINER -->
+        <div id="GraphContainerID">
+            <div id="GraphElementID"></div>
+
+            <div id="ToolbarElementID">
+                <ul>
+                    <li id="ViewerReduceAll">reduce all</li>
+                    <li id="ViewerExpandAll">expand all</li>
+                    <li id="ViewerDisconnectAll">disconnect all</li>
+                    <li id="ViewerReconnectAll">reconnect all</li>
+                    <li class="border" style="width: 15px; text-align: center" id="ViewerDeZoomID"> - </li>
+                    <li class="border" style="width: 15px; text-align: center" id="ViewerZoomID"> + </li>
+                </ul>
+            </div>
+        </div>
+
+        <!-- PARAMETERS CONTAINER -->
+        <div id="ParametersContainerID">
+            <div id="ParametersElementID"></div>
+            <div id="ParametersHelpElementID"></div>
+        </div>
+
+    </div>
+
+    <canvas id="RenderCanvasID" style="display:none;"></canvas>
+    <input id="ActionsBuilderObjectName" type="hidden" />
+    <input id="ActionsBuilderJSON" type="hidden" />
+
+    <!-- JS FILES -->
+    <script src="raphael.js" type="text/javascript"></script>
+    <script src="babylon.max.js" type="text/javascript"></script>
+    <script src="actionsbuilder.max.js" type="text/javascript"></script>
+
+    <script type="text/javascript">
+        run();
+    </script>
+
+</body>
+
+</html>

Tools/ActionsBuilder/Sources/raphael.js → Exporters/3ds Max/ActionsBuilder/Sources/raphael.js


+ 62 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.actionNode.js

@@ -0,0 +1,62 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Node = (function () {
+        function Node() {
+            this.rect = null;
+            this.text = null;
+            this.line = null;
+            this.detached = false;
+            this.minimized = false;
+        }
+        Node.prototype.isPointInside = function (x, y) {
+            return this.rect.isPointInside(x, y) || this.text.isPointInside(x, y);
+        };
+        return Node;
+    })();
+    ActionsBuilder.Node = Node;
+    var Action = (function () {
+        function Action(node) {
+            this.parent = null;
+            this.children = new Array();
+            this.name = "";
+            this.type = ActionsBuilder.Type.OBJECT;
+            this.properties = new Array();
+            this.propertiesResults = new Array();
+            this.combineArray = null;
+            this.hub = null;
+            this.combineAction = null;
+            this.node = node;
+        }
+        Action.prototype.removeCombinedAction = function (action) {
+            if (action === null || this.combineArray === null) {
+                return false;
+            }
+            var index = this.combineArray.indexOf(action);
+            if (index !== -1) {
+                this.combineArray.splice(index, 1);
+            }
+            return false;
+        };
+        Action.prototype.addChild = function (child) {
+            if (child === null) {
+                return false;
+            }
+            this.children.push(child);
+            child.parent = this;
+            return true;
+        };
+        Action.prototype.removeChild = function (child) {
+            var indice = this.children.indexOf(child);
+            if (indice !== -1) {
+                this.children.splice(indice, 1);
+                return true;
+            }
+            return false;
+        };
+        Action.prototype.clearChildren = function () {
+            this.children = new Array();
+        };
+        return Action;
+    })();
+    ActionsBuilder.Action = Action;
+})(ActionsBuilder || (ActionsBuilder = {}));

+ 101 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.actionNode.ts

@@ -0,0 +1,101 @@
+module ActionsBuilder {
+    export class Node {
+        public rect: Rect = null;
+        public text: Text = null;
+        public line: Path = null;
+
+        public detached: boolean = false;
+        public minimized: boolean = false;
+
+        constructor()
+        { }
+
+        /**
+        * Returns if the point (x, y) is inside the text or rect
+        * @param x: the x position of the point
+        * @param y: the y position of the point
+        */
+        public isPointInside(x: number, y: number): boolean {
+            return this.rect.isPointInside(x, y) || this.text.isPointInside(x, y);
+        }
+    }
+
+    export class Action {
+        public node: Node;
+        public parent: Action = null;
+        public children: Array<Action> = new Array<Action>();
+
+        public name: string = "";
+        public type: number = Type.OBJECT;
+
+        public properties: Array<ElementProperty> = new Array<ElementProperty>();
+        public propertiesResults: Array<ElementPropertyResult> = new Array<ElementPropertyResult>();
+
+        public combineArray: Array<Action> = null;
+        public hub: Action = null;
+        public combineAction: Action = null;
+
+        /**
+        * Constructor
+        * @param node: The associated node to draw in the viewer
+        */
+        constructor(node: Node) {
+            this.node = node;
+        }
+        
+        /*
+        * Removes a combined action from the combine array
+        * @param action: the action to remove
+        */
+        public removeCombinedAction(action: Action): boolean {
+            if (action === null || this.combineArray === null) {
+                return false;
+            }
+
+            var index = this.combineArray.indexOf(action);
+            if (index !== -1) {
+                this.combineArray.splice(index, 1);
+            }
+
+            return false;
+        }
+
+        /*
+        * Adds a child
+        * @param child: the action to add as child
+        */
+        public addChild(child: Action): boolean {
+            if (child === null) {
+                return false;
+            }
+
+            this.children.push(child);
+            child.parent = this;
+
+            return true;
+        }
+
+        /*
+        * Removes the given action to children
+        * @param child: the child to remove
+        */
+        public removeChild(child: Action): boolean {
+            var indice = this.children.indexOf(child);
+
+            if (indice !== -1) {
+                this.children.splice(indice, 1);
+                return true;
+            }
+
+            return false;
+        }
+
+        /*
+        * Clears the children's array
+        */
+        public clearChildren(): void {
+            this.children = new Array<Action>();
+        }
+
+    }
+}

+ 104 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.contextMenu.js

@@ -0,0 +1,104 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var ContextMenu = (function () {
+        function ContextMenu(viewer) {
+            this.showing = false;
+            this.savedColor = Raphael.rgb(255, 255, 255);
+            this.overColor = Raphael.rgb(140, 200, 230);
+            this._viewer = null;
+            this.elements = [
+                { text: "Reduce", node: null, action: "onReduce" },
+                { text: "Delete", node: null, action: "onRemoveNode" },
+                { text: "Delete branch", node: null, action: "onRemoveBranch" },
+                { text: "Connect / Disconnect", node: null, action: "onDetachAction" },
+                { text: "Copy", node: null, action: "onCopyStructure" },
+                { text: "Paste", node: null, action: "onPasteStructure" },
+                { text: "", node: null, action: null }
+            ];
+            this._viewer = viewer;
+            this.attachControl(this._viewer.paper.canvas);
+        }
+        ContextMenu.prototype.attachControl = function (element) {
+            var _this = this;
+            var onClick = function (event) {
+                var x = _this._viewer.mousex;
+                var y = _this._viewer.mousey;
+                if (_this.showing) {
+                    for (var i = 0; i < _this.elements.length; i++) {
+                        var element = _this.elements[i];
+                        if (element.action && element.node.rect.isPointInside(x, y)) {
+                            _this._viewer.utils[element.action]();
+                            _this._viewer.update();
+                        }
+                        element.node.rect.remove();
+                        element.node.text.remove();
+                    }
+                }
+                _this.showing = false;
+            };
+            var onMouseMove = function (event) {
+                if (_this.showing) {
+                    for (var i = 0; i < _this.elements.length; i++) {
+                        var element = _this.elements[i];
+                        if (element.text === "")
+                            continue;
+                        var x = _this._viewer.mousex;
+                        var y = _this._viewer.mousey;
+                        if (element.node.rect.isPointInside(x, y)) {
+                            element.node.rect.attr("fill", _this.overColor);
+                        }
+                        else {
+                            element.node.rect.attr("fill", _this.savedColor);
+                        }
+                    }
+                }
+            };
+            var onRightClick = function (event) {
+                var x = _this._viewer.mousex;
+                var y = _this._viewer.mousey;
+                _this._viewer.onClick(event);
+                var result = _this._viewer.traverseGraph(null, x, y, true);
+                if (result.hit) {
+                }
+                if (y + (ActionsBuilder.Viewer.NODE_HEIGHT * _this.elements.length) > _this._viewer.viewerElement.offsetHeight + _this._viewer.viewerElement.scrollTop) {
+                    y = (ActionsBuilder.Viewer.NODE_HEIGHT * _this.elements.length);
+                }
+                if (x + ActionsBuilder.Viewer.NODE_WIDTH > _this._viewer.viewerElement.offsetWidth + _this._viewer.viewerElement.scrollLeft) {
+                    x -= ActionsBuilder.Viewer.NODE_WIDTH;
+                }
+                if (!_this.showing) {
+                    if (_this._viewer.selectedNode === null)
+                        return;
+                    var yOffset = 10;
+                    for (var i = 0; i < _this.elements.length - 1; i++) {
+                        var element = _this.elements[i];
+                        element.node = _this._viewer._createNode(element.text, ActionsBuilder.Type.OBJECT, true);
+                        element.node.rect.attr("fill", Raphael.rgb(216, 216, 216));
+                        element.node.rect.attr("x", x);
+                        element.node.rect.attr("y", y + yOffset);
+                        element.node.text.attr("x", x + 5);
+                        element.node.text.attr("y", y + yOffset + element.node.rect.attr("height") / 2);
+                        yOffset += ActionsBuilder.Viewer.NODE_HEIGHT;
+                    }
+                    var separator = _this.elements[_this.elements.length - 1];
+                    separator.node = _this._viewer._createNode("", ActionsBuilder.Type.OBJECT, true);
+                    separator.node.rect.attr("fill", _this._viewer.getNodeColor(_this._viewer.selectedNode.type, _this._viewer.selectedNode.node.detached));
+                    separator.node.rect.attr("x", x);
+                    separator.node.rect.attr("y", y);
+                    separator.node.rect.attr("height", 10);
+                    _this.showing = true;
+                }
+                else {
+                    onClick(event);
+                    onRightClick(event);
+                }
+                window.event.returnValue = false;
+            };
+            document.addEventListener("click", onClick);
+            document.addEventListener("mousemove", onMouseMove);
+            element.addEventListener("contextmenu", onRightClick);
+        };
+        return ContextMenu;
+    })();
+    ActionsBuilder.ContextMenu = ContextMenu;
+})(ActionsBuilder || (ActionsBuilder = {}));

+ 149 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.contextMenu.ts

@@ -0,0 +1,149 @@
+module ActionsBuilder {
+
+    export interface ContextMenuElement {
+        text: string;
+        node: Node;
+        action: string;
+    }
+
+    export class ContextMenu {
+        public showing: boolean = false;
+        public savedColor: RaphaelColor = Raphael.rgb(255, 255, 255);
+        public overColor: RaphaelColor = Raphael.rgb(140, 200, 230);
+
+        private _viewer: Viewer = null;
+        private elements: Array<ContextMenuElement> = [
+            { text: "Reduce", node: null, action: "onReduce" },
+            { text: "Delete", node: null, action: "onRemoveNode" },
+            { text: "Delete branch", node: null, action: "onRemoveBranch" },
+            { text: "Connect / Disconnect", node: null, action: "onDetachAction" },
+            { text: "Copy", node: null, action: "onCopyStructure" },
+            { text: "Paste", node: null, action: "onPasteStructure" },
+            // Add other elements here
+            { text: "", node: null, action: null } // Color separator (top)
+        ];
+
+        /*
+        * Constructor
+        * @param viewer: the graph viewer
+        */
+        constructor(viewer: Viewer) {
+            // Members
+            this._viewer = viewer;
+
+            // Configure
+            this.attachControl(this._viewer.paper.canvas);
+        }
+
+        public attachControl(element: HTMLElement): void {
+            var onClick = (event: MouseEvent) => {
+                var x = this._viewer.mousex;
+                var y = this._viewer.mousey;
+
+                // Remove all context menu nodes, and run action if selected
+                if (this.showing) {
+                    for (var i = 0; i < this.elements.length; i++) {
+                        var element = this.elements[i];
+
+                        if (element.action && element.node.rect.isPointInside(x, y)) {
+                            this._viewer.utils[element.action]();
+                            this._viewer.update();
+                        }
+
+                        element.node.rect.remove();
+                        element.node.text.remove();
+                    }
+                }
+                this.showing = false;
+            };
+
+            var onMouseMove = (event: MouseEvent) => {
+                // Override context menu's node color if mouse is inside
+                if (this.showing) {
+                    for (var i = 0; i < this.elements.length; i++) {
+                        var element = this.elements[i];
+
+                        if (element.text === "")
+                            continue;
+
+                        var x = this._viewer.mousex;
+                        var y = this._viewer.mousey;
+
+                        if (element.node.rect.isPointInside(x, y)) {
+                            element.node.rect.attr("fill", this.overColor);
+                        }
+                        else {
+                            element.node.rect.attr("fill", this.savedColor);
+                        }
+                    }
+                }
+            };
+
+            var onRightClick = (event: MouseEvent) => {
+                var x = this._viewer.mousex;
+                var y = this._viewer.mousey;
+
+                this._viewer.onClick(event);
+
+                // Set selected node
+                var result = this._viewer.traverseGraph(null, x, y, true);
+                if (result.hit) {
+                    //this._viewer.selectedNode = result.element;
+                }
+
+                // Properly draw the context menu on the screen
+                if (y + (Viewer.NODE_HEIGHT * this.elements.length) > this._viewer.viewerElement.offsetHeight + this._viewer.viewerElement.scrollTop) {
+                    y = (Viewer.NODE_HEIGHT * this.elements.length);
+                }
+                if (x + Viewer.NODE_WIDTH > this._viewer.viewerElement.offsetWidth + this._viewer.viewerElement.scrollLeft) {
+                    x -= Viewer.NODE_WIDTH;
+                }
+
+                if (!this.showing) {
+                    if (this._viewer.selectedNode === null)
+                        return;
+
+                    // Create elements
+                    var yOffset = 10;
+
+                    for (var i = 0; i < this.elements.length - 1; i++) {
+                        var element = this.elements[i];
+
+                        element.node = this._viewer._createNode(element.text, Type.OBJECT, true);
+                        element.node.rect.attr("fill", Raphael.rgb(216, 216, 216));
+
+                        element.node.rect.attr("x", x);
+                        element.node.rect.attr("y", y + yOffset);
+
+                        element.node.text.attr("x", x + 5);
+                        element.node.text.attr("y", y + yOffset + element.node.rect.attr("height") / 2);
+
+                        yOffset += Viewer.NODE_HEIGHT;
+                    }
+
+                    // Color separator
+                    var separator = this.elements[this.elements.length - 1];
+                    separator.node = this._viewer._createNode("", Type.OBJECT, true);
+                    separator.node.rect.attr("fill", this._viewer.getNodeColor(this._viewer.selectedNode.type, this._viewer.selectedNode.node.detached));
+
+                    separator.node.rect.attr("x", x);
+                    separator.node.rect.attr("y", y);
+                    separator.node.rect.attr("height", 10);
+
+                    // Finish
+                    this.showing = true;
+                }
+                else {
+                    onClick(event);
+                    onRightClick(event);
+                }
+
+                window.event.returnValue = false;
+            };
+
+            document.addEventListener("click", onClick);
+            document.addEventListener("mousemove", onMouseMove);
+            element.addEventListener("contextmenu", onRightClick);
+        }
+    }
+} 

+ 316 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.js

@@ -0,0 +1,316 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Type = (function () {
+        function Type() {
+        }
+        Object.defineProperty(Type, "TRIGGER", {
+            get: function () {
+                return Type._TRIGGER;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Type, "ACTION", {
+            get: function () {
+                return Type._ACTION;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Type, "FLOW_CONTROL", {
+            get: function () {
+                return Type._FLOW_CONTROL;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Type, "OBJECT", {
+            get: function () {
+                return Type._OBJECT;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Type, "SCENE", {
+            get: function () {
+                return Type._SCENE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Type._TRIGGER = 0;
+        Type._ACTION = 1;
+        Type._FLOW_CONTROL = 2;
+        Type._OBJECT = 3;
+        Type._SCENE = 4;
+        return Type;
+    })();
+    ActionsBuilder.Type = Type;
+    var SceneElements = (function () {
+        function SceneElements() {
+        }
+        Object.defineProperty(SceneElements, "ENGINE", {
+            get: function () {
+                return SceneElements._ENGINE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "SCENE", {
+            get: function () {
+                return SceneElements._SCENE;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "MESH", {
+            get: function () {
+                return SceneElements._MESH;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "LIGHT", {
+            get: function () {
+                return SceneElements._LIGHT;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "CAMERA", {
+            get: function () {
+                return SceneElements._CAMERA;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "MESHES", {
+            get: function () {
+                return SceneElements._MESHES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "LIGHTS", {
+            get: function () {
+                return SceneElements._LIGHTS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "CAMERAS", {
+            get: function () {
+                return SceneElements._CAMERAS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "SOUNDS", {
+            get: function () {
+                return SceneElements._SOUNDS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "MESH_PROPERTIES", {
+            get: function () {
+                return SceneElements._MESH_PROPERTIES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "LIGHT_PROPERTIES", {
+            get: function () {
+                return SceneElements._LIGHT_PROPERTIES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "CAMERA_PROPERTIES", {
+            get: function () {
+                return SceneElements._CAMERA_PROPERTIES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "SCENE_PROPERTIES", {
+            get: function () {
+                return SceneElements._SCENE_PROPERTIES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "TYPES", {
+            get: function () {
+                return SceneElements._TYPES;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(SceneElements, "OPERATORS", {
+            get: function () {
+                return SceneElements._OPERATORS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        SceneElements.GetInstanceOf = function (object) {
+            if (object === null || object === undefined) {
+                return "";
+            }
+            return object.constructor.toString().match(/function (\w*)/)[1];
+        };
+        SceneElements.TestInstanceOf = function (object, propertyName) {
+            if (object === null || object.constructor === null) {
+                return false;
+            }
+            if (propertyName.length > 0 && propertyName[0] === "_")
+                return false;
+            var name = SceneElements.GetInstanceOf(object);
+            for (var i = 0; i < SceneElements.TYPES.length; i++) {
+                if (name === SceneElements.TYPES[i]) {
+                    return true;
+                }
+            }
+            return false;
+        };
+        SceneElements._ENGINE = new BABYLON.Engine(document.getElementById("RenderCanvasID"));
+        SceneElements._SCENE = new BABYLON.Scene(SceneElements.ENGINE);
+        SceneElements._MESH = new BABYLON.Mesh("mesh", SceneElements._SCENE);
+        SceneElements._LIGHT = new BABYLON.Light("light", SceneElements._SCENE);
+        SceneElements._CAMERA = new BABYLON.Camera("camera", BABYLON.Vector3.Zero(), SceneElements._SCENE);
+        SceneElements._MESHES = new Array();
+        SceneElements._LIGHTS = new Array();
+        SceneElements._CAMERAS = new Array();
+        SceneElements._SOUNDS = new Array();
+        SceneElements._MESH_PROPERTIES = new Array();
+        SceneElements._LIGHT_PROPERTIES = new Array();
+        SceneElements._CAMERA_PROPERTIES = new Array();
+        SceneElements._SCENE_PROPERTIES = new Array();
+        SceneElements._TYPES = new Array();
+        SceneElements._OPERATORS = new Array();
+        return SceneElements;
+    })();
+    ActionsBuilder.SceneElements = SceneElements;
+    var specialTypes = [
+        "StandardMaterial"
+    ];
+    SceneElements.MESH.material = new BABYLON.StandardMaterial("material", SceneElements.SCENE);
+    var addSpecialType = function (object, properties, thing) {
+        for (var specialThing in object[thing]) {
+            if (object[thing].hasOwnProperty(specialThing) && SceneElements.TestInstanceOf(object[thing][specialThing], specialThing)) {
+                properties.push(thing + "." + specialThing);
+            }
+        }
+    };
+    SceneElements.TYPES.push("Color3");
+    SceneElements.TYPES.push("Boolean");
+    SceneElements.TYPES.push("Number");
+    SceneElements.TYPES.push("Vector2");
+    SceneElements.TYPES.push("Vector3");
+    SceneElements.TYPES.push("String");
+    SceneElements.OPERATORS.push("IsEqual");
+    SceneElements.OPERATORS.push("IsDifferent");
+    SceneElements.OPERATORS.push("IsGreater");
+    SceneElements.OPERATORS.push("IsLesser");
+    for (var thing in SceneElements.MESH) {
+        var instance = SceneElements.GetInstanceOf(SceneElements.MESH[thing]);
+        if (SceneElements.MESH.hasOwnProperty(thing)) {
+            if (specialTypes.indexOf(instance) !== -1) {
+                addSpecialType(SceneElements.MESH, SceneElements.MESH_PROPERTIES, thing);
+            }
+            else if (SceneElements.TestInstanceOf(SceneElements.MESH[thing], thing)) {
+                SceneElements.MESH_PROPERTIES.push(thing);
+            }
+        }
+    }
+    for (var thing in SceneElements.LIGHT) {
+        if (SceneElements.LIGHT.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.LIGHT[thing], thing)) {
+            SceneElements.LIGHT_PROPERTIES.push(thing);
+        }
+    }
+    for (var thing in SceneElements.CAMERA) {
+        if (SceneElements.CAMERA.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.CAMERA[thing], thing)) {
+            SceneElements.CAMERA_PROPERTIES.push(thing);
+        }
+    }
+    for (var thing in SceneElements.SCENE) {
+        if (SceneElements.SCENE.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.SCENE[thing], thing)) {
+            SceneElements.SCENE_PROPERTIES.push(thing);
+        }
+    }
+    var Elements = (function () {
+        function Elements() {
+        }
+        Object.defineProperty(Elements, "TRIGGERS", {
+            get: function () {
+                return Elements._TRIGGERS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Elements, "ACTIONS", {
+            get: function () {
+                return Elements._ACTIONS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Elements, "FLOW_CONTROLS", {
+            get: function () {
+                return Elements._FLOW_CONTROLS;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Elements.GetElementFromName = function (name) {
+            for (var i = 0; i < Elements.TRIGGERS.length; i++) {
+                if (Elements.TRIGGERS[i].name === name) {
+                    return Elements._TRIGGERS[i];
+                }
+            }
+            for (var i = 0; i < Elements.ACTIONS.length; i++) {
+                if (Elements.ACTIONS[i].name === name) {
+                    return Elements._ACTIONS[i];
+                }
+            }
+            for (var i = 0; i < Elements.FLOW_CONTROLS.length; i++) {
+                if (Elements.FLOW_CONTROLS[i].name === name) {
+                    return Elements._FLOW_CONTROLS[i];
+                }
+            }
+            return null;
+        };
+        Elements._TRIGGERS = new Array();
+        Elements._ACTIONS = new Array();
+        Elements._FLOW_CONTROLS = new Array();
+        return Elements;
+    })();
+    ActionsBuilder.Elements = Elements;
+    Elements.TRIGGERS.push({ name: "OnPickTrigger", text: "pick", properties: [], description: "When the user picks the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnLeftPickTrigger", text: "left pick", properties: [], description: "When the user picks the edited mesh using the left click" });
+    Elements.TRIGGERS.push({ name: "OnRightPickTrigger", text: "right pick", properties: [], description: "When the user picks the edited mesh using the right click" });
+    Elements.TRIGGERS.push({ name: "OnCenterPickTrigger", text: "center pick", properties: [], description: "When the user picks the edited mesh using the click of the mouse wheel" });
+    Elements.TRIGGERS.push({ name: "OnPointerOverTrigger", text: "pointer over", properties: [], description: "When the user's mouse is over the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnPointerOutTrigger", text: "pointer out", properties: [], description: "When the user's mouse is out of the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnEveryFrameTrigger", text: "every frame", properties: [], description: "This trigger is called each frame (only on scene)" });
+    Elements.TRIGGERS.push({ name: "OnIntersectionEnterTrigger", text: "intersection enter", properties: [{ targetType: "MeshProperties", text: "parameter", value: "Object name?" }], description: "When the edited mesh intersects the another mesh predefined in the options" });
+    Elements.TRIGGERS.push({ name: "OnIntersectionExitTrigger", text: "intersection exit", properties: [{ targetType: "MeshProperties", text: "parameter", value: "Object name?" }], description: "When the edited mesh exits intersection with the another mesh predefined in the options" });
+    Elements.TRIGGERS.push({ name: "OnKeyDownTrigger", text: "key down", properties: [{ targetType: null, text: "parameter:", value: "a" }], description: "When the user pressed a key (enter the key character, example: \"r\")" });
+    Elements.TRIGGERS.push({ name: "OnKeyUpTrigger", text: "key up", properties: [{ targetType: null, text: "parameter:", value: "a" }], description: "When the user unpressed a key (enter the key character, example: \"p\")" });
+    Elements.ACTIONS.push({ name: "SwitchBooleanAction", text: "switch boolean", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }], description: "Switches the boolean value of a given parameter of the target object: true to false, or false to true" });
+    Elements.ACTIONS.push({ name: "SetStateAction", text: "set state", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "value", value: "" }], description: "Sets a new state value for the target object (example: \"off\" or \"on\")" });
+    Elements.ACTIONS.push({ name: "SetValueAction", text: "set value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }], description: "Sets a new value to the specified parameter of the target object (example: position.x to 0.0)" });
+    Elements.ACTIONS.push({ name: "SetParentAction", text: "set parent", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "parent", value: "" }], description: "Sets the new parent of the target object (example: a mesh or a light)" });
+    Elements.ACTIONS.push({ name: "IncrementValueAction", text: "increment value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }], description: "Increments the value of the given parameter of the target object. The value can be negative. (example: increment position.x of 5.0)" });
+    Elements.ACTIONS.push({ name: "PlayAnimationAction", text: "play animation", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "from", value: "0" }, { text: "to", value: "150" }, { text: "loop", value: "false" }], description: "Plays an animation of the target object. Specify the start frame, the end frame and if the animation should loop." });
+    Elements.ACTIONS.push({ name: "StopAnimationAction", text: "stop animation", properties: [{ targetType: "MeshProperties", text: "target", value: "" }], description: "Stops the animations of the target object." });
+    Elements.ACTIONS.push({ name: "DoNothingAction", text: "do nothing", properties: [], description: "Does nothing, can be used to balance/equilibrate the actions graph." });
+    Elements.ACTIONS.push({ name: "InterpolateValueAction", text: "interpolate value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "0" }, { text: "duration", value: "1000" }, { text: "stopOtherAnimations", value: "false" }], description: "Creates an animation (key frames) that animates the target object by interpolating the given parameter of the target value." });
+    Elements.ACTIONS.push({ name: "PlaySoundAction", text: "play sound", properties: [{ text: "sound", value: "" }], description: "Plays the specified sound." });
+    Elements.ACTIONS.push({ name: "StopSoundAction", text: "stop sound", properties: [{ text: "sound", value: "" }], description: "Stops the specified sound." });
+    Elements.ACTIONS.push({ name: "CombineAction", text: "combine", properties: [], description: "Special action that combines multiple actions. The combined actions are executed at the same time. Drag'n'drop the new actions inside to combine actions." });
+    Elements.FLOW_CONTROLS.push({ name: "ValueCondition", text: "value condition", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }, { text: "operator", value: SceneElements.OPERATORS[0] }], description: "A condition checking if a given value is equal, different, lesser or greater than the given parameter of the target object" });
+    Elements.FLOW_CONTROLS.push({ name: "StateCondition", text: "state condition", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "value", value: "" }], description: "A condition checking if the target object's state is equal to the given state. See \"set state\" action to set a state to an object." });
+    Elements.FLOW_CONTROLS.push({ name: "Hub", text: "hub", properties: [], description: "The hub is internally used by the Combine Action. It allows to add children to the Combine Action" });
+})(ActionsBuilder || (ActionsBuilder = {}));

+ 209 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.list.js

@@ -0,0 +1,209 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var ListElement = (function () {
+        function ListElement() {
+            this.rect = null;
+            this.text = null;
+            this.name = "";
+            this.type = ActionsBuilder.Type.TRIGGER;
+            this.element = null;
+        }
+        return ListElement;
+    })();
+    ActionsBuilder.ListElement = ListElement;
+    var List = (function () {
+        function List(viewer) {
+            var _this = this;
+            this._listElements = new Array();
+            this.listElement = document.getElementById("ListsElementID");
+            this.triggersElement = document.getElementById("TriggersListID");
+            this.actionsElement = document.getElementById("ActionsListID");
+            this.flowControlsElement = document.getElementById("FlowActionsListID");
+            this._parentContainer = document.getElementById("ParentContainerID");
+            this._viewer = viewer;
+            this.triggersList = Raphael("TriggersListID", (25 * screen.width) / 100, 400);
+            this.actionsList = Raphael("ActionsListID", (25 * screen.width) / 100, 400);
+            this.flowControlsList = Raphael("FlowActionsListID", (25 * screen.width) / 100, 400);
+            window.addEventListener("resize", function (event) {
+                _this.onResize(event);
+            });
+        }
+        Object.defineProperty(List, "ELEMENT_HEIGHT", {
+            get: function () {
+                return 25;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        List.prototype.onResize = function (event) {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.listElement.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 + "px";
+            var listElementWidth = this.listElement.getBoundingClientRect().width;
+            for (var i = 0; i < this._listElements.length; i++) {
+                var rect = this._listElements[i].rect;
+                rect.attr("width", listElementWidth - 40);
+            }
+            this.triggersList.setSize(listElementWidth, this.triggersList.height);
+            this.actionsList.setSize(listElementWidth, this.triggersList.height);
+            this.flowControlsList.setSize(listElementWidth, this.triggersList.height);
+        };
+        List.prototype.createListsElements = function () {
+            var excludedTriggers = [6, 9, 10];
+            var yPosition = 10;
+            var textColor = Raphael.rgb(61, 72, 76);
+            var whiteColor = Raphael.rgb(255, 255, 255);
+            var configureTitle = function (listElement, rectColor) {
+                listElement.text.attr("x", 15);
+                listElement.rect.attr("fill", rectColor);
+                listElement.text.attr("font-family", "Sinkin Sans Medium");
+                listElement.text.attr("font-size", "11");
+            };
+            var triggers = this._createListElement(this.triggersList, yPosition, "TRIGGERS", ActionsBuilder.Type.TRIGGER, whiteColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(triggers, Raphael.rgb(41, 129, 255));
+            for (var i = 0; i < ActionsBuilder.Elements.TRIGGERS.length; i++) {
+                var element = ActionsBuilder.Elements.TRIGGERS[i];
+                if (this._viewer.root.type === ActionsBuilder.Type.OBJECT && excludedTriggers.indexOf(i) !== -1) {
+                    continue;
+                }
+                else if (this._viewer.root.type === ActionsBuilder.Type.SCENE && excludedTriggers.indexOf(i) === -1) {
+                    continue;
+                }
+                var trigger = this._createListElement(this.triggersList, yPosition, element.text, ActionsBuilder.Type.TRIGGER, textColor, true, element);
+                trigger.rect.attr("fill", Raphael.rgb(133, 154, 185));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+            yPosition += List.ELEMENT_HEIGHT;
+            this.triggersElement.style.height = this.triggersList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.triggersList, this.triggersElement, triggers, yPosition);
+            yPosition = 10;
+            var actions = this._createListElement(this.actionsList, yPosition, "ACTIONS", ActionsBuilder.Type.ACTION, textColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(actions, Raphael.rgb(255, 220, 42));
+            for (var i = 0; i < ActionsBuilder.Elements.ACTIONS.length; i++) {
+                var element = ActionsBuilder.Elements.ACTIONS[i];
+                var action = this._createListElement(this.actionsList, yPosition, element.text, ActionsBuilder.Type.ACTION, textColor, true, element);
+                action.rect.attr("fill", Raphael.rgb(182, 185, 132));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+            yPosition += List.ELEMENT_HEIGHT;
+            this.actionsElement.style.height = this.actionsList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.actionsList, this.actionsElement, actions, yPosition);
+            yPosition = 10;
+            var flowControls = this._createListElement(this.flowControlsList, yPosition, "FLOW CONTROLS", ActionsBuilder.Type.FLOW_CONTROL, whiteColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(flowControls, Raphael.rgb(255, 41, 53));
+            for (var i = 0; i < ActionsBuilder.Elements.FLOW_CONTROLS.length - 1; i++) {
+                var element = ActionsBuilder.Elements.FLOW_CONTROLS[i];
+                var flowControl = this._createListElement(this.flowControlsList, yPosition, element.text, ActionsBuilder.Type.FLOW_CONTROL, textColor, true, element);
+                flowControl.rect.attr("fill", Raphael.rgb(185, 132, 140));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+            yPosition += List.ELEMENT_HEIGHT;
+            this.flowControlsElement.style.height = this.flowControlsList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.flowControlsList, this.flowControlsElement, flowControls, yPosition);
+        };
+        List.prototype.clearLists = function () {
+            for (var i = 0; i < this._listElements.length; i++) {
+                this._removeListElement(this._listElements[i]);
+            }
+            this._listElements.splice(0, this._listElements.length - 1);
+        };
+        List.prototype.setColorTheme = function (color) {
+            this.triggersList.canvas.style.backgroundColor = color;
+            this.actionsList.canvas.style.backgroundColor = color;
+            this.flowControlsList.canvas.style.backgroundColor = color;
+        };
+        List.prototype._createListElement = function (paper, yPosition, text, type, textColor, drag, element) {
+            var object = new ListElement();
+            object.rect = paper.rect(10, yPosition, 300, List.ELEMENT_HEIGHT);
+            object.text = paper.text(30, yPosition + object.rect.attr("height") / 2, text);
+            object.text.attr("fill", textColor);
+            object.text.attr("text-anchor", "start");
+            object.text.attr("font-size", "12");
+            object.text.attr("text-anchor", "start");
+            object.text.attr("font-family", "Sinkin Sans Light");
+            if (drag) {
+                this._createListElementAnimation(object);
+            }
+            object.type = type;
+            object.element = element;
+            this._listElements.push(object);
+            return object;
+        };
+        List.prototype._removeListElement = function (element) {
+            element.rect.remove();
+            element.text.remove();
+        };
+        List.prototype._createCollapseAnimation = function (paper, htmlElement, element, expandedHeight) {
+            var onClick = function (event) {
+                var height = htmlElement.style.height;
+                if (height === expandedHeight + "px") {
+                    htmlElement.style.height = paper.canvas.style.height = 35 + "px";
+                }
+                else {
+                    htmlElement.style.height = paper.canvas.style.height = expandedHeight + "px";
+                }
+            };
+            element.rect.click(onClick);
+        };
+        List.prototype._createListElementAnimation = function (element) {
+            var _this = this;
+            var onMove = function (dx, dy, x, y) { };
+            var onStart = function (x, y, event) {
+                _this._parentContainer.style.cursor = "copy";
+                element.rect.animate({
+                    x: -10,
+                    opacity: 0.25
+                }, 500, ">");
+                element.text.animate({
+                    x: 10,
+                    opacity: 0.25
+                }, 500, ">");
+            };
+            var onEnd = function (event) {
+                _this._parentContainer.style.cursor = "default";
+                element.rect.animate({
+                    x: 10,
+                    opacity: 1.0
+                }, 500, "<");
+                element.text.animate({
+                    x: 30,
+                    opacity: 1.0
+                }, 500, "<");
+                var dragResult = _this._viewer.traverseGraph(null, _this._viewer.mousex, _this._viewer.mousey, false);
+                if (dragResult.hit) {
+                    if (element.type === ActionsBuilder.Type.TRIGGER && dragResult.action !== _this._viewer.root) {
+                        alert("Triggers can be dragged only on the root node (the mesh)");
+                        return;
+                    }
+                    if (element.type === ActionsBuilder.Type.ACTION && dragResult.action === _this._viewer.root) {
+                        alert("Please add a trigger before.");
+                        return;
+                    }
+                    if (element.type === ActionsBuilder.Type.FLOW_CONTROL && dragResult.action === _this._viewer.root) {
+                        return;
+                    }
+                    if (element.type === ActionsBuilder.Type.FLOW_CONTROL && dragResult.action.combineArray !== null) {
+                        alert("A condition cannot be handled by a Combine Action.");
+                        return;
+                    }
+                    if ((element.type === ActionsBuilder.Type.FLOW_CONTROL || element.type === ActionsBuilder.Type.ACTION) && dragResult.action.type === ActionsBuilder.Type.TRIGGER && dragResult.action.children.length > 0) {
+                        alert("Triggers can have only one child. Please add another trigger of same type.");
+                        return;
+                    }
+                    if (!(dragResult.action.combineArray !== null) && dragResult.action.children.length > 0 && dragResult.action.type !== ActionsBuilder.Type.TRIGGER && dragResult.action !== _this._viewer.root) {
+                        alert("An action can have only one child.");
+                        return;
+                    }
+                    _this._viewer.addAction(dragResult.action, element.type, element.element);
+                    _this._viewer.update();
+                }
+            };
+            element.rect.drag(onMove, onStart, onEnd);
+            element.text.drag(onMove, onStart, onEnd);
+        };
+        return List;
+    })();
+    ActionsBuilder.List = List;
+})(ActionsBuilder || (ActionsBuilder = {}));

+ 306 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.list.ts

@@ -0,0 +1,306 @@
+module ActionsBuilder {
+    export class ListElement {
+        public rect: Rect = null;
+        public text: Text = null;
+        public name: string = "";
+        public type: number = Type.TRIGGER;
+        public element: Element = null;
+    }
+
+    export class List {
+        public listElement: HTMLElement;
+        public triggersElement: HTMLElement;
+        public actionsElement: HTMLElement;
+        public flowControlsElement: HTMLElement;
+
+        public triggersList: Paper;
+        public actionsList: Paper;
+        public flowControlsList: Paper;
+
+        private _parentContainer: HTMLElement;
+        private _listElements: Array<ListElement> = new Array<ListElement>();
+        private _viewer: Viewer;
+
+        public static get ELEMENT_HEIGHT(): number {
+            return 25;
+        }
+
+        /**
+        * Constructor
+        */
+        constructor(viewer: Viewer) {
+            // Get HTML elements
+            this.listElement = document.getElementById("ListsElementID");
+            this.triggersElement = document.getElementById("TriggersListID");
+            this.actionsElement = document.getElementById("ActionsListID");
+            this.flowControlsElement = document.getElementById("FlowActionsListID");
+            this._parentContainer = document.getElementById("ParentContainerID");
+
+            // Configure this
+            this._viewer = viewer;
+
+            // Create elements (lists)
+            this.triggersList = Raphael("TriggersListID", (25 * screen.width) / 100, 400);
+            this.actionsList = Raphael("ActionsListID", (25 * screen.width) / 100, 400);
+            this.flowControlsList = Raphael("FlowActionsListID", (25 * screen.width) / 100, 400);
+
+            // Manage events
+            window.addEventListener("resize", (event: Event) => {
+                this.onResize(event);
+            });
+        }
+
+        /**
+        * Resize event that resizes the list element dynamically
+        * @param event: the resize event
+        */
+        public onResize(event?: Event): void {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.listElement.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 + "px";
+
+            var listElementWidth = this.listElement.getBoundingClientRect().width;
+            for (var i = 0; i < this._listElements.length; i++) {
+                var rect = this._listElements[i].rect;
+                rect.attr("width", listElementWidth - 40);
+            }
+
+            this.triggersList.setSize(listElementWidth, this.triggersList.height);
+            this.actionsList.setSize(listElementWidth, this.triggersList.height);
+            this.flowControlsList.setSize(listElementWidth, this.triggersList.height);
+
+        }
+
+        public createListsElements(): void {
+            var excludedTriggers = [6, 9, 10];
+            var yPosition = 10;
+            var textColor = Raphael.rgb(61, 72, 76);
+            var whiteColor = Raphael.rgb(255, 255, 255);
+
+            var configureTitle = (listElement: ListElement, rectColor: RaphaelColor) => {
+                listElement.text.attr("x", 15);
+                listElement.rect.attr("fill", rectColor);
+                listElement.text.attr("font-family", "Sinkin Sans Medium");
+                listElement.text.attr("font-size", "11");
+            };
+
+            // Create triggers
+            var triggers = this._createListElement(this.triggersList, yPosition, "TRIGGERS", Type.TRIGGER, whiteColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(triggers, Raphael.rgb(41, 129, 255));
+
+            for (var i = 0; i < Elements.TRIGGERS.length; i++) {
+                var element: any = Elements.TRIGGERS[i];
+
+                if (this._viewer.root.type === Type.OBJECT && excludedTriggers.indexOf(i) !== -1) {
+                    continue;
+                }
+                else if (this._viewer.root.type === Type.SCENE && excludedTriggers.indexOf(i) === -1) {
+                    continue;
+                }
+
+                var trigger = this._createListElement(this.triggersList, yPosition, element.text, Type.TRIGGER, textColor, true, element);
+
+                trigger.rect.attr("fill", Raphael.rgb(133, 154, 185));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+
+            yPosition += List.ELEMENT_HEIGHT;
+            this.triggersElement.style.height = this.triggersList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.triggersList, this.triggersElement, triggers, yPosition);
+
+            // Create actions
+            yPosition = 10;
+            var actions = this._createListElement(this.actionsList, yPosition, "ACTIONS", Type.ACTION, textColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(actions, Raphael.rgb(255, 220, 42));
+
+            for (var i = 0; i < Elements.ACTIONS.length; i++) {
+                var element: any = Elements.ACTIONS[i];
+                var action = this._createListElement(this.actionsList, yPosition, element.text, Type.ACTION, textColor, true, element);
+
+                action.rect.attr("fill", Raphael.rgb(182, 185, 132));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+
+            yPosition += List.ELEMENT_HEIGHT;
+            this.actionsElement.style.height = this.actionsList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.actionsList, this.actionsElement, actions, yPosition);
+
+            // Create flow controls
+            yPosition = 10;
+            var flowControls = this._createListElement(this.flowControlsList, yPosition, "FLOW CONTROLS", Type.FLOW_CONTROL, whiteColor, false);
+            yPosition += List.ELEMENT_HEIGHT;
+            configureTitle(flowControls, Raphael.rgb(255, 41, 53));
+
+            for (var i = 0; i < Elements.FLOW_CONTROLS.length - 1; i++) {
+                var element: any = Elements.FLOW_CONTROLS[i];
+                var flowControl = this._createListElement(this.flowControlsList, yPosition, element.text, Type.FLOW_CONTROL, textColor, true, element);
+
+                flowControl.rect.attr("fill", Raphael.rgb(185, 132, 140));
+                yPosition += List.ELEMENT_HEIGHT;
+            }
+
+            yPosition += List.ELEMENT_HEIGHT;
+            this.flowControlsElement.style.height = this.flowControlsList.canvas.style.height = yPosition + "px";
+            this._createCollapseAnimation(this.flowControlsList, this.flowControlsElement, flowControls, yPosition);
+        }
+
+        /**
+        * Clears the list of elements and removes the elements
+        */
+        public clearLists(): void {
+            for (var i = 0; i < this._listElements.length; i++) {
+                this._removeListElement(this._listElements[i]);
+            }
+            this._listElements.splice(0, this._listElements.length - 1);
+        }
+
+        /**
+        * Sets the color theme of the lists
+        * @param color: the theme color
+        */
+        public setColorTheme(color: string): void {
+            this.triggersList.canvas.style.backgroundColor = color;
+            this.actionsList.canvas.style.backgroundColor = color;
+            this.flowControlsList.canvas.style.backgroundColor = color;
+        }
+
+        /**
+        * Creates a list element
+        * @param paper: the Raphael.js paper
+        * @param yPosition: the y position of the element
+        * @param text: the element text
+        * @param type: the element type (trigger, action, flow control)
+        * @param textColor: the text color
+        * @param drag: if the element should be drag'n'dropped
+        */
+        private _createListElement(paper: Paper, yPosition: number, text: string, type: number,
+                                   textColor: RaphaelColor, drag: boolean, element?: Element): ListElement
+        {
+            var object = new ListElement();
+
+            object.rect = paper.rect(10, yPosition, 300, List.ELEMENT_HEIGHT);
+
+            object.text = paper.text(30, yPosition + object.rect.attr("height") / 2, text);
+            object.text.attr("fill", textColor);
+            object.text.attr("text-anchor", "start");
+            object.text.attr("font-size", "12");
+            object.text.attr("text-anchor", "start");
+            object.text.attr("font-family", "Sinkin Sans Light");
+
+            if (drag) {
+                this._createListElementAnimation(object);
+            }
+
+            object.type = type;
+            object.element = element;
+
+            this._listElements.push(object);
+            return object;
+        }
+
+        /**
+        * Removes a list element
+        * @param element: the element to remove
+        */
+        private _removeListElement(element: ListElement): void {
+            element.rect.remove();
+            element.text.remove();
+        }
+
+        /*
+        * Creates the collapse animation of a list
+        * @param paper: the list paper
+        * @param htmlElement: the list div container
+        * @param element: the list element to click on
+        * @param expandedHeight: the height when the list is expanded
+        */
+        private _createCollapseAnimation(paper: Paper, htmlElement: HTMLElement, element: ListElement, expandedHeight: number): void {
+            var onClick = (event: MouseEvent) => {
+                var height = htmlElement.style.height;
+                if (height === expandedHeight + "px") {
+                    htmlElement.style.height = paper.canvas.style.height = 35 + "px";
+                }
+                else {
+                    htmlElement.style.height = paper.canvas.style.height = expandedHeight + "px";
+                }
+            };
+
+            element.rect.click(onClick);
+        }
+
+        /*
+        * Creates the animation of a list element
+        * @param element: the list element to animate
+        */
+        private _createListElementAnimation(element: ListElement): void {
+            var onMove = (dx: number, dy: number, x: number, y: number) =>
+            { };
+
+            var onStart = (x: number, y: number, event: MouseEvent) => {
+                this._parentContainer.style.cursor = "copy";
+                element.rect.animate({
+                    x: -10,
+                    opacity: 0.25
+                }, 500, ">");
+                element.text.animate({
+                    x: 10,
+                    opacity: 0.25
+                }, 500, ">");
+            };
+
+            var onEnd = (event: MouseEvent) => {
+                this._parentContainer.style.cursor = "default";
+                element.rect.animate({
+                    x: 10,
+                    opacity: 1.0
+                }, 500, "<");
+                element.text.animate({
+                    x: 30,
+                    opacity: 1.0
+                }, 500, "<");
+
+                var dragResult = this._viewer.traverseGraph(null, this._viewer.mousex, this._viewer.mousey, false);
+
+                if (dragResult.hit) {
+                    if (element.type === Type.TRIGGER && dragResult.action !== this._viewer.root) {
+                        alert("Triggers can be dragged only on the root node (the mesh)");
+                        return;
+                    }
+
+                    if (element.type === Type.ACTION && dragResult.action === this._viewer.root) {
+                        alert("Please add a trigger before.");
+                        return;
+                    }
+
+                    //if (element.type === Type.FLOW_CONTROL && (dragResult.action === this._viewer.root || (dragResult.action.type === Type.FLOW_CONTROL && dragResult.action.parent.hub === null))) {
+                    if (element.type === Type.FLOW_CONTROL && dragResult.action === this._viewer.root) {
+                        return;
+                    }
+
+                    if (element.type === Type.FLOW_CONTROL && dragResult.action.combineArray !== null) {
+                        alert("A condition cannot be handled by a Combine Action.");
+                        return;
+                    }
+
+                    if ((element.type === Type.FLOW_CONTROL || element.type === Type.ACTION) && dragResult.action.type === Type.TRIGGER && dragResult.action.children.length > 0) {
+                        alert("Triggers can have only one child. Please add another trigger of same type.");
+                        return;
+                    }
+
+                    if (!(dragResult.action.combineArray !== null) && dragResult.action.children.length > 0 && dragResult.action.type !== Type.TRIGGER && dragResult.action !== this._viewer.root) {
+                        alert("An action can have only one child.");
+                        return;
+                    }
+
+                    this._viewer.addAction(dragResult.action, element.type, element.element);
+                    this._viewer.update();
+                }
+            };
+
+            element.rect.drag(onMove, onStart, onEnd);
+            element.text.drag(onMove, onStart, onEnd);
+        }
+
+    }
+}

+ 96 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.main.js

@@ -0,0 +1,96 @@
+/*
+Global functions called by the plugins (3ds Max, etc.)
+*/
+var list = null;
+var viewer = null;
+var actionsBuilderJsonInput = document.getElementById("ActionsBuilderJSON");
+this.createJSON = function () {
+    var structure = viewer.utils.createJSON(viewer.root);
+    var asText = JSON.stringify(structure);
+    actionsBuilderJsonInput.value = asText;
+    console.log(asText);
+};
+this.loadFromJSON = function () {
+    var json = actionsBuilderJsonInput.value;
+    if (json !== "") {
+        var structure = JSON.parse(json);
+        viewer.utils.loadFromJSON(structure, null);
+    }
+};
+this.updateObjectName = function () {
+    var element = document.getElementById("ActionsBuilderObjectName");
+    var name = element.value;
+    viewer.objectName = name;
+    if (viewer.root.type === ActionsBuilder.Type.OBJECT) {
+        name += " - Mesh";
+    }
+    else {
+        name += " - Scene";
+    }
+    viewer.root.node.text.attr("text", name);
+};
+this.resetList = function () {
+    list.clearLists();
+    list.createListsElements();
+};
+this.setMeshesNames = function () {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i - 0] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.MESHES.push(args[i]);
+    }
+};
+this.setLightsNames = function () {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i - 0] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.LIGHTS.push(args[i]);
+    }
+};
+this.setCamerasNames = function () {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i - 0] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.CAMERAS.push(args[i]);
+    }
+};
+this.setSoundsNames = function () {
+    var args = [];
+    for (var _i = 0; _i < arguments.length; _i++) {
+        args[_i - 0] = arguments[_i];
+    }
+    for (var i = 0; i < args.length; i++) {
+        var sound = args[i];
+        if (sound !== "" && ActionsBuilder.SceneElements.SOUNDS.indexOf(sound) === -1) {
+            ActionsBuilder.SceneElements.SOUNDS.push(args[i]);
+        }
+    }
+};
+this.hideButtons = function () {
+};
+this.setIsObject = function () {
+    viewer.root.type = ActionsBuilder.Type.OBJECT;
+};
+this.setIsScene = function () {
+    viewer.root.type = ActionsBuilder.Type.SCENE;
+};
+this.run = function () {
+    viewer = new ActionsBuilder.Viewer(ActionsBuilder.Type.OBJECT);
+    viewer.setColorTheme("-ms-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("-webkit-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("-o-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.onResize();
+    viewer.update();
+    list = new ActionsBuilder.List(viewer);
+    list.setColorTheme("rgb(64, 64, 64)");
+    list.createListsElements();
+    list.onResize();
+    viewer.onResize();
+};

+ 118 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.main.ts

@@ -0,0 +1,118 @@
+/*
+Global functions called by the plugins (3ds Max, etc.)
+*/
+
+// Elements
+
+var list: ActionsBuilder.List = null;
+var viewer: ActionsBuilder.Viewer = null;
+
+var actionsBuilderJsonInput: HTMLInputElement = <HTMLInputElement>document.getElementById("ActionsBuilderJSON");
+
+this.getList = () => {
+    return list;
+};
+
+this.getViewer = () => {
+    return viewer;
+};
+
+
+this.createJSON = () => {
+    var structure = viewer.utils.createJSON(viewer.root);
+    var asText = JSON.stringify(structure);
+    actionsBuilderJsonInput.value = asText;
+    console.log(asText);
+};
+
+this.loadFromJSON = () => {
+    var json = actionsBuilderJsonInput.value;
+    if (json !== "") {
+        var structure = JSON.parse(json);
+        viewer.utils.loadFromJSON(structure, null);
+    }
+};
+
+this.updateObjectName = () => {
+    var element = <HTMLInputElement>document.getElementById("ActionsBuilderObjectName");
+    var name = element.value;
+
+    viewer.objectName = name;
+
+    if (viewer.root.type === ActionsBuilder.Type.OBJECT) {
+        name += " - Mesh";
+    }
+    else {
+        name += " - Scene";
+    }
+
+    viewer.root.node.text.attr("text", name);
+};
+
+this.resetList = () => {
+    list.clearLists();
+    list.createListsElements();
+};
+
+this.setMeshesNames = (...args: string[]) => {
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.MESHES.push(args[i]);
+    }
+};
+
+this.setLightsNames = (...args: string[]) => {
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.LIGHTS.push(args[i]);
+    }
+};
+
+this.setCamerasNames = (...args: string[]) => {
+    for (var i = 0; i < args.length; i++) {
+        ActionsBuilder.SceneElements.CAMERAS.push(args[i]);
+    }
+};
+
+this.setSoundsNames = (...args: string[]) => {
+    for (var i = 0; i < args.length; i++) {
+        var sound = args[i];
+
+        if (sound !== "" && ActionsBuilder.SceneElements.SOUNDS.indexOf(sound) === -1) {
+            ActionsBuilder.SceneElements.SOUNDS.push(args[i]);
+        }
+    }
+};
+
+this.hideButtons = () => {
+    // Empty
+};
+
+this.setIsObject = () => {
+    viewer.root.type = ActionsBuilder.Type.OBJECT;
+};
+
+this.setIsScene = () => {
+    viewer.root.type = ActionsBuilder.Type.SCENE;
+};
+
+this.run = () => {
+    // Configure viewer
+    viewer = new ActionsBuilder.Viewer(ActionsBuilder.Type.OBJECT);
+
+    viewer.setColorTheme("-ms-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("-webkit-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+    viewer.setColorTheme("-o-linear-gradient(top, rgba(38, 38, 38,1) 0%, rgba(125, 126, 125, 1) 100%)");
+
+    viewer.onResize();
+    viewer.update();
+
+    // Configure list
+    list = new ActionsBuilder.List(viewer);
+    list.setColorTheme("rgb(64, 64, 64)");
+
+    list.createListsElements();
+    list.onResize();
+
+    // 3ds Max fix
+    viewer.onResize();
+};

+ 440 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.parameters.js

@@ -0,0 +1,440 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Parameters = (function () {
+        function Parameters(viewer) {
+            var _this = this;
+            this._action = null;
+            this.parametersContainer = document.getElementById("ParametersElementID");
+            this.parametersHelpElement = document.getElementById("ParametersHelpElementID");
+            this._viewer = viewer;
+            window.addEventListener("resize", function (event) {
+                _this.onResize(event);
+            });
+        }
+        Parameters.prototype.clearParameters = function () {
+            if (this.parametersContainer.children === null) {
+                return;
+            }
+            while (this.parametersContainer.children.length > 0) {
+                this.parametersContainer.removeChild(this.parametersContainer.firstChild);
+            }
+        };
+        Parameters.prototype.createParameters = function (action) {
+            this._action = action;
+            this.clearParameters();
+            if (action === null) {
+                return;
+            }
+            this._createHelpSection(action);
+            this._createNodeSection(action);
+            var properties = action.properties;
+            var propertiesResults = action.propertiesResults;
+            var targetParameterSelect = null;
+            var targetParameterNameSelect = null;
+            var propertyPathSelect = null;
+            var propertyPathOptionalSelect = null;
+            var booleanSelect = null;
+            var propertyInput = null;
+            var propertyPathIndice = -1;
+            if (properties.length === 0) {
+                return;
+            }
+            for (var i = 0; i < properties.length; i++) {
+                var separator = document.createElement("hr");
+                separator.noShade = true;
+                separator.className = "ParametersElementSeparatorClass";
+                this.parametersContainer.appendChild(separator);
+                var parameterName = document.createElement("a");
+                parameterName.text = properties[i].text;
+                parameterName.className = "ParametersElementTitleClass";
+                this.parametersContainer.appendChild(parameterName);
+                if (properties[i].text === "parameter" || properties[i].text === "target" || properties[i].text === "parent") {
+                    targetParameterSelect = document.createElement("select");
+                    targetParameterSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(targetParameterSelect);
+                    targetParameterNameSelect = document.createElement("select");
+                    targetParameterNameSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(targetParameterNameSelect);
+                    (this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i))(null);
+                    targetParameterSelect.value = propertiesResults[i].targetType;
+                    targetParameterNameSelect.value = propertiesResults[i].value;
+                    targetParameterSelect.onchange = this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i);
+                    targetParameterNameSelect.onchange = this._parameterTargetNameChanged(targetParameterSelect, targetParameterNameSelect, i);
+                }
+                else if (properties[i].text === "propertyPath") {
+                    propertyPathIndice = i;
+                    propertyPathSelect = document.createElement("select");
+                    propertyPathSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(propertyPathSelect);
+                    propertyPathOptionalSelect = document.createElement("select");
+                    propertyPathOptionalSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(propertyPathOptionalSelect);
+                    (this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, null, null, i))(null);
+                    var property = this._action.propertiesResults[i].value.split(".");
+                    if (property.length > 0) {
+                        if (property.length === 1) {
+                            propertyPathSelect.value = property[0];
+                        }
+                        else {
+                            var completePropertyPath = "";
+                            for (var j = 0; j < property.length - 1; j++) {
+                                completePropertyPath += property[j];
+                                completePropertyPath += (j === property.length - 2) ? "" : ".";
+                            }
+                            propertyPathSelect.value = completePropertyPath;
+                            this._viewer.utils.setElementVisible(propertyPathOptionalSelect, true);
+                        }
+                        this._fillAdditionalPropertyPath(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect);
+                        propertyPathOptionalSelect.value = property[property.length - 1];
+                        if (propertyPathOptionalSelect.options.length === 0 || propertyPathOptionalSelect.options[0].text === "") {
+                            this._viewer.utils.setElementVisible(propertyPathOptionalSelect, false);
+                        }
+                    }
+                    targetParameterSelect.onchange = this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i - 1);
+                    propertyPathSelect.onchange = this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, null, null, i);
+                    propertyPathOptionalSelect.onchange = this._additionalPropertyPathSelectChanged(propertyPathSelect, propertyPathOptionalSelect, i);
+                }
+                else if (properties[i].text === "operator") {
+                    var conditionOperatorSelect = document.createElement("select");
+                    conditionOperatorSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(conditionOperatorSelect);
+                    (this._conditionOperatorSelectChanged(conditionOperatorSelect, i))(null);
+                    conditionOperatorSelect.value = propertiesResults[i].value;
+                    conditionOperatorSelect.onchange = this._conditionOperatorSelectChanged(conditionOperatorSelect, i);
+                }
+                else if (properties[i].text === "sound") {
+                    var soundSelect = document.createElement("select");
+                    soundSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(soundSelect);
+                    (this._soundSelectChanged(soundSelect, i))(null);
+                    soundSelect.value = propertiesResults[i].value;
+                    soundSelect.onchange = this._soundSelectChanged(soundSelect, i);
+                }
+                else {
+                    var isBoolean = propertiesResults[i].value === "true" || propertiesResults[i].value === "false";
+                    var object = this._getObjectFromType(targetParameterSelect.value);
+                    if (object !== null) {
+                        var property = this._action.propertiesResults[i - 1].value.split(".");
+                        for (var j = 0; j < property.length && object !== undefined; j++) {
+                            object = object[property[j]];
+                            if (j === property.length - 1) {
+                                isBoolean = isBoolean || typeof object === "boolean";
+                            }
+                        }
+                    }
+                    booleanSelect = document.createElement("select");
+                    booleanSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(booleanSelect);
+                    (this._booleanSelectChanged(booleanSelect, i))(null);
+                    booleanSelect.value = propertiesResults[i].value;
+                    booleanSelect.onchange = this._booleanSelectChanged(booleanSelect, i);
+                    propertyInput = document.createElement("input");
+                    propertyInput.value = propertiesResults[i].value;
+                    propertyInput.className = "ParametersElementInputClass";
+                    this.parametersContainer.appendChild(propertyInput);
+                    propertyInput.onkeyup = this._propertyInputChanged(propertyInput, i);
+                    if (propertyPathIndice !== -1 && properties[i].text === "value") {
+                        propertyPathSelect.onchange = this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, booleanSelect, propertyInput, propertyPathIndice);
+                    }
+                    if (isBoolean) {
+                        this._viewer.utils.setElementVisible(booleanSelect, true);
+                        this._viewer.utils.setElementVisible(propertyInput, false);
+                    }
+                    else {
+                        this._viewer.utils.setElementVisible(booleanSelect, false);
+                        this._viewer.utils.setElementVisible(propertyInput, true);
+                    }
+                }
+            }
+        };
+        Parameters.prototype.onResize = function (event) {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.parametersContainer.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 200 + "px";
+            this.parametersHelpElement.style.height = 200 + "px";
+        };
+        Parameters.prototype._booleanSelectChanged = function (booleanSelect, indice) {
+            var _this = this;
+            return function (ev) {
+                if (booleanSelect.options.length === 0) {
+                    var values = ["true", "false"];
+                    for (var i = 0; i < values.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = values[i];
+                        booleanSelect.options.add(option);
+                    }
+                }
+                else {
+                    _this._action.propertiesResults[indice].value = booleanSelect.value;
+                }
+            };
+        };
+        Parameters.prototype._soundSelectChanged = function (soundSelect, indice) {
+            var _this = this;
+            return function (ev) {
+                if (soundSelect.options.length === 0) {
+                    for (var i = 0; i < ActionsBuilder.SceneElements.SOUNDS.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = ActionsBuilder.SceneElements.SOUNDS[i];
+                        soundSelect.options.add(option);
+                    }
+                    _this._sortList(soundSelect);
+                }
+                else {
+                    _this._action.propertiesResults[indice].value = soundSelect.value;
+                }
+            };
+        };
+        Parameters.prototype._conditionOperatorSelectChanged = function (conditionOperatorSelect, indice) {
+            var _this = this;
+            return function (ev) {
+                if (conditionOperatorSelect.options.length === 0) {
+                    for (var i = 0; i < ActionsBuilder.SceneElements.OPERATORS.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = ActionsBuilder.SceneElements.OPERATORS[i];
+                        conditionOperatorSelect.options.add(option);
+                    }
+                }
+                else {
+                    _this._action.propertiesResults[indice].value = conditionOperatorSelect.value;
+                }
+            };
+        };
+        Parameters.prototype._propertyInputChanged = function (propertyInput, indice) {
+            var _this = this;
+            return function (ev) {
+                _this._action.propertiesResults[indice].value = propertyInput.value;
+            };
+        };
+        Parameters.prototype._propertyPathSelectChanged = function (targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect, booleanSelect, propertyInput, indice) {
+            var _this = this;
+            return function (event) {
+                if (propertyPathSelect.options.length === 0) {
+                    var properties = _this._getPropertiesFromType(targetParameterSelect.value);
+                    if (properties !== null) {
+                        for (var i = 0; i < properties.length; i++) {
+                            var option = document.createElement("option");
+                            option.value = option.text = properties[i];
+                            propertyPathSelect.options.add(option);
+                        }
+                    }
+                }
+                else {
+                    _this._action.propertiesResults[indice].value = propertyPathSelect.value;
+                    if (booleanSelect !== null && propertyInput !== null) {
+                        var object = _this._getObjectFromType(targetParameterSelect.value);
+                        var isBoolean = false;
+                        if (object !== null) {
+                            var property = _this._action.propertiesResults[indice].value.split(".");
+                            for (var j = 0; j < property.length; j++) {
+                                object = object[property[j]];
+                                if (j === property.length - 1) {
+                                    isBoolean = isBoolean || typeof object === "boolean";
+                                }
+                            }
+                        }
+                        if (isBoolean) {
+                            _this._viewer.utils.setElementVisible(booleanSelect, true);
+                            _this._viewer.utils.setElementVisible(propertyInput, false);
+                        }
+                        else {
+                            _this._viewer.utils.setElementVisible(booleanSelect, false);
+                            _this._viewer.utils.setElementVisible(propertyInput, true);
+                        }
+                    }
+                }
+                _this._fillAdditionalPropertyPath(targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect);
+                _this._sortList(propertyPathSelect);
+            };
+        };
+        Parameters.prototype._fillAdditionalPropertyPath = function (targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect) {
+            additionalPropertyPathSelect.options.length = 0;
+            var object = this._getObjectFromType(targetParameterSelect.value);
+            if (object !== null) {
+                var propertyPath = propertyPathSelect.value.split(".");
+                for (var i = 0; i < propertyPath.length; i++) {
+                    object = object[propertyPath[i]];
+                }
+            }
+            if (object === null || object === undefined || (typeof (object)).toLowerCase() === "string") {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, false);
+                return;
+            }
+            var emptyOption = document.createElement("option");
+            emptyOption.value = emptyOption.text = "";
+            additionalPropertyPathSelect.add(emptyOption);
+            for (var thing in object) {
+                var type = ActionsBuilder.SceneElements.GetInstanceOf(object[thing]);
+                var index = ActionsBuilder.SceneElements.TYPES.indexOf(type);
+                if (index !== -1) {
+                    var option = document.createElement("option");
+                    option.value = option.text = thing;
+                    additionalPropertyPathSelect.options.add(option);
+                    emptyOption.text += thing + ", ";
+                }
+            }
+            if (additionalPropertyPathSelect.options.length === 0 || additionalPropertyPathSelect.options[0].text === "") {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, false);
+            }
+            else {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, true);
+            }
+        };
+        Parameters.prototype._additionalPropertyPathSelectChanged = function (propertyPathSelect, additionalPropertyPathSelect, indice) {
+            var _this = this;
+            return function (event) {
+                var property = propertyPathSelect.value;
+                var additionalProperty = additionalPropertyPathSelect.value;
+                if (additionalProperty !== "") {
+                    property += ".";
+                    property += additionalPropertyPathSelect.value;
+                }
+                _this._action.propertiesResults[indice].value = property;
+            };
+        };
+        Parameters.prototype._parameterTargetChanged = function (targetParameterSelect, targetParameterNameSelect, propertyPathSelect, additionalPropertyPathSelect, indice) {
+            var _this = this;
+            return function (event) {
+                if (targetParameterSelect.options.length === 0) {
+                    var options = [
+                        { text: "Mesh", targetType: "MeshProperties" },
+                        { text: "Light", targetType: "LightProperties" },
+                        { text: "Camera", targetType: "CameraProperties" },
+                        { text: "Scene", targetType: "SceneProperties" }
+                    ];
+                    targetParameterSelect.options.length = 0;
+                    for (var i = 0; i < options.length; i++) {
+                        var option = document.createElement("option");
+                        option.text = options[i].text;
+                        option.value = options[i].targetType;
+                        targetParameterSelect.options.add(option);
+                    }
+                    targetParameterSelect.value = _this._action.propertiesResults[indice].targetType;
+                }
+                else {
+                    _this._action.propertiesResults[indice].targetType = targetParameterSelect.value;
+                    var names = _this._getListFromType(targetParameterSelect.value);
+                    if (names !== null && names.length > 0) {
+                        _this._action.propertiesResults[indice].value = names[0];
+                    }
+                    else {
+                        _this._action.propertiesResults[indice].value = "";
+                    }
+                    if (propertyPathSelect !== null) {
+                        _this._action.propertiesResults[indice + 1].value = "";
+                    }
+                }
+                var targetParameterProperties = _this._getTargetFromType(targetParameterSelect.value);
+                targetParameterNameSelect.options.length = 0;
+                if (targetParameterProperties !== null) {
+                    for (var i = 0; i < targetParameterProperties.length; i++) {
+                        var option = document.createElement("option");
+                        option.text = option.value = targetParameterProperties[i];
+                        targetParameterNameSelect.options.add(option);
+                    }
+                }
+                targetParameterNameSelect.value = _this._action.propertiesResults[indice].value;
+                if (propertyPathSelect !== null) {
+                    propertyPathSelect.options.length = 0;
+                    additionalPropertyPathSelect.options.length = 0;
+                    _this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect, null, null, indice + 1)(null);
+                }
+                _this._sortList(targetParameterNameSelect);
+                _this._sortList(targetParameterSelect);
+            };
+        };
+        Parameters.prototype._parameterTargetNameChanged = function (targetParameterSelect, targetParameterNameSelect, indice) {
+            var _this = this;
+            return function (event) {
+                _this._action.propertiesResults[indice].value = targetParameterNameSelect.value;
+            };
+        };
+        Parameters.prototype._getTargetFromType = function (type) {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return ActionsBuilder.SceneElements.MESHES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return ActionsBuilder.SceneElements.LIGHTS;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return ActionsBuilder.SceneElements.CAMERAS;
+            }
+            return null;
+        };
+        Parameters.prototype._getPropertiesFromType = function (type) {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return ActionsBuilder.SceneElements.MESH_PROPERTIES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return ActionsBuilder.SceneElements.LIGHT_PROPERTIES;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return ActionsBuilder.SceneElements.CAMERA_PROPERTIES;
+            }
+            if (type === "SceneProperties" || type === "Scene") {
+                return ActionsBuilder.SceneElements.SCENE_PROPERTIES;
+            }
+            return null;
+        };
+        Parameters.prototype._getListFromType = function (type) {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return ActionsBuilder.SceneElements.MESHES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return ActionsBuilder.SceneElements.LIGHTS;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return ActionsBuilder.SceneElements.CAMERAS;
+            }
+            return null;
+        };
+        Parameters.prototype._getObjectFromType = function (type) {
+            if (type === "MeshProperties" || type === "Mesh") {
+                this._currentObject = ActionsBuilder.SceneElements.MESH;
+                return ActionsBuilder.SceneElements.MESH;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                this._currentObject = ActionsBuilder.SceneElements.LIGHT;
+                return ActionsBuilder.SceneElements.LIGHT;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                this._currentObject = ActionsBuilder.SceneElements.CAMERA;
+                return ActionsBuilder.SceneElements.CAMERA;
+            }
+            if (type === "SceneProperties" || type === "Scene") {
+                this._currentObject = ActionsBuilder.SceneElements.SCENE;
+                return ActionsBuilder.SceneElements.SCENE;
+            }
+            return null;
+        };
+        Parameters.prototype._createNodeSection = function (action) {
+            var element = document.createElement("div");
+            element.style.background = this._viewer.getSelectedNodeColor(action.type, action.node.detached);
+            element.className = "ParametersElementNodeClass";
+            var text = document.createElement("a");
+            text.text = action.name;
+            text.className = "ParametersElementNodeTextClass";
+            element.appendChild(text);
+            this.parametersContainer.appendChild(element);
+        };
+        Parameters.prototype._createHelpSection = function (action) {
+            var element = ActionsBuilder.Elements.GetElementFromName(action.name);
+            if (element !== null) {
+                this.parametersHelpElement.textContent = element.description;
+            }
+        };
+        Parameters.prototype._sortList = function (element) {
+            var options = [];
+            for (var i = element.options.length - 1; i >= 0; i--) {
+                options.push(element.removeChild(element.options[i]));
+            }
+            options.sort(function (a, b) {
+                return a.innerHTML.localeCompare(b.innerHTML);
+            });
+            for (var i = 0; i < options.length; i++) {
+                element.options.add(options[i]);
+            }
+        };
+        return Parameters;
+    })();
+    ActionsBuilder.Parameters = Parameters;
+})(ActionsBuilder || (ActionsBuilder = {}));

+ 650 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.parameters.ts

@@ -0,0 +1,650 @@
+module ActionsBuilder {
+    export class Parameters {
+        public parametersContainer: HTMLElement;
+        public parametersHelpElement: HTMLElement;
+
+        private _action: Action = null;
+        private _viewer: Viewer;
+        private _currentObject: any;
+
+        /*
+        * Constructor
+        */
+        constructor(viewer: Viewer) {
+            // Get HTML elements
+            this.parametersContainer = document.getElementById("ParametersElementID");
+            this.parametersHelpElement = document.getElementById("ParametersHelpElementID");
+
+            // Configure this
+            this._viewer = viewer;
+
+            // Configure events
+            window.addEventListener("resize", (event: Event) => {
+                this.onResize(event);
+            });
+        }
+
+        /*
+        * Clears the parameters fileds in the parameters view
+        */
+        public clearParameters(): void {
+            if (this.parametersContainer.children === null) {
+                return;
+            }
+
+            while (this.parametersContainer.children.length > 0) {
+                this.parametersContainer.removeChild(this.parametersContainer.firstChild);
+            }
+        }
+
+        /*
+        * Creates parameters fields
+        * @param action: the action to configure
+        */
+        public createParameters(action: Action): void {
+            // Clear parameters fields and draw help description
+            this._action = action;
+            this.clearParameters();
+
+            if (action === null) {
+                return;
+            }
+
+            this._createHelpSection(action);
+            this._createNodeSection(action);
+
+            // Get properties
+            var properties = action.properties;
+            var propertiesResults = action.propertiesResults;
+
+            var targetParameterSelect: HTMLSelectElement = null;
+            var targetParameterNameSelect: HTMLSelectElement = null;
+            var propertyPathSelect: HTMLSelectElement = null;
+            var propertyPathOptionalSelect: HTMLSelectElement = null;
+
+            var booleanSelect: HTMLSelectElement = null;
+            var propertyInput: HTMLInputElement = null;
+
+            var propertyPathIndice = -1;
+
+            if (properties.length === 0) {
+                return;
+            }
+
+            // Draw properties
+            for (var i = 0; i < properties.length; i++) {
+                // Create separator
+                var separator = document.createElement("hr");
+                separator.noShade = true;
+                separator.className = "ParametersElementSeparatorClass";
+                this.parametersContainer.appendChild(separator);
+
+                // Create parameter text
+                var parameterName = document.createElement("a");
+                parameterName.text = properties[i].text;
+                parameterName.className = "ParametersElementTitleClass";
+                this.parametersContainer.appendChild(parameterName);
+
+                if (properties[i].text === "parameter" || properties[i].text === "target" || properties[i].text === "parent") {
+                    if (properties[i].targetType === null) {
+                        var parameterInput = document.createElement("input");
+                        parameterInput.value = propertiesResults[i].value;
+                        parameterInput.className = "ParametersElementInputClass";
+                        this.parametersContainer.appendChild(parameterInput);
+
+                        // Configure event
+                        parameterInput.onkeyup = this._propertyInputChanged(parameterInput, i);
+                    }
+                    else {
+                        // Create target select element
+                        targetParameterSelect = document.createElement("select");
+                        targetParameterSelect.className = "ParametersElementSelectClass";
+                        this.parametersContainer.appendChild(targetParameterSelect);
+
+                        // Create target name select element
+                        targetParameterNameSelect = document.createElement("select");
+                        targetParameterNameSelect.className = "ParametersElementSelectClass";
+                        this.parametersContainer.appendChild(targetParameterNameSelect);
+
+                        // Events and configure
+                        (this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i))(null);
+                        targetParameterSelect.value = propertiesResults[i].targetType;
+                        targetParameterNameSelect.value = propertiesResults[i].value;
+
+                        targetParameterSelect.onchange = this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i);
+                        targetParameterNameSelect.onchange = this._parameterTargetNameChanged(targetParameterSelect, targetParameterNameSelect, i);
+                    }
+                }
+                else if (properties[i].text === "propertyPath") {
+                    propertyPathIndice = i;
+
+                    // Create property path select
+                    propertyPathSelect = document.createElement("select");
+                    propertyPathSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(propertyPathSelect);
+
+                    // Create additional select
+                    propertyPathOptionalSelect = document.createElement("select");
+                    propertyPathOptionalSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(propertyPathOptionalSelect);
+
+                    // Events and configure
+                    (this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, null, null, i))(null);
+
+                    var property = this._action.propertiesResults[i].value.split(".");
+                    if (property.length > 0) {
+                        if (property.length === 1) {
+                            propertyPathSelect.value = property[0];
+                        }
+                        else {
+                            var completePropertyPath = "";
+                            for (var j = 0; j < property.length - 1; j++) {
+                                completePropertyPath += property[j];
+                                completePropertyPath += (j === property.length - 2) ? "" : ".";
+                            }
+                            propertyPathSelect.value = completePropertyPath;
+                            this._viewer.utils.setElementVisible(propertyPathOptionalSelect, true);
+                        }
+
+                        this._fillAdditionalPropertyPath(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect);
+                        propertyPathOptionalSelect.value = property[property.length - 1];
+
+                        if (propertyPathOptionalSelect.options.length === 0 || propertyPathOptionalSelect.options[0].textContent === "") {
+                            this._viewer.utils.setElementVisible(propertyPathOptionalSelect, false);
+                        }
+                    }
+
+                    targetParameterSelect.onchange = this._parameterTargetChanged(targetParameterSelect, targetParameterNameSelect, propertyPathSelect, propertyPathOptionalSelect, i - 1);
+                    propertyPathSelect.onchange = this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, null, null, i);
+                    propertyPathOptionalSelect.onchange = this._additionalPropertyPathSelectChanged(propertyPathSelect, propertyPathOptionalSelect, i);
+                }
+                else if (properties[i].text === "operator") {
+                    var conditionOperatorSelect = document.createElement("select");
+                    conditionOperatorSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(conditionOperatorSelect);
+
+                    // Configure event
+                    (this._conditionOperatorSelectChanged(conditionOperatorSelect, i))(null);
+                    conditionOperatorSelect.value = propertiesResults[i].value;
+                    conditionOperatorSelect.onchange = this._conditionOperatorSelectChanged(conditionOperatorSelect, i);
+                }
+                else if (properties[i].text === "sound") {
+                    var soundSelect = document.createElement("select");
+                    soundSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(soundSelect);
+
+                    // Configure event
+                    (this._soundSelectChanged(soundSelect, i))(null);
+                    soundSelect.value = propertiesResults[i].value;
+                    soundSelect.onchange = this._soundSelectChanged(soundSelect, i);
+                }
+                else {
+                    var isBoolean = propertiesResults[i].value === "true" || propertiesResults[i].value === "false";
+                    var object = this._getObjectFromType(targetParameterSelect.value);
+
+                    if (object !== null) {
+                        var property = this._action.propertiesResults[i - 1].value.split(".");
+
+                        for (var j = 0; j < property.length && object !== undefined; j++) {
+                            object = object[property[j]];
+                            if (j === property.length - 1) {
+                                isBoolean = isBoolean || typeof object === "boolean";
+                            }
+                        }
+                    }
+                    
+                    booleanSelect = document.createElement("select");
+                    booleanSelect.className = "ParametersElementSelectClass";
+                    this.parametersContainer.appendChild(booleanSelect);
+
+                    // Configure event
+                    (this._booleanSelectChanged(booleanSelect, i))(null);
+                    booleanSelect.value = propertiesResults[i].value;
+                    booleanSelect.onchange = this._booleanSelectChanged(booleanSelect, i);
+
+                    propertyInput = document.createElement("input");
+                    propertyInput.value = propertiesResults[i].value;
+                    propertyInput.className = "ParametersElementInputClass";
+                    this.parametersContainer.appendChild(propertyInput);
+
+                    // Configure event
+                    propertyInput.onkeyup = this._propertyInputChanged(propertyInput, i);
+
+                    if (propertyPathIndice !== -1 && properties[i].text === "value") {
+                        propertyPathSelect.onchange = this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, propertyPathOptionalSelect, booleanSelect, propertyInput, propertyPathIndice);
+                    }
+
+                    if (isBoolean) {
+                        this._viewer.utils.setElementVisible(booleanSelect, true);
+                        this._viewer.utils.setElementVisible(propertyInput, false);
+                    }
+                    else {
+                        this._viewer.utils.setElementVisible(booleanSelect, false);
+                        this._viewer.utils.setElementVisible(propertyInput, true);
+                    }
+                }
+            }
+        }
+
+        /*
+        * Resizes the parameters view
+        * @param: the resize event
+        */
+        public onResize(event?: Event): void {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.parametersContainer.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 200 + "px";
+
+            this.parametersHelpElement.style.height = 200 + "px";
+        }
+
+        /*
+        * Returns the boolean select change event
+        * @param booleanSelect: the boolean select element
+        * @param indice: the properties result indice
+        */
+        private _booleanSelectChanged(booleanSelect: HTMLSelectElement, indice: number): (ev: Event) => void {
+            return (ev: Event) => {
+                if (booleanSelect.options.length === 0) {
+                    var values = ["true", "false"];
+                    for (var i = 0; i < values.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = values[i];
+                        booleanSelect.add(option);
+                    }
+                }
+                else {
+                    this._action.propertiesResults[indice].value = booleanSelect.value;
+                }
+            };
+        }
+
+        /*
+        * Returns the sound select change event
+        * @param soundSelect: the sound select element
+        * @param indice: the properties result indice
+        */
+        private _soundSelectChanged(soundSelect: HTMLSelectElement, indice: number): (ev: Event) => void {
+            return (ev: Event) => {
+                if (soundSelect.options.length === 0) {
+                    for (var i = 0; i < SceneElements.SOUNDS.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = SceneElements.SOUNDS[i];
+                        soundSelect.add(option);
+                    }
+
+                    this._sortList(soundSelect);
+                }
+                else {
+                    this._action.propertiesResults[indice].value = soundSelect.value;
+                }
+            };
+        }
+
+        /*
+        * Returns the condition opeator select changed event
+        * @param conditionOperatorSelect: the condition operator select element
+        * @param indice: the properties result indice
+        */
+        private _conditionOperatorSelectChanged(conditionOperatorSelect: HTMLSelectElement, indice: number): (ev: Event) => void {
+            return (ev: Event) => {
+                if (conditionOperatorSelect.options.length === 0) {
+                    for (var i = 0; i < SceneElements.OPERATORS.length; i++) {
+                        var option = document.createElement("option");
+                        option.value = option.text = SceneElements.OPERATORS[i];
+                        //conditionOperatorSelect.options.add(option);
+                        conditionOperatorSelect.add(option);
+                    }
+                }
+                else {
+                    this._action.propertiesResults[indice].value = conditionOperatorSelect.value;
+                }
+            };
+        }
+
+        /*
+        * Returns the property input changed event
+        * @param propertyInput: the property input
+        * @param indice: the properties result indice
+        */
+        private _propertyInputChanged(propertyInput: HTMLInputElement, indice: number): (ev: Event) => void {
+            return (ev: Event) => {
+                this._action.propertiesResults[indice].value = propertyInput.value;
+            };
+        }
+
+        /*
+        * Returns the propertyPath select changed event
+        * @param targetParameterSelect: the target/parameter select element
+        * @param propertyPathSelect: the propertyPath select element
+        * @param additionalPropertyPathSelect: the additional propertyPath select element
+        * @param indice: the properties indice in action.properties
+        */
+        private _propertyPathSelectChanged(targetParameterSelect: HTMLSelectElement, propertyPathSelect: HTMLSelectElement,
+            additionalPropertyPathSelect: HTMLSelectElement, booleanSelect: HTMLSelectElement, propertyInput: HTMLInputElement,
+            indice: number): (event: Event) => void
+        {
+            return (event: Event) => {
+                if (propertyPathSelect.options.length === 0) {
+                    // Configure start values
+                    var properties = this._getPropertiesFromType(targetParameterSelect.value);
+
+                    if (properties !== null) {
+                        for (var i = 0; i < properties.length; i++) {
+                            var option = document.createElement("option");
+                            option.value = option.text = properties[i];
+                            propertyPathSelect.add(option);
+                        }
+                    }
+
+                } else {
+                    // Set property
+                    this._action.propertiesResults[indice].value = propertyPathSelect.value;
+
+                    if (booleanSelect !== null && propertyInput !== null) {
+                        var object = this._getObjectFromType(targetParameterSelect.value);
+                        var isBoolean = false;
+
+                        if (object !== null) {
+                            var property = this._action.propertiesResults[indice].value.split(".");
+
+                            for (var j = 0; j < property.length; j++) {
+                                object = object[property[j]];
+                                if (j === property.length - 1) {
+                                    isBoolean = isBoolean || typeof object === "boolean";
+                                }
+                            }
+                        }
+
+                        if (isBoolean) {
+                            this._viewer.utils.setElementVisible(booleanSelect, true);
+                            this._viewer.utils.setElementVisible(propertyInput, false);
+                        }
+                        else {
+                            this._viewer.utils.setElementVisible(booleanSelect, false);
+                            this._viewer.utils.setElementVisible(propertyInput, true);
+                        }
+                    }
+                }
+
+                // Configure addition property
+                this._fillAdditionalPropertyPath(targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect);
+
+                // Sort
+                this._sortList(propertyPathSelect);
+            };
+        }
+
+        private _fillAdditionalPropertyPath(targetParameterSelect: HTMLSelectElement, propertyPathSelect: HTMLSelectElement,
+            additionalPropertyPathSelect: HTMLSelectElement): void
+        {
+            additionalPropertyPathSelect.options.length = 0;
+            var object = this._getObjectFromType(targetParameterSelect.value);
+
+            if (object !== null) {
+                var propertyPath = propertyPathSelect.value.split(".");
+                for (var i = 0; i < propertyPath.length; i++) {
+                    object = object[propertyPath[i]];
+                }
+            }
+            if (object === null || object === undefined || (typeof (object)).toLowerCase() === "string") {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, false);
+                return;
+            }
+
+            // Add options
+            var emptyOption = document.createElement("option");
+            emptyOption.value = emptyOption.text = "";
+            additionalPropertyPathSelect.add(emptyOption);
+
+            for (var thing in object) {
+                var type = SceneElements.GetInstanceOf(object[thing]);
+                var index = SceneElements.TYPES.indexOf(type);
+
+                if (index !== -1) {
+                    var option = document.createElement("option");
+                    option.value = option.text = thing;
+                    additionalPropertyPathSelect.add(option);
+                    emptyOption.text += thing + ", ";
+                }
+            }
+
+            if (additionalPropertyPathSelect.options.length === 0 || additionalPropertyPathSelect.options[0].textContent === "") {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, false);
+            }
+            else {
+                this._viewer.utils.setElementVisible(additionalPropertyPathSelect, true);
+            }
+        }
+
+        /*
+        * Returns the additional propertyPath select changed event
+        * @param propertyPathSelect: the propertyPath select element
+        * @param additionalPropertyPathSelect: the additional propertyPath select element
+        * @param indice: the properties indice in action.properties
+        */
+        private _additionalPropertyPathSelectChanged(propertyPathSelect: HTMLSelectElement, additionalPropertyPathSelect: HTMLSelectElement,
+            indice: number): (event: Event) => void
+        {
+            return (event: Event) => {
+                var property = propertyPathSelect.value;
+                var additionalProperty = additionalPropertyPathSelect.value;
+
+                if (additionalProperty !== "") {
+                    property += ".";
+                    property += additionalPropertyPathSelect.value;
+                }
+                this._action.propertiesResults[indice].value = property;
+            };
+        }
+
+        /*
+        * Returns the parameter/target select changed event
+        * @param targetParameterSelect: the target/parameter select element
+        * @param targetParameterNameSelect: the target/parameter name select element
+        * @param propertyPathSelect: the propertyPath select element
+        * @param additionalPropertyPathSelect: the additional propertyPath select element
+        * @param indice: the properties indice in action.properties
+        */
+        private _parameterTargetChanged(targetParameterSelect: HTMLSelectElement, targetParameterNameSelect: HTMLSelectElement,
+            propertyPathSelect: HTMLSelectElement, additionalPropertyPathSelect: HTMLSelectElement, indice: number): (event: Event) => void
+        {
+            return (event: Event) => {
+                if (targetParameterSelect.options.length === 0) {
+                    // Configure start values
+                    var options = [
+                        { text: "Mesh", targetType: "MeshProperties" },
+                        { text: "Light", targetType: "LightProperties" },
+                        { text: "Camera", targetType: "CameraProperties" },
+                        { text: "Scene", targetType: "SceneProperties" }
+                    ];
+                    targetParameterSelect.options.length = 0;
+
+                    for (var i = 0; i < options.length; i++) {
+                        var option = document.createElement("option");
+                        option.text = options[i].text;
+                        option.value = options[i].targetType;
+                        targetParameterSelect.add(option);
+                    }
+
+                    targetParameterSelect.value = this._action.propertiesResults[indice].targetType;
+                } else {
+                    this._action.propertiesResults[indice].targetType = targetParameterSelect.value;
+
+                    var names = this._getListFromType(targetParameterSelect.value);
+
+                    if (names !== null && names.length > 0) {
+                        this._action.propertiesResults[indice].value = names[0];
+                    }
+                    else {
+                        this._action.propertiesResults[indice].value = "";
+                    }
+
+                    if (propertyPathSelect !== null) {
+                        this._action.propertiesResults[indice + 1].value = ""; // propertyPath
+                    }
+                }
+
+                // Configure target names
+                var targetParameterProperties = this._getTargetFromType(targetParameterSelect.value);
+                targetParameterNameSelect.options.length = 0;
+
+                if (targetParameterProperties !== null) {
+                    for (var i = 0; i < targetParameterProperties.length; i++) {
+                        var option = document.createElement("option");
+                        option.text = option.value = targetParameterProperties[i];
+                        targetParameterNameSelect.add(option);
+                    }
+                }
+                targetParameterNameSelect.value = this._action.propertiesResults[indice].value;
+
+                // Clear property path
+                if (propertyPathSelect !== null) {
+                    propertyPathSelect.options.length = 0;
+                    additionalPropertyPathSelect.options.length = 0;
+                    this._propertyPathSelectChanged(targetParameterSelect, propertyPathSelect, additionalPropertyPathSelect, null, null, indice + 1)(null);
+                }
+
+                this._sortList(targetParameterNameSelect);
+                this._sortList(targetParameterSelect);
+            };
+        }
+
+        /*
+        * Returns the parameter/target name select changed
+        * @param indice: the properties indice to change
+        */
+        private _parameterTargetNameChanged(targetParameterSelect: HTMLSelectElement, targetParameterNameSelect: HTMLSelectElement, indice: number): (event: Event) => void {
+            return (event: Event) => {
+                this._action.propertiesResults[indice].value = targetParameterNameSelect.value;
+            };
+        }
+
+        /*
+        * Returns the array of objects names in function of its type
+        * @param type: the target type
+        */
+        public _getTargetFromType(type: string): Array<string> {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return SceneElements.MESHES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return SceneElements.LIGHTS;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return SceneElements.CAMERAS;
+            }
+
+            return null;
+        }
+
+        /*
+        * Returns the properties in function of its type
+        * @param type: the target type
+        */
+        private _getPropertiesFromType(type: string): Array<string> {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return SceneElements.MESH_PROPERTIES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return SceneElements.LIGHT_PROPERTIES;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return SceneElements.CAMERA_PROPERTIES;
+            }
+            if (type === "SceneProperties" || type === "Scene") {
+                return SceneElements.SCENE_PROPERTIES;
+            }
+
+            return null;
+        }
+
+        public _getListFromType(type: string): string[] {
+            if (type === "MeshProperties" || type === "Mesh") {
+                return SceneElements.MESHES;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                return SceneElements.LIGHTS;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                return SceneElements.CAMERAS;
+            }
+
+            return null;
+        }
+
+        /*
+        * Returns the object in function of the given type
+        * @param type: the target type
+        */
+        public _getObjectFromType(type: string): any {
+            if (type === "MeshProperties" || type === "Mesh") {
+                this._currentObject = SceneElements.MESH;
+                return SceneElements.MESH;
+            }
+            if (type === "LightProperties" || type === "Light") {
+                this._currentObject = SceneElements.LIGHT;
+                return SceneElements.LIGHT;
+            }
+            if (type === "CameraProperties" || type === "Camera") {
+                this._currentObject = SceneElements.CAMERA;
+                return SceneElements.CAMERA;
+            }
+            if (type === "SceneProperties" || type === "Scene") {
+                this._currentObject = SceneElements.SCENE;
+                return SceneElements.SCENE;
+            }
+
+            return null;
+        }
+
+        /*
+        * Creates the node section (top of parameters)
+        * @param action: the action element to get color, text, name etc.
+        */
+        private _createNodeSection(action: Action): void {
+            var element = document.createElement("div");
+            element.style.background = <string>this._viewer.getSelectedNodeColor(action.type, action.node.detached);
+            element.className = "ParametersElementNodeClass";
+
+            var text = document.createElement("a");
+            text.text = action.name;
+            text.className = "ParametersElementNodeTextClass";
+
+            element.appendChild(text);
+            this.parametersContainer.appendChild(element);
+        }
+
+        /*
+        * Creates the help section
+        * @param action : the action containing the description
+        */
+        private _createHelpSection(action: Action): void {
+            // Get description
+            var element = Elements.GetElementFromName(action.name);
+
+            if (element !== null) {
+                this.parametersHelpElement.textContent = element.description;
+            }
+        }
+
+        /*
+        * Alphabetically sorts a HTML select element options
+        * @param element : the HTML select element to sort
+        */
+        private _sortList(element: HTMLSelectElement): void {
+            var options = [];
+
+            for (var i = element.options.length - 1; i >= 0; i--) {
+                options.push(element.removeChild(element.options[i]));
+            }
+
+            options.sort((a, b) => {
+                return a.innerHTML.localeCompare(b.innerHTML);
+            });
+
+            for (var i = 0; i < options.length; i++) {
+                element.add(options[i]);
+            }
+        }
+    }
+} 

+ 80 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.toolbar.js

@@ -0,0 +1,80 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Toolbar = (function () {
+        function Toolbar(viewer) {
+            var _this = this;
+            this.toolbarElement = document.getElementById("ToolbarElementID");
+            this._viewer = viewer;
+            window.addEventListener("resize", function (event) {
+                _this.onResize();
+            });
+            document.getElementById("ViewerDeZoomID").addEventListener("click", function (event) {
+                if (_this._viewer.zoom > 0.1) {
+                    _this._viewer.zoom -= 0.1;
+                }
+                _this._viewer.update();
+            });
+            document.getElementById("ViewerZoomID").addEventListener("click", function (event) {
+                if (_this._viewer.zoom < 1.0) {
+                    _this._viewer.zoom += 0.1;
+                }
+                _this._viewer.update();
+            });
+            document.getElementById("ViewerReconnectAll").addEventListener("click", function (event) {
+                for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                    _this._viewer.selectedNode = _this._viewer.root.children[i];
+                    _this._viewer.utils.onDetachAction(false, true);
+                }
+                _this._viewer.update();
+                _this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerDisconnectAll").addEventListener("click", function (event) {
+                for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                    _this._viewer.selectedNode = _this._viewer.root.children[i];
+                    _this._viewer.utils.onDetachAction(true, false);
+                }
+                _this._viewer.update();
+                _this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerReduceAll").addEventListener("click", function (event) {
+                for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                    _this._viewer.selectedNode = _this._viewer.root.children[i];
+                    _this._viewer.utils.onReduceAll(false);
+                }
+                _this._viewer.update();
+                _this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerExpandAll").addEventListener("click", function (event) {
+                for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                    _this._viewer.selectedNode = _this._viewer.root.children[i];
+                    _this._viewer.utils.onReduceAll(true);
+                }
+                _this._viewer.update();
+                _this._viewer.selectedNode = null;
+            });
+            this.saveActionGraphElement = document.getElementById("ToolsButtonIDSaveActionGraph");
+            this.drawSaveActionGraphButton(false);
+            document.getElementById("ResetActionGraphID").addEventListener("click", function (event) {
+                if (confirm("Are you sure?")) {
+                    for (var i = 0; i < _this._viewer.root.children.length; i++) {
+                        _this._viewer.selectedNode = _this._viewer.root.children[i];
+                        _this._viewer.utils.onRemoveBranch();
+                    }
+                    _this._viewer.update();
+                    _this._viewer.selectedNode = null;
+                }
+            });
+            document.getElementById("TestActionGraphID").addEventListener("click", function (event) {
+                _this._viewer.utils.onTestGraph();
+            });
+        }
+        Toolbar.prototype.onResize = function () {
+            this.toolbarElement.style.top = this._viewer.viewerElement.clientHeight + 20 + "px";
+        };
+        Toolbar.prototype.drawSaveActionGraphButton = function (draw) {
+            this.saveActionGraphElement.style.display = draw ? "block" : "none";
+        };
+        return Toolbar;
+    })();
+    ActionsBuilder.Toolbar = Toolbar;
+})(ActionsBuilder || (ActionsBuilder = {}));

+ 94 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.toolbar.ts

@@ -0,0 +1,94 @@
+module ActionsBuilder {
+    export class Toolbar {
+        public toolbarElement: HTMLElement;
+        public saveActionGraphElement: HTMLElement;
+
+        private _viewer: Viewer;
+
+        constructor(viewer: Viewer) {
+            // Get HTML elements
+            this.toolbarElement = document.getElementById("ToolbarElementID");
+
+            // Configure this
+            this._viewer = viewer;
+
+            // Manage events
+            window.addEventListener("resize", (event: Event) => {
+                this.onResize();
+            });
+
+            // Bottom toolbar
+            document.getElementById("ViewerDeZoomID").addEventListener("click", (event: MouseEvent) => {
+                if (this._viewer.zoom > 0.1) {
+                    this._viewer.zoom -= 0.1;
+                }
+                this._viewer.update();
+            });
+            document.getElementById("ViewerZoomID").addEventListener("click", (event: MouseEvent) => {
+                if (this._viewer.zoom < 1.0) {
+                    this._viewer.zoom += 0.1;
+                }
+                this._viewer.update();
+            });
+            document.getElementById("ViewerReconnectAll").addEventListener("click", (event: MouseEvent) => {
+                for (var i = 0; i < this._viewer.root.children.length; i++) {
+                    this._viewer.selectedNode = this._viewer.root.children[i];
+                    this._viewer.utils.onDetachAction(false, true);
+                }
+                this._viewer.update();
+                this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerDisconnectAll").addEventListener("click", (event: MouseEvent) => {
+                for (var i = 0; i < this._viewer.root.children.length; i++) {
+                    this._viewer.selectedNode = this._viewer.root.children[i];
+                    this._viewer.utils.onDetachAction(true, false);
+                }
+                this._viewer.update();
+                this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerReduceAll").addEventListener("click", (event: MouseEvent) => {
+                for (var i = 0; i < this._viewer.root.children.length; i++) {
+                    this._viewer.selectedNode = this._viewer.root.children[i];
+                    this._viewer.utils.onReduceAll(false);
+                }
+                this._viewer.update();
+                this._viewer.selectedNode = null;
+            });
+            document.getElementById("ViewerExpandAll").addEventListener("click", (event: MouseEvent) => {
+                for (var i = 0; i < this._viewer.root.children.length; i++) {
+                    this._viewer.selectedNode = this._viewer.root.children[i];
+                    this._viewer.utils.onReduceAll(true);
+                }
+                this._viewer.update();
+                this._viewer.selectedNode = null;
+            });
+
+            // Top toolbar
+            this.saveActionGraphElement = document.getElementById("ToolsButtonIDSaveActionGraph");
+            this.drawSaveActionGraphButton(false);
+
+            document.getElementById("ResetActionGraphID").addEventListener("click", (event: MouseEvent) => {
+                if (confirm("Are you sure?")) {
+                    for (var i = 0; i < this._viewer.root.children.length; i++) {
+                        this._viewer.selectedNode = this._viewer.root.children[i];
+                        this._viewer.utils.onRemoveBranch();
+                    }
+                    this._viewer.update();
+                    this._viewer.selectedNode = null;
+                }
+            });
+            document.getElementById("TestActionGraphID").addEventListener("click", (event: MouseEvent) => {
+                this._viewer.utils.onTestGraph();
+            });
+        }
+
+        public onResize(): void {
+            this.toolbarElement.style.top = this._viewer.viewerElement.clientHeight + 20 + "px";
+        }
+
+        public drawSaveActionGraphButton(draw: boolean) {
+            this.saveActionGraphElement.style.display = draw ? "block" : "none";
+        }
+    }
+}
+ 

+ 326 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.ts

@@ -0,0 +1,326 @@
+module ActionsBuilder {
+
+    /**
+    * Defines static types
+    */
+    export class Type {
+        private static _TRIGGER = 0;
+        private static _ACTION = 1;
+        private static _FLOW_CONTROL = 2;
+        private static _OBJECT = 3;
+        private static _SCENE = 4;
+
+        public static get TRIGGER(): number {
+            return Type._TRIGGER;
+        }
+
+        public static get ACTION(): number {
+            return Type._ACTION;
+        }
+
+        public static get FLOW_CONTROL(): number {
+            return Type._FLOW_CONTROL;
+        }
+
+        public static get OBJECT(): number {
+            return Type._OBJECT;
+        }
+
+        public static get SCENE(): number {
+            return Type._SCENE;
+        }
+    }
+
+    /*
+    * Defines the BABYLON.JS elements
+    */
+    export class SceneElements {
+        /*
+        * BabylonJS objects
+        */
+        private static _ENGINE: BABYLON.Engine = new BABYLON.Engine(<HTMLCanvasElement>document.getElementById("RenderCanvasID"));
+        private static _SCENE: BABYLON.Scene = new BABYLON.Scene(SceneElements.ENGINE);
+        private static _MESH: BABYLON.Mesh = new BABYLON.Mesh("mesh", SceneElements._SCENE);
+        private static _LIGHT: BABYLON.Light = new BABYLON.Light("light", SceneElements._SCENE);
+        private static _CAMERA: BABYLON.Camera = new BABYLON.Camera("camera", BABYLON.Vector3.Zero(), SceneElements._SCENE);
+
+        public static get ENGINE(): BABYLON.Engine {
+            return SceneElements._ENGINE;
+        }
+
+        public static get SCENE(): BABYLON.Scene {
+            return SceneElements._SCENE;
+        }
+
+        public static get MESH(): BABYLON.Mesh {
+            return SceneElements._MESH;
+        }
+
+        public static get LIGHT(): BABYLON.Light {
+            return SceneElements._LIGHT;
+        }
+
+        public static get CAMERA(): BABYLON.Camera {
+            return SceneElements._CAMERA;
+        }
+
+        /*
+        * Objects names
+        */
+        private static _MESHES = new Array<string>();
+        private static _LIGHTS = new Array<string>();
+        private static _CAMERAS = new Array<string>();
+        private static _SOUNDS = new Array<string>();
+
+        public static get MESHES(): Array<string> {
+            return SceneElements._MESHES;
+        }
+
+        public static get LIGHTS(): Array<string> {
+            return SceneElements._LIGHTS;
+        }
+
+        public static get CAMERAS(): Array<string> {
+            return SceneElements._CAMERAS;
+        }
+
+        public static get SOUNDS(): Array<string> {
+            return SceneElements._SOUNDS;
+        }
+
+        /*
+        * Properties
+        */
+        private static _MESH_PROPERTIES = new Array<string>();
+        private static _LIGHT_PROPERTIES = new Array<string>();
+        private static _CAMERA_PROPERTIES = new Array<string>();
+        private static _SCENE_PROPERTIES = new Array<string>();
+
+        public static get MESH_PROPERTIES(): Array<string> {
+            return SceneElements._MESH_PROPERTIES;
+        }
+
+        public static get LIGHT_PROPERTIES(): Array<string> {
+            return SceneElements._LIGHT_PROPERTIES;
+        }
+
+        public static get CAMERA_PROPERTIES(): Array<string> {
+            return SceneElements._CAMERA_PROPERTIES;
+        }
+
+        public static get SCENE_PROPERTIES(): Array<string> {
+            return SceneElements._SCENE_PROPERTIES;
+        }
+
+        /*
+        * Types
+        */
+        private static _TYPES = new Array<string>();
+
+        public static get TYPES(): Array<string> {
+            return SceneElements._TYPES;
+        }
+
+        /*
+        * Operators
+        */
+        private static _OPERATORS = new Array<string>();
+
+        public static get OPERATORS(): Array<string> {
+            return SceneElements._OPERATORS;
+        }
+
+        /*
+        * Methods
+        */
+        public static GetInstanceOf(object: Object): string {
+            if (object === null || object === undefined) {
+                return "";
+            }
+            return object.constructor.toString().match(/function (\w*)/)[1];
+        }
+
+        public static TestInstanceOf (object: Object, propertyName: string): boolean {
+            if (object === null || object.constructor === null) {
+                return false;
+            }
+
+            if (propertyName.length > 0 && propertyName[0] === "_")
+                return false;
+
+            var name = SceneElements.GetInstanceOf(object);
+
+            for (var i = 0; i < SceneElements.TYPES.length; i++) {
+                if (name === SceneElements.TYPES[i]) {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+    }
+
+    // Functions
+    var specialTypes = [
+        "StandardMaterial"
+    ];
+    SceneElements.MESH.material = new BABYLON.StandardMaterial("material", SceneElements.SCENE);
+
+    var addSpecialType = (object: any, properties: Array<string>, thing: string) => {
+        for (var specialThing in object[thing]) {
+            if (object[thing].hasOwnProperty(specialThing) && SceneElements.TestInstanceOf(object[thing][specialThing], specialThing)) {
+                properties.push(thing + "." + specialThing);
+            }
+        }
+    };
+
+    // Configure types
+    SceneElements.TYPES.push("Color3");
+    SceneElements.TYPES.push("Boolean");
+    SceneElements.TYPES.push("Number");
+    SceneElements.TYPES.push("Vector2");
+    SceneElements.TYPES.push("Vector3");
+    SceneElements.TYPES.push("String");
+
+    // Configure operators
+    SceneElements.OPERATORS.push("IsEqual");
+    SceneElements.OPERATORS.push("IsDifferent");
+    SceneElements.OPERATORS.push("IsGreater");
+    SceneElements.OPERATORS.push("IsLesser");
+
+    // Configure properties
+    for (var thing in SceneElements.MESH) {
+        var instance = SceneElements.GetInstanceOf(SceneElements.MESH[thing]);
+
+        if (SceneElements.MESH.hasOwnProperty(thing)) {
+            if (specialTypes.indexOf(instance) !== -1) {
+                addSpecialType(SceneElements.MESH, SceneElements.MESH_PROPERTIES, thing);
+            }
+            else if (SceneElements.TestInstanceOf(SceneElements.MESH[thing], thing)) {
+                SceneElements.MESH_PROPERTIES.push(thing);
+            }
+        }
+    }
+
+    for (var thing in SceneElements.LIGHT) {
+        if (SceneElements.LIGHT.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.LIGHT[thing], thing)) {
+            SceneElements.LIGHT_PROPERTIES.push(thing);
+        }
+    }
+
+    for (var thing in SceneElements.CAMERA) {
+        if (SceneElements.CAMERA.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.CAMERA[thing], thing)) {
+            SceneElements.CAMERA_PROPERTIES.push(thing);
+        }
+    }
+
+    for (var thing in SceneElements.SCENE) {
+        if (SceneElements.SCENE.hasOwnProperty(thing) && SceneElements.TestInstanceOf(SceneElements.SCENE[thing], thing)) {
+            SceneElements.SCENE_PROPERTIES.push(thing);
+        }
+    }
+
+    /**
+    * Defines an element property
+    */
+    export interface ElementProperty {
+        targetType?: string;
+        text: string;
+        value: string;
+    }
+
+    /**
+    * Defines an element property result
+    */
+    export interface ElementPropertyResult {
+        targetType?: string;
+        value: string;
+    }
+
+    /**
+    * Generic element, has a name, a text to draw, a description
+    * and a list of properties (ElementProperty)
+    */
+    export interface Element {
+        name: string;
+        text: string;
+        properties: Array<ElementProperty>;
+        description: string;
+    }
+
+    /**
+    * Actions Builder elements (triggers, actions & flow controls) that are
+    * arrays of Element
+    */
+    export class Elements {
+        private static _TRIGGERS = new Array<Element>();
+        private static _ACTIONS = new Array<Element>();
+        private static _FLOW_CONTROLS = new Array<Element>();
+
+        public static get TRIGGERS(): Array<Element> {
+            return Elements._TRIGGERS;
+        }
+
+        public static get ACTIONS(): Array<Element> {
+            return Elements._ACTIONS;
+        }
+
+        public static get FLOW_CONTROLS(): Array<Element> {
+            return Elements._FLOW_CONTROLS;
+        }
+
+        public static GetElementFromName(name: string): Element {
+            for (var i = 0; i < Elements.TRIGGERS.length; i++) {
+                if (Elements.TRIGGERS[i].name === name) {
+                    return Elements._TRIGGERS[i];
+                }
+            }
+
+            for (var i = 0; i < Elements.ACTIONS.length; i++) {
+                if (Elements.ACTIONS[i].name === name) {
+                    return Elements._ACTIONS[i];
+                }
+            }
+
+            for (var i = 0; i < Elements.FLOW_CONTROLS.length; i++) {
+                if (Elements.FLOW_CONTROLS[i].name === name) {
+                    return Elements._FLOW_CONTROLS[i];
+                }
+            }
+
+            return null;
+        }
+    }
+
+    // Configure triggers
+    Elements.TRIGGERS.push({ name: "OnPickTrigger", text: "pick", properties: [], description: "When the user picks the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnLeftPickTrigger", text: "left pick", properties: [], description: "When the user picks the edited mesh using the left click" });
+    Elements.TRIGGERS.push({ name: "OnRightPickTrigger", text: "right pick", properties: [], description: "When the user picks the edited mesh using the right click" });
+    Elements.TRIGGERS.push({ name: "OnCenterPickTrigger", text: "center pick", properties: [], description: "When the user picks the edited mesh using the click of the mouse wheel" });
+    Elements.TRIGGERS.push({ name: "OnPointerOverTrigger", text: "pointer over", properties: [], description: "When the user's mouse is over the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnPointerOutTrigger", text: "pointer out", properties: [], description: "When the user's mouse is out of the edited mesh" });
+    Elements.TRIGGERS.push({ name: "OnEveryFrameTrigger", text: "every frame", properties: [], description: "This trigger is called each frame (only on scene)" });
+    Elements.TRIGGERS.push({ name: "OnIntersectionEnterTrigger", text: "intersection enter", properties: [{ targetType: "MeshProperties", text: "parameter", value: "Object name?" }], description: "When the edited mesh intersects the another mesh predefined in the options" });
+    Elements.TRIGGERS.push({ name: "OnIntersectionExitTrigger", text: "intersection exit", properties: [{ targetType: "MeshProperties", text: "parameter", value: "Object name?" }], description: "When the edited mesh exits intersection with the another mesh predefined in the options" });
+    Elements.TRIGGERS.push({ name: "OnKeyDownTrigger", text: "key down", properties: [{ targetType: null, text: "parameter", value: "a" }], description: "When the user pressed a key (enter the key character, example: \"r\")" });
+    Elements.TRIGGERS.push({ name: "OnKeyUpTrigger", text: "key up", properties: [{ targetType: null, text: "parameter", value: "a" }], description: "When the user unpressed a key (enter the key character, example: \"p\")" });
+
+    // Configure actions
+    Elements.ACTIONS.push({ name: "SwitchBooleanAction", text: "switch boolean", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }], description: "Switches the boolean value of a given parameter of the target object: true to false, or false to true" });
+    Elements.ACTIONS.push({ name: "SetStateAction", text: "set state", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "value", value: "" }], description: "Sets a new state value for the target object (example: \"off\" or \"on\")" });
+    Elements.ACTIONS.push({ name: "SetValueAction", text: "set value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }], description: "Sets a new value to the specified parameter of the target object (example: position.x to 0.0)" });
+    Elements.ACTIONS.push({ name: "SetParentAction", text: "set parent", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "parent", value: "" }], description: "Sets the new parent of the target object (example: a mesh or a light)" });
+    Elements.ACTIONS.push({ name: "IncrementValueAction", text: "increment value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }], description: "Increments the value of the given parameter of the target object. The value can be negative. (example: increment position.x of 5.0)" });
+    Elements.ACTIONS.push({ name: "PlayAnimationAction", text: "play animation", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "from", value: "0" }, { text: "to", value: "150" }, { text: "loop", value: "false" }], description: "Plays an animation of the target object. Specify the start frame, the end frame and if the animation should loop." });
+    Elements.ACTIONS.push({ name: "StopAnimationAction", text: "stop animation", properties: [{ targetType: "MeshProperties", text: "target", value: "" }], description: "Stops the animations of the target object." });
+    Elements.ACTIONS.push({ name: "DoNothingAction", text: "do nothing", properties: [], description: "Does nothing, can be used to balance/equilibrate the actions graph." });
+    Elements.ACTIONS.push({ name: "InterpolateValueAction", text: "interpolate value", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "0" }, { text: "duration", value: "1000" }, { text: "stopOtherAnimations", value: "false" }], description: "Creates an animation (key frames) that animates the target object by interpolating the given parameter of the target value." });
+    Elements.ACTIONS.push({ name: "PlaySoundAction", text: "play sound", properties: [{ text: "sound", value: "" }], description: "Plays the specified sound." });
+    Elements.ACTIONS.push({ name: "StopSoundAction", text: "stop sound", properties: [{ text: "sound", value: "" }], description: "Stops the specified sound." });
+    Elements.ACTIONS.push({ name: "CombineAction", text: "combine", properties: [], description: "Special action that combines multiple actions. The combined actions are executed at the same time. Drag'n'drop the new actions inside to combine actions." });
+
+    // Configure flow control
+    Elements.FLOW_CONTROLS.push({ name: "ValueCondition", text: "value condition", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "propertyPath", value: "" }, { text: "value", value: "" }, { text: "operator", value: SceneElements.OPERATORS[0] }], description: "A condition checking if a given value is equal, different, lesser or greater than the given parameter of the target object" });
+    Elements.FLOW_CONTROLS.push({ name: "StateCondition", text: "state condition", properties: [{ targetType: "MeshProperties", text: "target", value: "" }, { text: "value", value: "" }], description: "A condition checking if the target object's state is equal to the given state. See \"set state\" action to set a state to an object." });
+    Elements.FLOW_CONTROLS.push({ name: "Hub", text: "hub", properties: [], description: "The hub is internally used by the Combine Action. It allows to add children to the Combine Action" });
+}

+ 370 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.utils.js

@@ -0,0 +1,370 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Utils = (function () {
+        function Utils(viewer) {
+            this.copiedStructure = null;
+            this._viewer = viewer;
+        }
+        Utils.prototype.onTestGraph = function () {
+            var _this = this;
+            if (this._viewer.root.children.length === 0) {
+                alert("Please add at least a Trigger and an Action to test the graph");
+            }
+            var onTestTarget = function (targetType, target) {
+                var targetExists = false;
+                var array = _this._viewer.parameters._getTargetFromType(targetType);
+                if (array === null) {
+                    return targetExists;
+                }
+                for (var i = 0; i < array.length; i++) {
+                    if (array[i] === target) {
+                        targetExists = true;
+                        break;
+                    }
+                }
+                return targetExists;
+            };
+            var onNodeError = function (action) {
+                var node = action.node;
+                node.rect.attr("fill", Raphael.rgb(255, 0, 0));
+                return false;
+            };
+            var onTestAction = function (action) {
+                console.log("Testing " + action.name);
+                if (action.combineArray !== null) {
+                    var foundError = false;
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        if (!onTestAction(action.combineArray[i])) {
+                            foundError = true;
+                        }
+                    }
+                    if (foundError) {
+                        return false;
+                    }
+                }
+                else {
+                    var properties = action.properties;
+                    var propertiesResults = action.propertiesResults;
+                    if (properties !== null) {
+                        var object = null;
+                        var propertyPath = null;
+                        for (var i = 0; i < properties.length; i++) {
+                            if (properties[i].text === "target" || properties[i].text === "parent") {
+                                object = _this._viewer.parameters._getObjectFromType(properties[i].targetType);
+                                var targetExists = onTestTarget(propertiesResults[i].targetType, propertiesResults[i].value);
+                                if (!targetExists) {
+                                    return onNodeError(action);
+                                }
+                            }
+                            else if (properties[i].text === "propertyPath") {
+                                var property = propertiesResults[i].value;
+                                var effectiveProperty = object;
+                                var p = property.split(".");
+                                for (var j = 0; j < p.length && effectiveProperty !== undefined; j++) {
+                                    effectiveProperty = effectiveProperty[p[j]];
+                                }
+                                if (effectiveProperty === undefined) {
+                                    return onNodeError(action);
+                                }
+                                else {
+                                    propertyPath = effectiveProperty;
+                                }
+                            }
+                            else if (properties[i].text == "value" && propertyPath != null) {
+                                var value = propertiesResults[i].value;
+                                if (!isNaN(propertyPath)) {
+                                    var num = parseFloat(value);
+                                    if (isNaN(num) || value === "") {
+                                        return onNodeError(action);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    var foundError = false;
+                    for (var i = 0; i < action.children.length; i++) {
+                        if (!onTestAction(action.children[i])) {
+                            foundError = true;
+                        }
+                    }
+                    return !foundError;
+                }
+            };
+            var root = this._viewer.root;
+            var foundError = false;
+            for (var i = 0; i < root.children.length; i++) {
+                var trigger = root.children[i];
+                var properties = trigger.properties;
+                if (properties !== null && properties.length > 0) {
+                    var parameter = trigger.propertiesResults[0].value;
+                    if (properties[0].targetType !== null) {
+                        if (!onTestTarget("MeshProperties", parameter)) {
+                            foundError = onNodeError(trigger);
+                        }
+                    }
+                    else {
+                        if (!parameter.match(/[a-z]/)) {
+                            foundError = onNodeError(trigger);
+                        }
+                    }
+                }
+                for (var j = 0; j < trigger.children.length; j++) {
+                    var child = trigger.children[j];
+                    var result = onTestAction(child);
+                    if (!result) {
+                        foundError = true;
+                    }
+                }
+            }
+            if (foundError) {
+                alert("Found error(s). the red nodes contain the error.");
+            }
+            else {
+                alert("No error found.");
+            }
+        };
+        Utils.prototype.onReduceAll = function (forceExpand) {
+            if (forceExpand === void 0) { forceExpand = false; }
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var action = this._viewer.selectedNode;
+            if (action.combineArray !== null) {
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    this._viewer.selectedNode = action.combineArray[i];
+                    this.onReduce(forceExpand, !forceExpand);
+                }
+            }
+            else {
+                this.onReduce(forceExpand, !forceExpand);
+            }
+            for (var i = 0; i < action.children.length; i++) {
+                this._viewer.selectedNode = action.children[i];
+                this.onReduceAll(forceExpand);
+            }
+        };
+        Utils.prototype.onReduce = function (forceExpand, forceReduce) {
+            if (forceExpand === void 0) { forceExpand = false; }
+            if (forceReduce === void 0) { forceReduce = false; }
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var node = this._viewer.selectedNode.node;
+            node.rect.stop(node.rect.animation);
+            if (forceExpand === true) {
+                node.minimized = false;
+            }
+            else if (forceReduce === true) {
+                node.minimized = true;
+            }
+            else {
+                node.minimized = !node.minimized;
+            }
+            if (node.minimized) {
+                node.text.hide();
+                node.rect.attr("width", ActionsBuilder.Viewer.NODE_MINIMIZED_WIDTH * this._viewer.zoom);
+            }
+            else {
+                node.text.show();
+                node.rect.attr("width", ActionsBuilder.Viewer.NODE_WIDTH * this._viewer.zoom);
+            }
+        };
+        Utils.prototype.onDetachAction = function (forceDetach, forceAttach) {
+            var _this = this;
+            if (forceDetach === void 0) { forceDetach = false; }
+            if (forceAttach === void 0) { forceAttach = false; }
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var action = this._viewer.selectedNode;
+            if (forceDetach === true) {
+                action.node.detached = true;
+            }
+            else if (forceAttach === true) {
+                action.node.detached = false;
+            }
+            else {
+                action.node.detached = !action.node.detached;
+            }
+            var onSetColor = function (root, detached) {
+                var rootNode = root.node;
+                rootNode.rect.attr("fill", _this._viewer.getNodeColor(root.type, detached));
+                if (root.combineArray !== null) {
+                    for (var i = 0; i < root.combineArray.length; i++) {
+                        var combineNode = root.combineArray[i].node;
+                        combineNode.rect.attr("fill", _this._viewer.getNodeColor(root.combineArray[i].type, detached));
+                    }
+                }
+                for (var i = 0; i < root.children.length; i++) {
+                    onSetColor(root.children[i], detached);
+                }
+            };
+            onSetColor(action, action.node.detached);
+        };
+        Utils.prototype.onRemoveNode = function () {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var action = this._viewer.selectedNode;
+            var parent = action.parent;
+            if (action.type === ActionsBuilder.Type.TRIGGER) {
+                this.onRemoveBranch();
+                return;
+            }
+            if (action.type === ActionsBuilder.Type.FLOW_CONTROL && parent !== null && parent.combineArray !== null) {
+                action = parent;
+                parent = action.parent;
+            }
+            if (parent !== null && parent.combineArray !== null) {
+                parent.removeCombinedAction(action);
+                if (parent.combineArray.length === 0) {
+                    parent.node.text.attr("text", "combine");
+                }
+            }
+            else {
+                if (action.combineArray !== null) {
+                    action.removeChild(action.hub);
+                }
+                action.parent.removeChild(action);
+            }
+            if (action.combineArray !== null) {
+                this._viewer.removeAction(action.hub, false);
+            }
+            this._viewer.removeAction(action, false);
+            this._viewer.update();
+            this._viewer.parameters.clearParameters();
+            this._viewer.selectedNode = null;
+        };
+        Utils.prototype.onRemoveBranch = function () {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            if (this._viewer.selectedNode === this._viewer.root) {
+                alert("Cannot remove the root node");
+                return;
+            }
+            var action = this._viewer.selectedNode;
+            var parent = action.parent;
+            if (action.parent !== null && action.parent.combineArray !== null) {
+                action = parent;
+                parent = action.parent;
+            }
+            if (action.combineArray !== null) {
+                action.removeChild(action.hub);
+            }
+            action.parent.removeChild(action);
+            this._viewer.removeAction(action, true);
+            this._viewer.update();
+            this._viewer.parameters.clearParameters();
+            this._viewer.selectedNode = null;
+        };
+        Utils.prototype.onCopyStructure = function () {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var structure = this.createJSON(this._viewer.selectedNode);
+            var asText = JSON.stringify(structure);
+            if (window.clipboardData !== undefined) {
+                window.clipboardData.setData("text", asText);
+            }
+            else {
+                this.copiedStructure = asText;
+            }
+        };
+        Utils.prototype.onPasteStructure = function () {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+            var asText = (window.clipboardData !== undefined) ? window.clipboardData.getData("text") : this.copiedStructure;
+            var isJson = asText.length > 0 && asText[0] == "{" && asText[asText.length - 1] == "}";
+            var structure = JSON.parse(asText);
+            var action = this._viewer.selectedNode;
+            if (structure.type === ActionsBuilder.Type.TRIGGER && action !== this._viewer.root) {
+                alert("You can't paste a trigger if the selected node isn't the root object");
+                return;
+            }
+            if (structure.type !== ActionsBuilder.Type.TRIGGER && action === this._viewer.root) {
+                alert("You can't paste an action or condition if the selected node is the root object");
+                return;
+            }
+            this.loadFromJSON(structure, action);
+            this._viewer.update();
+        };
+        Utils.prototype.loadFromJSON = function (graph, startAction) {
+            var _this = this;
+            if (startAction === null) {
+                for (var i = 0; i < this._viewer.root.children.length; i++) {
+                    this._viewer.removeAction(this._viewer.root.children[i], true);
+                }
+                this._viewer.root.clearChildren();
+            }
+            var load = function (root, parent, detached, combine) {
+                if (parent === null) {
+                    parent = _this._viewer.root;
+                }
+                var newAction = null;
+                if (root.type !== ActionsBuilder.Type.OBJECT && root.type !== ActionsBuilder.Type.SCENE) {
+                    var action = _this._viewer.addAction(parent, root.type, ActionsBuilder.Elements.GetElementFromName(root.name));
+                    for (var i = 0; i < root.properties.length; i++) {
+                        var targetType = root.properties[i].targetType;
+                        if (targetType === undefined) {
+                            targetType = "MeshProperties";
+                        }
+                        action.propertiesResults[i] = { value: root.properties[i].value, targetType: targetType };
+                    }
+                    var node = action.node;
+                    node.detached = root.detached;
+                    if (detached) {
+                        node.rect.attr("fill", _this._viewer.getNodeColor(action.type, detached));
+                    }
+                    if (root.combine !== undefined) {
+                        for (var i = 0; i < root.combine.length; i++) {
+                            load(root.combine[i], action, detached, true);
+                        }
+                    }
+                    if (!combine) {
+                        parent = parent.children[parent.children.length - 1];
+                    }
+                }
+                for (var i = 0; i < root.children.length; i++) {
+                    load(root.children[i], newAction !== null && newAction.combineArray !== null ? newAction.hub : parent, root.detached, false);
+                }
+            };
+            load(graph, startAction, false, false);
+            this._viewer.update();
+        };
+        Utils.prototype.createJSON = function (root) {
+            var action = {
+                type: root.type,
+                name: root.name,
+                detached: root.node.detached,
+                children: new Array(),
+                combine: new Array(),
+                properties: new Array()
+            };
+            for (var i = 0; i < root.properties.length; i++) {
+                action.properties.push({
+                    name: root.properties[i].text,
+                    value: root.propertiesResults[i].value,
+                    targetType: root.propertiesResults[i].targetType
+                });
+            }
+            if (root.combineArray !== null) {
+                for (var i = 0; i < root.combineArray.length; i++) {
+                    var combinedAction = root.combineArray[i];
+                    action.combine.push(this.createJSON(combinedAction));
+                }
+                root = root.children[0];
+            }
+            for (var i = 0; i < root.children.length; i++) {
+                action.children.push(this.createJSON(root.children[i]));
+            }
+            return action;
+        };
+        Utils.prototype.setElementVisible = function (element, visible) {
+            element.style.display = visible ? "block" : "none";
+        };
+        return Utils;
+    })();
+    ActionsBuilder.Utils = Utils;
+})(ActionsBuilder || (ActionsBuilder = {}));

+ 527 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.utils.ts

@@ -0,0 +1,527 @@
+interface Window {
+    clipboardData: any;
+}
+
+module ActionsBuilder {
+    export interface JSONObjectProperty {
+        targetType?: string;
+        name: string;
+        value: string;
+    }
+
+    export interface JSONObject {
+        type: number;
+        name: string;
+        detached: boolean;
+        children: Array<JSONObject>;
+        combine: Array<JSONObject>;
+        properties: Array<JSONObjectProperty>;
+    }
+
+    export class Utils {
+        // Members
+        public copiedStructure: string = null;
+
+        private _viewer: Viewer;
+
+        /*
+        * Constructor
+        * @param viewer: the viewer instance
+        */
+        constructor(viewer: Viewer) {
+            // Configure this
+            this._viewer = viewer;
+        }
+
+        /*
+        * Tests the graph and reports errors
+        */
+        public onTestGraph(): void {
+            if (this._viewer.root.children.length === 0) {
+                alert("Please add at least a Trigger and an Action to test the graph");
+            }
+
+            var onTestTarget = (targetType: string, target: any) => {
+                var targetExists = false;
+                var array = this._viewer.parameters._getTargetFromType(targetType);
+
+                if (array === null) {
+                    return targetExists;
+                }
+
+                for (var i = 0; i < array.length; i++) {
+                    if (array[i] === target) {
+                        targetExists = true;
+                        break;
+                    }
+                }
+
+                return targetExists;
+            };
+
+            var onNodeError = (action: Action): boolean => {
+                var node = action.node;
+                node.rect.attr("fill", Raphael.rgb(255, 0, 0));
+
+                return false;
+            };
+
+            var onTestAction = (action: Action): boolean => {
+                console.log("Testing " + action.name);
+
+                if (action.combineArray !== null) {
+                    var foundError = false;
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        if (!onTestAction(action.combineArray[i])) {
+                            foundError = true;
+                        }
+                    }
+
+                    if (foundError) {
+                        return false;
+                    }
+                }
+                else {
+                    // Test properties
+                    var properties = action.properties;
+                    var propertiesResults = action.propertiesResults;
+
+                    if (properties !== null) {
+                        var object: any = null;
+                        var propertyPath: any = null;
+
+                        for (var i = 0; i < properties.length; i++) {
+                            // Target
+                            if (properties[i].text === "target" || properties[i].text === "parent") {
+                                object = this._viewer.parameters._getObjectFromType(properties[i].targetType);
+                                var targetExists = onTestTarget(propertiesResults[i].targetType, propertiesResults[i].value);
+
+                                if (!targetExists) {
+                                    return onNodeError(action);
+                                }
+                            }
+                            // Property Path
+                            else if (properties[i].text === "propertyPath") {
+                                var property = <string>propertiesResults[i].value;
+                                var effectiveProperty = object;
+
+                                var p = property.split(".");
+                                for (var j = 0; j < p.length && effectiveProperty !== undefined; j++) {
+                                    effectiveProperty = effectiveProperty[p[j]];
+                                }
+
+                                if (effectiveProperty === undefined) {
+                                    return onNodeError(action);
+                                }
+                                else {
+                                    propertyPath = effectiveProperty;
+                                }
+                            }
+                            // value
+                            else if (properties[i].text == "value" && propertyPath != null) {
+                                var value = propertiesResults[i].value;
+
+                                if (!isNaN(propertyPath)) {
+                                    var num = parseFloat(value);
+                                    if (isNaN(num) || value === "") {
+                                        return onNodeError(action);
+                                    }
+                                }
+                            }
+                        }
+                    }
+
+                    var foundError = false;
+                    for (var i = 0; i < action.children.length; i++) {
+                        if (!onTestAction(action.children[i])) {
+                            foundError = true;
+                        }
+                    }
+
+                    return !foundError;
+                }
+            };
+
+            var root = this._viewer.root;
+            var foundError = false;
+
+            for (var i = 0; i < root.children.length; i++) {
+                var trigger = root.children[i];
+                var properties = trigger.properties;
+
+                // Test properties of trigger (parameter)
+                if (properties !== null && properties.length > 0) {
+                    // Only one property
+                    var parameter = trigger.propertiesResults[0].value;
+
+                    if (properties[0].targetType !== null) {
+                        // Intersection trigger
+                        if (!onTestTarget("MeshProperties", parameter)) {
+                            foundError = onNodeError(trigger);
+                        }
+                    }
+                    else {
+                        // Key trigger
+                        if (!parameter.match(/[a-z]/)) {
+                            foundError = onNodeError(trigger);
+                        }
+                    }
+                }
+
+                for (var j = 0; j < trigger.children.length; j++) {
+                    var child = trigger.children[j];
+                    var result = onTestAction(child);
+
+                    if (!result) {
+                        foundError = true;
+                    }
+                }
+            }
+
+            if (foundError) {
+                alert("Found error(s). the red nodes contain the error.");
+            }
+            else {
+                alert("No error found.");
+            }
+        }
+
+        /*
+        * Recursively reduce/expand nodes
+        */
+        public onReduceAll(forceExpand: boolean = false): void {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+
+            var action = this._viewer.selectedNode;
+
+            if (action.combineArray !== null) {
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    this._viewer.selectedNode = action.combineArray[i];
+                    this.onReduce(forceExpand, !forceExpand);
+                }
+            }
+            else {
+                this.onReduce(forceExpand, !forceExpand);
+            }
+
+            for (var i = 0; i < action.children.length; i++) {
+                this._viewer.selectedNode = action.children[i];
+                this.onReduceAll(forceExpand);
+            }
+        }
+
+        /*
+        * Reduces the selected node
+        */
+        public onReduce(forceExpand: boolean = false, forceReduce: boolean = false): void {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+
+            var node = this._viewer.selectedNode.node;
+            node.rect.stop(node.rect.animation);
+
+            // Set minimized
+            if (forceExpand === true) {
+                node.minimized = false;
+            }
+            else if (forceReduce === true) {
+                node.minimized = true;
+            }
+            else {
+                node.minimized = !node.minimized;
+            }
+
+            // Set size
+            if (node.minimized) {
+                node.text.hide();
+                node.rect.attr("width", Viewer.NODE_MINIMIZED_WIDTH * this._viewer.zoom);
+            }
+            else {
+                node.text.show();
+                node.rect.attr("width", Viewer.NODE_WIDTH * this._viewer.zoom);
+            }
+        }
+
+        /*
+        * Detaches the selected action
+        */
+        public onDetachAction(forceDetach: boolean = false, forceAttach: boolean = false): void {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+
+            var action = this._viewer.selectedNode;
+
+            if (forceDetach === true) {
+                action.node.detached = true;
+            }
+            else if (forceAttach === true) {
+                action.node.detached = false;
+            }
+            else {
+                action.node.detached = !action.node.detached;
+            }
+
+            var onSetColor = (root: Action, detached: boolean) => {
+                var rootNode = root.node;
+                rootNode.rect.attr("fill", this._viewer.getNodeColor(root.type, detached));
+
+                if (root.combineArray !== null) {
+                    for (var i = 0; i < root.combineArray.length; i++) {
+                        var combineNode = root.combineArray[i].node;
+                        combineNode.rect.attr("fill", this._viewer.getNodeColor(root.combineArray[i].type, detached));
+                    }
+                }
+
+                for (var i = 0; i < root.children.length; i++) {
+                    onSetColor(root.children[i], detached);
+                }
+            };
+
+            onSetColor(action, action.node.detached);
+        }
+
+        /*
+        * Removes the selected node
+        */
+        public onRemoveNode(): void {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+
+            var action = this._viewer.selectedNode;
+            var parent = action.parent;
+
+            // If trigger, remove branch
+            if (action.type === Type.TRIGGER) {
+                this.onRemoveBranch();
+                return;
+            }
+
+            // If it is a combine hub
+            if (action.type === Type.FLOW_CONTROL && parent !== null && parent.combineArray !== null) {
+                action = parent;
+                parent = action.parent;
+            }
+
+            // Remove
+            if (parent !== null && parent.combineArray !== null) {
+                parent.removeCombinedAction(action);
+                if (parent.combineArray.length === 0) {
+                    parent.node.text.attr("text", "combine");
+
+                }
+            }
+            else {
+                if (action.combineArray !== null) {
+                    action.removeChild(action.hub);
+                }
+                action.parent.removeChild(action);
+            }
+
+            if (action.combineArray !== null) {
+                this._viewer.removeAction(action.hub, false);
+            }
+            this._viewer.removeAction(action, false);
+
+            // Finish
+            this._viewer.update();
+            this._viewer.parameters.clearParameters();
+            this._viewer.selectedNode = null;
+        }
+
+        /*
+        * Removes a branch starting from the selected node
+        */
+        public onRemoveBranch(): void {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+
+            if (this._viewer.selectedNode === this._viewer.root) {
+                alert("Cannot remove the root node");
+                return;
+            }
+
+            var action = this._viewer.selectedNode;
+            var parent = action.parent;
+
+            // If combine
+            if (action.parent !== null && action.parent.combineArray !== null) {
+                action = parent;
+                parent = action.parent;
+            }
+
+            // Remove
+            if (action.combineArray !== null) {
+                action.removeChild(action.hub);
+            }
+            action.parent.removeChild(action);
+            this._viewer.removeAction(action, true);
+
+            // Finish
+            this._viewer.update();
+            this._viewer.parameters.clearParameters();
+            this._viewer.selectedNode = null;
+        }
+
+        /*
+        * Copies the selected structure
+        */ 
+        public onCopyStructure(): void {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+
+            var structure = this.createJSON(this._viewer.selectedNode);
+            var asText = JSON.stringify(structure);
+
+            if (window.clipboardData !== undefined) {
+                window.clipboardData.setData("text", asText);
+            }
+            else {
+                this.copiedStructure = asText;
+            }
+        }
+
+        /*
+        * Pastes the graph structure previously copied
+        */
+        public onPasteStructure(): void {
+            if (this._viewer.selectedNode === null) {
+                return;
+            }
+
+            var asText = (window.clipboardData !== undefined) ? window.clipboardData.getData("text") : this.copiedStructure;
+            var isJson = asText.length > 0 && asText[0] == "{" && asText[asText.length - 1] == "}";
+            var structure: JSONObject = JSON.parse(asText);
+            var action = this._viewer.selectedNode;
+
+            if (structure.type === Type.TRIGGER && action !== this._viewer.root) {
+                alert("You can't paste a trigger if the selected node isn't the root object");
+                return;
+            }
+
+            if (structure.type !== Type.TRIGGER && action === this._viewer.root) {
+                alert("You can't paste an action or condition if the selected node is the root object");
+                return;
+            }
+
+            this.loadFromJSON(structure, action);
+            this._viewer.update();
+        }
+
+        /*
+        * Loads a graph from JSON
+        * @pram graph: the graph structure
+        * @param startAction: the action to start load
+        */
+        public loadFromJSON(graph: JSONObject, startAction: Action): void {
+            // If startNode is null, means it replaces all the graph
+            // If not, it comes from a copy/paste
+            if (startAction === null) {
+                for (var i = 0; i < this._viewer.root.children.length; i++) {
+                    this._viewer.removeAction(this._viewer.root.children[i], true);
+                }
+                this._viewer.root.clearChildren();
+            }
+
+            var load = (root: JSONObject, parent: Action, detached: boolean, combine: boolean) => {
+                if (parent === null) {
+                    parent = this._viewer.root;
+                }
+
+                var newAction: Action = null;
+
+                if (root.type !== Type.OBJECT && root.type !== Type.SCENE) {
+                    var action = this._viewer.addAction(parent, root.type, Elements.GetElementFromName(root.name));
+
+                    for (var i = 0; i < root.properties.length; i++) {
+                        var targetType = root.properties[i].targetType;
+                        if (targetType === undefined) {
+                            targetType = "MeshProperties"; // Default is mesh properties
+                        }
+                        action.propertiesResults[i] = { value: root.properties[i].value, targetType: targetType };
+                    }
+
+                    var node = action.node;
+                    node.detached = root.detached;
+
+                    if (detached) {
+                        node.rect.attr("fill", this._viewer.getNodeColor(action.type, detached));
+                    }
+
+                    // If combine array
+                    if (root.combine !== undefined) {
+                        for (var i = 0; i < root.combine.length; i++) {
+                            load(root.combine[i], action, detached, true);
+                        }
+                    }
+
+                    if (!combine) {
+                        parent = parent.children[parent.children.length - 1];
+                    }
+                }
+
+                for (var i = 0; i < root.children.length; i++) {
+                    load(root.children[i], newAction !== null && newAction.combineArray !== null ? newAction.hub : parent, root.detached, false);
+                }
+            };
+
+            // Finish
+            load(graph, startAction, false, false);
+            this._viewer.update();
+        }
+
+        /*
+        * Creates a JSON object starting from a root action
+        * @param root: the root action
+        */
+        public createJSON(root: Action): JSONObject {
+            var action: JSONObject = {
+                type: root.type,
+                name: root.name,
+                detached: root.node.detached,
+                children: new Array<JSONObject>(),
+                combine: new Array<JSONObject>(),
+                properties: new Array<JSONObjectProperty>()
+            };
+
+            // Set properties
+            for (var i = 0; i < root.properties.length; i++) {
+                action.properties.push({
+                    name: root.properties[i].text,
+                    value: root.propertiesResults[i].value,
+                    targetType: root.propertiesResults[i].targetType
+                });
+            }
+
+            // If combine
+            if (root.combineArray !== null) {
+                for (var i = 0; i < root.combineArray.length; i++) {
+                    var combinedAction = root.combineArray[i];
+                    action.combine.push(this.createJSON(combinedAction));
+                }
+
+                root = root.children[0]; // Hub
+            }
+
+            for (var i = 0; i < root.children.length; i++) {
+                action.children.push(this.createJSON(root.children[i]));
+            }
+
+            return action;
+        }
+
+        /*
+        *
+        */
+        public setElementVisible(element: HTMLElement, visible: boolean): void {
+            element.style.display = visible ? "block" : "none";
+        }
+
+    }
+} 

+ 539 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.viewer.js

@@ -0,0 +1,539 @@
+var ActionsBuilder;
+(function (ActionsBuilder) {
+    var Viewer = (function () {
+        function Viewer(type) {
+            var _this = this;
+            this.objectName = "Unnamed Object";
+            this.zoom = 1.0;
+            this._firstUpdate = true;
+            this.viewerContainer = document.getElementById("GraphContainerID");
+            this.viewerElement = document.getElementById("GraphElementID");
+            this.paper = Raphael("GraphElementID", screen.width, screen.height);
+            this.root = this.addAction(null, type, { name: this.objectName, text: this.objectName, properties: [], description: "" });
+            this.selectedNode = null;
+            window.addEventListener("resize", function (event) {
+                _this.onResize(event);
+            });
+            window.addEventListener("mousemove", function (event) {
+                _this.onMove(event);
+            });
+            this.paper.canvas.addEventListener("click", function (event) {
+                _this.onClick(event);
+            });
+            this._toolbar = new ActionsBuilder.Toolbar(this);
+            this._contextMenu = new ActionsBuilder.ContextMenu(this);
+            this.parameters = new ActionsBuilder.Parameters(this);
+            this.utils = new ActionsBuilder.Utils(this);
+            this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
+            this.onResize(null);
+        }
+        Object.defineProperty(Viewer, "NODE_WIDTH", {
+            get: function () {
+                return Viewer._NODE_WIDTH;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Viewer, "NODE_HEIGHT", {
+            get: function () {
+                return Viewer._NODE_HEIGHT;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Viewer, "NODE_MINIMIZED_WIDTH", {
+            get: function () {
+                return Viewer._NODE_MINIMIZE_WIDTH;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(Viewer, "VERTICAL_OFFSET", {
+            get: function () {
+                return Viewer._VERTICAL_OFFSET;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Viewer.prototype.onResize = function (event) {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.viewerContainer.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
+            this.viewerElement.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
+            this.parameters.onResize();
+            this._toolbar.onResize();
+            if (this.paper.height < window.innerHeight) {
+                this.paper.setSize(this.paper.width, window.innerHeight);
+            }
+            if (this._firstUpdate) {
+                this.viewerElement.scrollLeft = ((this.viewerElement.scrollWidth / 2) - (this.viewerElement.getBoundingClientRect().width / 2));
+                this._firstUpdate = false;
+            }
+        };
+        Viewer.prototype.onMove = function (event) {
+            this.mousex = event.clientX - this.paper.canvas.getBoundingClientRect().left;
+            this.mousey = event.clientY - this.paper.canvas.getBoundingClientRect().top;
+        };
+        Viewer.prototype.onClick = function (event) {
+            if (this._contextMenu.showing) {
+                return;
+            }
+            if (this.selectedNode !== null) {
+                var node = this.selectedNode.node;
+                node.rect.attr("fill", this.getNodeColor(this.selectedNode.type, node.detached));
+            }
+            var result = this.traverseGraph(null, this.mousex, this.mousey, true);
+            if (result.hit) {
+                this.selectedNode = result.action;
+                var node = this.selectedNode.node;
+                node.rect.attr("fill", this.getSelectedNodeColor(this.selectedNode.type, node.detached));
+            }
+            else {
+                this.selectedNode = null;
+                this.parameters.clearParameters();
+                this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
+            }
+        };
+        Viewer.prototype.setColorTheme = function (color) {
+            this.paper.canvas.style.background = color;
+        };
+        Viewer.prototype.getNodeColor = function (type, detached) {
+            if (detached) {
+                return Raphael.rgb(96, 122, 14);
+            }
+            switch (type) {
+                case ActionsBuilder.Type.TRIGGER:
+                    return Raphael.rgb(133, 154, 185);
+                    break;
+                case ActionsBuilder.Type.ACTION:
+                    return Raphael.rgb(182, 185, 132);
+                    break;
+                case ActionsBuilder.Type.FLOW_CONTROL:
+                    return Raphael.rgb(185, 132, 140);
+                    break;
+                case ActionsBuilder.Type.OBJECT:
+                case ActionsBuilder.Type.SCENE:
+                    return Raphael.rgb(255, 255, 255);
+                    break;
+                default: break;
+            }
+            return null;
+        };
+        Viewer.prototype.getSelectedNodeColor = function (type, detached) {
+            if (detached) {
+                return Raphael.rgb(96, 122, 14);
+            }
+            switch (type) {
+                case ActionsBuilder.Type.TRIGGER:
+                    return Raphael.rgb(41, 129, 255);
+                    break;
+                case ActionsBuilder.Type.ACTION:
+                    return Raphael.rgb(255, 220, 42);
+                    break;
+                case ActionsBuilder.Type.FLOW_CONTROL:
+                    return Raphael.rgb(255, 41, 53);
+                    break;
+                case ActionsBuilder.Type.OBJECT:
+                case ActionsBuilder.Type.SCENE:
+                    return Raphael.rgb(255, 255, 255);
+                    break;
+                default: break;
+            }
+            return null;
+        };
+        Viewer.prototype.removeAction = function (action, removeChildren) {
+            if (action.parent !== null && action.parent.hub === action) {
+                this.removeAction(action.parent, false);
+                return;
+            }
+            this.removeNode(action.node);
+            if (action.combineArray !== null) {
+                this.removeNode(action.hub.node);
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    this.removeNode(action.combineArray[i].node);
+                }
+                action.combineArray.length = 0;
+            }
+            if (removeChildren) {
+                for (var i = 0; i < action.children.length; i++) {
+                    this.removeAction(action.children[i], removeChildren);
+                }
+                action.clearChildren();
+            }
+            else {
+                for (var i = 0; i < action.children.length; i++) {
+                    action.parent.addChild(action.children[i]);
+                    action.children[i].parent = action.parent;
+                }
+            }
+        };
+        Viewer.prototype.removeNode = function (node) {
+            node.rect.remove();
+            node.text.remove();
+            if (node.line !== null) {
+                node.line.remove();
+            }
+        };
+        Viewer.prototype.update = function () {
+            var _this = this;
+            this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
+            var onSetNodeSize = function (node) {
+                node.rect.attr("width", node.minimized ? Viewer.NODE_MINIMIZED_WIDTH : Viewer.NODE_WIDTH * _this.zoom);
+                node.rect.attr("height", Viewer.NODE_HEIGHT * _this.zoom);
+                node.text.attr("font-size", 11 * _this.zoom);
+            };
+            var onSetPositionPass = function (action, yPosition) {
+                var node = action.node;
+                var parent = action.parent !== null ? action.parent : null;
+                if (action.combineArray !== null) {
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        var combinedNode = action.combineArray[i].node;
+                        onSetNodeSize(combinedNode);
+                    }
+                }
+                onSetNodeSize(node);
+                if (parent) {
+                    var parentx = parent.node.rect.attr("x");
+                    if (parent.combineArray !== null && parent.combineArray.length > 1) {
+                        parentx += parent.node.rect.attr("width") / 2;
+                    }
+                    _this._setActionPosition(action, parentx, yPosition);
+                    _this._setActionLine(action);
+                }
+                var totalSize = 0;
+                for (var i = 0; i < action.children.length; i++) {
+                    var childNode = action.children[i].node;
+                    totalSize += childNode.rect.attr("width");
+                }
+                var nodeWidth = node.rect.attr("width");
+                var startingPositionX = node.rect.attr("x");
+                for (var i = 0; i < action.children.length; i++) {
+                    var childAction = action.children[i];
+                    var childNode = childAction.node;
+                    var newPositionX = startingPositionX;
+                    if (childAction.combineArray !== null && childAction.combineArray.length > 1) {
+                        newPositionX -= (childNode.rect.attr("width") / 2) - nodeWidth / 2;
+                    }
+                    var newPositionY = yPosition + Viewer.VERTICAL_OFFSET * _this.zoom;
+                    onSetPositionPass(childAction, newPositionY);
+                    _this._setActionPosition(childAction, newPositionX, newPositionY);
+                    _this._setActionLine(childAction);
+                }
+            };
+            onSetPositionPass(this.root, 10 * this.zoom);
+            var onGetSizePass = function (action, maxSize) {
+                var mySize = 0;
+                if (action.combineArray !== null) {
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        mySize += action.combineArray[i].node.rect.attr("width");
+                    }
+                }
+                else {
+                    mySize = action.node.rect.attr("width");
+                }
+                if (mySize > maxSize) {
+                    maxSize = mySize;
+                }
+                for (var i = 0; i < action.children.length; i++) {
+                    maxSize = onGetSizePass(action.children[i], maxSize);
+                }
+                return maxSize;
+            };
+            var onResizeCanvas = function (action) {
+                var node = action.node;
+                var nodex = node.rect.attr("x");
+                var nodey = node.rect.attr("y");
+                if (nodex < 0 || nodex > _this.paper.width) {
+                    _this.paper.setSize(_this.paper.width + 1000, _this.paper.height);
+                    _this._setActionPosition(_this.root, (_this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * _this.zoom, 10);
+                }
+                if (nodey > _this.paper.height) {
+                    _this.paper.setSize(_this.paper.width, _this.paper.height + 1000);
+                    _this._setActionPosition(_this.root, (_this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * _this.zoom, 10);
+                }
+            };
+            var widths = new Array();
+            for (var i = 0; i < this.root.children.length; i++) {
+                var trigger = this.root.children[i];
+                var triggerResult = { triggerWidth: onGetSizePass(trigger, 0), childrenWidths: new Array() };
+                if (trigger.children.length > 0) {
+                    triggerResult.triggerWidth = 0;
+                }
+                for (var j = 0; j < trigger.children.length; j++) {
+                    var actionWidth = onGetSizePass(trigger.children[j], 0);
+                    triggerResult.triggerWidth += actionWidth + 15;
+                    triggerResult.childrenWidths.push({
+                        triggerWidth: actionWidth,
+                        childrenWidths: null
+                    });
+                }
+                widths.push(triggerResult);
+            }
+            var onSetNodePosition = function (action, widthArray, isChild) {
+                var actionsCount = action.children.length;
+                var actionsMiddle = actionsCount % 2;
+                var actionsHasMiddle = actionsMiddle !== 0;
+                var actionsLeftOffset = 0;
+                var actionsRightOffset = 0;
+                var actionWidth = action.node.rect.attr("width");
+                if (actionsHasMiddle && actionsCount > 1) {
+                    var middle = Math.floor(actionsCount / 2);
+                    actionsLeftOffset += widthArray[middle].triggerWidth / 2;
+                    actionsRightOffset += widthArray[middle].triggerWidth / 2;
+                }
+                var leftStart = actionsHasMiddle ? Math.floor(actionsCount / 2) - 1 : (actionsCount / 2) - 1;
+                for (var i = leftStart; i >= 0; i--) {
+                    var child = action.children[i];
+                    var node = child.node;
+                    var width = (widthArray[i].triggerWidth) + 15;
+                    _this._setActionPosition(action.children[i], node.rect.attr("x") - actionsLeftOffset - (width / 2), node.rect.attr("y"));
+                    _this._setActionLine(child);
+                    onResizeCanvas(child);
+                    actionsLeftOffset += width;
+                }
+                var rightStart = actionsHasMiddle ? Math.round(actionsCount / 2) : actionsCount / 2;
+                for (var i = rightStart; i < actionsCount; i++) {
+                    var child = action.children[i];
+                    var node = child.node;
+                    var width = (widthArray[i].triggerWidth) + 15;
+                    _this._setActionPosition(action.children[i], node.rect.attr("x") + actionsRightOffset + (width / 2), node.rect.attr("y"));
+                    _this._setActionLine(child);
+                    onResizeCanvas(child);
+                    actionsRightOffset += width;
+                }
+            };
+            onSetNodePosition(this.root, widths, false);
+            for (var i = 0; i < this.root.children.length; i++) {
+                onSetNodePosition(this.root.children[i], widths[i].childrenWidths, true);
+            }
+        };
+        Viewer.prototype.addAction = function (parent, type, element) {
+            var node = this._createNode(element.text, type, parent === null);
+            var action = new ActionsBuilder.Action(node);
+            if (element.name === "CombineAction") {
+                action.combineArray = new Array();
+                var hubElement = ActionsBuilder.Elements.FLOW_CONTROLS[ActionsBuilder.Elements.FLOW_CONTROLS.length - 1];
+                var hub = this.addAction(action, ActionsBuilder.Type.FLOW_CONTROL, hubElement);
+                action.hub = hub;
+                action.addChild(hub);
+                this._createActionAnimation(hub);
+            }
+            action.name = element.name;
+            action.properties = element.properties;
+            action.type = type;
+            for (var i = 0; i < action.properties.length; i++) {
+                action.propertiesResults.push({ targetType: action.properties[i].targetType, value: action.properties[i].value });
+            }
+            if (action.properties !== null && action.properties.length > 0) {
+                if (action.properties[0].text === "target") {
+                    action.propertiesResults[0].value = this.objectName;
+                }
+            }
+            if (parent !== null) {
+                if (parent.combineArray === null) {
+                    parent.addChild(action);
+                }
+                else if (parent.combineArray !== null && action.name !== "Hub") {
+                    parent.combineArray.push(action);
+                    action.parent = parent;
+                    action.combineAction = parent;
+                    parent.node.text.attr("text", "");
+                }
+            }
+            this._createActionAnimation(action);
+            return action;
+        };
+        Viewer.prototype.traverseGraph = function (start, x, y, traverseCombine) {
+            if (start === null)
+                start = this.root;
+            var result = { action: start, hit: true };
+            if (start.node.isPointInside(x, y)) {
+                return result;
+            }
+            for (var i = 0; i < start.children.length; i++) {
+                var action = start.children[i];
+                if (action.node.isPointInside(x, y)) {
+                    result.hit = true;
+                    result.action = start.children[i];
+                    if (traverseCombine && action.combineArray !== null) {
+                        for (var j = 0; j < action.combineArray.length; j++) {
+                            if (action.combineArray[j].node.isPointInside(x, y)) {
+                                result.action = action.combineArray[j];
+                                break;
+                            }
+                        }
+                    }
+                    return result;
+                }
+                result = this.traverseGraph(action, x, y, traverseCombine);
+                if (result.hit) {
+                    return result;
+                }
+            }
+            result.hit = false;
+            result.action = null;
+            return result;
+        };
+        Viewer.prototype._setActionPosition = function (action, x, y) {
+            var node = action.node;
+            var offsetx = node.rect.attr("x") - x;
+            var parent = action.parent;
+            if (parent !== null && parent.combineArray !== null && parent.combineArray.length > 1) {
+                var parentNode = parent.node;
+                x = parentNode.rect.attr("x") + (parent.node.rect.attr("width") / 2) - (node.rect.attr("width") / 2);
+            }
+            node.rect.attr("x", x);
+            node.rect.attr("y", y);
+            var textBBox = node.text.getBBox();
+            var textWidth = 0;
+            if (textBBox !== null && textBBox !== undefined) {
+                textWidth = textBBox.width;
+            }
+            node.text.attr("x", x + node.rect.attr("width") / 2 - textWidth / 2);
+            node.text.attr("y", y + node.rect.attr("height") / 2);
+            if (action.combineArray !== null && action.combineArray.length > 0) {
+                var length = 0;
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    var combinedAction = action.combineArray[i];
+                    var combinedNode = combinedAction.node;
+                    combinedNode.rect.attr("x", node.rect.attr("x") + length);
+                    combinedNode.rect.attr("y", node.rect.attr("y"));
+                    textBBox = combinedNode.text.getBBox();
+                    if (textBBox !== null) {
+                        textWidth = textBBox.width;
+                    }
+                    combinedNode.text.attr("x", combinedNode.rect.attr("x") + combinedNode.rect.attr("width") / 2 - textWidth / 2);
+                    combinedNode.text.attr("y", y + combinedNode.rect.attr("height") / 2);
+                    length += combinedNode.rect.attr("width");
+                }
+                node.rect.attr("width", length);
+            }
+            for (var i = 0; i < action.children.length; i++) {
+                var child = action.children[i];
+                this._setActionPosition(child, child.node.rect.attr("x") - offsetx, y + Viewer.VERTICAL_OFFSET * this.zoom);
+                this._setActionLine(child);
+            }
+        };
+        Viewer.prototype._setActionLine = function (action) {
+            if (action.node.line === null) {
+                return;
+            }
+            var node = action.node;
+            var nodex = node.rect.attr("x");
+            var nodey = node.rect.attr("y");
+            var nodeWidth = node.rect.attr("width");
+            var nodeHeight = node.rect.attr("height");
+            var parent = action.parent.node;
+            var parentx = parent.rect.attr("x");
+            var parenty = parent.rect.attr("y");
+            var parentWidth = parent.rect.attr("width");
+            var parentHeight = parent.rect.attr("height");
+            if (node.detached) {
+                node.line.attr("path", ["M", nodex, nodey, "L", nodex, nodey]);
+                return;
+            }
+            var line1x = nodex + (nodeWidth / 2);
+            var line1y = nodey;
+            var line2y = line1y - (line1y - parenty - parentHeight) / 2;
+            var line3x = parentx + (parentWidth / 2);
+            var line4y = parenty + parentHeight;
+            node.line.attr("path", ["M", line1x, line1y, "L", line1x, line2y, "L", line3x, line2y, "L", line3x, line4y]);
+        };
+        Viewer.prototype._createNode = function (text, type, noLine) {
+            var node = new ActionsBuilder.Node();
+            var color = this.getNodeColor(type, false);
+            node.rect = this.paper.rect(20, 20, Viewer.NODE_WIDTH, Viewer.NODE_HEIGHT, 0);
+            node.rect.attr("fill", color);
+            node.text = this.paper.text(20, 20, text);
+            node.text.attr("font-size", 11);
+            node.text.attr("text-anchor", "start");
+            node.text.attr("font-family", "Sinkin Sans Light");
+            if (!noLine) {
+                node.line = this.paper.path("");
+                node.line.attr("stroke", color);
+            }
+            return node;
+        };
+        Viewer.prototype._createActionAnimation = function (action) {
+            var _this = this;
+            var node = action.node;
+            var finished = true;
+            var nodex = 0;
+            var nodey = 0;
+            var onMove = function (dx, dy, x, y) { };
+            var onStart = function (x, y, event) {
+                if (node.minimized) {
+                    return;
+                }
+                if (finished) {
+                    nodex = node.rect.attr("x");
+                    nodey = node.rect.attr("y");
+                }
+                finished = false;
+                node.rect.animate({
+                    x: node.rect.attr("x") - 10,
+                    y: node.rect.attr("y"),
+                    width: (Viewer.NODE_WIDTH + 20) * _this.zoom,
+                    height: (Viewer.NODE_HEIGHT + 10) * _this.zoom,
+                    opacity: 0.25
+                }, 500, ">");
+            };
+            var onEnd = function (event) {
+                if (!node.minimized) {
+                    node.rect.animate({
+                        x: nodex,
+                        y: nodey,
+                        width: Viewer.NODE_WIDTH * _this.zoom,
+                        height: Viewer.NODE_HEIGHT * _this.zoom,
+                        opacity: 1.0
+                    }, 500, ">", function () { finished = true; });
+                }
+                var dragResult = _this.traverseGraph(null, _this.mousex, _this.mousey, true);
+                if (dragResult.hit && dragResult.action === action || !dragResult.hit) {
+                    _this.parameters.createParameters(action);
+                }
+                else {
+                    if (dragResult.action.children.length > 0 && action.type !== ActionsBuilder.Type.TRIGGER) {
+                        return;
+                    }
+                    if (action.type === ActionsBuilder.Type.TRIGGER && dragResult.action !== _this.root) {
+                        return;
+                    }
+                    if (action.type === ActionsBuilder.Type.ACTION && dragResult.action === _this.root) {
+                        return;
+                    }
+                    if (action.type === ActionsBuilder.Type.FLOW_CONTROL && (dragResult.action === _this.root || dragResult.action.type === ActionsBuilder.Type.FLOW_CONTROL)) {
+                        return;
+                    }
+                    if (action === dragResult.action.parent) {
+                        return;
+                    }
+                    if (action.parent !== null && action.parent.combineArray !== null) {
+                        return;
+                    }
+                    node.rect.stop(node.rect.animation);
+                    node.text.stop(node.text.animation);
+                    node.rect.undrag();
+                    node.text.undrag();
+                    node.rect.attr("opacity", 1.0);
+                    node.rect.attr("width", Viewer.NODE_WIDTH);
+                    node.rect.attr("height", Viewer.NODE_HEIGHT);
+                    if (action.parent !== null) {
+                        action.parent.removeChild(action);
+                        dragResult.action.addChild(action);
+                        _this.update();
+                        _this._createActionAnimation(action);
+                    }
+                }
+            };
+            node.rect.drag(onMove, onStart, onEnd);
+            node.text.drag(onMove, onStart, onEnd);
+        };
+        Viewer._NODE_WIDTH = 150;
+        Viewer._NODE_HEIGHT = 25;
+        Viewer._NODE_MINIMIZE_WIDTH = 50;
+        Viewer._VERTICAL_OFFSET = 70;
+        Viewer._DEFAULT_INFO_MESSAGE = "Select or add a node to customize actions";
+        return Viewer;
+    })();
+    ActionsBuilder.Viewer = Viewer;
+})(ActionsBuilder || (ActionsBuilder = {}));

+ 773 - 0
Exporters/3ds Max/ActionsBuilder/actionsbuilder.viewer.ts

@@ -0,0 +1,773 @@
+module ActionsBuilder {
+    export interface TraverseResult {
+        action: Action;
+        hit: boolean;
+    }
+
+    export interface UpdateResult {
+        triggerWidth: number;
+        childrenWidths: Array<UpdateResult>;
+    }
+
+    export class Viewer {
+        // Statics
+        private static _NODE_WIDTH: number = 150;
+        private static _NODE_HEIGHT: number = 25;
+        private static _NODE_MINIMIZE_WIDTH: number = 50;
+        private static _VERTICAL_OFFSET: number = 70;
+        
+        private static _DEFAULT_INFO_MESSAGE = "Select or add a node to customize actions";
+        
+        public static get NODE_WIDTH(): number {
+            return Viewer._NODE_WIDTH;
+        }
+
+        public static get NODE_HEIGHT(): number {
+            return Viewer._NODE_HEIGHT;
+        }
+
+        public static get NODE_MINIMIZED_WIDTH(): number {
+            return Viewer._NODE_MINIMIZE_WIDTH;
+        }
+
+        public static get VERTICAL_OFFSET(): number {
+            return Viewer._VERTICAL_OFFSET;
+        }
+
+        // Members
+        // Public
+        public viewerContainer: HTMLElement;
+        public viewerElement: HTMLElement;
+
+        public objectName: string = "Unnamed Object";
+        public zoom: number = 1.0;
+        public mousex: number;
+        public mousey: number;
+
+        public paper: Paper;
+        public root: Action;
+        public selectedNode: Action;
+
+        public parameters: Parameters;
+        public utils: Utils;
+
+        // Private
+        private _toolbar: Toolbar;
+        private _contextMenu: ContextMenu;
+
+        private _firstUpdate: boolean = true;
+
+        /*
+        * Constructor
+        * @param type: the root type object (OBJECT or SCENE)
+        */
+        constructor(type: number) {
+            // Get HTML elements
+            this.viewerContainer = document.getElementById("GraphContainerID");
+            this.viewerElement = document.getElementById("GraphElementID");
+
+            // Create element
+            this.paper = Raphael("GraphElementID", screen.width, screen.height);
+
+            // Configure this
+            //var name = type === Type.OBJECT ? "Unnamed object" : "Scene";
+            this.root = this.addAction(null, type, { name: this.objectName, text: this.objectName, properties: [], description: "" });
+            this.selectedNode = null;
+
+            // Configure events
+            window.addEventListener("resize", (event: Event) => {
+                this.onResize(event);
+            });
+            window.addEventListener("mousemove", (event: MouseEvent) => {
+                this.onMove(event);
+            });
+            this.paper.canvas.addEventListener("click", (event: MouseEvent) => {
+                this.onClick(event);
+            });
+
+            // Load modules
+            this._toolbar = new Toolbar(this);
+            this._contextMenu = new ContextMenu(this);
+
+            this.parameters = new Parameters(this);
+            this.utils = new Utils(this);
+
+            // Finish
+            this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
+            this.onResize(null);
+        }
+
+        /*
+        * Resize event
+        * @param event: the resize event
+        */
+        public onResize(event?: Event): void {
+            var tools = document.getElementById("ToolsButtonsID");
+            this.viewerContainer.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
+            this.viewerElement.style.height = window.innerHeight - tools.getBoundingClientRect().height - 25 - 50 + "px";
+
+            this.parameters.onResize();
+            this._toolbar.onResize();
+
+            if (this.paper.height < window.innerHeight) {
+                this.paper.setSize(this.paper.width, window.innerHeight);
+            }
+
+            if (this._firstUpdate) {
+                this.viewerElement.scrollLeft = ((this.viewerElement.scrollWidth / 2) - (this.viewerElement.getBoundingClientRect().width / 2));
+                this._firstUpdate = false;
+            }
+        }
+
+        /*
+        * Handles the onMove event
+        * @param event: the onMove mouse event
+        */
+        public onMove(event: MouseEvent): void {
+            this.mousex = event.clientX - this.paper.canvas.getBoundingClientRect().left;
+            this.mousey = event.clientY - this.paper.canvas.getBoundingClientRect().top;
+        }
+
+        /*
+        * Handles the onClick event to get selected node
+        * @param event: the onClick mouse event
+        */
+        public onClick(event: MouseEvent): void {
+            if (this._contextMenu.showing) {
+                return;
+            }
+
+            // Reset selected node
+            if (this.selectedNode !== null) {
+                var node = this.selectedNode.node;
+                node.rect.attr("fill", this.getNodeColor(this.selectedNode.type, node.detached));
+            }
+
+            // Configure new selected node
+            var result = this.traverseGraph(null, this.mousex, this.mousey, true);
+            if (result.hit) {
+                this.selectedNode = result.action;
+
+                var node = this.selectedNode.node;
+                node.rect.attr("fill", this.getSelectedNodeColor(this.selectedNode.type, node.detached));
+            }
+            else {
+                this.selectedNode = null;
+                this.parameters.clearParameters();
+                this.parameters.parametersHelpElement.textContent = Viewer._DEFAULT_INFO_MESSAGE;
+            }
+        }
+
+        /*
+        * Set the color theme of the viewer
+        * @param color: the color theme ( ex: "rgb(64, 64, 64)" )
+        */
+        public setColorTheme(color: string): void {
+            this.paper.canvas.style.background = color;
+        }
+
+        /*
+        * Returns the color according to the given parameters
+        * @param action: the action used to select the color
+        * @param detached: if the node is attached to its parent or not
+        */
+        public getNodeColor(type: number, detached: boolean): RaphaelColor {
+            if (detached) {
+                return Raphael.rgb(96, 122, 14);
+            }
+
+            switch (type) {
+                case Type.TRIGGER: return Raphael.rgb(133, 154, 185);
+                case Type.ACTION:  return Raphael.rgb(182, 185, 132);
+                case Type.FLOW_CONTROL: return Raphael.rgb(185, 132, 140);
+                case Type.OBJECT:
+                case Type.SCENE: return Raphael.rgb(255, 255, 255);
+                default: break;
+            }
+
+            return null;
+        }
+
+        /*
+        * Returns the selected node color according to the given parameters
+        * @param action: the action used to select the color
+        * @param detached: if the node is attached to its parent or not
+        */
+        public getSelectedNodeColor(type: number, detached: boolean): RaphaelColor {
+            if (detached) {
+                return Raphael.rgb(96, 122, 14);
+            }
+
+            switch (type) {
+                case Type.TRIGGER: return Raphael.rgb(41, 129, 255);
+                case Type.ACTION: return Raphael.rgb(255, 220, 42);
+                case Type.FLOW_CONTROL: return Raphael.rgb(255, 41, 53);
+                case Type.OBJECT:
+                case Type.SCENE: return Raphael.rgb(255, 255, 255);
+                default: break;
+            }
+
+            return null;
+        }
+
+        /*
+        * Removes the given action from the graph
+        * @param action: the action to remove
+        * @param removeChildren: if remove the branch or not
+        */
+        public removeAction(action: Action, removeChildren: boolean): void {
+            // If selected node is combine
+            if (action.parent !== null && action.parent.hub === action) {
+                this.removeAction(action.parent, false);
+                return;
+            }
+
+            // Basic suppress
+            this.removeNode(action.node);
+
+            if (action.combineArray !== null) {
+                this.removeNode(action.hub.node);
+                // Remove combine array
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    this.removeNode(action.combineArray[i].node);
+                }
+                action.combineArray.length = 0;
+            }
+
+            if (removeChildren) {
+                for (var i = 0; i < action.children.length; i++) {
+                    this.removeAction(action.children[i], removeChildren);
+                }
+
+                action.clearChildren();
+            }
+            else {
+                for (var i = 0; i < action.children.length; i++) {
+                    action.parent.addChild(action.children[i]);
+                    action.children[i].parent = action.parent;
+                }
+            }
+        }
+
+        /*
+        * Removes the given node (not the action)
+        * @param node: the node to remove
+        */
+        public removeNode(node: Node): void {
+            node.rect.remove();
+            node.text.remove();
+
+            if (node.line !== null) {
+                node.line.remove();
+            }
+        }
+
+        /*
+        * Updates the graph viewer
+        */
+        public update(): void {
+            // Set root position
+            this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
+
+            // Sets node size
+            var onSetNodeSize = (node: Node) => {
+                node.rect.attr("width", node.minimized ? Viewer.NODE_MINIMIZED_WIDTH : Viewer.NODE_WIDTH * this.zoom);
+                node.rect.attr("height", Viewer.NODE_HEIGHT * this.zoom);
+                node.text.attr("font-size", 11 * this.zoom);
+            };
+
+            // First pass: set actions positions according to parents
+            var onSetPositionPass = (action: Action, yPosition: number): void => {
+
+                var node = action.node;
+                var parent = action.parent !== null ? action.parent : null;
+
+                // Set node properties (size, text size, etc.)
+                if (action.combineArray !== null) {
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        var combinedNode = action.combineArray[i].node;
+                        onSetNodeSize(combinedNode);
+                    }
+                }
+                onSetNodeSize(node);
+
+                // Set position from parent
+                if (parent) {
+                    var parentx = parent.node.rect.attr("x");
+                    if (parent.combineArray !== null && parent.combineArray.length > 1) {
+                        parentx += parent.node.rect.attr("width") / 2;
+                    }
+                    this._setActionPosition(action, parentx, yPosition);
+                    this._setActionLine(action);
+                }
+
+                // Calculate total width for current action
+                var totalSize = 0;
+                for (var i = 0; i < action.children.length; i++) {
+                    var childNode = action.children[i].node;
+                    totalSize += childNode.rect.attr("width");
+                }
+
+                // Get values to place nodes according to the parent position
+                var nodeWidth = node.rect.attr("width");
+                var startingPositionX = node.rect.attr("x");
+
+                // Set children positions
+                for (var i = 0; i < action.children.length; i++) {
+                    var childAction = action.children[i];
+                    var childNode = childAction.node;
+
+                    var newPositionX = startingPositionX;
+                    if (childAction.combineArray !== null && childAction.combineArray.length > 1) {
+                        newPositionX -= (childNode.rect.attr("width") / 2) - nodeWidth / 2;
+                    }
+
+                    var newPositionY = yPosition + Viewer.VERTICAL_OFFSET * this.zoom;
+
+                    onSetPositionPass(childAction, newPositionY);
+
+                    this._setActionPosition(childAction, newPositionX, newPositionY);
+                    this._setActionLine(childAction);
+                }
+            };
+
+            onSetPositionPass(this.root, 10 * this.zoom);
+
+            // Seconds pass, get sizes of groups
+            var onGetSizePass = (action: Action, maxSize: number): number => {
+                var mySize = 0;
+
+                if (action.combineArray !== null) {
+                    for (var i = 0; i < action.combineArray.length; i++) {
+                        mySize += action.combineArray[i].node.rect.attr("width");
+                    }
+                }
+                else {
+                    mySize = action.node.rect.attr("width");
+                }
+
+                if (mySize > maxSize) {
+                    maxSize = mySize;
+                }
+
+                for (var i = 0; i < action.children.length; i++) {
+                    maxSize = onGetSizePass(action.children[i], maxSize);
+                }
+
+                return maxSize;
+            };
+
+            // Resize canvas
+            var onResizeCanvas = (action: Action): void => {
+                var node = action.node;
+                var nodex = node.rect.attr("x");
+                var nodey = node.rect.attr("y");
+
+                if (nodex < 0 || nodex > this.paper.width) {
+                    this.paper.setSize(this.paper.width + 1000, this.paper.height);
+                    this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
+                }
+                if (nodey > this.paper.height) {
+                    this.paper.setSize(this.paper.width, this.paper.height + 1000);
+                    this._setActionPosition(this.root, (this.paper.width / 2) - (Viewer.NODE_WIDTH / 2) * this.zoom, 10);
+                }
+            };
+
+            var widths = new Array<UpdateResult>();
+
+            for (var i = 0; i < this.root.children.length; i++) {
+                var trigger = this.root.children[i];
+                var triggerResult: UpdateResult = { triggerWidth: onGetSizePass(trigger, 0), childrenWidths: new Array<UpdateResult>() };
+
+                if (trigger.children.length > 0) {
+                    triggerResult.triggerWidth = 0;
+                }
+
+                for (var j = 0; j < trigger.children.length; j++) {
+                    var actionWidth = onGetSizePass(trigger.children[j], 0);
+
+                    triggerResult.triggerWidth += actionWidth + 15;
+
+                    triggerResult.childrenWidths.push({
+                        triggerWidth: actionWidth,
+                        childrenWidths: null
+                    });
+                }
+
+                widths.push(triggerResult);
+            }
+
+            // Third pass, set positions of nodes
+            var onSetNodePosition = (action: Action, widthArray: Array<UpdateResult>, isChild: boolean) => {
+                var actionsCount = action.children.length;
+                var actionsMiddle = actionsCount % 2;
+                var actionsHasMiddle = actionsMiddle !== 0;
+                var actionsLeftOffset = 0;
+                var actionsRightOffset = 0;
+                var actionWidth = action.node.rect.attr("width");
+
+                if (actionsHasMiddle && actionsCount > 1) {
+                    var middle = Math.floor(actionsCount / 2);
+                    actionsLeftOffset += widthArray[middle].triggerWidth / 2;
+                    actionsRightOffset += widthArray[middle].triggerWidth / 2;
+                }
+
+                // Move left
+                var leftStart = actionsHasMiddle ? Math.floor(actionsCount / 2) - 1 : (actionsCount / 2) - 1;
+                for (var i = leftStart; i >= 0; i--) {
+                    var child = action.children[i];
+                    var node = child.node;
+                    var width = (widthArray[i].triggerWidth) + 15;
+
+                    this._setActionPosition(action.children[i], node.rect.attr("x") - actionsLeftOffset - (width / 2), node.rect.attr("y"));
+                    this._setActionLine(child);
+                    onResizeCanvas(child);
+
+                    actionsLeftOffset += width;
+                }
+
+                // Move right
+                var rightStart = actionsHasMiddle ? Math.round(actionsCount / 2) : actionsCount / 2;
+                for (var i = rightStart; i < actionsCount; i++) {
+                    var child = action.children[i];
+                    var node = child.node;
+                    var width = (widthArray[i].triggerWidth) + 15;
+
+                    this._setActionPosition(action.children[i], node.rect.attr("x") + actionsRightOffset + (width / 2), node.rect.attr("y"));
+                    this._setActionLine(child);
+                    onResizeCanvas(child);
+
+                    actionsRightOffset += width;
+                }
+            };
+
+            onSetNodePosition(this.root, widths, false);
+            for (var i = 0; i < this.root.children.length; i++) {
+                onSetNodePosition(this.root.children[i], widths[i].childrenWidths, true);
+            }
+            
+        }
+
+        /*
+        * Adds an action to the graph viewer and returns it
+        * @param parent: the parent action
+        * @param type: the action type
+        * @param element: the Actions Builder type (TRIGGERS, ACTIONS, FLOW_CONTROLS)
+        */
+        public addAction(parent: Action, type: number, element: Element): Action {
+            var node = this._createNode(element.text, type, parent === null);
+            var action = new Action(node);
+
+            if (element.name === "CombineAction") {
+                action.combineArray = new Array<Action>();
+
+                var hubElement = Elements.FLOW_CONTROLS[Elements.FLOW_CONTROLS.length - 1];
+                var hub = this.addAction(action, Type.FLOW_CONTROL, hubElement);
+
+                action.hub = hub;
+                action.addChild(hub);
+                this._createActionAnimation(hub);
+            }
+
+            action.name = element.name;
+            action.properties = element.properties;
+            action.type = type;
+
+            // Configure properties
+            for (var i = 0; i < action.properties.length; i++) {
+                action.propertiesResults.push({ targetType: action.properties[i].targetType, value: action.properties[i].value });
+            }
+
+            if (action.properties !== null && action.properties.length > 0) {
+                if (action.properties[0].text === "target") {
+                    action.propertiesResults[0].value = this.objectName;
+                }
+            }
+
+            if (parent !== null) {
+                if (parent.combineArray === null) {
+                    parent.addChild(action);
+                }
+                else if (parent.combineArray !== null && action.name !== "Hub") {
+                    parent.combineArray.push(action);
+                    action.parent = parent;
+                    action.combineAction = parent;
+                    parent.node.text.attr("text", "");
+                }
+            }
+
+            // Create animation
+            this._createActionAnimation(action);
+
+            return action;
+        }
+
+        /*
+        * Traverses the graph viewer and returns if an action
+        * is selected at coordinates (x, y)
+        * @param start: the start node. Can be null
+        * @param x: the x coordinate
+        * @param y: the y coordinate
+        * @param traverseCombine: if we traverse combine actions children
+        */
+        public traverseGraph(start: Action, x: number, y: number, traverseCombine: boolean): TraverseResult {
+            if (start === null) start = this.root;
+
+            var result: TraverseResult = { action: start, hit: true };
+
+            if (start.node.isPointInside(x, y)) {
+                return result;
+            }
+
+            for (var i = 0; i < start.children.length; i++) {
+                var action = start.children[i];
+
+                if (action.node.isPointInside(x, y)) {
+                    result.hit = true;
+                    result.action = start.children[i];
+
+                    if (traverseCombine && action.combineArray !== null) {
+                        for (var j = 0; j < action.combineArray.length; j++) {
+                            if (action.combineArray[j].node.isPointInside(x, y)) {
+                                result.action = action.combineArray[j];
+                                break;
+                            }
+                        }
+                    }
+
+                    return result;
+                }
+
+                result = this.traverseGraph(action, x, y, traverseCombine);
+                if (result.hit) {
+                    return result;
+                }
+            }
+
+            result.hit = false;
+            result.action = null;
+            return result;
+        }
+
+        /*
+        * Sets the action's position (node)
+        * @param action: the action to place
+        * @param x: the x position of the action
+        * @param y: the y position of the action
+        */
+        public _setActionPosition(action: Action, x: number, y: number): void {
+            var node = action.node;
+            var offsetx = node.rect.attr("x") - x;
+            var parent = action.parent;
+
+            if (parent !== null && parent.combineArray !== null && parent.combineArray.length > 1) {
+                var parentNode = parent.node;
+                x = parentNode.rect.attr("x") + (parent.node.rect.attr("width") / 2) - (node.rect.attr("width") / 2);
+            }
+            
+            node.rect.attr("x", x);
+            node.rect.attr("y", y);
+
+            var textBBox = node.text.getBBox();
+            var textWidth = 0;
+            if (textBBox !== null && textBBox !== undefined) {
+                textWidth = textBBox.width;
+            }
+
+            node.text.attr("x", x + node.rect.attr("width") / 2 - textWidth / 2);
+            node.text.attr("y", y + node.rect.attr("height") / 2);
+
+            if (action.combineArray !== null && action.combineArray.length > 0) {
+                var length = 0;
+
+                for (var i = 0; i < action.combineArray.length; i++) {
+                    var combinedAction = action.combineArray[i];
+                    var combinedNode = combinedAction.node;
+
+                    combinedNode.rect.attr("x", node.rect.attr("x") + length);
+                    combinedNode.rect.attr("y", node.rect.attr("y"));
+
+                    textBBox = combinedNode.text.getBBox();
+                    if (textBBox !== null) {
+                        textWidth = textBBox.width;
+                    }
+                    combinedNode.text.attr("x", combinedNode.rect.attr("x") + combinedNode.rect.attr("width") / 2 - textWidth / 2);
+                    combinedNode.text.attr("y", y + combinedNode.rect.attr("height") / 2);
+
+                    length += combinedNode.rect.attr("width");
+                }
+
+                node.rect.attr("width", length);
+            }
+
+            for (var i = 0; i < action.children.length; i++) {
+                var child = action.children[i];
+                this._setActionPosition(child, child.node.rect.attr("x") - offsetx, y + Viewer.VERTICAL_OFFSET * this.zoom);
+                this._setActionLine(child);
+            }
+        }
+
+        /*
+        * Configures the line (link) between the action and its parent
+        * @param action: the action to configure
+        */
+        private _setActionLine(action: Action): void {
+            if (action.node.line === null) {
+                return;
+            }
+
+            var node = action.node;
+            var nodex = node.rect.attr("x");
+            var nodey = node.rect.attr("y");
+            var nodeWidth = node.rect.attr("width");
+            var nodeHeight = node.rect.attr("height");
+
+            var parent = action.parent.node;
+            var parentx = parent.rect.attr("x");
+            var parenty = parent.rect.attr("y");
+            var parentWidth = parent.rect.attr("width");
+            var parentHeight = parent.rect.attr("height");
+
+            if (node.detached) {
+                node.line.attr("path", ["M", nodex, nodey, "L", nodex, nodey]);
+                return;
+            }
+
+            var line1x = nodex + (nodeWidth / 2);
+            var line1y = nodey;
+
+            var line2y = line1y - (line1y - parenty - parentHeight) / 2;
+            var line3x = parentx + (parentWidth / 2);
+            var line4y = parenty + parentHeight;
+
+            node.line.attr("path", ["M", line1x, line1y, "L", line1x, line2y, "L", line3x, line2y, "L", line3x, line4y]);
+        }
+
+        /*
+        * Creates and returns a node
+        * @param text: the text to draw in the nde
+        * @param color: the node's color
+        * @param noLine: if draw a line to the parent or not
+        */
+        public _createNode(text: string, type: number, noLine: boolean): Node {
+            var node = new Node();
+            var color = this.getNodeColor(type, false);
+
+            node.rect = this.paper.rect(20, 20, Viewer.NODE_WIDTH, Viewer.NODE_HEIGHT, 0);
+            node.rect.attr("fill", color);
+
+            node.text = this.paper.text(20, 20, text);
+            node.text.attr("font-size", 11);
+            node.text.attr("text-anchor", "start");
+            node.text.attr("font-family", "Sinkin Sans Light");
+
+            if (!noLine) {
+                node.line = this.paper.path("");
+                node.line.attr("stroke", color);
+            }
+
+            return node;
+        }
+
+        /*
+        * Creates the drag animation
+        * @param action: the action to animate
+        */
+        private _createActionAnimation(action: Action): void {
+            var node = action.node;
+            var finished = true;
+            var nodex: number = 0;
+            var nodey: number = 0;
+
+            var onMove = (dx: number, dy: number, x: number, y: number) =>
+            { };
+
+            var onStart = (x: number, y: number, event: MouseEvent) => {
+                if (node.minimized) {
+                    return;
+                }
+
+                if (finished) {
+                    nodex = node.rect.attr("x");
+                    nodey = node.rect.attr("y");
+                }
+                finished = false;
+
+                node.rect.animate({
+                    x: node.rect.attr("x") - 10,
+                    y: node.rect.attr("y"),
+                    width: (Viewer.NODE_WIDTH + 20) * this.zoom,
+                    height: (Viewer.NODE_HEIGHT + 10) * this.zoom,
+                    opacity: 0.25
+                }, 500, ">");
+            };
+
+            var onEnd = (event: MouseEvent) => {
+                if (!node.minimized) {
+                    node.rect.animate({
+                        x: nodex,
+                        y: nodey,
+                        width: Viewer.NODE_WIDTH * this.zoom,
+                        height: Viewer.NODE_HEIGHT * this.zoom,
+                        opacity: 1.0
+                    }, 500, ">", () => { finished = true; });
+                }
+
+                var dragResult = this.traverseGraph(null, this.mousex, this.mousey, true);
+
+                if (dragResult.hit && dragResult.action === action || !dragResult.hit) {
+                    // Create parameters. Action can be null
+                    this.parameters.createParameters(action);
+                }
+                else {
+                    // Manage drag'n'drop
+                    if (dragResult.action.children.length > 0 && action.type !== Type.TRIGGER) {
+                        return;
+                    }
+
+                    if (action.type === Type.TRIGGER && dragResult.action !== this.root) {
+                        return;
+                    }
+
+                    if (action.type === Type.ACTION && dragResult.action === this.root) {
+                        return;
+                    }
+
+                    if (action.type === Type.FLOW_CONTROL && (dragResult.action === this.root || dragResult.action.type === Type.FLOW_CONTROL)) {
+                        return;
+                    }
+
+                    if (action === dragResult.action.parent) {
+                        return;
+                    }
+
+                    if (action.parent !== null && action.parent.combineArray !== null) { // Musn't move hubs of combine actions
+                        return;
+                    }
+
+                    // Reset node
+                    node.rect.stop(node.rect.animation);
+                    node.text.stop(node.text.animation);
+
+                    node.rect.undrag();
+                    node.text.undrag();
+
+                    node.rect.attr("opacity", 1.0);
+                    node.rect.attr("width", Viewer.NODE_WIDTH);
+                    node.rect.attr("height", Viewer.NODE_HEIGHT);
+
+                    if (action.parent !== null) {
+                        // Configure drag'n'drop
+                        action.parent.removeChild(action);
+                        dragResult.action.addChild(action);
+                        this.update();
+                        this._createActionAnimation(action);
+                    }
+                }
+            };
+
+            node.rect.drag(onMove, onStart, onEnd);
+            node.text.drag(onMove, onStart, onEnd);
+        }
+    }
+}

+ 42 - 0
Exporters/3ds Max/ActionsBuilder/gulpfile.js

@@ -0,0 +1,42 @@
+/// <binding />
+var gulp = require('gulp');
+var ts = require('gulp-typescript');
+
+var files = [
+    // Files
+    // Equivalent to "./*.ts",
+    "actionsbuilder.actionNode.ts",
+    "actionsbuilder.contextMenu.ts",
+	"actionsbuilder.list.ts",
+    "actionsbuilder.main.ts",
+    "actionsbuilder.parameters.ts",
+    "actionsbuilder.toolbar.ts",
+    "actionsbuilder.ts",
+    "actionsbuilder.utils.ts",
+    "actionsbuilder.viewer.ts",
+    // References
+    "raphaeljs.d.ts",
+    "../../../dist/preview release/babylon.d.ts"
+];
+
+gulp.task("default", function () {
+    var result = gulp.src(files)
+		.pipe(ts({
+            target: "ES5",
+		    out: "actionsbuilder.max.js" // Merge
+		}));
+    return result.js.pipe(gulp.dest("./Sources/"));
+});
+
+gulp.task("debug", function () {
+    var result = gulp.src(files)
+		.pipe(ts({
+		    target: "ES5",
+            outDir: "./Sources/"
+		}));
+    return result.js.pipe(gulp.dest("./Sources/"));
+});
+
+gulp.task("watch", function () {
+    gulp.watch(files, ["default"]);
+});

+ 10 - 0
Exporters/3ds Max/ActionsBuilder/package.json

@@ -0,0 +1,10 @@
+{
+  "name": "BabylonJSActionsBuilder",
+  "version": "2.2.0",
+  "description": "",
+  "main": "",
+  "devDependencies": {
+    "gulp": "^3.8.11",
+    "gulp-typescript": "^3.1.4"
+  }
+}

+ 69 - 0
Exporters/3ds Max/ActionsBuilder/raphaeljs.d.ts

@@ -0,0 +1,69 @@
+/*
+Raphael.js declarations
+*/
+
+// Raphael.js bounding box interface
+interface RaphaelBoundingBox {
+    width: number;
+    height: number;
+}
+
+// Raphael.js animation interface
+interface RaphaelAnimation
+{ }
+
+// Raphael.js element interface
+interface RaphaelElement {
+    attr(attribute: string, value?: any): any;
+
+    remove(): void;
+
+    hide(): void;
+    show(): void;
+
+    isPointInside(x: number, y: number): boolean;
+    getBBox(): RaphaelBoundingBox;
+
+    animate(attributes: any, time: number, type: string, callback?: () => void): void;
+    stop(animation: RaphaelAnimation);
+    animation: RaphaelAnimation;
+
+    click(onClick: (event: MouseEvent) => void);
+    drag(onMove: (dx: number, dy: number, x: number, y: number) => void, onStart: (x: number, y: number, event: MouseEvent) => void, onEnd: (event: MouseEvent) => void): void;
+    undrag(): void;
+}
+
+// Raphael.js rect interface
+interface Rect extends RaphaelElement
+{ }
+
+// Raphael.js text interface
+interface Text extends RaphaelElement
+{ }
+
+// Raphael.js path interface
+interface Path extends RaphaelElement
+{ }
+
+// Raphael.js rgb interface
+interface RaphaelColor
+{ }
+
+// Raphael.js paper interface
+interface Paper {
+    canvas: HTMLCanvasElement;
+
+    width: number;
+    height: number;
+    setSize(width: number, height: number);
+
+    rect(x: number, y: number, width: number, height: number, r?: number): Rect;
+    text(x: number, y: number, text: string): Text;
+    path(pathString: string);
+}
+
+// Raphael.js
+declare var Raphael: {
+    (containerID: string, width: number, height: number): Paper;
+    rgb(r: number, g: number, b: number): RaphaelColor;
+}

+ 31 - 0
Exporters/3ds Max/ActionsBuilder/web.Debug.config

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Pour plus d'informations sur l'utilisation de la transformation web.config, consultez la page http://go.microsoft.com/fwlink/?LinkId=125889 -->
+
+<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
+  <!--
+    Dans l'exemple ci-dessous, la transformation "SetAttributes" changera la valeur de 
+    "connectionString" afin d'utiliser "ReleaseSQLServer" uniquement lorsque le localisateur "Match" 
+    trouve un attribut "name" qui a une valeur "MyDB".
+    
+    <connectionStrings>
+      <add name="MyDB" 
+        connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" 
+        xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
+    </connectionStrings>
+  -->
+  <system.web>
+    <!--
+      
+      Dans l'exemple ci-dessous, la transformation "Replace" remplacera toute la section 
+      <customErrors>  de votre fichier web.config.
+      Dans la mesure où il n'y a qu'une section customErrors sous le 
+      nœud <system.web>, il n'est pas nécessaire d'utiliser l'attribut "xdt:Locator".
+      
+      <customErrors defaultRedirect="GenericError.htm"
+        mode="RemoteOnly" xdt:Transform="Replace">
+        <error statusCode="500" redirect="InternalError.htm"/>
+      </customErrors>
+    -->
+  </system.web>
+</configuration>

+ 32 - 0
Exporters/3ds Max/ActionsBuilder/web.Release.config

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Pour plus d'informations sur l'utilisation de la transformation web.config, consultez la page http://go.microsoft.com/fwlink/?LinkId=125889 -->
+
+<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
+  <!--
+    Dans l'exemple ci-dessous, la transformation "SetAttributes" changera la valeur de 
+    "connectionString" afin d'utiliser "ReleaseSQLServer" uniquement lorsque le localisateur "Match" 
+    trouve un attribut "name" qui a une valeur "MyDB".
+    
+    <connectionStrings>
+      <add name="MyDB" 
+        connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" 
+        xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
+    </connectionStrings>
+  -->
+  <system.web>
+    <compilation xdt:Transform="RemoveAttributes(debug)" />
+    <!--
+      
+      Dans l'exemple ci-dessous, la transformation "Replace" remplacera toute la section 
+      <customErrors>  de votre fichier web.config.
+      Dans la mesure où il n'y a qu'une section customErrors sous le 
+      nœud <system.web>, il n'est pas nécessaire d'utiliser l'attribut "xdt:Locator".
+      
+      <customErrors defaultRedirect="GenericError.htm"
+        mode="RemoteOnly" xdt:Transform="Replace">
+        <error statusCode="500" redirect="InternalError.htm"/>
+      </customErrors>
+    -->
+  </system.web>
+</configuration>

+ 11 - 0
Exporters/3ds Max/ActionsBuilder/web.config

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Pour plus d'informations sur la configuration de votre application ASP.NET, visitez
+  http://go.microsoft.com/fwlink/?LinkId=169433
+  -->
+<configuration>
+  <system.web>
+    <compilation debug="true" targetFramework="4.5" />
+    <httpRuntime targetFramework="4.5" />
+  </system.web>
+</configuration>

+ 6 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonAbstractMesh.cs

@@ -6,6 +6,9 @@ namespace BabylonExport.Entities
     public class BabylonAbstractMesh: BabylonIAnimatable
     {
         [DataMember]
+        public string id { get; set; }
+
+        [DataMember]
         public string name { get; set; }
         
         [DataMember]
@@ -38,6 +41,9 @@ namespace BabylonExport.Entities
         [DataMember]
         public bool autoAnimateLoop { get; set; }
 
+        [DataMember]
+        public string parentId { get; set; }
+
         public BabylonAbstractMesh()
         {
             position = new[] { 0f, 0f, 0f };

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

@@ -15,6 +15,12 @@ namespace BabylonExport.Entities
         public int dataType { get; set; }
 
         [DataMember]
+        public bool enableBlending { get; set; }
+
+        [DataMember]
+        public float blendingSpeed { get; set; }
+
+        [DataMember]
         public int loopBehavior { get; set; }
 
         [DataMember]
@@ -38,5 +44,11 @@ namespace BabylonExport.Entities
             Cycle = 1,
             Constant = 2
         }
+
+        public BabylonAnimation()
+        {
+            this.enableBlending = false;
+            this.blendingSpeed = 0.01f;
+        }
     }
 }

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

@@ -45,6 +45,9 @@ namespace BabylonExport.Entities
         public float inertia { get; set; }
 
         [DataMember]
+        public float interaxialDistance { get; set; }
+
+        [DataMember]
         public bool checkCollisions { get; set; }
 
         [DataMember]
@@ -68,6 +71,30 @@ namespace BabylonExport.Entities
         [DataMember]
         public BabylonAnimation[] animations { get; set; }
 
+        [DataMember]
+        public int mode { get; set; }
+
+        [DataMember]
+        public float? orthoLeft { get; set; }
+
+        [DataMember]
+        public float? orthoRight { get; set; }
+
+        [DataMember]
+        public float? orthoBottom { get; set; }
+
+        [DataMember]
+        public float? orthoTop { get; set; }
+
+        [DataMember]
+        public bool isStereoscopicSideBySide;
+
+        [DataMember]
+        public object metadata { get; set; }
+
+        [DataMember]
+        public string tags { get; set; }
+
         public BabylonCamera()
         {
             position = new[] { 0f, 0f, 0f };
@@ -79,6 +106,15 @@ namespace BabylonExport.Entities
             maxZ = 5000.0f;
             speed = 1.0f;
             inertia = 0.9f;
+            interaxialDistance = 0.0637f;
+
+            mode = 0;
+            orthoLeft = null;
+            orthoRight = null;
+            orthoBottom = null;
+            orthoTop = null;
+
+            type = "FreeCamera";
         }
     }
 }

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

@@ -0,0 +1,16 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonDirectionalLight : BabylonLight
+    {
+        [DataMember]
+        public float shadowOrthoScale { get; set; }
+
+        public BabylonDirectionalLight()
+        {
+            shadowOrthoScale = 0.5f;
+        }
+    }
+}

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

@@ -17,6 +17,8 @@
     <SccProvider>SAK</SccProvider>
     <TargetFrameworkProfile>
     </TargetFrameworkProfile>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DebugSymbols>true</DebugSymbols>
@@ -38,9 +40,14 @@
     <Prefer32Bit>false</Prefer32Bit>
   </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="System.Data" />
@@ -74,7 +81,17 @@
     <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. Use 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">

+ 37 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonHDRCubeTexture.cs

@@ -0,0 +1,37 @@
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonHDRCubeTexture : BabylonTexture
+    {
+        [DataMember]
+        public string customType { get; private set; }
+
+        [DataMember]
+        public int size { get; set; }
+
+        [DataMember]
+        public bool useInGammaSpace { get; set; }
+
+        [DataMember]
+        public bool generateHarmonics { get; set; }
+
+        [DataMember]
+        public bool usePMREMGenerator { get; set; }
+
+        [DataMember]
+        public bool isBABYLONPreprocessed { get; set; }
+
+        public BabylonHDRCubeTexture()
+        {
+            this.customType = "BABYLON.HDRCubeTexture";
+            this.size = 0;
+            this.isCube = true;
+            this.useInGammaSpace = false;
+            this.generateHarmonics = true;
+            this.usePMREMGenerator = false;
+            this.isBABYLONPreprocessed = false;
+        }
+    }
+}

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

@@ -65,6 +65,12 @@ namespace BabylonExport.Entities
         [DataMember]
         public BabylonAnimation[] animations { get; set; }
 
+        [DataMember]
+        public object metadata { get; set; }
+
+        [DataMember]
+        public string tags { get; set; }
+
         public BabylonLight()
         {
             diffuse = new[] {1.0f, 1.0f, 1.0f};

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

@@ -19,12 +19,16 @@ namespace BabylonExport.Entities
         [DataMember]
         public float alpha { get; set; }
 
+        [DataMember]
+        public int alphaMode { get; set; }
 
         public BabylonMaterial()
         {
             backFaceCulling = true;
 
             alpha = 1.0f;
+
+            alphaMode = 2;
         }
     }
 }

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

@@ -6,12 +6,6 @@ namespace BabylonExport.Entities
     public class BabylonMesh : BabylonAbstractMesh
     {
         [DataMember]
-        public string id { get; set; }
-
-        [DataMember]
-        public string parentId { get; set; }
-
-        [DataMember]
         public string materialId { get; set; }
 
         [DataMember]
@@ -110,6 +104,12 @@ namespace BabylonExport.Entities
         [DataMember]
         public float physicsRestitution { get; set; }
 
+        [DataMember]
+        public object metadata { get; set; }
+
+        [DataMember]
+        public string tags { get; set; }
+
         public BabylonMesh()
         {
             isEnabled = true;
@@ -126,6 +126,8 @@ namespace BabylonExport.Entities
             skeletonId = -1;
 
             pickable = true;
+
+            numBoneInfluencers = 4;
         }
     }
 }

+ 52 - 53
Exporters/3ds Max/BabylonExport.Entities/BabylonPBRMaterial.cs

@@ -29,48 +29,6 @@ namespace BabylonExport.Entities
         public float microSurface { get; set; }
 
         [DataMember]
-        public float overloadedShadowIntensity { get; set; }
-
-        [DataMember]
-        public float overloadedShadeIntensity { get; set; }
-
-        [DataMember]
-        public float overloadedAmbientIntensity { get; set; }
-
-        [DataMember]
-        public float overloadedAlbedoIntensity { get; set; }
-
-        [DataMember]
-        public float overloadedReflectivityIntensity { get; set; }
-
-        [DataMember]
-        public float overloadedEmissiveIntensity { get; set; }
-
-        [DataMember]
-        public float[] overloadedAmbient { get; set; }
-
-        [DataMember]
-        public float[] overloadedAlbedo { get; set; }
-
-        [DataMember]
-        public float[] overloadedReflectivity { get; set; }
-
-        [DataMember]
-        public float[] overloadedEmissive { get; set; }
-
-        [DataMember]
-        public float[] overloadedReflection { get; set; }
-
-        [DataMember]
-        public float overloadedMicroSurface { get; set; }
-
-        [DataMember]
-        public float overloadedMicroSurfaceIntensity { get; set; }
-
-        [DataMember]
-        public float overloadedReflectionIntensity { get; set; }
-
-        [DataMember]
         public BabylonTexture albedoTexture { get; set; }
 
         [DataMember]
@@ -95,6 +53,9 @@ namespace BabylonExport.Entities
         public BabylonTexture lightmapTexture { get; set; }
 
         [DataMember]
+        public BabylonTexture metallicTexture { get; set; }
+
+        [DataMember]
         public bool useLightmapAsShadowmap { get; set; }
 
         [DataMember]
@@ -116,6 +77,24 @@ namespace BabylonExport.Entities
         public float[] emissive { get; set; }
 
         [DataMember]
+        public float? roughness { get; set; }
+
+        [DataMember]
+        public float? metallic { get; set; }
+
+        [DataMember]
+        public bool useMicroSurfaceFromReflectivityMapAplha { get; set; }
+
+        [DataMember]
+        public bool linkRefractionWithTransparency { get; set; }
+
+        [DataMember]
+        public bool useRoughnessFromMetallicTextureAlpha { get; set; }
+
+        [DataMember]
+        public bool useRoughnessFromMetallicTextureGreen { get; set; }
+
+        [DataMember]
         public bool useAlphaFromAlbedoTexture { get; set; }
 
         [DataMember]
@@ -131,6 +110,9 @@ namespace BabylonExport.Entities
         public bool useRadianceOverAlpha { get; set; }
 
         [DataMember]
+        public bool usePhysicalLightFalloff { get; set; }
+
+        [DataMember]
         public float indexOfRefraction { get; set; }
 
         [DataMember]
@@ -142,6 +124,12 @@ namespace BabylonExport.Entities
         [DataMember]
         public BabylonFresnelParameters opacityFresnelParameters { get; set; }
 
+        [DataMember]
+        public bool disableLighting { get; set; }
+
+        [DataMember]
+        public bool twoSidedLighting { get; set; }
+
         public BabylonPBRMaterial() : base()
         {
             customType = "BABYLON.PBRMaterial";
@@ -152,21 +140,32 @@ namespace BabylonExport.Entities
             specularIntensity = 1.0f;
             cameraExposure = 1.0f;
             cameraContrast = 1.0f;
-
-            overloadedShadowIntensity = 1.0f;
-            overloadedShadeIntensity = 1.0f;
+            indexOfRefraction = 0.66f;
+            twoSidedLighting = false;
+            useRadianceOverAlpha = true;
+            useSpecularOverAlpha = true;
+            usePhysicalLightFalloff = true;
+            useEmissiveAsIllumination = false;
+
+            // Default Null Metallic Workflow
+            metallic = null;
+            roughness = null;
+            useRoughnessFromMetallicTextureAlpha = true;
+            useRoughnessFromMetallicTextureGreen = false;
+
+            microSurface = 0.9f;
+            useMicroSurfaceFromReflectivityMapAplha = false;
 
             ambient = new[] { 0f, 0f, 0f };
-            albedo = new[] { 0f, 0f, 0f };
-            reflectivity = new[] { 0f, 0f, 0f };
-            reflection = new[] { 0f, 0f, 0f };
+            albedo = new[] { 1f, 1f, 1f };
+            reflectivity = new[] { 1f, 1f, 1f };
+            reflection = new[] { 0.5f, 0.5f, 0.5f };
             emissive = new[] { 0f, 0f, 0f };
+        }
 
-            overloadedAmbient = new[] { 0f, 0f, 0f };
-            overloadedAlbedo = new[] { 0f, 0f, 0f };
-            overloadedReflectivity = new[] { 0f, 0f, 0f };
-            overloadedEmissive = new[] { 0f, 0f, 0f };
-            overloadedReflection = new[] { 0f, 0f, 0f };
+        public void SetCustomType(string type)
+        {
+            this.customType = type;
         }
     }
 }

+ 24 - 4
Exporters/3ds Max/BabylonExport.Entities/BabylonParticleSystem.cs

@@ -6,6 +6,15 @@ namespace BabylonExport.Entities
     public class BabylonParticleSystem
     {
         [DataMember]
+        public string name { get; set; }
+
+        [DataMember]
+        public bool preventAutoStart { get; set; }
+
+        [DataMember]
+        public bool autoAnimate { get; set; }
+
+        [DataMember]
         public string emitterId { get; set; }
 
         [DataMember]
@@ -33,16 +42,13 @@ namespace BabylonExport.Entities
         public float[] colorDead { get; set; }
 
         [DataMember]
-        public float deadAlpha { get; set; }
-
-        [DataMember]
         public float emitRate { get; set; }
 
         [DataMember]
         public float updateSpeed { get; set; }
 
         [DataMember]
-        public int targetStopFrame { get; set; }
+        public float targetStopDuration { get; set; }
 
         [DataMember]
         public float minEmitPower { get; set; }
@@ -82,5 +88,19 @@ namespace BabylonExport.Entities
 
         [DataMember]
         public bool linkToEmitter { get; set; }
+
+        [DataMember]
+        public object[] animations { get; set; }
+
+        [DataMember]
+        public object customShader { get; set; }
+
+        public BabylonParticleSystem()
+        {
+            this.preventAutoStart = true;
+            this.autoAnimate = false;
+            this.animations = null;
+            this.customShader = null;
+        }
     }
 }

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

@@ -37,7 +37,10 @@ namespace BabylonExport.Entities
 
         [DataMember]
         public float[] gravity { get; set; }
-        
+
+        [DataMember]
+        public string physicsEngine { get; set; }
+
         [DataMember]
         public bool physicsEnabled { get; set; }
 
@@ -80,6 +83,12 @@ namespace BabylonExport.Entities
         [DataMember]
         public BabylonActions actions { get; set; }
 
+        [DataMember]
+        public object metadata { get; set; }
+
+        [DataMember]
+        public bool workerCollisions { get; set; }
+
         public BabylonVector3 MaxVector { get; set; }
         public BabylonVector3 MinVector { get; set; }
 
@@ -119,7 +128,7 @@ namespace BabylonExport.Entities
             MinVector = new BabylonVector3 { X = float.MaxValue, Y = float.MaxValue, Z = float.MaxValue };
         }
 
-        public void Prepare(bool generateDefaultLight = true)
+        public void Prepare(bool generateDefaultLight = true, bool generateDefaultCamera = true)
         {
             meshes = MeshesList.ToArray();
             sounds = SoundsList.ToArray();
@@ -129,7 +138,7 @@ namespace BabylonExport.Entities
             shadowGenerators = ShadowGeneratorsList.ToArray();
             skeletons = SkeletonsList.ToArray();
 
-            if (CamerasList.Count == 0)
+            if (CamerasList.Count == 0 && generateDefaultCamera)
             {
                 var camera = new BabylonCamera { name = "Default camera", id = Guid.NewGuid().ToString() };
 

+ 99 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonShaderMaterial.cs

@@ -0,0 +1,99 @@
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonShaderMaterial : BabylonMaterial
+    {
+        [DataMember]
+        public string customType { get; private set; }
+
+        [DataMember]
+        public object shaderPath;
+
+        [DataMember]
+        public BabylonShaderOptions options;
+
+        [DataMember]
+        public Dictionary<string, object> textures;
+
+        [DataMember]
+        public Dictionary<string, object[]> textureArrays;
+
+        [DataMember]
+        public Dictionary<string, object> floats;
+
+        [DataMember]
+        public Dictionary<string, object[]> floatArrays;
+
+        [DataMember]
+        public Dictionary<string, object> colors3;
+
+        [DataMember]
+        public Dictionary<string, object> colors4;
+
+        [DataMember]
+        public Dictionary<string, object> vectors2;
+
+        [DataMember]
+        public Dictionary<string, object> vectors3;
+
+        [DataMember]
+        public Dictionary<string, object> vectors4;
+
+        [DataMember]
+        public Dictionary<string, object> matrices;
+
+        [DataMember]
+        public Dictionary<string, object> matrices2x2;
+
+        [DataMember]
+        public Dictionary<string, object> matrices3x3;
+
+        [DataMember]
+        public Dictionary<string, object[]> vectors3Arrays;
+
+        public BabylonShaderMaterial()
+        {
+            this.customType = "BABYLON.ShaderMaterial";
+            this.shaderPath = null;
+            this.options = new BabylonShaderOptions();
+            this.textures = new Dictionary<string, object>();
+            this.textureArrays = new Dictionary<string, object[]>();
+            this.floats = new Dictionary<string, object>();
+            this.floatArrays = new Dictionary<string, object[]>();
+            this.colors3 = new Dictionary<string, object>();
+            this.colors4 = new Dictionary<string, object>();
+            this.vectors2 = new Dictionary<string, object>();
+            this.vectors3 = new Dictionary<string, object>();
+            this.vectors4 = new Dictionary<string, object>();
+            this.matrices = new Dictionary<string, object>();
+            this.matrices2x2 = new Dictionary<string, object>();
+            this.matrices3x3 = new Dictionary<string, object>();
+            this.vectors3Arrays = new Dictionary<string, object[]>();
+        }
+    }
+
+    [DataContract]
+    public class BabylonShaderOptions
+    {
+        [DataMember]
+        public string[] attributes;
+
+        [DataMember]
+        public string[] uniforms;
+
+        [DataMember]
+        public bool needAlphaBlending;
+
+        [DataMember]
+        public bool needAlphaTesting;
+
+        [DataMember]
+        public string[] samplers;
+
+        [DataMember]
+        public string[] defines;
+    }
+}

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

@@ -15,13 +15,19 @@ namespace BabylonExport.Entities
         public string lightId { get; set; }
 
         [DataMember]
-        public bool useVarianceShadowMap { get; set; }
+        public bool useExponentialShadowMap { get; set; }
 
         [DataMember]
         public bool usePoissonSampling { get; set; }
 
         [DataMember]
-        public bool useBlurVarianceShadowMap { get; set; }
+        public bool useBlurExponentialShadowMap { get; set; }
+
+        [DataMember]
+        public float? depthScale { get; set; }
+
+        [DataMember]
+        public float darkness { get; set; }
 
         [DataMember]
         public float blurScale { get; set; }
@@ -35,5 +41,14 @@ namespace BabylonExport.Entities
         [DataMember]
         public bool forceBackFacesOnly { get; set; }
 
+        public BabylonShadowGenerator()
+        {
+            darkness = 0;
+            blurScale = 2;
+            blurBoxOffset = 0;
+            bias = 0.00005f;
+            depthScale = null;
+            forceBackFacesOnly = false;
+        }
     }
 }

+ 11 - 0
Exporters/3ds Max/BabylonExport.Entities/BabylonStandardMaterial.cs

@@ -59,6 +59,15 @@ namespace BabylonExport.Entities
         [DataMember]
         public bool useSpecularOverAlpha { get; set; }
 
+        [DataMember]
+        public bool disableLighting { get; set; }
+
+        [DataMember]
+        public bool useEmissiveAsIllumination { get; set; }
+
+        [DataMember]
+        public bool linkEmissiveWithDiffuse { get; set; }
+
         public BabylonStandardMaterial() : base()
         {
             ambient = new[] {1.0f, 1.0f, 1.0f};
@@ -67,6 +76,8 @@ namespace BabylonExport.Entities
             emissive = new[] { 0f, 0f, 0f };
             specularPower = 64;
             useSpecularOverAlpha = true;
+            useEmissiveAsIllumination = false;
+            linkEmissiveWithDiffuse = false;
         }
     }
 }

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

@@ -67,7 +67,13 @@ namespace BabylonExport.Entities
 
         [DataMember]
         public BabylonAnimation[] animations { get; set; }
-        
+
+        [DataMember]
+        public string[] extensions { get; set; }
+
+        [DataMember]
+        public int samplingMode { get; set; }
+
         public BabylonTexture()
         {
             level = 1.0f;
@@ -82,6 +88,7 @@ namespace BabylonExport.Entities
             wrapV = 1;
             hasAlpha = false;
             coordinatesIndex = 0;
+            samplingMode = 3;
         }
     }
 }

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

@@ -0,0 +1,12 @@
+using System.Runtime.Serialization;
+namespace BabylonExport.Entities
+{
+    [DataContract]
+    public class BabylonUniversalCamera : BabylonCamera
+    {
+        public BabylonUniversalCamera()
+        {
+            this.type = "UniversalCamera";
+        }
+    }
+}

+ 3 - 3
Exporters/3ds Max/BabylonExport.Entities/packages.config

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="SharpDX" version="2.6.3" targetFramework="net40" />
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="SharpDX" version="2.6.3" targetFramework="net35" />
 </packages>

+ 66 - 0
Exporters/3ds Max/BabylonFileConverter/BabylonFileConverter.csproj

@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{A6B76356-1D1C-4C82-8199-A6406DA85A95}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>BabylonFileConverter</RootNamespace>
+    <AssemblyName>BabylonFileConverter</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <SccProjectName>SAK</SccProjectName>
+    <SccLocalPath>SAK</SccLocalPath>
+    <SccAuxPath>SAK</SccAuxPath>
+    <SccProvider>SAK</SccProvider>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
+    <RestorePackages>true</RestorePackages>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <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="BinaryConverter.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>

+ 374 - 0
Exporters/3ds Max/BabylonFileConverter/BinaryConverter.cs

@@ -0,0 +1,374 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace BabylonFileConverter
+{
+    public static class BinaryConverter
+    {
+        public enum DataType { Int32, Float };
+
+        public static void Convert(string srcFilename, string dstPath, Action<string> onMessage, Action<string> onError)
+        {
+            try
+            {
+                if (!Directory.Exists(dstPath))
+                    Directory.CreateDirectory(dstPath);
+
+                string srcPath = Path.GetDirectoryName(srcFilename);
+                string dstFilename = Path.Combine(dstPath, Path.GetFileNameWithoutExtension(srcFilename) + ".binary.babylon");
+
+                dynamic scene;
+
+                // Loading
+                onMessage("Loading " + srcFilename);
+
+                using (var streamReader = new StreamReader(srcFilename))
+                {
+                    using (var reader = new JsonTextReader(streamReader))
+                    {
+                        scene = JObject.Load(reader);
+                    }
+                }
+
+                // Marking scene
+                string objName = scene.name;
+
+                if (string.IsNullOrEmpty(objName))
+                    objName = Path.GetFileNameWithoutExtension(srcFilename);
+
+                var atDot = objName.IndexOf(".incremental");
+                if (atDot > 0)
+                    objName = objName.Substring(0, atDot);
+
+                scene["autoClear"] = true;
+                scene["useDelayedTextureLoading"] = true;
+
+                var doNotDelayLoadingForGeometries = new List<string>();
+
+                // Parsing meshes
+                var meshes = (JArray)scene.meshes;
+                foreach (dynamic mesh in meshes)
+                {
+                    if (mesh.checkCollisions.Value) // Do not delay load collisions object
+                    {
+                        if (mesh.geometryId != null)
+                            doNotDelayLoadingForGeometries.Add(mesh.geometryId.Value);
+                        continue;
+                    }
+
+                    Extract(srcPath, dstPath, objName, mesh, true);
+                }
+
+
+                // Parsing vertexData
+                var geometries = scene.geometries;
+                if (geometries != null)
+                {
+                    var vertexData = (JArray)geometries.vertexData;
+                    foreach (dynamic geometry in vertexData)
+                    {
+                        var id = geometry.id.Value;
+
+                        if (doNotDelayLoadingForGeometries.Any(g => g == id))
+                            continue;
+
+                        Extract(srcPath, dstPath, objName, geometry, false);
+                    }
+                }
+
+                // Saving                
+                onMessage("Saving " + dstFilename);
+                string json = scene.ToString(Formatting.Indented);
+
+                using (var writer = new StreamWriter(WebUtility.UrlDecode(dstFilename)))
+                {
+                    writer.Write(json);
+                }
+            }
+            catch (Exception ex)
+            {
+                onError(ex.Message);
+            }
+        }
+
+
+        static void Extract(string srcPath, string dstPath, string objName, dynamic meshObj, bool isMesh)
+        {
+            string dstFilename = meshObj.delayLoadingFile;
+            string dstExt = (isMesh ? ".babylonbinarymeshdata" : ".babylonbinarygeometrydata");
+
+            if (!string.IsNullOrEmpty(dstFilename))
+            {
+                string filename = WebUtility.UrlDecode(Path.Combine(srcPath, (string)meshObj.delayLoadingFile));
+
+                using (var streamReader = new StreamReader(filename))
+                {
+                    using (var reader = new JsonTextReader(streamReader))
+                    {
+                        var meshData = JObject.Load(reader);
+                        meshObj.positions = meshData["positions"];
+                        meshObj.normals = meshData["normals"];
+                        meshObj.indices = meshData["indices"];
+                        meshObj.uvs = meshData["uvs"];
+                        meshObj.uvs2 = meshData["uvs2"];
+                        meshObj.colors = meshData["colors"];
+                        meshObj.matricesIndices = meshData["matricesIndices"];
+                        meshObj.matricesWeights = meshData["matricesWeights"];
+                        meshObj.subMeshes = meshData["subMeshes"];
+                    }
+                }
+            }
+
+            if (meshObj.positions == null || meshObj.positions.Count == 0 
+                || meshObj.normals == null || meshObj.normals.Count == 0
+                || meshObj.indices == null || meshObj.indices.Count == 0)
+                return;
+
+            ComputeBoundingBox(meshObj);
+
+            string meshName = meshObj.name.ToString();
+            meshName = meshName.Trim();
+            if (meshName.Length > 40)
+                meshName = meshName.Substring(0, 40);
+
+            if (isMesh && !string.IsNullOrEmpty(meshName))
+                dstFilename = objName + "." + meshName + "." + meshObj.id.ToString() + dstExt;
+            else
+                dstFilename = objName + meshObj.id.ToString() + dstExt;
+
+            dstFilename = dstFilename.Replace("+", "_").Replace(" ", "_").Replace("/", "_").Replace("\\", "_").Replace(":", "_");
+
+            meshObj.delayLoadingFile = WebUtility.UrlEncode(dstFilename);
+
+            var binaryInfo = new JObject();
+
+            using (var stream = File.Open(WebUtility.UrlDecode(Path.Combine(dstPath, dstFilename)), FileMode.Create))
+            {
+                var writer = new BinaryWriter(stream);
+
+                if (meshObj.positions != null && meshObj.positions.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.positions.Count;
+                    attrData["stride"] = 3;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Float;
+
+                    binaryInfo["positionsAttrDesc"] = attrData;
+
+                    for (int x = 0; x < meshObj.positions.Count; x++)
+                        writer.Write((float)meshObj.positions[x]);
+
+                    meshObj.positions = null;
+                }
+
+
+                if (meshObj.colors != null && meshObj.colors.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.colors.Count;
+                    attrData["stride"] = 3;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Float;
+
+                    binaryInfo["colorsAttrDesc"] = attrData;
+
+                    for (int x = 0; x < meshObj.colors.Count; x++)
+                        writer.Write((float)meshObj.colors[x]);
+
+                    meshObj["hasColors"] = true;
+                    meshObj.colors = null;
+                }
+
+
+                if (meshObj.normals != null && meshObj.normals.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.normals.Count;
+                    attrData["stride"] = 3;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Float;
+
+                    binaryInfo["normalsAttrDesc"] = attrData;
+
+                    for (int x = 0; x < meshObj.normals.Count; x++)
+                        writer.Write((float)meshObj.normals[x]);
+
+                    meshObj.normals = null;
+                }
+
+
+                if (meshObj.uvs != null && meshObj.uvs.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.uvs.Count;
+                    attrData["stride"] = 2;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Float;
+
+                    binaryInfo["uvsAttrDesc"] = attrData;
+
+                    for (int x = 0; x < meshObj.uvs.Count; x++)
+                        writer.Write((float)meshObj.uvs[x]);
+
+                    meshObj["hasUVs"] = true;
+                    meshObj.uvs = null;
+                }
+
+
+                if (meshObj.uvs2 != null && meshObj.uvs2.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.uvs2.Count;
+                    attrData["stride"] = 2;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Float;
+
+                    binaryInfo["uvs2AttrDesc"] = attrData;
+
+                    for (int x = 0; x < meshObj.uvs2.Count; x++)
+                        writer.Write((float)meshObj.uvs2[x]);
+
+                    meshObj["hasUVs2"] = true;
+                    meshObj.uvs2 = null;
+                }
+
+
+                if (meshObj.indices != null && meshObj.indices.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.indices.Count;
+                    attrData["stride"] = 1;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Int32;
+
+                    binaryInfo["indicesAttrDesc"] = attrData;
+
+                    for (int x = 0; x < meshObj.indices.Count; x++)
+                        writer.Write((int)meshObj.indices[x]);
+
+                    meshObj.indices = null;
+                }
+
+
+                if (meshObj.matricesIndices != null && meshObj.matricesIndices.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.matricesIndices.Count;
+                    attrData["stride"] = 1;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Int32;
+
+                    binaryInfo["matricesIndicesAttrDesc"] = attrData;
+
+                    for (int x = 0; x < meshObj.matricesIndices.Count; x++)
+                        writer.Write((int)meshObj.matricesIndices[x]);
+
+                    meshObj["hasMatricesIndices"] = true;
+                    meshObj.matricesIndices = null;
+                }
+
+
+                if (meshObj.matricesWeights != null && meshObj.matricesWeights.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.matricesWeights.Count;
+                    attrData["stride"] = 2;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Float;
+
+                    binaryInfo["matricesWeightsAttrDesc"] = attrData;
+
+                    for (int x = 0; x < meshObj.matricesWeights.Count; x++)
+                        writer.Write((float)meshObj.matricesWeights[x]);
+
+                    meshObj["hasMatricesWeights"] = true;
+                    meshObj.matricesWeights = null;
+                }
+
+
+                if (isMesh && meshObj.subMeshes != null && meshObj.subMeshes.Count > 0)
+                {
+                    var attrData = new JObject();
+                    attrData["count"] = meshObj.subMeshes.Count;
+                    attrData["stride"] = 5;
+                    attrData["offset"] = stream.Length;
+                    attrData["dataType"] = (int)DataType.Int32;
+
+                    binaryInfo["subMeshesAttrDesc"] = attrData;
+
+                    var smData = new int[5];
+
+                    for (int x = 0; x < meshObj.subMeshes.Count; x++)
+                    {
+                        smData[0] = meshObj.subMeshes[x].materialIndex;
+                        smData[1] = meshObj.subMeshes[x].verticesStart;
+                        smData[2] = meshObj.subMeshes[x].verticesCount;
+                        smData[3] = meshObj.subMeshes[x].indexStart;
+                        smData[4] = meshObj.subMeshes[x].indexCount;
+
+                        for (int y = 0; y < smData.Length; y++)
+                            writer.Write(smData[y]);
+                    }
+
+                    meshObj.subMeshes = null;
+                }
+            }
+
+            meshObj["_binaryInfo"] = binaryInfo;
+        }
+
+
+        static void ComputeBoundingBox(dynamic meshOrGeometry)
+        {
+            // Compute bounding boxes
+            var positions = ((JArray)meshOrGeometry.positions).Select(v => v.Value<float>()).ToArray();
+            var minimum = new[] { float.MaxValue, float.MaxValue, float.MaxValue };
+            var maximum = new[] { float.MinValue, float.MinValue, float.MinValue };
+
+            for (var index = 0; index < positions.Length; index += 3)
+            {
+                var x = positions[index];
+                var y = positions[index + 1];
+                var z = positions[index + 2];
+
+                if (x < minimum[0])
+                {
+                    minimum[0] = x;
+                }
+                if (x > maximum[0])
+                {
+                    maximum[0] = x;
+                }
+
+                if (y < minimum[1])
+                {
+                    minimum[1] = y;
+                }
+                if (y > maximum[1])
+                {
+                    maximum[1] = y;
+                }
+
+                if (z < minimum[2])
+                {
+                    minimum[2] = z;
+                }
+                if (z > maximum[2])
+                {
+                    maximum[2] = z;
+                }
+            }
+
+            meshOrGeometry["boundingBoxMinimum"] = new JArray(minimum);
+            meshOrGeometry["boundingBoxMaximum"] = new JArray(maximum);
+        }
+    }
+}

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

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

+ 4 - 0
Exporters/3ds Max/BabylonFileConverter/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
+</packages>

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


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


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


+ 223 - 34
Exporters/3ds Max/Max2Babylon.sln

@@ -1,34 +1,223 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.30501.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon", "Max2Babylon\Max2Babylon.csproj", "{DD7C931A-8FAF-4318-BB74-71DC858CC489}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BabylonExport.Entities", "BabylonExport.Entities\BabylonExport.Entities.csproj", "{6150965A-658C-4263-89AD-4F980EB0675D}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon2015", "Max2Babylon\2015\Max2Babylon2015.csproj", "{DD7C931A-8FAF-4318-BB74-71DC858CC400}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Any CPU.Build.0 = Release|Any CPU
-		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Any CPU.Build.0 = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "3ds Max", "3ds Max", "{2139CC27-1C89-49C8-95AC-7715ACBADC1F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BabylonExport.Entities", "BabylonExport.Entities\BabylonExport.Entities.csproj", "{6150965A-658C-4263-89AD-4F980EB0675D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon", "Max2Babylon\Max2Babylon.csproj", "{DD7C931A-8FAF-4318-BB74-71DC858CC489}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon2015", "Max2Babylon\2015\Max2Babylon2015.csproj", "{DD7C931A-8FAF-4318-BB74-71DC858CC400}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BabylonFileConverter", "BabylonFileConverter\BabylonFileConverter.csproj", "{A6B76356-1D1C-4C82-8199-A6406DA85A95}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActionsBuilder", "ActionsBuilder\ActionsBuilder.csproj", "{293A754C-3D80-42FB-99D8-7C9386CD297E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Max2Babylon2017", "Max2Babylon\2017\Max2Babylon2017.csproj", "{2F49C726-A1F8-40D4-859F-1355949608DC}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Debug|ARM = Debug|ARM
+		Debug|Mixed Platforms = Debug|Mixed Platforms
+		Debug|Win32 = Debug|Win32
+		Debug|x64 = Debug|x64
+		Debug|x86 = Debug|x86
+		Profile|Any CPU = Profile|Any CPU
+		Profile|ARM = Profile|ARM
+		Profile|Mixed Platforms = Profile|Mixed Platforms
+		Profile|Win32 = Profile|Win32
+		Profile|x64 = Profile|x64
+		Profile|x86 = Profile|x86
+		Release|Any CPU = Release|Any CPU
+		Release|ARM = Release|ARM
+		Release|Mixed Platforms = Release|Mixed Platforms
+		Release|Win32 = Release|Win32
+		Release|x64 = Release|x64
+		Release|x86 = Release|x86
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|Win32.ActiveCfg = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|x64.Build.0 = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Profile|Any CPU.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Profile|Any CPU.Build.0 = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Profile|ARM.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Profile|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Profile|Mixed Platforms.Build.0 = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Profile|Win32.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Profile|x64.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Profile|x86.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Any CPU.Build.0 = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|ARM.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|Win32.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|x64.ActiveCfg = Release|Any CPU
+		{6150965A-658C-4263-89AD-4F980EB0675D}.Release|x86.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|Win32.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|x64.Build.0 = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Any CPU.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Any CPU.Build.0 = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|ARM.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Mixed Platforms.Build.0 = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|Win32.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|x64.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Profile|x86.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Any CPU.Build.0 = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|ARM.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|Win32.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|x64.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489}.Release|x86.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|Win32.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|x64.Build.0 = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Any CPU.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Any CPU.Build.0 = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|ARM.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Mixed Platforms.Build.0 = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|Win32.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|x64.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Profile|x86.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Any CPU.Build.0 = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|ARM.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|Win32.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|x64.ActiveCfg = Release|Any CPU
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400}.Release|x86.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|ARM.Build.0 = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|Win32.ActiveCfg = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|Win32.Build.0 = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|x64.Build.0 = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Debug|x86.Build.0 = Debug|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|Any CPU.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|Any CPU.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|ARM.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|ARM.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|Mixed Platforms.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|Win32.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|Win32.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|x64.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|x64.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|x86.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Profile|x86.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|ARM.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|ARM.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|Win32.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|Win32.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|x64.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|x64.Build.0 = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|x86.ActiveCfg = Release|Any CPU
+		{A6B76356-1D1C-4C82-8199-A6406DA85A95}.Release|x86.Build.0 = Release|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|Win32.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|x64.Build.0 = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Any CPU.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Any CPU.Build.0 = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|ARM.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Mixed Platforms.Build.0 = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|Win32.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|x64.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Profile|x86.ActiveCfg = Debug|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Any CPU.Build.0 = Release|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|ARM.ActiveCfg = Release|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|Win32.ActiveCfg = Release|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|x64.ActiveCfg = Release|Any CPU
+		{293A754C-3D80-42FB-99D8-7C9386CD297E}.Release|x86.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|ARM.ActiveCfg = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|ARM.Build.0 = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|Win32.ActiveCfg = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|Win32.Build.0 = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|x64.Build.0 = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Debug|x86.Build.0 = Debug|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|Any CPU.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|Any CPU.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|ARM.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|ARM.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|Mixed Platforms.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|Win32.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|Win32.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|x64.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|x64.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|x86.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Profile|x86.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|ARM.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|ARM.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|Win32.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|Win32.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|x64.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|x64.Build.0 = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|x86.ActiveCfg = Release|Any CPU
+		{2F49C726-A1F8-40D4-859F-1355949608DC}.Release|x86.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{DD7C931A-8FAF-4318-BB74-71DC858CC489} = {2139CC27-1C89-49C8-95AC-7715ACBADC1F}
+		{DD7C931A-8FAF-4318-BB74-71DC858CC400} = {2139CC27-1C89-49C8-95AC-7715ACBADC1F}
+		{2F49C726-A1F8-40D4-859F-1355949608DC} = {2139CC27-1C89-49C8-95AC-7715ACBADC1F}
+	EndGlobalSection
+EndGlobal

+ 273 - 293
Exporters/3ds Max/Max2Babylon/2015/Max2Babylon2015.csproj

@@ -1,294 +1,274 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
-  <PropertyGroup>
-    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
-    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProjectGuid>{DD7C931A-8FAF-4318-BB74-71DC858CC400}</ProjectGuid>
-    <OutputType>Library</OutputType>
-    <AppDesignerFolder>Properties</AppDesignerFolder>
-    <RootNamespace>Max2Babylon</RootNamespace>
-    <AssemblyName>Max2Babylon</AssemblyName>
-    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
-    <FileAlignment>512</FileAlignment>
-    <TargetFrameworkProfile />
-    <SccProjectName>SAK</SccProjectName>
-    <SccLocalPath>SAK</SccLocalPath>
-    <SccAuxPath>SAK</SccAuxPath>
-    <SccProvider>SAK</SccProvider>
-    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
-    <RestorePackages>true</RestorePackages>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
-    <DebugSymbols>true</DebugSymbols>
-    <DebugType>full</DebugType>
-    <Optimize>false</Optimize>
-    <OutputPath>C:\Program Files\Autodesk\3ds Max 2015\bin\assemblies\</OutputPath>
-    <DefineConstants>TRACE;DEBUG;MAX2015</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-  </PropertyGroup>
-  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>pdbonly</DebugType>
-    <Optimize>true</Optimize>
-    <OutputPath>C:\Program Files\Autodesk\3ds Max 2013\bin\assemblies\</OutputPath>
-    <DefineConstants>TRACE;MAX2015</DefineConstants>
-    <ErrorReport>prompt</ErrorReport>
-    <WarningLevel>4</WarningLevel>
-    <Prefer32Bit>false</Prefer32Bit>
-  </PropertyGroup>
-  <ItemGroup>
-    <Reference Include="Autodesk.Max, Version=17.0.630.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\..\..\..\..\Repos\Babylon.js\Exporters\3ds Max\Max2Babylon\2015\Refs\Autodesk.Max.dll</HintPath>
-    </Reference>
-    <Reference Include="BabylonFileConverter, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\Refs\BabylonFileConverter.dll</HintPath>
-    </Reference>
-    <Reference Include="Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
-      <SpecificVersion>False</SpecificVersion>
-      <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>..\..\BabylonExport.Core\Refs\SharpDX.dll</HintPath>
-    </Reference>
-    <Reference Include="System" />
-    <Reference Include="System.Core" />
-    <Reference Include="System.Drawing" />
-    <Reference Include="System.Runtime.Serialization" />
-    <Reference Include="System.Web" />
-    <Reference Include="System.Windows.Forms" />
-    <Reference Include="System.Data.DataSetExtensions" />
-    <Reference Include="Microsoft.CSharp" />
-    <Reference Include="System.Data" />
-    <Reference Include="System.Xml" />
-  </ItemGroup>
-  <ItemGroup>
-    <Compile Include="..\BabylonActionCallback.cs">
-      <Link>BabylonActionCallback.cs</Link>
-    </Compile>
-    <Compile Include="..\BabylonExportActionItem.cs">
-      <Link>BabylonExportActionItem.cs</Link>
-    </Compile>
-    <Compile Include="..\BabylonPropertiesActionItem.cs">
-      <Link>BabylonPropertiesActionItem.cs</Link>
-    </Compile>
-    <Compile Include="..\Descriptor.cs">
-      <Link>Descriptor.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\ActionBuilder\BabylonActionsBuilderActionItem.cs">
-      <Link>Exporter\ActionBuilder\BabylonActionsBuilderActionItem.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\ActionBuilder\BabylonExporter.Action.cs">
-      <Link>Exporter\ActionBuilder\BabylonExporter.Action.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.Animation.cs">
-      <Link>Exporter\BabylonExporter.Animation.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.Camera.cs">
-      <Link>Exporter\BabylonExporter.Camera.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.cs">
-      <Link>Exporter\BabylonExporter.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.Light.cs">
-      <Link>Exporter\BabylonExporter.Light.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.Material.cs">
-      <Link>Exporter\BabylonExporter.Material.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.Mesh.cs">
-      <Link>Exporter\BabylonExporter.Mesh.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.ShadowGenerator.cs">
-      <Link>Exporter\BabylonExporter.ShadowGenerator.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.Skeleton.cs">
-      <Link>Exporter\BabylonExporter.Skeleton.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\BabylonExporter.Texture.cs">
-      <Link>Exporter\BabylonExporter.Texture.cs</Link>
-    </Compile>
-    <Compile Include="..\Exporter\GlobalVertex.cs">
-      <Link>Exporter\GlobalVertex.cs</Link>
-    </Compile>
-    <Compile Include="..\Forms\ActionsBuilderForm.cs">
-      <Link>Forms\ActionsBuilderForm.cs</Link>
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="..\Forms\ActionsBuilderForm.designer.cs">
-      <Link>Forms\ActionsBuilderForm.designer.cs</Link>
-      <DependentUpon>ActionsBuilderForm.cs</DependentUpon>
-    </Compile>
-    <Compile Include="..\Forms\CameraPropertiesForm.cs">
-      <Link>Forms\CameraPropertiesForm.cs</Link>
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="..\Forms\CameraPropertiesForm.Designer.cs">
-      <Link>Forms\CameraPropertiesForm.Designer.cs</Link>
-      <DependentUpon>CameraPropertiesForm.cs</DependentUpon>
-    </Compile>
-    <Compile Include="..\Forms\ExporterForm.cs">
-      <Link>Forms\ExporterForm.cs</Link>
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="..\Forms\ExporterForm.Designer.cs">
-      <Link>Forms\ExporterForm.Designer.cs</Link>
-      <DependentUpon>ExporterForm.cs</DependentUpon>
-    </Compile>
-    <Compile Include="..\Forms\LightPropertiesForm.cs">
-      <Link>Forms\LightPropertiesForm.cs</Link>
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="..\Forms\LightPropertiesForm.Designer.cs">
-      <Link>Forms\LightPropertiesForm.Designer.cs</Link>
-      <DependentUpon>LightPropertiesForm.cs</DependentUpon>
-    </Compile>
-    <Compile Include="..\Forms\ObjectPropertiesForm.cs">
-      <Link>Forms\ObjectPropertiesForm.cs</Link>
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="..\Forms\ObjectPropertiesForm.Designer.cs">
-      <Link>Forms\ObjectPropertiesForm.Designer.cs</Link>
-      <DependentUpon>ObjectPropertiesForm.cs</DependentUpon>
-    </Compile>
-    <Compile Include="..\Forms\ScenePropertiesForm.cs">
-      <Link>Forms\ScenePropertiesForm.cs</Link>
-      <SubType>Form</SubType>
-    </Compile>
-    <Compile Include="..\Forms\ScenePropertiesForm.Designer.cs">
-      <Link>Forms\ScenePropertiesForm.Designer.cs</Link>
-      <DependentUpon>ScenePropertiesForm.cs</DependentUpon>
-    </Compile>
-    <Compile Include="..\Forms\Vector3Control.cs">
-      <Link>Forms\Vector3Control.cs</Link>
-      <SubType>UserControl</SubType>
-    </Compile>
-    <Compile Include="..\Forms\Vector3Control.Designer.cs">
-      <Link>Forms\Vector3Control.Designer.cs</Link>
-      <DependentUpon>Vector3Control.cs</DependentUpon>
-    </Compile>
-    <Compile Include="..\GlobalUtility.cs">
-      <Link>GlobalUtility.cs</Link>
-    </Compile>
-    <Compile Include="..\JsonTextWriterOptimized.cs">
-      <Link>JsonTextWriterOptimized.cs</Link>
-    </Compile>
-    <Compile Include="..\Loader.cs">
-      <Link>Loader.cs</Link>
-    </Compile>
-    <Compile Include="..\Tools\Tools.cs">
-      <Link>Tools\Tools.cs</Link>
-    </Compile>
-    <Compile Include="..\Tools\VNormal.cs">
-      <Link>Tools\VNormal.cs</Link>
-    </Compile>
-    <Compile Include="..\Tools\WebServer.cs">
-      <Link>Tools\WebServer.cs</Link>
-    </Compile>
-    <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Properties\Resources.Designer.cs">
-      <AutoGen>True</AutoGen>
-      <DesignTime>True</DesignTime>
-      <DependentUpon>Resources.resx</DependentUpon>
-    </Compile>
-  </ItemGroup>
-  <ItemGroup>
-    <Content Include="..\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>
-    </EmbeddedResource>
-    <EmbeddedResource Include="..\Forms\ExporterForm.resx">
-      <Link>Forms\ExporterForm.resx</Link>
-      <DependentUpon>ExporterForm.cs</DependentUpon>
-    </EmbeddedResource>
-    <EmbeddedResource Include="..\Forms\LightPropertiesForm.resx">
-      <Link>Forms\LightPropertiesForm.resx</Link>
-      <DependentUpon>LightPropertiesForm.cs</DependentUpon>
-    </EmbeddedResource>
-    <EmbeddedResource Include="..\Forms\ObjectPropertiesForm.resx">
-      <Link>Forms\ObjectPropertiesForm.resx</Link>
-      <DependentUpon>ObjectPropertiesForm.cs</DependentUpon>
-    </EmbeddedResource>
-    <EmbeddedResource Include="..\Forms\ScenePropertiesForm.resx">
-      <Link>Forms\ScenePropertiesForm.resx</Link>
-      <DependentUpon>ScenePropertiesForm.cs</DependentUpon>
-    </EmbeddedResource>
-    <EmbeddedResource Include="..\Forms\Vector3Control.resx">
-      <Link>Forms\Vector3Control.resx</Link>
-      <DependentUpon>Vector3Control.cs</DependentUpon>
-    </EmbeddedResource>
-    <EmbeddedResource Include="Properties\Resources.resx">
-      <Generator>ResXFileCodeGenerator</Generator>
-      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
-    </EmbeddedResource>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\..\BabylonExport.Entities\BabylonExport.Entities.csproj">
-      <Project>{6150965a-658c-4263-89ad-4f980eb0675d}</Project>
-      <Name>BabylonExport.Entities</Name>
-    </ProjectReference>
-  </ItemGroup>
-  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
-  <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">
-  </Target>
-  <Target Name="AfterBuild">
-  </Target>
-  -->
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{DD7C931A-8FAF-4318-BB74-71DC858CC400}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Max2Babylon</RootNamespace>
+    <AssemblyName>Max2Babylon</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkProfile />
+    <SccProjectName>SAK</SccProjectName>
+    <SccLocalPath>SAK</SccLocalPath>
+    <SccAuxPath>SAK</SccAuxPath>
+    <SccProvider>SAK</SccProvider>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
+    <RestorePackages>true</RestorePackages>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>C:\Program Files\Autodesk\3ds Max 2016\bin\assemblies\</OutputPath>
+    <DefineConstants>TRACE;DEBUG;MAX2015</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>C:\Program Files\Autodesk\3ds Max 2013\bin\assemblies\</OutputPath>
+    <DefineConstants>TRACE;MAX2015</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <Prefer32Bit>false</Prefer32Bit>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Autodesk.Max, Version=17.0.630.0, Culture=neutral, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\..\..\..\Repos\Babylon.js\Exporters\3ds Max\Max2Babylon\2015\Refs\Autodesk.Max.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
+    <Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="SharpDX, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="SharpDX.Mathematics, Version=3.1.1.0, Culture=neutral, PublicKeyToken=b4dcf0f35e5521f1, processorArchitecture=MSIL">
+      <HintPath>..\..\packages\SharpDX.Mathematics.3.1.1\lib\net45\SharpDX.Mathematics.dll</HintPath>
+      <Private>True</Private>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Drawing" />
+    <Reference Include="System.Runtime.Serialization" />
+    <Reference Include="System.Web" />
+    <Reference Include="System.Windows.Forms" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="..\BabylonActionCallback.cs">
+      <Link>BabylonActionCallback.cs</Link>
+    </Compile>
+    <Compile Include="..\BabylonExportActionItem.cs">
+      <Link>BabylonExportActionItem.cs</Link>
+    </Compile>
+    <Compile Include="..\BabylonPropertiesActionItem.cs">
+      <Link>BabylonPropertiesActionItem.cs</Link>
+    </Compile>
+    <Compile Include="..\Descriptor.cs">
+      <Link>Descriptor.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\ActionBuilder\BabylonActionsBuilderActionItem.cs">
+      <Link>Exporter\ActionBuilder\BabylonActionsBuilderActionItem.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\ActionBuilder\BabylonExporter.Action.cs">
+      <Link>Exporter\ActionBuilder\BabylonExporter.Action.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Animation.cs">
+      <Link>Exporter\BabylonExporter.Animation.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Camera.cs">
+      <Link>Exporter\BabylonExporter.Camera.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.cs">
+      <Link>Exporter\BabylonExporter.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Light.cs">
+      <Link>Exporter\BabylonExporter.Light.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Material.cs">
+      <Link>Exporter\BabylonExporter.Material.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Mesh.cs">
+      <Link>Exporter\BabylonExporter.Mesh.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.ShadowGenerator.cs">
+      <Link>Exporter\BabylonExporter.ShadowGenerator.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Skeleton.cs">
+      <Link>Exporter\BabylonExporter.Skeleton.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\BabylonExporter.Texture.cs">
+      <Link>Exporter\BabylonExporter.Texture.cs</Link>
+    </Compile>
+    <Compile Include="..\Exporter\GlobalVertex.cs">
+      <Link>Exporter\GlobalVertex.cs</Link>
+    </Compile>
+    <Compile Include="..\Forms\ActionsBuilderForm.cs">
+      <Link>Forms\ActionsBuilderForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ActionsBuilderForm.designer.cs">
+      <Link>Forms\ActionsBuilderForm.designer.cs</Link>
+      <DependentUpon>ActionsBuilderForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\CameraPropertiesForm.cs">
+      <Link>Forms\CameraPropertiesForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\CameraPropertiesForm.Designer.cs">
+      <Link>Forms\CameraPropertiesForm.Designer.cs</Link>
+      <DependentUpon>CameraPropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\ExporterForm.cs">
+      <Link>Forms\ExporterForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ExporterForm.Designer.cs">
+      <Link>Forms\ExporterForm.Designer.cs</Link>
+      <DependentUpon>ExporterForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\LightPropertiesForm.cs">
+      <Link>Forms\LightPropertiesForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\LightPropertiesForm.Designer.cs">
+      <Link>Forms\LightPropertiesForm.Designer.cs</Link>
+      <DependentUpon>LightPropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\ObjectPropertiesForm.cs">
+      <Link>Forms\ObjectPropertiesForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ObjectPropertiesForm.Designer.cs">
+      <Link>Forms\ObjectPropertiesForm.Designer.cs</Link>
+      <DependentUpon>ObjectPropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\ScenePropertiesForm.cs">
+      <Link>Forms\ScenePropertiesForm.cs</Link>
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="..\Forms\ScenePropertiesForm.Designer.cs">
+      <Link>Forms\ScenePropertiesForm.Designer.cs</Link>
+      <DependentUpon>ScenePropertiesForm.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\Forms\Vector3Control.cs">
+      <Link>Forms\Vector3Control.cs</Link>
+      <SubType>UserControl</SubType>
+    </Compile>
+    <Compile Include="..\Forms\Vector3Control.Designer.cs">
+      <Link>Forms\Vector3Control.Designer.cs</Link>
+      <DependentUpon>Vector3Control.cs</DependentUpon>
+    </Compile>
+    <Compile Include="..\GlobalUtility.cs">
+      <Link>GlobalUtility.cs</Link>
+    </Compile>
+    <Compile Include="..\JsonTextWriterOptimized.cs">
+      <Link>JsonTextWriterOptimized.cs</Link>
+    </Compile>
+    <Compile Include="..\Loader.cs">
+      <Link>Loader.cs</Link>
+    </Compile>
+    <Compile Include="..\Tools\Tools.cs">
+      <Link>Tools\Tools.cs</Link>
+    </Compile>
+    <Compile Include="..\Tools\VNormal.cs">
+      <Link>Tools\VNormal.cs</Link>
+    </Compile>
+    <Compile Include="..\Tools\WebServer.cs">
+      <Link>Tools\WebServer.cs</Link>
+    </Compile>
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <Content Include="Refs\Autodesk.Max.dll" />
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="..\Forms\ActionsBuilderForm.resx">
+      <Link>Forms\ActionsBuilderForm.resx</Link>
+      <DependentUpon>ActionsBuilderForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\CameraPropertiesForm.resx">
+      <Link>Forms\CameraPropertiesForm.resx</Link>
+      <DependentUpon>CameraPropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\ExporterForm.resx">
+      <Link>Forms\ExporterForm.resx</Link>
+      <DependentUpon>ExporterForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\LightPropertiesForm.resx">
+      <Link>Forms\LightPropertiesForm.resx</Link>
+      <DependentUpon>LightPropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\ObjectPropertiesForm.resx">
+      <Link>Forms\ObjectPropertiesForm.resx</Link>
+      <DependentUpon>ObjectPropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\ScenePropertiesForm.resx">
+      <Link>Forms\ScenePropertiesForm.resx</Link>
+      <DependentUpon>ScenePropertiesForm.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="..\Forms\Vector3Control.resx">
+      <Link>Forms\Vector3Control.resx</Link>
+      <DependentUpon>Vector3Control.cs</DependentUpon>
+    </EmbeddedResource>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\..\BabylonExport.Entities\BabylonExport.Entities.csproj">
+      <Project>{6150965a-658c-4263-89ad-4f980eb0675d}</Project>
+      <Name>BabylonExport.Entities</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\..\BabylonFileConverter\BabylonFileConverter.csproj">
+      <Project>{a6b76356-1d1c-4c82-8199-a6406da85a95}</Project>
+      <Name>BabylonFileConverter</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <WCFMetadata Include="Service References\" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <PropertyGroup>
+    <PostBuildEvent>mkdir "$(OutDir)BabylonActionsBuilder"
+mkdir "$(OutDir)BabylonActionsBuilder\fonts"
+mkdir "$(OutDir)BabylonActionsBuilder\fonts\SinkinSans"
+copy "$(SolutionDir)ActionsBuilder\Sources\fonts.css" "$(OutDir)BabylonActionsBuilder\fonts.css"
+copy "$(SolutionDir)ActionsBuilder\Sources\index.css" "$(OutDir)BabylonActionsBuilder\index.css"
+copy "$(SolutionDir)ActionsBuilder\Sources\index.html" "$(OutDir)BabylonActionsBuilder\index.html"
+copy "$(SolutionDir)ActionsBuilder\Sources\actionsbuilder.max.js" "$(OutDir)BabylonActionsBuilder\actionsbuilder.max.js"
+copy "$(SolutionDir)ActionsBuilder\Sources\raphael.js" "$(OutDir)BabylonActionsBuilder\raphael.js"
+copy "$(SolutionDir)babylon.max.js" "$(OutDir)BabylonActionsBuilder\babylon.max.js"
+copy "$(SolutionDir)ActionsBuilder\Sources\fonts\SinkinSans\" "$(OutDir)BabylonActionsBuilder\fonts\SinkinSans\"</PostBuildEvent>
+  </PropertyGroup>
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
 </Project>

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

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

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

@@ -1,63 +1,63 @@
-//------------------------------------------------------------------------------
-// <auto-generated>
-//     This code was generated by a tool.
-//     Runtime Version:4.0.30319.34014
-//
-//     Changes to this file may cause incorrect behavior and will be lost if
-//     the code is regenerated.
-// </auto-generated>
-//------------------------------------------------------------------------------
-
-namespace Max2Babylon.Properties {
-    using System;
-    
-    
-    /// <summary>
-    ///   A strongly-typed resource class, for looking up localized strings, etc.
-    /// </summary>
-    // This class was auto-generated by the StronglyTypedResourceBuilder
-    // class via a tool like ResGen or Visual Studio.
-    // To add or remove a member, edit your .ResX file then rerun ResGen
-    // with the /str option, or rebuild your VS project.
-    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
-    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
-    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
-    internal class Resources {
-        
-        private static global::System.Resources.ResourceManager resourceMan;
-        
-        private static global::System.Globalization.CultureInfo resourceCulture;
-        
-        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
-        internal Resources() {
-        }
-        
-        /// <summary>
-        ///   Returns the cached ResourceManager instance used by this class.
-        /// </summary>
-        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
-        internal static global::System.Resources.ResourceManager ResourceManager {
-            get {
-                if (object.ReferenceEquals(resourceMan, null)) {
-                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Max2Babylon.Properties.Resources", typeof(Resources).Assembly);
-                    resourceMan = temp;
-                }
-                return resourceMan;
-            }
-        }
-        
-        /// <summary>
-        ///   Overrides the current thread's CurrentUICulture property for all
-        ///   resource lookups using this strongly typed resource class.
-        /// </summary>
-        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
-        internal static global::System.Globalization.CultureInfo Culture {
-            get {
-                return resourceCulture;
-            }
-            set {
-                resourceCulture = value;
-            }
-        }
-    }
-}
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:4.0.30319.34014
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Max2Babylon.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Max2Babylon.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+    }
+}

+ 119 - 119
Exporters/3ds Max/Max2Babylon/2015/Properties/Resources.resx

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

+ 6 - 0
Exporters/3ds Max/Max2Babylon/2015/packages.config

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net45" />
+  <package id="SharpDX" version="3.1.1" targetFramework="net45" />
+  <package id="SharpDX.Mathematics" version="3.1.1" targetFramework="net45" />
+</packages>

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


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.