Browse Source

Core C# and Workflow Progress (+20 squashed commits)
Squashed commits:
[a8adeb4] Working on C#
[e718ac3] Working on C#
[a76e6a0] C# Work
[50d89d7] For AtomicProject assembly copy on compile to resources
[d5a02dd] Add support for Main entry point with command line args for project assembly, fix crash for CS SharedPtr return values
[287712d] C# Work
[0aadeae] Expire event receivers
[b02e6a3] Adding generic version of ResourceCache query
[e6315f3] C# Work
[e5043a3] Fix bool marshaling
[c16e251] Allow multiple CSComponents on node, only call update if CS component is enabled
[13acad0] Adding C# component template
[19b54ed] Adding CSharpLanguageExtension also initial hook for playing managed player from editor
[ce26115] Compile AtomicProject.dll to Bin folder and copy resulting assembly to resources
[69a8573] Only run nuget restore before msbuild if necessary
[bfcf75f] Updating CS inpector fields on the fly
[087d4b5] NETBuildSystem able to build project assembly, from within the Atomic Editor
[9e1ec85] NETBuildSystem can now handle solution JSON configuration files
[f241a7e] CSharp Importer
[cc62b79] Adding NETBuildSystem, C# resource editor and importer, 2hz ToolSystem update event, cleanups

Josh Engebretson 9 years ago
parent
commit
0b944f12ca
71 changed files with 2519 additions and 619 deletions
  1. 1 2
      Build/Scripts/BuildCommon.js
  2. 9 2
      Resources/EditorData/AtomicEditor/templates/file_template_definitions.json
  3. 12 0
      Resources/EditorData/AtomicEditor/templates/template_cs_component.cs
  4. 2 0
      Script/AtomicEditor/hostExtensions/ServiceLocator.ts
  5. 190 0
      Script/AtomicEditor/hostExtensions/languageExtensions/CSharpLanguageExtension.ts
  6. 2 0
      Script/AtomicEditor/ui/ResourceEditorProvider.ts
  7. 27 0
      Script/AtomicEditor/ui/frames/inspector/SelectionEditTypes.ts
  8. 50 3
      Script/AtomicEditor/ui/frames/inspector/SelectionInspector.ts
  9. 35 0
      Script/AtomicEditor/ui/resourceEditors/CSharpResourceEditorBuilder.ts
  10. 57 3
      Script/AtomicNET/AtomicNET/Application/NETIPCPlayerApp.cs
  11. 7 0
      Script/AtomicNET/AtomicNET/Core/AObject.cs
  12. 9 6
      Script/AtomicNET/AtomicNET/Core/AtomicNET.cs
  13. 3 0
      Script/AtomicNET/AtomicNET/Core/Constants.cs
  14. 130 31
      Script/AtomicNET/AtomicNET/Core/NativeCore.cs
  15. 12 6
      Script/AtomicNET/AtomicNET/Core/NativeEvents.cs
  16. 22 0
      Script/AtomicNET/AtomicNET/Graphics/CustomGeometry.cs
  17. 33 0
      Script/AtomicNET/AtomicNET/Graphics/Graphics.cs
  18. 65 0
      Script/AtomicNET/AtomicNET/Graphics/GraphicsDefs.cs
  19. 21 0
      Script/AtomicNET/AtomicNET/Graphics/VertexBuffer.cs
  20. 22 0
      Script/AtomicNET/AtomicNET/Graphics/Viewport.cs
  21. 48 17
      Script/AtomicNET/AtomicNET/Math/Color.cs
  22. 46 13
      Script/AtomicNET/AtomicNET/Math/IntRect.cs
  23. 316 285
      Script/AtomicNET/AtomicNET/Math/MathHelper.cs
  24. 47 0
      Script/AtomicNET/AtomicNET/Math/Matrix3x4.cs
  25. 33 13
      Script/AtomicNET/AtomicNET/Math/Quaternion.cs
  26. 75 4
      Script/AtomicNET/AtomicNET/Math/Rect.cs
  27. 65 2
      Script/AtomicNET/AtomicNET/Math/Vector2.cs
  28. 51 7
      Script/AtomicNET/AtomicNET/Math/Vector3.cs
  29. 18 0
      Script/AtomicNET/AtomicNET/Resource/ResourceCache.cs
  30. 86 12
      Script/AtomicNET/AtomicNET/Scene/CSComponentCore.cs
  31. 29 0
      Script/AtomicNET/AtomicNET/Scene/Node.cs
  32. 1 1
      Script/AtomicNET/AtomicPlayer/Program.cs
  33. 1 1
      Script/Packages/Atomic/Atomic3D.json
  34. 4 2
      Script/Packages/Atomic/Graphics.json
  35. 1 1
      Script/Packages/ToolCore/ToolCore.json
  36. 3 0
      Script/tsconfig.json
  37. 6 0
      Source/Atomic/Graphics/GraphicsEvents.h
  38. 5 0
      Source/Atomic/Graphics/RenderPath.cpp
  39. 4 1
      Source/Atomic/Graphics/RenderPath.h
  40. 11 0
      Source/Atomic/Graphics/View.cpp
  41. 2 0
      Source/AtomicEditor/Application/AEEditorApp.cpp
  42. 16 6
      Source/AtomicEditor/EditorMode/AEEditorMode.cpp
  43. 50 0
      Source/AtomicNET/NETNative/NETCInterop.cpp
  44. 2 0
      Source/AtomicNET/NETNative/NETCore.cpp
  45. 4 0
      Source/AtomicNET/NETNative/NETCore.h
  46. 7 0
      Source/AtomicNET/NETNative/NETEventDispatcher.cpp
  47. 6 3
      Source/AtomicNET/NETNative/NETIPCPlayerApp.cpp
  48. 4 2
      Source/AtomicNET/NETNative/NETServiceApplication.cpp
  49. 1 1
      Source/AtomicNET/NETScript/CSComponent.cpp
  50. 7 0
      Source/AtomicNET/NETScript/NETScriptEvents.h
  51. 5 0
      Source/ToolCore/Assets/Asset.cpp
  52. 93 0
      Source/ToolCore/Assets/CSharpImporter.cpp
  53. 12 22
      Source/ToolCore/Assets/CSharpImporter.h
  54. 7 0
      Source/ToolCore/Assets/NETAssemblyImporter.cpp
  55. 0 3
      Source/ToolCore/Assets/TypeScriptImporter.cpp
  56. 25 9
      Source/ToolCore/Command/NETCmd.cpp
  57. 2 3
      Source/ToolCore/Command/NETCmd.h
  58. 12 2
      Source/ToolCore/JSBind/CSharp/CSFunctionWriter.cpp
  59. 9 0
      Source/ToolCore/JSBind/CSharp/CSModuleWriter.cpp
  60. 6 3
      Source/ToolCore/JSBind/CSharp/CSTypeHelper.cpp
  61. 8 1
      Source/ToolCore/NETTools/AtomicNETService.cpp
  62. 412 0
      Source/ToolCore/NETTools/NETBuildSystem.cpp
  63. 106 0
      Source/ToolCore/NETTools/NETBuildSystem.h
  64. 0 146
      Source/ToolCore/NETTools/NETCompile.cpp
  65. 93 0
      Source/ToolCore/NETTools/NETProjectGen.cpp
  66. 16 4
      Source/ToolCore/NETTools/NETProjectGen.h
  67. 15 1
      Source/ToolCore/ToolEnvironment.cpp
  68. 12 0
      Source/ToolCore/ToolEnvironment.h
  69. 5 0
      Source/ToolCore/ToolEvents.h
  70. 19 1
      Source/ToolCore/ToolSystem.cpp
  71. 4 0
      Source/ToolCore/ToolSystem.h

+ 1 - 2
Build/Scripts/BuildCommon.js

@@ -90,8 +90,7 @@ namespace('build', function() {
       var cmds = [];
 
       if (os.platform() == "win32") {
-        cmds.push(host.atomicTool + " net genproject " + atomicRoot + "/Script/AtomicNET/AtomicNETProject.json " + platform);
-        cmds.push(host.atomicTool + " net compile " + atomicRoot + "Artifacts\\AtomicNET\\Build\\AtomicNET.sln " + configuration);
+        cmds.push(host.atomicTool + " net compile " + atomicRoot + "Script/AtomicNET/AtomicNETProject.json " + platform + " " + configuration);
       }
 
       jake.exec(cmds, function() {

+ 9 - 2
Resources/EditorData/AtomicEditor/templates/file_template_definitions.json

@@ -1,16 +1,23 @@
 {
     "component": [{
-        "name": "Basic (JavaScript)",
+        "name": "Basic - JavaScript",
         "desc": "An empty component for JavaScript",
         "templateType": "component",
         "ext": ".js",
         "filename": "AtomicEditor/templates/template_component.js"
     }, {
-        "name": "Basic (TypeScript)",
+        "name": "Basic - TypeScript",
         "desc": "An empty component for TypeScript",
         "templateType": "component",
         "ext": ".ts",
         "filename": "AtomicEditor/templates/template_ts_component.ts"
+    },
+    {
+        "name": "Basic - C#",
+        "desc": "An empty component for CSharp",
+        "templateType": "component",
+        "ext": ".cs",
+        "filename": "AtomicEditor/templates/template_cs_component.cs"
     }],
     "script": [{
         "name": "Basic (JavaScript)",

+ 12 - 0
Resources/EditorData/AtomicEditor/templates/template_cs_component.cs

@@ -0,0 +1,12 @@
+using System;
+using AtomicEngine;
+
+public class Component : CSComponent
+{
+
+    void Update(float timeStep)
+    {
+
+    }
+
+}

+ 2 - 0
Script/AtomicEditor/hostExtensions/ServiceLocator.ts

@@ -24,6 +24,7 @@ import * as HostExtensionServices from "./HostExtensionServices";
 import * as EditorUI from "../ui/EditorUI";
 import ProjectBasedExtensionLoader from "./coreExtensions/ProjectBasedExtensionLoader";
 import TypescriptLanguageExtension from "./languageExtensions/TypscriptLanguageExtension";
+import CSharpLanguageExtension from "./languageExtensions/CSharpLanguageExtension";
 
 /**
  * Generic service locator of editor services that may be injected by either a plugin
@@ -94,3 +95,4 @@ export default serviceLocator;
 // Load up all the internal services
 serviceLocator.loadService(new ProjectBasedExtensionLoader());
 serviceLocator.loadService(new TypescriptLanguageExtension());
+serviceLocator.loadService(new CSharpLanguageExtension());

+ 190 - 0
Script/AtomicEditor/hostExtensions/languageExtensions/CSharpLanguageExtension.ts

@@ -0,0 +1,190 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import * as EditorEvents from "../../editor/EditorEvents";
+
+/**
+* Resource extension that supports the web view typescript extension
+*/
+export default class CSharpLanguageExtension implements Editor.HostExtensions.ResourceServicesEventListener, Editor.HostExtensions.ProjectServicesEventListener {
+    name: string = "HostCSharpLanguageExtension";
+    description: string = "This service supports the csharp language.";
+
+    /**
+    * Indicates if this project contains NET files.
+    * @type {Boolean}
+    */
+    private isNETProject = false;
+    private serviceRegistry: Editor.HostExtensions.HostServiceLocator = null;
+
+    private menuCreated = false;
+    /** Reference to the compileOnSaveMenuItem */
+    private compileOnSaveMenuItem: Atomic.UIMenuItem;
+
+    /**
+    * Determines if the file name/path provided is something we care about
+    * @param  {string} path
+    * @return {boolean}
+    */
+    private isValidFiletype(path: string): boolean {
+        const ext = Atomic.getExtension(path);
+        if (ext == ".cs" || ext == ".dll") {
+            return true;
+        }
+    }
+
+    /**
+    * Configures the project to be a Typescript Project
+    * @return {[type]}
+    */
+    private configureNETProjectMenu() {
+
+        if (this.isNETProject && !this.menuCreated) {
+
+            // Build the menu - First build up an empty menu then manually add the items so we can have reference to them
+            const menu = this.serviceRegistry.uiServices.createPluginMenuItemSource("AtomicNET", {});
+            menu.addItem(new Atomic.UIMenuItem("Open Solution", `${this.name}.opensolution`));
+            menu.addItem(new Atomic.UIMenuItem("Compile Project", `${this.name}.compileproject`));
+            this.menuCreated = true;
+        }
+
+    }
+
+    /**
+    * Inject this language service into the registry
+    * @return {[type]}             True if successful
+    */
+    initialize(serviceLocator: Editor.HostExtensions.HostServiceLocator) {
+        // We care about both resource events as well as project events
+        serviceLocator.resourceServices.register(this);
+        serviceLocator.projectServices.register(this);
+        serviceLocator.uiServices.register(this);
+        this.serviceRegistry = serviceLocator;
+    }
+
+    /**
+    * Handle when a new file is loaded and we have not yet configured the editor for TS.
+    * This could be when someone adds a CS or assembly file to a vanilla project
+    * @param  {Editor.EditorEvents.EditResourceEvent} ev
+    */
+    edit(ev: Editor.EditorEvents.EditResourceEvent) {
+        if (this.isValidFiletype(ev.path)) {
+
+            if (this.isNETProject) {
+                this.configureNETProjectMenu();
+            }
+        }
+    }
+
+    /**
+    * Handle the delete.  This should delete the corresponding javascript file
+    * @param  {Editor.EditorEvents.DeleteResourceEvent} ev
+    */
+    delete(ev: Editor.EditorEvents.DeleteResourceEvent) {
+
+    }
+
+    /**
+    * Handle the rename.  Should rename the corresponding .js file
+    * @param  {Editor.EditorEvents.RenameResourceEvent} ev
+    */
+    rename(ev: Editor.EditorEvents.RenameResourceEvent) {
+
+    }
+
+    /**
+    * Handles the save event and detects if a cs file has been added to a non-csharp project
+    * @param  {Editor.EditorEvents.SaveResourceEvent} ev
+    * @return {[type]}
+    */
+    save(ev: Editor.EditorEvents.SaveResourceEvent) {
+
+        // let's check to see if we have created a csharp file
+        if (!this.isNETProject) {
+            if (Atomic.getExtension(ev.path) == ".cs") {
+                this.isNETProject = true;
+            }
+        }
+    }
+
+    /*** ProjectService implementation ****/
+
+    /**
+    * Called when the project is being loaded to allow the typscript language service to reset and
+    * possibly compile
+    */
+    projectLoaded(ev: Editor.EditorEvents.LoadProjectEvent) {
+        // got a load, we need to reset the language service
+        this.isNETProject = false;
+
+        let found = false;
+
+        //scan all the files in the project for any csharp or assembly files so we can determine if this is a csharp project
+        if (Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.cs", Atomic.SCAN_FILES, true).length > 0) {
+            found = true;
+        } else if (Atomic.fileSystem.scanDir(ToolCore.toolSystem.project.resourcePath, "*.dll", Atomic.SCAN_FILES, true).length > 0) {
+            found = true;
+        };
+
+        if (found) {
+            this.isNETProject = true;
+            this.configureNETProjectMenu();
+        }
+    }
+
+
+    /**
+    * Called when the project is unloaded
+    */
+    projectUnloaded() {
+        // Clean up
+        this.serviceRegistry.uiServices.removePluginMenuItemSource("AtomicNET");
+        this.menuCreated = false;
+        this.isNETProject = false;
+    }
+
+    /*** UIService implementation ***/
+
+    /**
+    * Called when a plugin menu item is clicked
+    * @param  {string} refId
+    * @return {boolean}
+    */
+    menuItemClicked(refId: string): boolean {
+        let [extension, action] = refId.split(".");
+        if (extension == this.name) {
+            switch (action) {
+                case "compileproject":
+                this.doFullCompile();
+                return true;
+            }
+        }
+    }
+
+    /**
+    * Perform a full compile of the solution
+    */
+    doFullCompile() {
+
+    }
+
+}

+ 2 - 0
Script/AtomicEditor/ui/ResourceEditorProvider.ts

@@ -26,6 +26,7 @@ import Scene3dResourceEditorBuilder from "./resourceEditors/Scene3dResourceEdito
 import TextFileResourceEditorBuilder from "./resourceEditors/TextFileResourceEditorBuilder";
 import TurboBadgerResourceEditorBuilder from "./resourceEditors/TurboBadgerResourceEditorBuilder";
 import TypescriptResourceEditorBuilder from "./resourceEditors/TypescriptResourceEditorBuilder";
+import CSharpResourceEditorBuilder from "./resourceEditors/CSharpResourceEditorBuilder";
 import XMLResourceEditorBuilder from "./resourceEditors/XMLResourceEditorBuilder";
 import ShaderResourceEditorBuilder from "./resourceEditors/ShaderResourceEditorBuilder";
 
@@ -99,6 +100,7 @@ export default class ResourceEditorProvider {
         this.registerStandardEditor(new TextFileResourceEditorBuilder());
         this.registerStandardEditor(new JavascriptResourceEditorBuilder());
         this.registerStandardEditor(new JsonResourceEditorBuilder());
+        this.registerStandardEditor(new CSharpResourceEditorBuilder());
         this.registerStandardEditor(new TypescriptResourceEditorBuilder());
         this.registerStandardEditor(new Scene3dResourceEditorBuilder());
         this.registerStandardEditor(new XMLResourceEditorBuilder());

+ 27 - 0
Script/AtomicEditor/ui/frames/inspector/SelectionEditTypes.ts

@@ -48,6 +48,33 @@ class JSComponentEditType extends SerializableEditType {
     })();
 
 
+}
+
+class CSComponentEditType extends SerializableEditType {
+
+    compareTypes(otherType: SerializableEditType, multiSelect: boolean = false): boolean {
+
+        if (this.typeName != otherType.typeName) {
+            return false;
+        }
+
+        if (!multiSelect)
+            return false;
+
+        var csc1 = <AtomicNETScript.CSComponent>(otherType.objects[0]);
+        var csc2 = <AtomicNETScript.CSComponent>(this.objects[0]);
+
+        return csc1.assemblyFile == csc2.assemblyFile && csc1.componentClassName == csc2.componentClassName;
+
+    }
+
+    private static Ctor = (() => {
+
+        SelectionInspector.registerEditType("CSComponent", CSComponentEditType);
+
+    })();
+
+
 }
 
 export = JSComponentEditType;

+ 50 - 3
Script/AtomicEditor/ui/frames/inspector/SelectionInspector.ts

@@ -162,6 +162,50 @@ class JSComponentSection extends ComponentSection {
 
 }
 
+class CSComponentSection extends ComponentSection {
+
+    constructor(editType: SerializableEditType, inspector: SelectionInspector) {
+
+        super(editType, inspector);
+
+        this.hasDynamicAttr = true;
+
+        this.subscribeToEvent(this, "AttributeEditResourceChanged", (ev) => this.handleAttributeEditResourceChanged(ev));
+
+        this.subscribeToEvent("CSComponentAssemblyChanged", (ev) => this.handleCSComponentAssemblyChanged(ev));
+
+    }
+
+    private handleCSComponentAssemblyChanged(ev) {
+
+        var csc = <AtomicNETScript.CSComponent>this.editType.getFirstObject();
+
+        if (!csc)
+          return;
+
+        if (csc.assemblyFile == <AtomicNETScript.CSComponentAssembly> ev.resource) {
+
+          var attrInfos = csc.getAttributes();
+          this.updateDynamicAttrInfos(attrInfos);
+
+        }
+
+    }
+
+    private handleAttributeEditResourceChanged(ev: AttributeEditResourceChangedEvent) {
+
+
+        var csc = <AtomicNETScript.CSComponent>this.editType.getFirstObject();
+
+        if (!csc)
+            return;
+
+        var attrInfos = csc.getAttributes();
+        this.updateDynamicAttrInfos(attrInfos);
+
+    }
+
+}
 
 // Node Inspector + Component Inspectors
 
@@ -330,12 +374,15 @@ class SelectionInspector extends ScriptWidget {
             section = new SceneSection(editType);
 
         } else if (editType.typeName == "JSComponent") {
+
             section = new JSComponentSection(editType, this);
+
+        } else if (editType.typeName == "CSComponent") {
+
+            section = new CSComponentSection(editType, this);
         }
         else {
-
             section = new ComponentSection(editType, this);
-
         }
 
         section.value = SelectionInspector.sectionStates[editType.typeName] ? 1 : 0;
@@ -650,7 +697,7 @@ class SelectionInspector extends ScriptWidget {
 
         var valid = true;
 
-        if (ev.componentTypeName != "JSComponent") {
+        if (ev.componentTypeName != "JSComponent" && ev.componentTypeName != "CSComponent") {
 
             for (var i in this.nodes) {
 

+ 35 - 0
Script/AtomicEditor/ui/resourceEditors/CSharpResourceEditorBuilder.ts

@@ -0,0 +1,35 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+import {AbstractTextResourceEditorBuilder} from "./AbstractTextResourceEditorBuilder";
+
+export default class CSharpResourceEditorBuilder extends AbstractTextResourceEditorBuilder {
+
+    constructor() {
+        super();
+    }
+
+    canHandleResource(resourcePath: string) : boolean {
+        var ext = Atomic.getExtension(resourcePath).toLowerCase();
+        return ext == ".cs";
+    }
+}

+ 57 - 3
Script/AtomicNET/AtomicNET/Application/NETIPCPlayerApp.cs

@@ -1,14 +1,56 @@
 using System;
-using System.Collections.Generic;
-using System.Runtime.InteropServices;
+using System.Reflection;
+
 
 namespace AtomicEngine
 {
+    public partial class PlayerApp : AppBase
+    {
+        protected static void ExecuteAtomicMain(string[] args)
+        {
+            var file = AtomicNET.Cache.GetFile("AtomicProject.dll");
+
+            if (file == null)
+                return;
+
+            Assembly assembly = Assembly.LoadFrom(file.FullPath);
+
+            if (assembly == null)
+                return;
+
+            Type atomicMainType = null;
+            Type[] assemblyTypes = assembly.GetTypes();
+
+            for (int j = 0; j < assemblyTypes.Length; j++)
+            {
+                if (assemblyTypes[j].Name == "AtomicMain")
+                {
+                    atomicMainType = assemblyTypes[j];
+                    break;
+                }
+            }
+
+            if (atomicMainType == null)
+                return;
+
+            Type[] mainParms = new Type[1] { typeof(string[]) };
+
+            var method = atomicMainType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, Type.DefaultBinder, mainParms, null);
+
+            if (method == null)
+                return;
+
+            Object[] parms = new Object[1] { args };
+            method.Invoke(null, parms);
+
+        }
+
+    }
 
     public partial class NETIPCPlayerApp : IPCPlayerApp
     {
 
-        public static NETIPCPlayerApp Create(bool headless = false)
+        public static NETIPCPlayerApp Create(string[] args, bool headless = false)
         {
             // Initialize AtomicNET
             AtomicNET.Initialize();
@@ -17,12 +59,24 @@ namespace AtomicEngine
 
             app.Initialize();
 
+            // TODO: Refactor these registrations
+            AtomicNET.RegisterSubsystem("FileSystem");
+
             AtomicNET.RegisterSubsystem("Graphics");
             AtomicNET.RegisterSubsystem("Player");
 
+            AtomicNET.RegisterSubsystem("Input");
+            AtomicNET.RegisterSubsystem("Renderer");
+
+            AtomicNET.RegisterSubsystem("ResourceCache");
+            AtomicNET.Cache = AtomicNET.GetSubsystem<ResourceCache>();
+
+            ExecuteAtomicMain(args);
+
             return app;
         }
 
+
     }
 
 }

+ 7 - 0
Script/AtomicNET/AtomicNET/Core/AObject.cs

@@ -9,6 +9,13 @@ namespace AtomicEngine
     {
         internal Dictionary<uint, EventDelegate> eventHandlers = new Dictionary<uint, EventDelegate>();
 
+        public static T GetSubsystem<T>() where T : AObject
+        {
+            return AtomicNET.GetSubsystem<T>();
+        }
+
+
+
         internal void HandleEvent(uint eventType, ScriptVariantMap eventData)
         {
             eventHandlers[eventType](eventType, eventData);

+ 9 - 6
Script/AtomicNET/AtomicNET/Core/AtomicNET.cs

@@ -11,6 +11,7 @@ namespace AtomicEngine
     {
 
         public static Context Context => context;
+        public static ResourceCache Cache;
 
         public static T GetSubsystem<T>() where T : AObject
         {
@@ -66,16 +67,18 @@ namespace AtomicEngine
             UIModule.Initialize();
             IPCModule.Initialize();
             AtomicAppModule.Initialize();
+            ScriptModule.Initialize();
 
             AtomicNETScriptModule.Initialize();
             AtomicNETNativeModule.Initialize();
 
-            PlayerModule.Initialize();            
+            PlayerModule.Initialize();
 
-            CoreDelegates delegates = new CoreDelegates();
-            delegates.eventDispatch = NativeCore.EventDispatch;
+            coreDelegates = new CoreDelegates();
+            coreDelegates.eventDispatch = NativeCore.EventDispatch;
+            coreDelegates.updateDispatch = NativeCore.UpdateDispatch;
 
-            IntPtr coreptr = csb_Atomic_NETCore_Initialize(ref delegates);
+            IntPtr coreptr = csb_Atomic_NETCore_Initialize(ref coreDelegates);
 
             NETCore core = (coreptr == IntPtr.Zero ? null : NativeCore.WrapNative<NETCore>(coreptr));
 
@@ -85,15 +88,15 @@ namespace AtomicEngine
             context = core.Context;
 
             NativeCore.Initialize();
-
             CSComponentCore.Initialize();
 
         }
 
         [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
-        private static extern IntPtr csb_Atomic_NETCore_Initialize(ref CoreDelegates delegates);
+        private static extern IntPtr csb_Atomic_NETCore_Initialize(ref CoreDelegates delegates);        
 
         private static Context context;
+        private static CoreDelegates coreDelegates;
         private static Dictionary<Type, AObject> subSystems = new Dictionary<Type, AObject>();
 
     }

+ 3 - 0
Script/AtomicNET/AtomicNET/Core/Constants.cs

@@ -8,6 +8,9 @@ namespace AtomicEngine
     public static partial class Constants
     {
         public const string LIBNAME = "AtomicNETNative.dll";
+
+        // Atomic2D
+        public const float PIXEL_SIZE = 0.01f;
     }
 
 }

+ 130 - 31
Script/AtomicNET/AtomicNET/Core/NativeCore.cs

@@ -1,6 +1,8 @@
 using System;
+using System.Diagnostics;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
+using System.Linq;
 
 namespace AtomicEngine
 {
@@ -30,37 +32,41 @@ namespace AtomicEngine
 
         static internal void SubscribeToEvent(AObject receiver, uint eventType)
         {
-            List<WeakReference> eventReceivers;
+            List<WeakReference<AObject>> eventReceivers;
 
             if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
             {
-                eventReceivers = eventReceiverLookup[eventType] = new List<WeakReference>();
+                eventReceivers = eventReceiverLookup[eventType] = new List<WeakReference<AObject>>();
             }
 
-            foreach (WeakReference wr in eventReceivers)
+            AObject obj;
+
+            foreach (WeakReference<AObject> wr in eventReceivers)
             {
-                // GC'd?
-                if (!wr.IsAlive)
-                    continue;
+
+                if (!wr.TryGetTarget(out obj))
+                    continue; // GC'd
 
                 // already on list?
-                if (((AObject)wr.Target) == receiver)
+                if (obj == receiver)
                     return;
             }
 
-            WeakReference w = null;
+            WeakReference<RefCounted> w = null;
 
             if (!nativeLookup.TryGetValue(receiver.nativeInstance, out w))
             {
                 throw new System.InvalidOperationException("NativeCore.SubscribeToEvent - unable to find native instance");
             }
 
-            if (!w.IsAlive)
+            RefCounted refcounted;
+
+            if (!w.TryGetTarget(out refcounted))
             {
                 throw new System.InvalidOperationException("NativeCore.SubscribeToEvent - attempting to subscribe a GC'd AObject");
             }
 
-            eventReceivers.Add(w);
+            eventReceivers.Add(new WeakReference<AObject>(receiver));
         }
 
         static ScriptVariantMap[] svm;
@@ -77,22 +83,23 @@ namespace AtomicEngine
 
         public static void EventDispatch(uint eventType, IntPtr eventData)
         {
-            List<WeakReference> eventReceivers;
+            List<WeakReference<AObject>> eventReceivers;
 
             if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
             {
-
                 // This should not happen, as event NET objects are subscribed to are filtered 
                 throw new System.InvalidOperationException("NativeCore.EventDispatch - received unregistered event type");
 
             }
 
             ScriptVariantMap scriptMap = null;
+            AObject receiver;
 
-            foreach (var w in eventReceivers)
+            // iterate over copy of list so we can modify it while running
+            foreach (var w in eventReceivers.ToList())
             {
                 // GC'd?
-                if (!w.IsAlive)
+                if (!w.TryGetTarget(out receiver))
                     continue;
 
                 if (scriptMap == null)
@@ -106,7 +113,7 @@ namespace AtomicEngine
                     scriptMap.CopyVariantMap(eventData);
                 }
 
-                ((AObject)w.Target).HandleEvent(eventType, scriptMap);
+                receiver.HandleEvent(eventType, scriptMap);
             }
 
             if (scriptMap != null)
@@ -114,6 +121,77 @@ namespace AtomicEngine
 
         }
 
+        static void ExpireNatives()
+        {
+            var watch = new Stopwatch();
+            watch.Start();
+
+            // expire event listeners
+
+            int eventListenersRemoved = 0;
+            int nativesRemoved = 0;
+
+            AObject obj;
+
+            foreach (List<WeakReference<AObject>> receiverList in eventReceiverLookup.Values)
+            {
+                foreach (WeakReference<AObject> receiver in receiverList.ToList())
+                {
+                    if (!receiver.TryGetTarget(out obj))
+                    {
+                        receiverList.Remove(receiver);
+                        eventListenersRemoved++;
+                    }
+
+                    if (watch.ElapsedMilliseconds > 16)
+                        break;
+
+                }
+
+                if (watch.ElapsedMilliseconds > 16)
+                    break;
+            }
+
+            RefCounted r;
+
+            foreach (var native in nativeLookup.Keys.ToList())
+            {
+                var w = nativeLookup[native];
+
+                if (!w.TryGetTarget(out r))
+                {
+                    // expired
+                    csb_AtomicEngine_ReleaseRef(native);
+                    nativeLookup.Remove(native);
+                    nativesRemoved++;
+                }
+
+                if (watch.ElapsedMilliseconds > 16)
+                    break;
+            }
+
+            /*
+            if (nativesRemoved != 0 || eventListenersRemoved != 0)
+            {
+                Console.WriteLine("Released {0} natives and {1} event receivers", nativesRemoved, eventListenersRemoved);
+            }
+            */
+
+        }
+
+        static float expireDelta = 0.0f;
+
+        // called ahead of E_UPDATE event
+        public static void UpdateDispatch(float timeStep)
+        {
+            expireDelta += timeStep;
+            if (expireDelta > 2.0f)
+            {
+                expireDelta = 0.0f;
+                ExpireNatives();
+            }
+        }
+
         // register a newly created native
         public static IntPtr RegisterNative(IntPtr native, RefCounted r)
         {
@@ -123,13 +201,11 @@ namespace AtomicEngine
             }
 
             r.nativeInstance = native;
-
-            var w = new WeakReference(r);
-            nativeLookup[native] = w;
-
             // keep native side alive
             r.AddRef();
 
+            nativeLookup[native] = new WeakReference<RefCounted>(r);
+
             return native;
         }
 
@@ -140,24 +216,30 @@ namespace AtomicEngine
             if (native == IntPtr.Zero)
                 return null;
 
-            WeakReference w;
+            RefCounted r;
+            WeakReference<RefCounted> w;
 
             // first see if we're already available
             if (nativeLookup.TryGetValue(native, out w))
             {
 
-                if (w.IsAlive)
+                if (w.TryGetTarget(out r))
                 {
-
                     // we're alive!
-                    return (T)w.Target;
-
+                    return (T)r;
                 }
                 else
                 {
-
                     // we were seen before, but have since been GC'd, remove!
                     nativeLookup.Remove(native);
+
+                    if (csb_Atomic_RefCounted_Refs(native) == 1)
+                    {
+                        // only managed ref remains, so release and return null
+                        csb_AtomicEngine_ReleaseRef(native);
+                        return null;
+                    }
+
                     csb_AtomicEngine_ReleaseRef(native);
                 }
             }
@@ -174,8 +256,9 @@ namespace AtomicEngine
                 throw new System.InvalidOperationException("NativeCore.WrapNative - Attempting to wrap unknown native class id");
             }
 
-            RefCounted r = nativeType.managedConstructor(native);
-            w = new WeakReference(r);
+            r = nativeType.managedConstructor(native);
+
+            w = new WeakReference<RefCounted>(r);
             NativeCore.nativeLookup[native] = w;
 
             // store a ref, so native side will not be released while we still have a reference in managed code
@@ -184,6 +267,12 @@ namespace AtomicEngine
             return (T)r;
         }
 
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern IntPtr csb_Atomic_AObject_GetTypeName(IntPtr self);
+
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern int csb_Atomic_RefCounted_Refs(IntPtr self);
+
         public static void RegisterNativeType(NativeType nativeType)
         {
             if (nativeClassIDToNativeType.ContainsKey(nativeType.nativeClassID))
@@ -200,13 +289,21 @@ namespace AtomicEngine
 
         }
 
+        public static bool IsNativeType(Type type)
+        {
+            if (typeToNativeType.ContainsKey(type))
+                return true;
+
+            return false;
+        }
+
         static public IntPtr NativeContructorOverride
         {
             get
             {
                 IntPtr value = nativeContructorOverride;
                 nativeContructorOverride = IntPtr.Zero;
-                return value;                
+                return value;
             }
 
             set
@@ -232,13 +329,15 @@ namespace AtomicEngine
         private static IntPtr nativeContructorOverride = IntPtr.Zero;
 
         // weak references here, hold a ref native side
-        internal static Dictionary<IntPtr, WeakReference> nativeLookup = new Dictionary<IntPtr, WeakReference>();
+        internal static Dictionary<IntPtr, WeakReference<RefCounted>> nativeLookup = new Dictionary<IntPtr, WeakReference<RefCounted>>();
+
+        // weak references here, hold a ref native side
+        internal static Dictionary<uint, List<WeakReference<AObject>>> eventReceiverLookup = new Dictionary<uint, List<WeakReference<AObject>>>();
+
 
         // Native ClassID to NativeType lookup
         internal static Dictionary<IntPtr, NativeType> nativeClassIDToNativeType = new Dictionary<IntPtr, NativeType>();
 
-        // weak references here, hold a ref native side
-        internal static Dictionary<uint, List<WeakReference>> eventReceiverLookup = new Dictionary<uint, List<WeakReference>>();
 
         // Managed Type to NativeType lookup
         internal static Dictionary<Type, NativeType> typeToNativeType = new Dictionary<Type, NativeType>();

+ 12 - 6
Script/AtomicNET/AtomicNET/Core/NativeEvents.cs

@@ -4,19 +4,25 @@ using System.Runtime.InteropServices;
 
 namespace AtomicEngine
 {
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate void EventDispatchDelegate(uint eventType, IntPtr eventData);
+
+    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+    public delegate void UpdateDispatchDelegate(float timeStep);
+
+    public delegate void EventDelegate(uint eventType, ScriptVariantMap eventData);
+
+    public delegate void HandleUpdateDelegate(float timeStep);
 
     [StructLayout(LayoutKind.Sequential)]
     public struct CoreDelegates
     {
         [MarshalAs(UnmanagedType.FunctionPtr)]
         public EventDispatchDelegate eventDispatch;
-    }
 
-
-    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
-    public delegate void EventDispatchDelegate(uint eventType, IntPtr eventData);
-
-    public delegate void EventDelegate(uint eventType, ScriptVariantMap eventData);
+        [MarshalAs(UnmanagedType.FunctionPtr)]
+        public UpdateDispatchDelegate updateDispatch;
+    }
 
     public partial class ScriptVariantMap
     {

+ 22 - 0
Script/AtomicNET/AtomicNET/Graphics/CustomGeometry.cs

@@ -0,0 +1,22 @@
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+    /// Custom geometry vertex.
+    [StructLayout(LayoutKind.Sequential)]
+    public struct CustomGeometryVertex
+    {
+        /// Position.
+        public Vector3 position_;
+        /// Normal.
+        public Vector3 normal_;
+        /// Color.
+        public uint color_;
+        /// Texture coordinates.
+        public Vector2 texCoord_;
+        /// Tangent.
+        public Vector4 tangent_;
+    };
+
+}
+

+ 33 - 0
Script/AtomicNET/AtomicNET/Graphics/Graphics.cs

@@ -0,0 +1,33 @@
+
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+    public partial class Graphics : AObject
+    {
+
+        // Shader Parameters
+
+        public void SetShaderParameter(string param, Matrix3x4 matrix)
+        {
+            csb_Atomic_Graphics_SetShaderParameter_Matrix3x4(nativeInstance, param, ref matrix);
+        }
+
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern IntPtr csb_Atomic_Graphics_SetShaderParameter_Matrix3x4(IntPtr self, string param, ref Matrix3x4 matrix);
+
+        public void SetShaderParameter(string param, Color color)
+        {
+            csb_Atomic_Graphics_SetShaderParameter_Color(nativeInstance, param, ref color);
+        }
+
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern IntPtr csb_Atomic_Graphics_SetShaderParameter_Color(IntPtr self, string param, ref Color color);
+
+
+    };
+
+}
+

+ 65 - 0
Script/AtomicNET/AtomicNET/Graphics/GraphicsDefs.cs

@@ -0,0 +1,65 @@
+
+
+namespace AtomicEngine
+{
+
+    // These must be kept in sync with GraphicsDef.cpp
+    // These could also be convertied to StringHash, would need override for setting on Graphics subsystem
+
+    public static class ShaderParams
+    {
+        // Vertex
+        public const string VSP_AMBIENTSTARTCOLOR = "AmbientStartColor";
+        public const string VSP_AMBIENTENDCOLOR = "AmbientEndColor";
+        public const string VSP_BILLBOARDROT = "BillboardRot";
+        public const string VSP_CAMERAPOS = "CameraPos";
+        public const string VSP_CAMERAROT = "CameraRot";
+        public const string VSP_CLIPPLANE = "ClipPlane";
+        public const string VSP_NEARCLIP = "NearClip";
+        public const string VSP_FARCLIP = "FarClip";
+        public const string VSP_DEPTHMODE = "DepthMode";
+        public const string VSP_DELTATIME = "DeltaTime";
+        public const string VSP_ELAPSEDTIME = "ElapsedTime";
+        public const string VSP_FRUSTUMSIZE = "FrustumSize";
+        public const string VSP_GBUFFEROFFSETS = "GBufferOffsets";
+        public const string VSP_LIGHTDIR = "LightDir";
+        public const string VSP_LIGHTPOS = "LightPos";
+        public const string VSP_MODEL = "Model";
+        public const string VSP_VIEWPROJ = "ViewProj";
+        public const string VSP_UOFFSET = "UOffset";
+        public const string VSP_VOFFSET = "VOffset";
+        public const string VSP_ZONE = "Zone";
+        public const string VSP_LIGHTMATRICES = "LightMatrices";
+        public const string VSP_SKINMATRICES = "SkinMatrices";
+        public const string VSP_VERTEXLIGHTS = "VertexLights";
+
+        // Pixel
+        public const string PSP_AMBIENTCOLOR = "AmbientColor";
+        public const string PSP_CAMERAPOS = "CameraPosPS";
+        public const string PSP_DELTATIME = "DeltaTimePS";
+        public const string PSP_DEPTHRECONSTRUCT = "DepthReconstruct";
+        public const string PSP_ELAPSEDTIME = "ElapsedTimePS";
+        public const string PSP_FOGCOLOR = "FogColor";
+        public const string PSP_FOGPARAMS = "FogParams";
+        public const string PSP_GBUFFERINVSIZE = "GBufferInvSize";
+        public const string PSP_LIGHTCOLOR = "LightColor";
+        public const string PSP_LIGHTDIR = "LightDirPS";
+        public const string PSP_LIGHTPOS = "LightPosPS";
+        public const string PSP_MATDIFFCOLOR = "MatDiffColor";
+        public const string PSP_MATEMISSIVECOLOR = "MatEmissiveColor";
+        public const string PSP_MATENVMAPCOLOR = "MatEnvMapColor";
+        public const string PSP_MATSPECCOLOR = "MatSpecColor";
+        public const string PSP_NEARCLIP = "NearClipPS";
+        public const string PSP_FARCLIP = "FarClipPS";
+        public const string PSP_SHADOWCUBEADJUST = "ShadowCubeAdjust";
+        public const string PSP_SHADOWDEPTHFADE = "ShadowDepthFade";
+        public const string PSP_SHADOWINTENSITY = "ShadowIntensity";
+        public const string PSP_SHADOWMAPINVSIZE = "ShadowMapInvSize";
+        public const string PSP_SHADOWSPLITS = "ShadowSplits";
+        public const string PSP_LIGHTMATRICES = "LightMatricesPS";
+
+
+    }
+
+
+}

+ 21 - 0
Script/AtomicNET/AtomicNET/Graphics/VertexBuffer.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+    public partial class VertexBuffer: AObject
+    {
+
+        public IntPtr Lock(uint start, uint count, bool discard = false)
+        {
+            return csb_Atomic_VertexBuffer_Lock(this.nativeInstance, start, count, discard);
+        }
+
+
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern IntPtr csb_Atomic_VertexBuffer_Lock(IntPtr self, uint start, uint count, bool discard);
+
+    };
+
+}
+

+ 22 - 0
Script/AtomicNET/AtomicNET/Graphics/Viewport.cs

@@ -0,0 +1,22 @@
+
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+    public partial class Viewport : AObject
+    {
+
+        public void SetRenderPath(RenderPath renderPath)
+        {
+            csb_Atomic_Viewport_SetRenderPath_RenderPath(nativeInstance, renderPath != null ? renderPath.nativeInstance : IntPtr.Zero);
+        }
+
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern IntPtr csb_Atomic_Viewport_SetRenderPath_RenderPath(IntPtr self, IntPtr renderPath);
+
+    };
+
+}
+

+ 48 - 17
Script/AtomicNET/AtomicNET/Math/Color.cs

@@ -4,21 +4,52 @@ namespace AtomicEngine
 {
 
 
-[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-public struct Color
-	{
-		public Color (float r, float g, float b, float a = 1.0f)
-		{
-			this.r = r;
-			this.g = g;
-			this.b = b;
-			this.a = a;
-		}
-
-		public float r;
-		public float g;
-		public float b;
-		public float a;
-
-	}
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    public struct Color
+    {
+        public Color(float r, float g, float b, float a = 1.0f)
+        {
+            R = r;
+            G = g;
+            B = b;
+            A = a;
+        }
+
+        public uint ToUInt()
+        {
+            uint r = (uint)(R * 255.0f);
+            uint g = (uint)(G * 255.0f);
+            uint b = (uint)(B * 255.0f);
+            uint a = (uint)(A * 255.0f);
+            return (a << 24) | (b << 16) | (g << 8) | r;
+        }
+
+
+
+        public static readonly Color White = new Color(1, 1, 1, 1);
+        public static readonly Color LightBlue = new Color(0.50f, 0.88f, 0.81f);
+        public static readonly Color Transparent = new Color(0, 0, 0, 0);
+
+        public static Color operator *(Color value, float scale)
+        {
+            return new Color((float)(value.R * scale), (float)(value.G * scale), (float)(value.B * scale), (float)(value.A * scale));
+        }
+
+        public static Color Lerp(Color value1, Color value2, float amount)
+        {
+            amount = MathHelper.Clamp(amount, 0, 1);
+            return new Color(
+                MathHelper.Lerp(value1.R, value2.R, amount),
+                MathHelper.Lerp(value1.G, value2.G, amount),
+                MathHelper.Lerp(value1.B, value2.B, amount),
+                MathHelper.Lerp(value1.A, value2.A, amount));
+        }
+
+
+        public float R;
+        public float G;
+        public float B;
+        public float A;
+
+    }
 }

+ 46 - 13
Script/AtomicNET/AtomicNET/Math/IntRect.cs

@@ -5,18 +5,51 @@ namespace AtomicEngine
 {
 
 
-	[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-	public struct IntRect
-	{
-		/// Left coordinate.
-    int left;
-    /// Top coordinate.
-    int top;
-    /// Right coordinate.
-    int right;
-    /// Bottom coordinate.
-    int bottom;
-
-	}
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    public struct IntRect
+    {
+
+        public IntRect(int left, int top, int right, int bottom)
+        {
+            this.Left = left;
+            this.Top = top;
+            this.Right = right;
+            this.Bottom = bottom;
+        }
+
+        public int Width { get { return Right - Left; } }
+        public int Height { get { return Bottom - Top; } }
+
+        public void Inflate(int horizontalAmount, int verticalAmount)
+        {
+            Left -= horizontalAmount;
+            Right += horizontalAmount;
+
+            Top -= verticalAmount;
+            Bottom += verticalAmount;
+
+        }
+
+        public bool Contains(Vector2 vector)
+        {
+            int x = (int)vector.X;
+            int y = (int)vector.Y;
+
+            if (x < Left || y < Top || x >= Right || y >= Bottom)
+                return false;
+
+            return true;
+        }
+
+        /// Left coordinate.
+        public int Left;
+        /// Top coordinate.
+        public int Top;
+        /// Right coordinate.
+        public int Right;
+        /// Bottom coordinate.
+        public int Bottom;
+
+    }
 
 }

+ 316 - 285
Script/AtomicNET/AtomicNET/Math/MathHelper.cs

@@ -14,191 +14,191 @@ using System.Text;
 
 namespace AtomicEngine
 {
-	/// <summary>
-	/// Contains common mathematical functions and constants.
-	/// </summary>
-	public static class MathHelper
-	{
-		#region Fields
-
-		/// <summary>
-		/// Defines the value of Pi as a <see cref="System.Single"/>.
-		/// </summary>
-		public const float Pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930382f;
-
-		/// <summary>
-		/// Defines the value of Pi divided by two as a <see cref="System.Single"/>.
-		/// </summary>
-		public const float PiOver2 = Pi / 2;
-
-		/// <summary>
-		/// Defines the value of Pi divided by three as a <see cref="System.Single"/>.
-		/// </summary>
-		public const float PiOver3 = Pi / 3;
-
-		/// <summary>
-		/// Definesthe value of  Pi divided by four as a <see cref="System.Single"/>.
-		/// </summary>
-		public const float PiOver4 = Pi / 4;
-
-		/// <summary>
-		/// Defines the value of Pi divided by six as a <see cref="System.Single"/>.
-		/// </summary>
-		public const float PiOver6 = Pi / 6;
-
-		/// <summary>
-		/// Defines the value of Pi multiplied by two as a <see cref="System.Single"/>.
-		/// </summary>
-		public const float TwoPi = 2 * Pi;
-
-		/// <summary>
-		/// Defines the value of Pi multiplied by 3 and divided by two as a <see cref="System.Single"/>.
-		/// </summary>
-		public const float ThreePiOver2 = 3 * Pi / 2;
-
-		/// <summary>
-		/// Defines the value of E as a <see cref="System.Single"/>.
-		/// </summary>
-		public const float E = 2.71828182845904523536f;
-
-		/// <summary>
-		/// Defines the base-10 logarithm of E.
-		/// </summary>
-		public const float Log10E = 0.434294482f;
-
-		/// <summary>
-		/// Defines the base-2 logarithm of E.
-		/// </summary>
-		public const float Log2E = 1.442695041f;
-
-		public static readonly float DTORF = Pi / 180.0f;
-
-		#endregion
-
-		#region Public Members
-
-		#region NextPowerOfTwo
-
-		/// <summary>
-		/// Returns the next power of two that is larger than the specified number.
-		/// </summary>
-		/// <param name="n">The specified number.</param>
-		/// <returns>The next power of two.</returns>
-		public static long NextPowerOfTwo(long n)
-		{
-			if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
-			return (long)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
-		}
-
-		/// <summary>
-		/// Returns the next power of two that is larger than the specified number.
-		/// </summary>
-		/// <param name="n">The specified number.</param>
-		/// <returns>The next power of two.</returns>
-		public static int NextPowerOfTwo(int n)
-		{
-			if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
-			return (int)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
-		}
-
-		/// <summary>
-		/// Returns the next power of two that is larger than the specified number.
-		/// </summary>
-		/// <param name="n">The specified number.</param>
-		/// <returns>The next power of two.</returns>
-		public static float NextPowerOfTwo(float n)
-		{
-			if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
-			return (float)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
-		}
-
-		/// <summary>
-		/// Returns the next power of two that is larger than the specified number.
-		/// </summary>
-		/// <param name="n">The specified number.</param>
-		/// <returns>The next power of two.</returns>
-		public static double NextPowerOfTwo(double n)
-		{
-			if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
-			return System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
-		}
-
-		#endregion
-
-		#region Factorial
-
-		/// <summary>Calculates the factorial of a given natural number.
-		/// </summary>
-		/// <param name="n">The number.</param>
-		/// <returns>n!</returns>
-		public static long Factorial(int n)
-		{
-			long result = 1;
-
-			for (; n > 1; n--)
-				result *= n;
-
-			return result;
-		}
-
-		#endregion
-
-		#region BinomialCoefficient
-
-		/// <summary>
-		/// Calculates the binomial coefficient <paramref name="n"/> above <paramref name="k"/>.
-		/// </summary>
-		/// <param name="n">The n.</param>
-		/// <param name="k">The k.</param>
-		/// <returns>n! / (k! * (n - k)!)</returns>
-		public static long BinomialCoefficient(int n, int k)
-		{
-			return Factorial(n) / (Factorial(k) * Factorial(n - k));
-		}
-
-		#endregion
-
-		#region InverseSqrtFast
-
-		/// <summary>
-		/// Returns an approximation of the inverse square root of left number.
-		/// </summary>
-		/// <param name="x">A number.</param>
-		/// <returns>An approximation of the inverse square root of the specified number, with an upper error bound of 0.001</returns>
-		/// <remarks>
-		/// This is an improved implementation of the the method known as Carmack's inverse square root
-		/// which is found in the Quake III source code. This implementation comes from
-		/// http://www.codemaestro.com/reviews/review00000105.html. For the history of this method, see
-		/// http://www.beyond3d.com/content/articles/8/
-		/// </remarks>
-		public static float InverseSqrtFast(float x)
-		{
-			unsafe
-			{
-				float xhalf = 0.5f * x;
-				int i = *(int*)&x;              // Read bits as integer.
-				i = 0x5f375a86 - (i >> 1);      // Make an initial guess for Newton-Raphson approximation
-				x = *(float*)&i;                // Convert bits back to float
-				x = x * (1.5f - xhalf * x * x); // Perform left single Newton-Raphson step.
-				return x;
-			}
-		}
-
-		/// <summary>
-		/// Returns an approximation of the inverse square root of left number.
-		/// </summary>
-		/// <param name="x">A number.</param>
-		/// <returns>An approximation of the inverse square root of the specified number, with an upper error bound of 0.001</returns>
-		/// <remarks>
-		/// This is an improved implementation of the the method known as Carmack's inverse square root
-		/// which is found in the Quake III source code. This implementation comes from
-		/// http://www.codemaestro.com/reviews/review00000105.html. For the history of this method, see
-		/// http://www.beyond3d.com/content/articles/8/
-		/// </remarks>
-		public static double InverseSqrtFast(double x)
-		{
-			return InverseSqrtFast((float)x);
-			// TODO: The following code is wrong. Fix it, to improve precision.
+    /// <summary>
+    /// Contains common mathematical functions and constants.
+    /// </summary>
+    public static class MathHelper
+    {
+        #region Fields
+
+        /// <summary>
+        /// Defines the value of Pi as a <see cref="System.Single"/>.
+        /// </summary>
+        public const float Pi = 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930382f;
+
+        /// <summary>
+        /// Defines the value of Pi divided by two as a <see cref="System.Single"/>.
+        /// </summary>
+        public const float PiOver2 = Pi / 2;
+
+        /// <summary>
+        /// Defines the value of Pi divided by three as a <see cref="System.Single"/>.
+        /// </summary>
+        public const float PiOver3 = Pi / 3;
+
+        /// <summary>
+        /// Definesthe value of  Pi divided by four as a <see cref="System.Single"/>.
+        /// </summary>
+        public const float PiOver4 = Pi / 4;
+
+        /// <summary>
+        /// Defines the value of Pi divided by six as a <see cref="System.Single"/>.
+        /// </summary>
+        public const float PiOver6 = Pi / 6;
+
+        /// <summary>
+        /// Defines the value of Pi multiplied by two as a <see cref="System.Single"/>.
+        /// </summary>
+        public const float TwoPi = 2 * Pi;
+
+        /// <summary>
+        /// Defines the value of Pi multiplied by 3 and divided by two as a <see cref="System.Single"/>.
+        /// </summary>
+        public const float ThreePiOver2 = 3 * Pi / 2;
+
+        /// <summary>
+        /// Defines the value of E as a <see cref="System.Single"/>.
+        /// </summary>
+        public const float E = 2.71828182845904523536f;
+
+        /// <summary>
+        /// Defines the base-10 logarithm of E.
+        /// </summary>
+        public const float Log10E = 0.434294482f;
+
+        /// <summary>
+        /// Defines the base-2 logarithm of E.
+        /// </summary>
+        public const float Log2E = 1.442695041f;
+
+        public static readonly float DTORF = Pi / 180.0f;
+
+        #endregion
+
+        #region Public Members
+
+        #region NextPowerOfTwo
+
+        /// <summary>
+        /// Returns the next power of two that is larger than the specified number.
+        /// </summary>
+        /// <param name="n">The specified number.</param>
+        /// <returns>The next power of two.</returns>
+        public static long NextPowerOfTwo(long n)
+        {
+            if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
+            return (long)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
+        }
+
+        /// <summary>
+        /// Returns the next power of two that is larger than the specified number.
+        /// </summary>
+        /// <param name="n">The specified number.</param>
+        /// <returns>The next power of two.</returns>
+        public static int NextPowerOfTwo(int n)
+        {
+            if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
+            return (int)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
+        }
+
+        /// <summary>
+        /// Returns the next power of two that is larger than the specified number.
+        /// </summary>
+        /// <param name="n">The specified number.</param>
+        /// <returns>The next power of two.</returns>
+        public static float NextPowerOfTwo(float n)
+        {
+            if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
+            return (float)System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
+        }
+
+        /// <summary>
+        /// Returns the next power of two that is larger than the specified number.
+        /// </summary>
+        /// <param name="n">The specified number.</param>
+        /// <returns>The next power of two.</returns>
+        public static double NextPowerOfTwo(double n)
+        {
+            if (n < 0) throw new ArgumentOutOfRangeException("n", "Must be positive.");
+            return System.Math.Pow(2, System.Math.Ceiling(System.Math.Log((double)n, 2)));
+        }
+
+        #endregion
+
+        #region Factorial
+
+        /// <summary>Calculates the factorial of a given natural number.
+        /// </summary>
+        /// <param name="n">The number.</param>
+        /// <returns>n!</returns>
+        public static long Factorial(int n)
+        {
+            long result = 1;
+
+            for (; n > 1; n--)
+                result *= n;
+
+            return result;
+        }
+
+        #endregion
+
+        #region BinomialCoefficient
+
+        /// <summary>
+        /// Calculates the binomial coefficient <paramref name="n"/> above <paramref name="k"/>.
+        /// </summary>
+        /// <param name="n">The n.</param>
+        /// <param name="k">The k.</param>
+        /// <returns>n! / (k! * (n - k)!)</returns>
+        public static long BinomialCoefficient(int n, int k)
+        {
+            return Factorial(n) / (Factorial(k) * Factorial(n - k));
+        }
+
+        #endregion
+
+        #region InverseSqrtFast
+
+        /// <summary>
+        /// Returns an approximation of the inverse square root of left number.
+        /// </summary>
+        /// <param name="x">A number.</param>
+        /// <returns>An approximation of the inverse square root of the specified number, with an upper error bound of 0.001</returns>
+        /// <remarks>
+        /// This is an improved implementation of the the method known as Carmack's inverse square root
+        /// which is found in the Quake III source code. This implementation comes from
+        /// http://www.codemaestro.com/reviews/review00000105.html. For the history of this method, see
+        /// http://www.beyond3d.com/content/articles/8/
+        /// </remarks>
+        public static float InverseSqrtFast(float x)
+        {
+            unsafe
+            {
+                float xhalf = 0.5f * x;
+                int i = *(int*)&x;              // Read bits as integer.
+                i = 0x5f375a86 - (i >> 1);      // Make an initial guess for Newton-Raphson approximation
+                x = *(float*)&i;                // Convert bits back to float
+                x = x * (1.5f - xhalf * x * x); // Perform left single Newton-Raphson step.
+                return x;
+            }
+        }
+
+        /// <summary>
+        /// Returns an approximation of the inverse square root of left number.
+        /// </summary>
+        /// <param name="x">A number.</param>
+        /// <returns>An approximation of the inverse square root of the specified number, with an upper error bound of 0.001</returns>
+        /// <remarks>
+        /// This is an improved implementation of the the method known as Carmack's inverse square root
+        /// which is found in the Quake III source code. This implementation comes from
+        /// http://www.codemaestro.com/reviews/review00000105.html. For the history of this method, see
+        /// http://www.beyond3d.com/content/articles/8/
+        /// </remarks>
+        public static double InverseSqrtFast(double x)
+        {
+            return InverseSqrtFast((float)x);
+            // TODO: The following code is wrong. Fix it, to improve precision.
 #if false
 			unsafe
 			{
@@ -210,104 +210,135 @@ namespace AtomicEngine
 				return x;
 			}
 #endif
-		}
-
-		#endregion
-
-		#region DegreesToRadians
-
-		/// <summary>
-		/// Convert degrees to radians
-		/// </summary>
-		/// <param name="degrees">An angle in degrees</param>
-		/// <returns>The angle expressed in radians</returns>
-		public static float DegreesToRadians(float degrees)
-		{
-			const float degToRad = (float)System.Math.PI / 180.0f;
-			return degrees * degToRad;
-		}
-
-		/// <summary>
-		/// Convert radians to degrees
-		/// </summary>
-		/// <param name="radians">An angle in radians</param>
-		/// <returns>The angle expressed in degrees</returns>
-		public static float RadiansToDegrees(float radians)
-		{
-			const float radToDeg = 180.0f / (float)System.Math.PI;
-			return radians * radToDeg;
-		}
-
-		/// <summary>
-		/// Convert degrees to radians
-		/// </summary>
-		/// <param name="degrees">An angle in degrees</param>
-		/// <returns>The angle expressed in radians</returns>
-		public static double DegreesToRadians(double degrees)
-		{
-			const double degToRad = System.Math.PI / 180.0;
-			return degrees * degToRad;
-		}
-
-		/// <summary>
-		/// Convert radians to degrees
-		/// </summary>
-		/// <param name="radians">An angle in radians</param>
-		/// <returns>The angle expressed in degrees</returns>
-		public static double RadiansToDegrees(double radians)
-		{
-			const double radToDeg = 180.0 / System.Math.PI;
-			return radians * radToDeg;
-		}
-
-		#endregion
-
-		#region Swap
-
-		/// <summary>
-		/// Swaps two double values.
-		/// </summary>
-		/// <param name="a">The first value.</param>
-		/// <param name="b">The second value.</param>
-		public static void Swap(ref double a, ref double b)
-		{
-			double temp = a;
-			a = b;
-			b = temp;
-		}
-
-		/// <summary>
-		/// Swaps two float values.
-		/// </summary>
-		/// <param name="a">The first value.</param>
-		/// <param name="b">The second value.</param>
-		public static void Swap(ref float a, ref float b)
-		{
-			float temp = a;
-			a = b;
-			b = temp;
-		}
-
-		#endregion
-
-		#endregion
-
-		/// <summary>
-		/// Clamp a float to a range.
-		/// </summary>
-		public static float Clamp(float value, float min, float max)
-		{
-			if (value < min)
-				return min;
-			else if (value > max)
-				return max;
-			else
-				return value;
-		}
-
-		public static float Lerp(float lhs, float rhs, float t)
-		{
-			return lhs * (1.0f - t) + rhs * t;
-		}
-	}
+        }
+
+        #endregion
+
+        #region DegreesToRadians
+
+        /// <summary>
+        /// Convert degrees to radians
+        /// </summary>
+        /// <param name="degrees">An angle in degrees</param>
+        /// <returns>The angle expressed in radians</returns>
+        public static float DegreesToRadians(float degrees)
+        {
+            const float degToRad = (float)System.Math.PI / 180.0f;
+            return degrees * degToRad;
+        }
+
+        /// <summary>
+        /// Convert radians to degrees
+        /// </summary>
+        /// <param name="radians">An angle in radians</param>
+        /// <returns>The angle expressed in degrees</returns>
+        public static float RadiansToDegrees(float radians)
+        {
+            const float radToDeg = 180.0f / (float)System.Math.PI;
+            return radians * radToDeg;
+        }
+
+        /// <summary>
+        /// Convert degrees to radians
+        /// </summary>
+        /// <param name="degrees">An angle in degrees</param>
+        /// <returns>The angle expressed in radians</returns>
+        public static double DegreesToRadians(double degrees)
+        {
+            const double degToRad = System.Math.PI / 180.0;
+            return degrees * degToRad;
+        }
+
+        /// <summary>
+        /// Convert radians to degrees
+        /// </summary>
+        /// <param name="radians">An angle in radians</param>
+        /// <returns>The angle expressed in degrees</returns>
+        public static double RadiansToDegrees(double radians)
+        {
+            const double radToDeg = 180.0 / System.Math.PI;
+            return radians * radToDeg;
+        }
+
+        #endregion
+
+        #region Swap
+
+        /// <summary>
+        /// Swaps two double values.
+        /// </summary>
+        /// <param name="a">The first value.</param>
+        /// <param name="b">The second value.</param>
+        public static void Swap(ref double a, ref double b)
+        {
+            double temp = a;
+            a = b;
+            b = temp;
+        }
+
+        /// <summary>
+        /// Swaps two float values.
+        /// </summary>
+        /// <param name="a">The first value.</param>
+        /// <param name="b">The second value.</param>
+        public static void Swap(ref float a, ref float b)
+        {
+            float temp = a;
+            a = b;
+            b = temp;
+        }
+
+        #endregion
+
+        #endregion
+
+        /// <summary>
+        /// Clamp a float to a range.
+        /// </summary>
+        public static float Clamp(float value, float min, float max)
+        {
+            if (value < min)
+                return min;
+            else if (value > max)
+                return max;
+            else
+                return value;
+        }
+
+        public static float Lerp(float lhs, float rhs, float t)
+        {
+            return lhs * (1.0f - t) + rhs * t;
+        }
+
+        public static float WrapAngle(float angle)
+        {
+            angle = (float)Math.IEEERemainder((double)angle, 6.2831854820251465);
+            if (angle <= -3.14159274f)
+            {
+                angle += 6.28318548f;
+            }
+            else
+            {
+                if (angle > 3.14159274f)
+                {
+                    angle -= 6.28318548f;
+                }
+            }
+            return angle;
+        }
+
+        public static float CatmullRom(float value1, float value2, float value3, float value4, float amount)
+        {
+            // Using formula from http://www.mvps.org/directx/articles/catmull/
+            // Internally using doubles not to lose precission
+            double amountSquared = amount * amount;
+            double amountCubed = amountSquared * amount;
+            return (float)(0.5 * (2.0 * value2 +
+                (value3 - value1) * amount +
+                (2.0 * value1 - 5.0 * value2 + 4.0 * value3 - value4) * amountSquared +
+                (3.0 * value2 - value1 - 3.0 * value3 + value4) * amountCubed));
+        }
+
+
+    }
 }

+ 47 - 0
Script/AtomicNET/AtomicNET/Math/Matrix3x4.cs

@@ -0,0 +1,47 @@
+
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    public struct Matrix3x4
+    {
+        static public Matrix3x4 IDENTITY = CreateIdentity();
+
+        static Matrix3x4 CreateIdentity()
+        {
+            var m = new Matrix3x4();
+            m.m00_ = 1.0f;
+            m.m01_ = 0.0f;
+            m.m02_ = 0.0f;
+            m.m03_ = 0.0f;
+            m.m10_ = 0.0f;
+            m.m11_ = 1.0f;
+            m.m12_ = 0.0f;
+            m.m13_ = 0.0f;
+            m.m20_ = 0.0f;
+            m.m21_ = 0.0f;
+            m.m22_ = 1.0f;
+            m.m23_ = 0.0f;
+            return m;
+
+        }
+
+        float m00_;
+        float m01_;
+        float m02_;
+        float m03_;
+        float m10_;
+        float m11_;
+        float m12_;
+        float m13_;
+        float m20_;
+        float m21_;
+        float m22_;
+        float m23_;
+
+    }
+
+
+}

+ 33 - 13
Script/AtomicNET/AtomicNET/Math/Quaternion.cs

@@ -650,19 +650,39 @@ namespace AtomicEngine
 				return Identity;
 		}
 
-		#endregion
-
-		#endregion
-
-		#region Operators
-
-		/// <summary>
-		/// Adds two instances.
-		/// </summary>
-		/// <param name="left">The first instance.</param>
-		/// <param name="right">The second instance.</param>
-		/// <returns>The result of the calculation.</returns>
-		public static Quaternion operator +(Quaternion left, Quaternion right)
+        #endregion
+
+        public static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll)
+        {
+            float halfRoll = roll * 0.5f;
+            float halfPitch = pitch * 0.5f;
+            float halfYaw = yaw * 0.5f;
+
+            float sinRoll = (float)Math.Sin(halfRoll);
+            float cosRoll = (float)Math.Cos(halfRoll);
+            float sinPitch = (float)Math.Sin(halfPitch);
+            float cosPitch = (float)Math.Cos(halfPitch);
+            float sinYaw = (float)Math.Sin(halfYaw);
+            float cosYaw = (float)Math.Cos(halfYaw);
+
+            return new Quaternion((cosYaw * sinPitch * cosRoll) + (sinYaw * cosPitch * sinRoll),
+                                  (sinYaw * cosPitch * cosRoll) - (cosYaw * sinPitch * sinRoll),
+                                  (cosYaw * cosPitch * sinRoll) - (sinYaw * sinPitch * cosRoll),
+                                  (cosYaw * cosPitch * cosRoll) + (sinYaw * sinPitch * sinRoll));
+        }
+
+
+        #endregion
+
+        #region Operators
+
+        /// <summary>
+        /// Adds two instances.
+        /// </summary>
+        /// <param name="left">The first instance.</param>
+        /// <param name="right">The second instance.</param>
+        /// <returns>The result of the calculation.</returns>
+        public static Quaternion operator +(Quaternion left, Quaternion right)
 		{
 			left.Xyz += right.Xyz;
 			left.W += right.W;

+ 75 - 4
Script/AtomicNET/AtomicNET/Math/Rect.cs

@@ -5,9 +5,80 @@ namespace AtomicEngine
 {
 
 
-[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Ansi)]
-public struct Rect
-{
-}
+    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+    public struct Rect
+    {
+        public Rect(float left, float top, float right, float bottom)
+        {
+            Min.X = left;
+            Min.Y = top;
+            Max.X = right;
+            Max.Y = bottom;
+
+            // for native
+            Defined = true;
+        }
+
+        public float Left
+        {
+            get
+            {
+                return Min.X;
+            }
+            set
+            {
+                Min.X = value;
+            }
+        }
+
+        public float Right
+        {
+            get
+            {
+                return Max.X;
+            }
+            set
+            {
+                Max.X = value;
+            }
+        }
+
+        public float Top
+        {
+            get
+            {
+                return Min.Y;
+            }
+            set
+            {
+                Min.Y = value;
+            }
+        }
+
+        public float Bottom
+        {
+            get
+            {
+                return Max.Y;
+            }
+            set
+            {
+                Max.Y = value;
+            }
+        }
+
+        public float Width { get { return Max.X - Min.X; } }
+        public float Height {  get { return Max.Y - Min.Y; } }
+
+        /// Minimum vector.
+        public Vector2 Min;
+        
+        /// Maximum vector.
+         public Vector2 Max;
+
+        /// Defined flag.
+        bool Defined;
+
+    }
 
 }

+ 65 - 2
Script/AtomicNET/AtomicNET/Math/Vector2.cs

@@ -1051,6 +1051,69 @@ namespace AtomicEngine
 				Y == other.Y;
 		}
 
-		#endregion
-	}
+        #endregion
+
+        public static Vector2 operator *(Vector2 value1, Vector2 value2)
+        {
+            value1.X *= value2.X;
+            value1.Y *= value2.Y;
+            return value1;
+        }
+
+        public static float Distance(Vector2 value1, Vector2 value2)
+        {
+            float v1 = value1.X - value2.X, v2 = value1.Y - value2.Y;
+            return (float)Math.Sqrt((v1 * v1) + (v2 * v2));
+        }
+
+        public static void Distance(ref Vector2 value1, ref Vector2 value2, out float result)
+        {
+            float v1 = value1.X - value2.X, v2 = value1.Y - value2.Y;
+            result = (float)Math.Sqrt((v1 * v1) + (v2 * v2));
+        }
+
+        public static float DistanceSquared(Vector2 value1, Vector2 value2)
+        {
+            float v1 = value1.X - value2.X, v2 = value1.Y - value2.Y;
+            return (v1 * v1) + (v2 * v2);
+        }
+
+        public static void DistanceSquared(ref Vector2 value1, ref Vector2 value2, out float result)
+        {
+            float v1 = value1.X - value2.X, v2 = value1.Y - value2.Y;
+            result = (v1 * v1) + (v2 * v2);
+        }
+
+
+        public static Vector2 Transform(Vector2 value, Quaternion rotation)
+        {
+            Transform(ref value, ref rotation, out value);
+            return value;
+        }
+
+        public static void Transform(ref Vector2 value, ref Quaternion rotation, out Vector2 result)
+        {
+            var rot1 = new Vector3(rotation.X + rotation.X, rotation.Y + rotation.Y, rotation.Z + rotation.Z);
+            var rot2 = new Vector3(rotation.X, rotation.X, rotation.W);
+            var rot3 = new Vector3(1, rotation.Y, rotation.Z);
+            var rot4 = rot1 * rot2;
+            var rot5 = rot1 * rot3;
+
+            var v = new Vector2();
+            v.X = (float)((double)value.X * (1.0 - (double)rot5.Y - (double)rot5.Z) + (double)value.Y * ((double)rot4.Y - (double)rot4.Z));
+            v.Y = (float)((double)value.X * ((double)rot4.Y + (double)rot4.Z) + (double)value.Y * (1.0 - (double)rot4.X - (double)rot5.Z));
+            result.X = v.X;
+            result.Y = v.Y;
+        }
+
+        public static Vector2 CatmullRom(Vector2 value1, Vector2 value2, Vector2 value3, Vector2 value4, float amount)
+        {
+            return new Vector2(
+                MathHelper.CatmullRom(value1.X, value2.X, value3.X, value4.X, amount),
+                MathHelper.CatmullRom(value1.Y, value2.Y, value3.Y, value4.Y, amount));
+        }
+
+
+
+    }
 }

+ 51 - 7
Script/AtomicNET/AtomicNET/Math/Vector3.cs

@@ -70,11 +70,18 @@ namespace AtomicEngine
 			Z = z;
 		}
 
-		/// <summary>
-		/// Constructs a new Vector3 from the given Vector2.
-		/// </summary>
-		/// <param name="v">The Vector2 to copy components from.</param>
-		public Vector3(Vector2 v)
+        public Vector3(Vector2 v2, float z)
+        {
+            X = v2.X;
+            Y = v2.Y;
+            Z = z;
+        }
+
+        /// <summary>
+        /// Constructs a new Vector3 from the given Vector2.
+        /// </summary>
+        /// <param name="v">The Vector2 to copy components from.</param>
+        public Vector3(Vector2 v)
 		{
 			X = v.X;
 			Y = v.Y;
@@ -1353,6 +1360,43 @@ namespace AtomicEngine
 				Z == other.Z;
 		}
 
-		#endregion
-	}
+        #endregion
+
+        public static Vector3 operator *(Vector3 value1, Vector3 value2)
+        {
+            value1.X *= value2.X;
+            value1.Y *= value2.Y;
+            value1.Z *= value2.Z;
+            return value1;
+        }
+
+
+        public static float Distance(Vector3 value1, Vector3 value2)
+        {
+            float result;
+            DistanceSquared(ref value1, ref value2, out result);
+            return (float)Math.Sqrt(result);
+        }
+
+        public static void Distance(ref Vector3 value1, ref Vector3 value2, out float result)
+        {
+            DistanceSquared(ref value1, ref value2, out result);
+            result = (float)Math.Sqrt(result);
+        }
+
+        public static float DistanceSquared(Vector3 value1, Vector3 value2)
+        {
+            return (value1.X - value2.X) * (value1.X - value2.X) +
+                    (value1.Y - value2.Y) * (value1.Y - value2.Y) +
+                    (value1.Z - value2.Z) * (value1.Z - value2.Z);
+        }
+
+        public static void DistanceSquared(ref Vector3 value1, ref Vector3 value2, out float result)
+        {
+            result = (value1.X - value2.X) * (value1.X - value2.X) +
+                     (value1.Y - value2.Y) * (value1.Y - value2.Y) +
+                     (value1.Z - value2.Z) * (value1.Z - value2.Z);
+        }
+
+    }
 }

+ 18 - 0
Script/AtomicNET/AtomicNET/Resource/ResourceCache.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+
+    public partial class ResourceCache : AObject
+    {
+
+        public T GetResource<T>(string path) where T : Resource
+        {
+            return (T) GetResource(typeof(T).Name, path);
+        }
+
+    }
+
+}

+ 86 - 12
Script/AtomicNET/AtomicNET/Scene/CSComponentCore.cs

@@ -24,8 +24,12 @@ namespace AtomicEngine
             }
 
             // Methods
-            Type[] parms = new Type[1] { typeof(float) };
-            UpdateMethod = type.GetMethod("Update", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Type.DefaultBinder, parms, null);
+            Type[] updateParms = new Type[1] { typeof(float) };
+            UpdateMethod = type.GetMethod("Update", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Type.DefaultBinder, updateParms, null);
+
+            Type[] startParms = new Type[0] { };
+            StartMethod = type.GetMethod("Start", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Type.DefaultBinder, startParms, null);
+
         }
 
         public void ApplyFieldValues(CSComponent component, IntPtr fieldValuePtr)
@@ -93,38 +97,108 @@ namespace AtomicEngine
         public void RegisterInstance(CSComponent component)
         {
             Instances.Add(component);
+
+            if (StartMethod != null)
+            {
+                StartList.Add(component);
+            }
+
         }
 
+        public void Update(Object[] args)
+        {
+            if (StartMethod != null)
+            {
+                foreach (var instance in StartList)
+                {
+                    var node = instance.Node;
+
+                    if (node != null && node.IsEnabled())
+                    {
+                        if (node.Scene != null)
+                        {
+                            StartMethod.Invoke(instance, null);
+                        }
+                    }
+
+                }
+
+                // TODO: need to handle delayed starts when node isn't enabled
+                StartList.Clear();
+
+            }
+
+            if (UpdateMethod != null)
+            {
+                foreach (var instance in Instances)
+                {
+                    bool remove = false;
+
+                    var node = instance.Node;
+
+                    if (node != null && node.IsEnabled())
+                    {
+
+                        if (node.Scene != null)
+                        {
+                            UpdateMethod.Invoke(instance, args);
+                        }
+                        else
+                        {
+                            remove = true;
+                        }
+                    }
+                    else
+                    {
+                        remove = true;
+                    }
+
+                    if (remove)
+                        RemoveList.Add(instance);
+                }
+            }
+
+            foreach (var instance in RemoveList)
+            {
+                Instances.Remove(instance);                
+            }
+
+            RemoveList.Clear();
+        }
 
         public List<CSComponent> Instances = new List<CSComponent>();
+        public List<CSComponent> StartList = new List<CSComponent>();
+
+        public List<CSComponent> RemoveList = new List<CSComponent>();
+
         public FieldInfo[] InspectorFields;
         public Type Type;
+
         public MethodInfo UpdateMethod = null;
+        public MethodInfo StartMethod = null;
 
         ScriptVariantMap fieldMap = new ScriptVariantMap();
 
         public Dictionary<uint, FieldInfo> fieldLookup = new Dictionary<uint, FieldInfo>();
     }
 
-    internal class CSComponentCore : NETScriptObject
+    public class CSComponentCore : NETScriptObject
     {
 
+        public static void RegisterInstance(CSComponent component)
+        {
+            instance.csinfoLookup[component.GetType()].RegisterInstance(component);
+        }
+
         void HandleUpdate(uint eventType, ScriptVariantMap eventData)
         {
             Object[] args = new Object[1] { eventData.GetFloat("timestep") };
 
             foreach (var csinfo in csinfoLookup.Values)
             {
-                var updateMethod = csinfo.UpdateMethod;
-
-                if (updateMethod != null)
-                {
-                    foreach (var instance in csinfo.Instances)
-                    {
-                        updateMethod.Invoke(instance, args);
-                    }
-                }
+                csinfo.Update(args);
             }
+
         }
 
         void HandleComponentLoad(uint eventType, ScriptVariantMap eventData)

+ 29 - 0
Script/AtomicNET/AtomicNET/Scene/Node.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace AtomicEngine
+{
+
+    public partial class Node : Animatable
+    {
+
+        public T CreateComponent<T>(CreateMode mode = CreateMode.REPLICATED, uint id = 0) where T : Component
+        {
+            var type = typeof(T);
+
+            if (type.IsSubclassOf(typeof(CSComponent)))
+            {
+                Component component = (Component)Activator.CreateInstance(type);
+                CSComponentCore.RegisterInstance((CSComponent) component);
+                AddComponent(component, id, mode);
+                return (T) component;
+            }
+
+            return (T) CreateComponent(type.Name, mode, id);
+        }
+
+
+    }
+
+}

+ 1 - 1
Script/AtomicNET/AtomicPlayer/Program.cs

@@ -6,7 +6,7 @@ public class Program
     public static void Main(string[] args)
     {               
         // Create the Application
-        var app = NETIPCPlayerApp.Create();
+        var app = NETIPCPlayerApp.Create(args);
 
         // Managed code in charge of main loop
         while (app.RunFrame())

+ 1 - 1
Script/Packages/Atomic/Atomic3D.json

@@ -3,7 +3,7 @@
 	"sources" : ["Source/Atomic/Atomic3D"],
 	"requires" : ["3D"],
 	"includes" : ["<Atomic/Container/ArrayPtr.h>","<Atomic/Scene/Scene.h>", "<Atomic/Atomic3D/Animation.h>", "<Atomic/Graphics/OcclusionBuffer.h>",
-			      "<Atomic/Scene/ValueAnimation.h>", "<Atomic/Graphics/Material.h>", "<Atomic/Resource/Image.h>"],
+			      "<Atomic/Scene/ValueAnimation.h>", "<Atomic/Graphics/Material.h>", "<Atomic/Resource/Image.h>", "<Atomic/Graphics/VertexBuffer.h>"],
 	"classes" : ["Model",
 				 "StaticModel",
 				 "Animation", "AnimatedModel", "AnimationController", "AnimationState", "Billboard", "BillboardSet", "CustomGeometry",

+ 4 - 2
Script/Packages/Atomic/Graphics.json

@@ -7,7 +7,8 @@
 				 "DebugRenderer",
 				 "RenderSurface", "Shader", "ShaderPrecache", "ShaderVariation",
 				 "Pass", "Technique",
-				 "Texture3D", "TextureCube", "View"],
+				 "Texture3D", "TextureCube", "View",
+			 	 "VertexBuffer"],
 	"overloads" : {
 		"Viewport" : {
 			"Viewport" : ["Context", "Scene", "Camera", "RenderPath"],
@@ -17,7 +18,8 @@
 			"SetOrthoSize" : ["float"]
 		},
 		"Graphics" : {
-			"SetWindowPosition" : ["int", "int"]
+			"SetWindowPosition" : ["int", "int"],
+			"Draw" : ["PrimitiveType", "unsigned", "unsigned"]
 		}
 
 	},

+ 1 - 1
Script/Packages/ToolCore/ToolCore.json

@@ -8,7 +8,7 @@
 							 "PlatformWindows", "PlatformAndroid", "PlatformIOS", "Command", "PlayCmd", "OpenAssetImporter",
 							 "Asset", "AssetDatabase", "AssetImporter", "AudioImporter", "ModelImporter", "MaterialImporter", "AnimationImportInfo",
 							 "PrefabImporter", "JavascriptImporter", "JSONImporter",
-							 "TextureImporter", "SpriterImporter", "PEXImporter", "NETAssemblyImporter",
+							 "TextureImporter", "SpriterImporter", "PEXImporter", "CSharpImporter", "NETAssemblyImporter",
 							 "LicenseSystem",
 						 	 "ProjectUserPrefs", "ProjectBuildSettings",
 						 	 "BuildBase", "BuildSystem", "BuildMac", "BuildWeb", "BuildWindows", "BuildAndroid", "BuildIOS",

+ 3 - 0
Script/tsconfig.json

@@ -29,6 +29,7 @@
         "./AtomicEditor/editor/Preferences.ts",
         "./AtomicEditor/hostExtensions/coreExtensions/ProjectBasedExtensionLoader.ts",
         "./AtomicEditor/hostExtensions/HostExtensionServices.ts",
+        "./AtomicEditor/hostExtensions/languageExtensions/CSharpLanguageExtension.ts",
         "./AtomicEditor/hostExtensions/languageExtensions/TypscriptLanguageExtension.ts",
         "./AtomicEditor/hostExtensions/ServiceLocator.ts",
         "./AtomicEditor/main.ts",
@@ -94,6 +95,7 @@
         "./AtomicEditor/ui/playmode/PlayMode.ts",
         "./AtomicEditor/ui/ResourceEditorProvider.ts",
         "./AtomicEditor/ui/resourceEditors/AbstractTextResourceEditorBuilder.ts",
+        "./AtomicEditor/ui/resourceEditors/CSharpResourceEditorBuilder.ts",
         "./AtomicEditor/ui/resourceEditors/JavascriptResourceEditorBuilder.ts",
         "./AtomicEditor/ui/resourceEditors/JsonResourceEditorBuilder.ts",
         "./AtomicEditor/ui/resourceEditors/Scene3dResourceEditorBuilder.ts",
@@ -106,6 +108,7 @@
         "./AtomicEditor/ui/Shortcuts.ts",
         "./AtomicEditor/ui/UIEvents.ts",
         "./TypeScript/Atomic.d.ts",
+        "./TypeScript/AtomicApp.d.ts",
         "./TypeScript/AtomicNETScript.d.ts",
         "./TypeScript/AtomicPlayer.d.ts",
         "./TypeScript/AtomicWork.d.ts",

+ 6 - 0
Source/Atomic/Graphics/GraphicsEvents.h

@@ -99,6 +99,12 @@ EVENT(E_ENDVIEWRENDER, EndViewRender)
     PARAM(P_CAMERA, Camera);                // Camera pointer
 }
 
+/// A render path event has occurred.
+EVENT(E_RENDERPATHEVENT, RenderPathEvent)
+{
+    PARAM(P_NAME, Name);                    // String
+}
+
 /// Graphics context has been lost. Some or all (depending on the API) GPU objects have lost their contents.
 EVENT(E_DEVICELOST, DeviceLost)
 {

+ 5 - 0
Source/Atomic/Graphics/RenderPath.cpp

@@ -46,6 +46,7 @@ static const char* commandTypeNames[] =
     "forwardlights",
     "lightvolumes",
     "renderui",
+    "sendevent",
     0
 };
 
@@ -180,6 +181,10 @@ void RenderPathCommand::Load(const XMLElement& element)
         }
         break;
 
+    case CMD_SENDEVENT:
+        eventName_ = element.GetAttribute("name");
+        break;
+
     default:
         break;
     }

+ 4 - 1
Source/Atomic/Graphics/RenderPath.h

@@ -43,7 +43,8 @@ enum RenderCommandType
     CMD_QUAD,
     CMD_FORWARDLIGHTS,
     CMD_LIGHTVOLUMES,
-    CMD_RENDERUI
+    CMD_RENDERUI,
+    CMD_SENDEVENT
 };
 
 /// Rendering path sorting modes.
@@ -199,6 +200,8 @@ struct RenderPathCommand
     bool useLitBase_;
     /// Vertex lights flag.
     bool vertexLights_;
+    /// Event name.
+    String eventName_;
 };
 
 /// Rendering path definition.

+ 11 - 0
Source/Atomic/Graphics/View.cpp

@@ -1619,6 +1619,17 @@ void View::ExecuteRenderPathCommands()
                 }
                 break;
 
+            case CMD_SENDEVENT:
+            {
+                using namespace RenderPathEvent;
+
+                VariantMap& eventData = GetEventDataMap();
+                eventData[P_NAME] = command.eventName_;
+                renderer_->SendEvent(E_RENDERPATHEVENT, eventData);
+            }
+            break;
+
+
             default:
                 break;
             }

+ 2 - 0
Source/AtomicEditor/Application/AEEditorApp.cpp

@@ -31,6 +31,7 @@
 #include <ToolCore/License/LicenseSystem.h>
 #include <ToolCore/ToolSystem.h>
 #include <ToolCore/ToolEnvironment.h>
+#include <ToolCore/NETTools/NETBuildSystem.h>
 
 #include "../EditorMode/AEEditorMode.h"
 #include "../EditorMode/AEEditorNETService.h"
@@ -156,6 +157,7 @@ namespace AtomicEditor
 #endif
 
         context_->RegisterSubsystem(new EditorMode(context_));
+        context_->RegisterSubsystem(new NETBuildSystem(context_));
         context_->RegisterSubsystem(new EditorNETService(context_));
 
         vm_->SetModuleSearchPaths("AtomicEditor/JavaScript;AtomicEditor/EditorScripts;AtomicEditor/EditorScripts/AtomicEditor");

+ 16 - 6
Source/AtomicEditor/EditorMode/AEEditorMode.cpp

@@ -127,16 +127,26 @@ void EditorMode::HandleIPCJSError(StringHash eventType, VariantMap& eventData)
 
 bool EditorMode::PlayProject(String addArgs, bool debug)
 {
-    ToolEnvironment* env = GetSubsystem<ToolEnvironment>();
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
     ToolSystem* tsystem = GetSubsystem<ToolSystem>();
-
-    const String& editorBinary = env->GetEditorBinary();
-
     Project* project = tsystem->GetProject();
 
     if (!project)
         return false;
 
+    ToolEnvironment* env = GetSubsystem<ToolEnvironment>();
+
+    String playerBinary = env->GetEditorBinary();
+
+    // TODO: We need to configure project as managed
+    bool managed = false;
+    if (fileSystem->FileExists(project->GetResourcePath() + "AtomicProject.dll"))
+    {
+        managed = true;
+        playerBinary = env->GetAtomicNETManagedPlayerBinary();
+    }
+
+
     Vector<String> paths;
     paths.Push(env->GetCoreDataDir());
     paths.Push(env->GetPlayerDataDir());
@@ -163,10 +173,10 @@ bool EditorMode::PlayProject(String addArgs, bool debug)
 
     String dump;
     dump.Join(vargs, " ");
-    LOGINFOF("Launching Broker %s %s", editorBinary.CString(), dump.CString());
+    LOGINFOF("Launching Broker %s %s", playerBinary.CString(), dump.CString());
 
     IPC* ipc = GetSubsystem<IPC>();
-    playerBroker_ = ipc->SpawnWorker(editorBinary, vargs);
+    playerBroker_ = ipc->SpawnWorker(playerBinary, vargs);
 
     if (playerBroker_)
     {

+ 50 - 0
Source/AtomicNET/NETNative/NETCInterop.cpp

@@ -1,6 +1,12 @@
+
 #include <Atomic/Script/ScriptVariantMap.h>
 #include <Atomic/IPC/IPC.h>
 
+#include <Atomic/Graphics/VertexBuffer.h>
+#include <Atomic/Graphics/Viewport.h>
+#include <Atomic/Graphics/Graphics.h>
+
+
 #include "NETCore.h"
 
 #ifdef ATOMIC_PLATFORM_WINDOWS
@@ -9,6 +15,8 @@
 #define ATOMIC_EXPORT_API
 #endif
 
+// TODO: Split into separate module files
+
 namespace Atomic
 {
 
@@ -23,6 +31,14 @@ namespace Atomic
             return refCounted->GetClassID();
         }
 
+        ATOMIC_EXPORT_API void csb_AtomicEngine_ReleaseRef(RefCounted* refCounted)
+        {
+            if (!refCounted)
+                return;
+
+            refCounted->ReleaseRef();
+        }
+
         ATOMIC_EXPORT_API void csb_Atomic_AObject_SendEvent(Object* obj, const char* eventType, ScriptVariantMap* vmap)
         {
             obj->SendEvent(eventType, vmap ? vmap->GetVariantMap() : obj->GetEventDataMap());
@@ -88,6 +104,40 @@ namespace Atomic
 
         }
 
+        ATOMIC_EXPORT_API void* csb_Atomic_VertexBuffer_Lock(VertexBuffer* vb , unsigned start, unsigned count, bool discard)
+        {
+            if (!vb)
+                return nullptr;
+
+            return vb->Lock(start, count, discard);
+
+        }
+
+        ATOMIC_EXPORT_API void csb_Atomic_Graphics_SetShaderParameter_Matrix3x4(Graphics* graphics, const char* param, Matrix3x4* matrix)
+        {
+            if (!graphics || !param || !strlen(param))
+                return;
+
+            graphics->SetShaderParameter(param, *matrix);
+        }
+
+        ATOMIC_EXPORT_API void csb_Atomic_Graphics_SetShaderParameter_Color(Graphics* graphics, const char* param, Color* color)
+        {
+            if (!graphics || !param || !strlen(param) || !color)
+                return;
+
+            graphics->SetShaderParameter(param, *color);
+        }
+
+
+        ATOMIC_EXPORT_API void csb_Atomic_Viewport_SetRenderPath_RenderPath(Viewport* viewport, RenderPath* renderPath)
+        {
+            if (!viewport)
+                return;
+
+            viewport->SetRenderPath(renderPath);
+        }
+
     }
 }
 

+ 2 - 0
Source/AtomicNET/NETNative/NETCore.cpp

@@ -12,6 +12,7 @@ namespace Atomic
 
 SharedPtr<Context> NETCore::csContext_;
 NETCoreEventDispatchFunction NETCore::eventDispatch_ = nullptr;
+NETCoreUpdateDispatchFunction NETCore::updateDispatch_ = nullptr;
 
 NETCore::NETCore(Context* context, NETCoreDelegates* delegates) :
     Object(context)
@@ -20,6 +21,7 @@ NETCore::NETCore(Context* context, NETCoreDelegates* delegates) :
     csContext_ = context;
 
     eventDispatch_ = delegates->eventDispatch;
+    updateDispatch_ = delegates->updateDispatch;
 
     NETEventDispatcher* dispatcher = new NETEventDispatcher(context_);
     context_->RegisterSubsystem(dispatcher);

+ 4 - 0
Source/AtomicNET/NETNative/NETCore.h

@@ -29,10 +29,12 @@ namespace Atomic
 {
 
 typedef void (*NETCoreEventDispatchFunction)(unsigned eventID, VariantMap* eventData);
+typedef void(*NETCoreUpdateDispatchFunction)(float timeStep);
 
 struct NETCoreDelegates
 {
     NETCoreEventDispatchFunction eventDispatch;
+    NETCoreUpdateDispatchFunction updateDispatch;
 };
 
 class ATOMIC_API NETCore : public Object
@@ -53,6 +55,7 @@ public:
     static void RegisterNETEventType(unsigned eventType);
 
     inline static void DispatchEvent(unsigned eventID, VariantMap* eventData = nullptr) { eventDispatch_(eventID, eventData); }
+    inline static void DispatchUpdateEvent(float timeStep) { if (updateDispatch_) updateDispatch_(timeStep); }
 
     /// We access this directly in binding code, where there isn't a context
     /// to get a reference from
@@ -62,6 +65,7 @@ private:
 
     static SharedPtr<Context> csContext_;
 
+    static NETCoreUpdateDispatchFunction updateDispatch_;
     static NETCoreEventDispatchFunction eventDispatch_;
 
 };

+ 7 - 0
Source/AtomicNET/NETNative/NETEventDispatcher.cpp

@@ -20,6 +20,8 @@
 // THE SOFTWARE.
 //
 
+#include <Atomic/Core/CoreEvents.h>
+
 #include "NETCore.h"
 #include "NETEventDispatcher.h"
 
@@ -39,6 +41,11 @@ namespace Atomic
 
     void NETEventDispatcher::BeginSendEvent(Context* context, Object* sender, StringHash eventType, VariantMap& eventData)
     {
+        if (eventType == E_UPDATE)
+        {
+            NETCore::DispatchUpdateEvent(eventData[Update::P_TIMESTEP].GetFloat());
+        }
+
         if (!netEvents_.Contains(eventType))
             return;
 

+ 6 - 3
Source/AtomicNET/NETNative/NETIPCPlayerApp.cpp

@@ -83,14 +83,17 @@ namespace Atomic
 
     bool NETIPCPlayerApp::RunFrame()
     {
+        engine_->RunFrame();
+
         if (engine_->IsExiting())
+        {            
             return false;
-
-        engine_->RunFrame();
+        }
 
         return true;
+        
     }
-
+    
     void NETIPCPlayerApp::Shutdown()
     {
         Stop();

+ 4 - 2
Source/AtomicNET/NETNative/NETServiceApplication.cpp

@@ -89,10 +89,12 @@ namespace Atomic
 
     bool NETServiceApplication::RunFrame()
     {
+        engine_->RunFrame();
+
         if (engine_->IsExiting())
+        {
             return false;
-
-        engine_->RunFrame();
+        }
 
         return true;
     }

+ 1 - 1
Source/AtomicNET/NETScript/CSComponent.cpp

@@ -40,7 +40,7 @@ extern const char* LOGIC_CATEGORY;
 CSComponent::CSComponent(Context* context) :
     ScriptComponent(context)
 {
-
+    
 }
 
 CSComponent::~CSComponent()

+ 7 - 0
Source/AtomicNET/NETScript/NETScriptEvents.h

@@ -41,4 +41,11 @@ namespace Atomic
         PARAM(P_FIELDVALUES, FieldValues);  // VariantMap as void*
     }
 
+    EVENT(E_CSCOMPONENTASSEMBLYCHANGED, CSComponentAssemblyChanged)
+    {
+        PARAM(P_RESOURCE, Resource); // resource* 
+        PARAM(P_ASSEMBLYPATH, AssemblyPath); // String
+    }
+
+
 }

+ 5 - 0
Source/ToolCore/Assets/Asset.cpp

@@ -43,6 +43,7 @@
 #include "TextImporter.h"
 #include "TypeScriptImporter.h"
 #include "ParticleEffectImporter.h"
+#include "CSharpImporter.h"
 #include "NETAssemblyImporter.h"
 
 #include "AssetEvents.h"
@@ -326,6 +327,10 @@ bool Asset::CreateImporter()
         {
             importer_ = new NETAssemblyImporter(context_, this);
         }
+        else if (ext == ".cs")
+        {
+            importer_ = new CSharpImporter(context_, this);
+        }
         else if (textureFormats.Contains(ext))
         {
             importer_ = new TextureImporter(context_, this);

+ 93 - 0
Source/ToolCore/Assets/CSharpImporter.cpp

@@ -0,0 +1,93 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/File.h>
+#include <Atomic/Resource/ResourceCache.h>
+
+#include "../ToolSystem.h"
+#include "../Project/Project.h"
+#include "../NETTools/NETBuildSystem.h"
+
+#include "Asset.h"
+#include "AssetDatabase.h"
+#include "CSharpImporter.h"
+
+
+namespace ToolCore
+{
+
+    CSharpImporter::CSharpImporter(Context* context, Asset *asset) : AssetImporter(context, asset)
+    {
+        requiresCacheFile_ = false;
+    }
+
+    CSharpImporter::~CSharpImporter()
+    {
+
+    }
+
+    void CSharpImporter::SetDefaults()
+    {
+        AssetImporter::SetDefaults();
+    }
+
+    bool CSharpImporter::Import()
+    {
+        using namespace NETBuildAtomicProject;
+
+        VariantMap eventData;
+        eventData[P_PROJECT] = GetSubsystem<ToolSystem>()->GetProject();
+
+        // We need to rebuild the project assembly
+        SendEvent(E_NETBUILDATOMICPROJECT, eventData);
+
+        return true;
+    }
+
+    bool CSharpImporter::LoadSettingsInternal(JSONValue& jsonRoot)
+    {
+        if (!AssetImporter::LoadSettingsInternal(jsonRoot))
+            return false;
+
+        JSONValue import = jsonRoot.Get("CSharpImporter");       
+        return true;
+    }
+
+    bool CSharpImporter::SaveSettingsInternal(JSONValue& jsonRoot)
+    {
+        if (!AssetImporter::SaveSettingsInternal(jsonRoot))
+            return false;
+
+        JSONValue import;
+        jsonRoot.Set("CSharpImporter", import);
+
+        return true;
+    }
+
+    Resource* CSharpImporter::GetResource(const String& typeName)
+    {
+        return 0;
+
+    }
+
+}

+ 12 - 22
Source/ToolCore/NETTools/NETCompile.h → Source/ToolCore/Assets/CSharpImporter.h

@@ -22,40 +22,30 @@
 
 #pragma once
 
-#include <Atomic/Core/Object.h>
-
-using namespace Atomic;
+#include "AssetImporter.h"
 
 namespace ToolCore
 {
 
-    EVENT(E_NETCOMPILERESULT, NETCompilerResult)
-    {
-        PARAM(P_SUCCESS, Success); // bool = success = true;
-        PARAM(P_ERRORTEXT, ErrorText); // for failure, the compilation output
-    }
-
-    class NETCompile : public Object
+    class CSharpImporter : public AssetImporter
     {
-
-    // NET solution compilation, in ToolCore for native compilation requirements
-        OBJECT(NETCompile)
+        OBJECT(CSharpImporter)
 
     public:
+        /// Construct.
+        CSharpImporter(Context* context, Asset* asset);
+        virtual ~CSharpImporter();
 
-        NETCompile(Context* context);
-        virtual ~NETCompile();
+        virtual void SetDefaults();
 
-        bool Compile(const String& solutionPath, const String& configuration = "Release");
+        Resource* GetResource(const String& typeName = String::EMPTY);
 
-    private:
+    protected:
 
-        void HandleSubprocessOutput(StringHash eventType, VariantMap& eventData);
-        void HandleCompileProcessComplete(StringHash eventType, VariantMap& eventData);
+        bool Import();
 
-        String allArgs_;
-        String solutionPath_;
-        String compileText_;
+        virtual bool LoadSettingsInternal(JSONValue& jsonRoot);
+        virtual bool SaveSettingsInternal(JSONValue& jsonRoot);
 
     };
 

+ 7 - 0
Source/ToolCore/Assets/NETAssemblyImporter.cpp

@@ -26,6 +26,7 @@
 #include <Atomic/Resource/ResourceCache.h>
 #include <Atomic/Resource/Image.h>
 
+#include <AtomicNET/NETScript/NETScriptEvents.h>
 #include <AtomicNET/NETScript/CSComponentAssembly.h>
 
 #include "Asset.h"
@@ -84,6 +85,12 @@ namespace ToolCore
                     else
                     {
                         asset_->Save();
+
+                        using namespace CSComponentAssemblyChanged;
+                        VariantMap assemblyChangedEventData;
+                        assemblyChangedEventData[P_RESOURCE] = asset_->GetResource();
+                        assemblyChangedEventData[P_ASSEMBLYPATH] = asset_->GetPath();        
+                        SendEvent(E_CSCOMPONENTASSEMBLYCHANGED, assemblyChangedEventData);
                     }
                 }
                 else

+ 0 - 3
Source/ToolCore/Assets/TypeScriptImporter.cpp

@@ -23,15 +23,12 @@
 #include <Atomic/IO/Log.h>
 #include <Atomic/IO/File.h>
 #include <Atomic/Resource/ResourceCache.h>
-#include <Atomic/Resource/Image.h>
-
 #include <AtomicJS/Javascript/JSComponentFile.h>
 
 #include "Asset.h"
 #include "AssetDatabase.h"
 #include "TypeScriptImporter.h"
 
-
 namespace ToolCore
 {
 

+ 25 - 9
Source/ToolCore/Command/NETCmd.cpp

@@ -35,8 +35,8 @@
 
 #include "NETCmd.h"
 #include "../NETTools/AtomicNETService.h"
+#include "../NETTools/NETBuildSystem.h"
 #include "../NETTools/NETProjectGen.h"
-#include "../NETTools/NETCompile.h"
 
 namespace ToolCore
 {
@@ -104,7 +104,8 @@ bool NETCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String&
     else if (command_ == "compile")
     {
         solutionPath_ = startIndex + 2 < arguments.Size() ? arguments[startIndex + 2] : String::EMPTY;
-        configuration_ = startIndex + 3 < arguments.Size() ? arguments[startIndex + 3] : "Release";
+        platform_ = startIndex + 3 < arguments.Size() ? arguments[startIndex + 3] : String::EMPTY;
+        configuration_ = startIndex + 4 < arguments.Size() ? arguments[startIndex + 4] : "Release";
 
         bool exists = false;
 
@@ -120,6 +121,12 @@ bool NETCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String&
             return false;
         }
 
+        if (!platform_.Length())
+        {
+            errorMsg = "Platform not specified";
+            return false;
+        }
+
         return true;
     }
     else
@@ -132,13 +139,13 @@ bool NETCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String&
     return true;
 }
 
-void NETCmd::HandleNETCompileResult(StringHash eventType, VariantMap& eventData)
+void NETCmd::HandleNETBuildResult(StringHash eventType, VariantMap& eventData)
 {
-    using namespace NETCompilerResult;
+    using namespace NETBuildResult;
 
     if (eventData[P_SUCCESS].GetBool())
     {
-        LOGINFOF("NETCompile Success for solution: %s", solutionPath_.CString());
+        LOGINFOF("NETBuild Success for solution: %s", solutionPath_.CString());
         Finished();
     }
     else
@@ -146,7 +153,7 @@ void NETCmd::HandleNETCompileResult(StringHash eventType, VariantMap& eventData)
         const String& errorText = eventData[P_ERRORTEXT].GetString();
 
         LOGERRORF("\n%s\n", errorText.CString());
-        Error(ToString("NETCompile Error for solution: %s", solutionPath_.CString()));
+        Error(ToString("NETBuild Error for solution: %s", solutionPath_.CString()));
         Finished();
     }
     
@@ -186,11 +193,20 @@ void NETCmd::Run()
     }
     else if (command_ == "compile")
     {
-        compiler_ = new NETCompile(context_);
 
-        SubscribeToEvent(E_NETCOMPILERESULT, HANDLER(NETCmd, HandleNETCompileResult));
+        NETBuildSystem* buildSystem = new NETBuildSystem(context_);
+        context_->RegisterSubsystem(buildSystem);
+       
+        NETBuild* build = buildSystem->Build(solutionPath_, platform_, configuration_);
+
+        if (!build)
+        {
+            Error("Unable to start build");
+            Finished();
+            return;
+        }
 
-        compiler_->Compile(solutionPath_, configuration_);
+        build->SubscribeToEvent(E_NETBUILDRESULT, HANDLER(NETCmd, HandleNETBuildResult));        
 
     }
     else if (command_ == "genproject")

+ 2 - 3
Source/ToolCore/Command/NETCmd.h

@@ -30,7 +30,6 @@ namespace ToolCore
 {
 
 class AtomicNETService;
-class NETCompile;
 
 class NETCmd: public Command
 {
@@ -51,7 +50,7 @@ public:
 
 private:
 
-    void HandleNETCompileResult(StringHash eventType, VariantMap& eventData);
+    void HandleNETBuildResult(StringHash eventType, VariantMap& eventData);
 
     String command_;
 
@@ -63,8 +62,8 @@ private:
     String assemblyPath_;
 
     // compile
-    SharedPtr<NETCompile> compiler_;
     String solutionPath_;
+    String platform_;
     String configuration_;
 
     WeakPtr<AtomicNETService> netService_;

+ 12 - 2
Source/ToolCore/JSBind/CSharp/CSFunctionWriter.cpp

@@ -257,7 +257,7 @@ void CSFunctionWriter::WriteNativeFunction(String& source)
 
     if (sharedPtrReturn)
     {
-        source += IndentLine("returnValue->AddRef();\n");
+        source += IndentLine("if (returnValue.NotNull()) returnValue->AddRef();\n");
         source += IndentLine("return returnValue;\n");
     }
     else if (returnType == "const char*")
@@ -313,11 +313,19 @@ void CSFunctionWriter::WriteManagedPInvokeFunctionSignature(String& source)
 
     String line = "[DllImport (Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n";
     source += IndentLine(line);
+
     JSBClass* klass = function_->GetClass();
     JSBPackage* package = klass->GetPackage();
 
     String returnType = CSTypeHelper::GetPInvokeTypeString(function_->GetReturnType());
 
+    if (returnType == "bool")
+    {
+        // default boolean marshal is 4 byte windows type BOOL and not 1 byte bool
+        // https://blogs.msdn.microsoft.com/jaredpar/2008/10/14/pinvoke-and-bool-or-should-i-say-bool/        
+        source += IndentLine("[return: MarshalAs(UnmanagedType.I1)]\n");
+    }
+
     if (returnType == "string")
         returnType = "IntPtr";
 
@@ -478,7 +486,9 @@ void CSFunctionWriter::WriteManagedConstructor(String& source)
 
     source += IndentLine(ToString("var classType = typeof(%s);\n", klass->GetName().CString()));
     source += IndentLine("var thisType = this.GetType();\n");
-    source += IndentLine("if ( thisType == classType || thisType.BaseType == classType)\n");
+    source += IndentLine("var nativeThisType = NativeCore.IsNativeType(thisType);\n");    
+    source += IndentLine("var nativeBaseType = NativeCore.IsNativeType(thisType.BaseType);\n");
+    source += IndentLine("if ( (nativeThisType && (thisType == classType)) || (!nativeThisType && (nativeBaseType && (thisType.BaseType == classType))))\n");
     source += IndentLine("{\n");
 
     Indent();

+ 9 - 0
Source/ToolCore/JSBind/CSharp/CSModuleWriter.cpp

@@ -221,6 +221,15 @@ void CSModuleWriter::GenerateManagedEnumsAndConstants(String& source)
             String name = (*itr).first_;
             String value = (*itr).second_;
 
+            // BodyType2D enum order is assigned in RigidBody2D.h in incorrect order
+            if (jenum->GetName() == "BodyType2D")
+            {                 
+                if (name == "BT_KINEMATIC")
+                    value = "1";
+                else if (name == "BT_DYNAMIC")
+                    value = "2";
+            }
+
             if (value.Length())
             {
                 line = name + " = " + value;

+ 6 - 3
Source/ToolCore/JSBind/CSharp/CSTypeHelper.cpp

@@ -20,7 +20,7 @@
 // THE SOFTWARE.
 //
 
-
+#include <Atomic/Core/ProcessUtils.h>
 #include "../JSBPackage.h"
 #include "CSTypeHelper.h"
 
@@ -143,12 +143,15 @@ String CSTypeHelper::GetManagedPrimitiveType(JSBPrimitiveType* ptype)
         return "float";
     if (ptype->kind_ == JSBPrimitiveType::Char && ptype->isUnsigned_)
         return "byte";
-    else if (ptype->kind_ == JSBPrimitiveType::Char)
+    if (ptype->kind_ == JSBPrimitiveType::Char)
         return "char";
     if (ptype->kind_ == JSBPrimitiveType::Short)
         return "short";
+    if (ptype->kind_ == JSBPrimitiveType::LongLong)
+        return "long";
+
+    ErrorDialog("CSTypeHelper::GetManagedPrimitiveType", ToString("%i", ptype->kind_));
 
-    assert(0);
     return "";
 }
 

+ 8 - 1
Source/ToolCore/NETTools/AtomicNETService.cpp

@@ -61,7 +61,14 @@ namespace ToolCore
 
         ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
 
-        execPath = tenv->GetRootSourceDir() + "Artifacts/AtomicNET/Debug/AtomicNETService.exe";
+#ifdef _DEBUG
+
+        String config = "Debug";
+#else
+        String config = "Release";
+#endif
+
+        execPath = tenv->GetRootSourceDir() + "Artifacts/AtomicNET/" + config + "/AtomicNETService.exe";
 
 #elif ATOMIC_PLATFORM_OSX
 

+ 412 - 0
Source/ToolCore/NETTools/NETBuildSystem.cpp

@@ -0,0 +1,412 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#include <Poco/Exception.h>
+#include <Poco/Environment.h>
+
+
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/FileSystem.h>
+
+#include "../ToolEvents.h"
+#include "../ToolSystem.h"
+#include "../ToolEnvironment.h"
+#include "../Subprocess/SubprocessSystem.h"
+#include "../Project/Project.h"
+
+#include "NETProjectGen.h"
+#include "NETBuildSystem.h"
+
+namespace ToolCore
+{
+
+    NETBuild::NETBuild(Context* context, const String& solutionPath, const String& platform, const String& configuration) :
+        Object(context),
+        solutionPath_(solutionPath),
+        platform_(platform),
+        configuration_(configuration),
+        status_(NETBUILD_PENDING)
+    {
+
+    }
+
+    NETBuildSystem::NETBuildSystem(Context* context) :
+        Object(context)
+    {
+        SubscribeToEvent(E_TOOLUPDATE, HANDLER(NETBuildSystem, HandleToolUpdate));
+        SubscribeToEvent(E_NETBUILDATOMICPROJECT, HANDLER(NETBuildSystem, HandleBuildAtomicProject));
+    }
+
+    NETBuildSystem::~NETBuildSystem()
+    {
+
+    }
+
+    void NETBuildSystem::HandleSubprocessOutput(StringHash eventType, VariantMap& eventData)
+    {
+        if (curBuild_.Null())
+        {
+            LOGERRORF("NETBuildSystem::HandleSubprocessOutput - output received without current build");
+            return;
+        }
+
+        const String& text = eventData[SubprocessOutput::P_TEXT].GetString();
+
+        // LOGINFOF(text.CString());
+
+        curBuild_->output_ += text;
+
+    }
+
+    void NETBuildSystem::HandleCompileProcessComplete(StringHash eventType, VariantMap& eventData)
+    {
+        UnsubscribeFromEvent(E_SUBPROCESSCOMPLETE);
+        UnsubscribeFromEvent(E_SUBPROCESSOUTPUT);
+
+        if (curBuild_.Null())
+        {
+            LOGERROR("NETBuildSystem::HandleCompileProcessComplete - called with no current build");
+            return;
+        }
+        curBuild_->status_ = NETBUILD_COMPLETE;
+
+        int code = eventData[SubprocessComplete::P_RETCODE].GetInt();
+
+        using namespace NETBuildResult;
+        VariantMap buildEventData;
+
+        buildEventData[P_BUILD] = curBuild_;
+
+        bool success = true;
+        String errorMsg;
+
+        if (!code)
+        {
+            if (curBuild_->project_.NotNull())
+            {
+                // Copy compiled assembly to resource path
+                String srcAssembly = AddTrailingSlash(curBuild_->project_->GetProjectPath()) + "AtomicNET/Bin/";
+                srcAssembly += curBuild_->configuration_;
+                srcAssembly += "/AtomicProject.dll";
+
+                String dstAssembly = AddTrailingSlash(curBuild_->project_->GetResourcePath()) + "AtomicProject.dll";
+
+                FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+                bool result = fileSystem->Copy(srcAssembly, dstAssembly);
+
+                if (!result)
+                {
+                    success = false;
+                    errorMsg = ToString("BuildBase::BuildCopyFile: Unable to copy assembly %s -> %s", srcAssembly.CString(), dstAssembly.CString());
+                    errorMsg += ToString("\nCompilation Command: %s", curBuild_->allArgs_.CString());
+                }
+
+            }
+            
+        }
+        else
+        {
+            success = false;
+            errorMsg = curBuild_->output_;
+            errorMsg += ToString("\nCompilation Command: %s", curBuild_->allArgs_.CString());
+        }
+
+        buildEventData[P_SUCCESS] = success;
+
+        if (!success)
+        {
+            buildEventData[P_ERRORTEXT] = errorMsg;
+        }
+
+        curBuild_->SendEvent(E_NETBUILDRESULT, buildEventData);
+
+        curBuild_ = nullptr;
+    }
+
+    void NETBuildSystem::CurrentBuildError(String errorText)
+    {
+        if (curBuild_.Null())
+        {
+            LOGERRORF("NETBuildSystem::CurrentBuildError - Error %s with no current build", errorText.CString());
+            return;
+        }
+
+        using namespace NETBuildResult;
+        VariantMap buildEventData;
+
+        buildEventData[P_BUILD] = curBuild_;
+        buildEventData[P_SUCCESS] = false;
+        buildEventData[P_ERRORTEXT] = errorText;
+        curBuild_->SendEvent(E_NETBUILDRESULT, buildEventData);
+        curBuild_ = nullptr;
+
+    }
+
+    void NETBuildSystem::HandleToolUpdate(StringHash eventType, VariantMap& eventData)
+    {
+        if (curBuild_.Null() && !builds_.Size())
+            return;
+
+        if (curBuild_.Null())
+        {
+            // kick off a new build
+
+            curBuild_ = builds_.Front();
+            builds_.PopFront();
+
+
+            FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+            // Ensure solution still exists
+            if (!fileSystem->FileExists(curBuild_->solutionPath_))
+            {
+                CurrentBuildError(ToString("Solution does not exist(%s)", curBuild_->solutionPath_.CString()));
+                return;
+            }
+
+            String solutionPath = curBuild_->solutionPath_;
+
+            String ext = GetExtension(solutionPath);
+
+            bool requiresNuGet = true;
+
+            if (ext == ".atomic")
+            {
+                if (curBuild_->project_.Null() || curBuild_->project_.Expired())
+                {
+                    CurrentBuildError(ToString("Error loading project (%s), project expired", solutionPath.CString()));
+                }
+
+                Project* project = curBuild_->project_;
+
+                SharedPtr<NETProjectGen> gen(new NETProjectGen(context_));
+
+                gen->SetScriptPlatform(curBuild_->platform_);
+
+                if (!gen->LoadProject(project))
+                {
+                    CurrentBuildError(ToString("Error loading project (%s)", solutionPath.CString()));
+                    return;
+                }
+
+                if (!gen->Generate())
+                {
+                    CurrentBuildError(ToString("Error generating project (%s)", solutionPath.CString()));
+                    return;
+                }
+
+                solutionPath = gen->GetSolution()->GetOutputFilename();
+                requiresNuGet = gen->GetRequiresNuGet();
+
+
+                if (!fileSystem->FileExists(solutionPath))
+                {
+                    CurrentBuildError(ToString("Generated solution does not exist (%s : %s)", curBuild_->solutionPath_.CString(), solutionPath.CString()));
+                    return;
+                }
+
+            }
+            else if (ext == ".json")
+            {
+                SharedPtr<NETProjectGen> gen(new NETProjectGen(context_));
+
+                gen->SetScriptPlatform(curBuild_->platform_);
+
+                if (!gen->LoadProject(solutionPath))
+                {
+                    CurrentBuildError(ToString("Error loading project (%s)", solutionPath.CString()));
+                    return;
+                }
+
+                if (!gen->Generate())
+                {
+                    CurrentBuildError(ToString("Error generating project (%s)", solutionPath.CString()));
+                    return;
+                }
+
+                solutionPath = gen->GetSolution()->GetOutputFilename();
+                requiresNuGet = gen->GetRequiresNuGet();
+
+                if (!fileSystem->FileExists(solutionPath))
+                {
+                    CurrentBuildError(ToString("Generated solution does not exist (%s : %s)", curBuild_->solutionPath_.CString(), solutionPath.CString()));
+                    return;
+                }
+
+            }
+
+            ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
+            const String& nugetBinary = tenv->GetAtomicNETNuGetBinary();
+
+            if (!fileSystem->FileExists(nugetBinary))
+            {
+                CurrentBuildError(ToString("NuGet binary is missing (%s)", nugetBinary.CString()));
+                return;
+            }
+
+            String cmdToolsPath = Poco::Environment::get("VS140COMNTOOLS").c_str();
+
+            if (!cmdToolsPath.Length())
+            {
+                CurrentBuildError("VS140COMNTOOLS environment variable not found, cannot proceed");
+                return;
+            }
+
+            String vcvars64 = ToString("%s..\\..\\VC\\bin\\amd64\\vcvars64.bat", cmdToolsPath.CString());
+            
+            const String configuration = curBuild_->configuration_;
+
+            String cmd = "cmd";
+            Vector<String> args;
+            args.Push("/A");
+            args.Push("/C");
+
+            // vcvars bat
+            String compile = ToString("\"\"%s\" ", vcvars64.CString());
+
+            if (requiresNuGet)
+            {
+                compile += ToString("&& \"%s\" restore \"%s\" ", nugetBinary.CString(), solutionPath.CString());
+            }
+
+            compile += ToString("&& msbuild \"%s\" /p:Configuration=%s /p:Platform=\"Any CPU\"\"", solutionPath.CString(), configuration.CString());
+
+            args.Push(compile);
+
+            curBuild_->allArgs_.Join(args, " ");
+
+            SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
+            Subprocess* subprocess = nullptr;
+
+            try
+            {
+                subprocess = subs->Launch(cmd, args, "C:\\");
+            }
+            catch (Poco::SystemException)
+            {
+                subprocess = nullptr;
+            }
+
+            if (!subprocess)
+            {
+                CurrentBuildError(ToString("NETCompile::Compile - Unable to launch MSBuild subprocess\n%s", curBuild_->allArgs_.CString()));
+                return;
+            }
+
+            SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(NETBuildSystem, HandleCompileProcessComplete));
+            SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(NETBuildSystem, HandleSubprocessOutput));
+
+            curBuild_->status_ = NETBUILD_BUILDING;
+
+        }
+
+    }
+
+
+    NETBuild* NETBuildSystem::GetBuild(const String& solutionPath, const String& platform,  const String& configuration)
+    {
+        List<SharedPtr<NETBuild>>::ConstIterator itr = builds_.Begin();
+
+        while (itr != builds_.End())
+        {
+            NETBuild* build = *itr;
+
+            if (build->solutionPath_ == solutionPath && build->platform_ == platform && build->configuration_ == configuration)
+                return build;
+
+            itr++;
+        }
+
+        return nullptr;
+
+    }
+
+    void NETBuildSystem::HandleBuildAtomicProject(StringHash eventType, VariantMap& eventData)
+    {
+        using namespace NETBuildAtomicProject;
+
+        Project* project = static_cast<Project*>(eventData[P_PROJECT].GetPtr());
+
+        if (!project)
+        {
+            LOGERROR("NETBuildSystem::HandleBuildAtomicProject - null project");
+            return;
+        }
+
+        String platform;
+        String configuration;
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+        platform = "WINDOWS";
+#elif ATOMIC_PLATFORM_OSX
+        platform = "MACOSX";
+#else
+        platform = "LINUX";
+#endif
+
+#ifdef _DEBUG
+        configuration = "Debug";
+#else
+        configuration = "Release";
+#endif
+
+        String projectPath = project->GetProjectFilePath();
+
+        NETBuild* build = Build(projectPath, platform, configuration);
+
+        if (build)
+        {
+            build->project_ = project;
+        }
+
+        LOGINFOF("Received build for project %s", project->GetProjectFilePath().CString());
+            
+    }
+
+    NETBuild* NETBuildSystem::Build(const String& solutionPath, const String& platform, const String& configuration)
+    {
+
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+        if (!fileSystem->FileExists(solutionPath))
+        {
+            LOGERRORF("NETBuildSystem::Build - Solution does not exist (%s)", solutionPath.CString());
+            return 0;
+        }
+
+        // Get existing build
+        SharedPtr<NETBuild> build(GetBuild(solutionPath, platform, configuration));
+
+        if (build.NotNull())
+            return build;
+
+        // Create a new build
+        build = new NETBuild(context_, solutionPath, platform, configuration);
+
+        builds_.Push(build);        
+
+        return build;
+    }
+
+
+}

+ 106 - 0
Source/ToolCore/NETTools/NETBuildSystem.h

@@ -0,0 +1,106 @@
+//
+// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+
+#pragma once
+
+#include <Atomic/Core/Object.h>
+
+using namespace Atomic;
+
+namespace ToolCore
+{
+
+    EVENT(E_NETBUILDRESULT, NETBuildResult)
+    {
+        PARAM(P_BUILD, Build); // NETBuild*
+        PARAM(P_SUCCESS, Success); // bool = success = true;
+        PARAM(P_ERRORTEXT, ErrorText); // for failure, the compilation output        
+    }
+
+    EVENT(E_NETBUILDATOMICPROJECT, NETBuildAtomicProject)
+    {
+        PARAM(P_PROJECT, Project); // Project*
+    }
+
+    enum NETBuildStatus
+    {
+        NETBUILD_PENDING = 0,
+        NETBUILD_BUILDING,
+        NETBUILD_COMPLETE        
+    };
+
+    class Subprocess;
+
+    class NETBuild : public Object
+    {
+        friend class NETBuildSystem;
+
+        OBJECT(NETBuild)
+    
+    public:
+
+        NETBuild(Context* context, const String& solutionPath, const String& platform, const String& configuration);
+        virtual ~NETBuild() {};
+
+    private:
+        /// .sln or .json configuration file
+        String solutionPath_;
+        String configuration_;
+        String platform_;
+        NETBuildStatus status_;
+        String allArgs_;
+        String output_;
+        WeakPtr<Project> project_;
+        WeakPtr<Subprocess> subprocess_;
+    };
+
+    // NET Build subsystem
+    class NETBuildSystem : public Object
+    {
+        
+        OBJECT(NETBuildSystem)
+
+    public:
+
+        NETBuildSystem(Context* context);
+        virtual ~NETBuildSystem();
+
+        /// Build either a .sln or .json configuration file
+        NETBuild* Build(const String& solutionPath, const String& platform, const String& configuration = "Release");
+
+    private:
+
+        void CurrentBuildError(String errorText);
+
+        NETBuild* GetBuild(const String& solutionPath, const String& platform, const String& configuration);
+
+        void HandleBuildAtomicProject(StringHash eventType, VariantMap& eventData);
+        void HandleToolUpdate(StringHash eventType, VariantMap& eventData);
+        void HandleSubprocessOutput(StringHash eventType, VariantMap& eventData);
+        void HandleCompileProcessComplete(StringHash eventType, VariantMap& eventData);
+
+        SharedPtr<NETBuild> curBuild_;
+        List<SharedPtr<NETBuild>> builds_;
+
+    };
+
+}

+ 0 - 146
Source/ToolCore/NETTools/NETCompile.cpp

@@ -1,146 +0,0 @@
-//
-// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#include <Poco/Exception.h>
-#include <Poco/Environment.h>
-
-
-#include <Atomic/IO/Log.h>
-#include <Atomic/IO/FileSystem.h>
-
-#include "../ToolSystem.h"
-#include "../ToolEnvironment.h"
-#include "../Subprocess/SubprocessSystem.h"
-
-#include "NETCompile.h"
-
-namespace ToolCore
-{
-
-    NETCompile::NETCompile(Context* context) :
-        Object(context)
-    {
-
-    }
-
-    NETCompile::~NETCompile()
-    {
-    }
-
-    void NETCompile::HandleSubprocessOutput(StringHash eventType, VariantMap& eventData)
-    {
-        const String& text = eventData[SubprocessOutput::P_TEXT].GetString();
-
-        compileText_ += text;
-
-    }
-
-    void NETCompile::HandleCompileProcessComplete(StringHash eventType, VariantMap& eventData)
-    {
-        int code = eventData[SubprocessComplete::P_RETCODE].GetInt();
-
-        VariantMap compileData;
-
-        using namespace NETCompilerResult;
-
-        if (!code)
-        {
-            compileData[P_SUCCESS] = true;            
-        }
-        else
-        {
-            compileData[P_SUCCESS] = false; 
-            compileText_ += ToString("\nCompilation Command: %s", allArgs_.CString());
-            compileData[P_ERRORTEXT] = compileText_;
-        }   
-
-        SendEvent(E_NETCOMPILERESULT, compileData);
-    }
-
-    bool NETCompile::Compile(const String& solutionPath, const String& configuration)
-    {
-        FileSystem* fileSystem = GetSubsystem<FileSystem>();
-        ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
-        ToolPrefs* prefs = tenv->GetToolPrefs();
-        SubprocessSystem* subs = GetSubsystem<SubprocessSystem>();
-
-        solutionPath_ = solutionPath;
-        compileText_.Clear();
-
-        String cmdToolsPath = Poco::Environment::get("VS140COMNTOOLS").c_str();
-
-        VariantMap compileData;
-
-        if (!cmdToolsPath.Length())
-        {            
-            compileData[NETCompilerResult::P_SUCCESS] = false;
-            compileData[NETCompilerResult::P_ERRORTEXT] = "NETCompile::Compile - VS140COMNTOOLS environment variable not found, cannot proceed";
-            SendEvent(E_NETCOMPILERESULT, compileData);
-            return false;
-        }
-
-        // This will need to be fixed for deployed build
-        String nugetBin = ToString("%s/Build/Managed/nuget/nuget", tenv->GetRootSourceDir().CString());
-            
-        String vcvars64 = ToString("%s..\\..\\VC\\bin\\amd64\\vcvars64.bat", cmdToolsPath.CString());
-       
-        String cmd = "cmd";
-        Vector<String> args;
-        args.Push("/A");
-        args.Push("/C");        
-        args.Push(ToString("\"\"%s\" && \"%s\" restore \"%s\" && msbuild \"%s\" /p:Configuration=%s /p:Platform=\"Any CPU\"\"", 
-                            vcvars64.CString(), nugetBin.CString(), solutionPath.CString(), solutionPath.CString(), configuration.CString()));
-
-
-        allArgs_.Join(args, " ");
-        
-        LOGINFOF("Compiling NET Solution: %s", solutionPath.CString());
-
-        Subprocess* subprocess = nullptr;
-
-        try
-        {
-            subprocess = subs->Launch(cmd, args, "C:\\");
-        }
-        catch (Poco::SystemException)
-        {
-            subprocess = nullptr;
-        }        
-
-        if (!subprocess)
-        {            
-            String allArgs;
-            
-            compileData[NETCompilerResult::P_SUCCESS] = false;
-            compileData[NETCompilerResult::P_ERRORTEXT] = ToString("NETCompile::Compile - Unable to launch MSBuild subprocess\n%s", allArgs_.CString());
-            SendEvent(E_NETCOMPILERESULT, compileData);            
-            return false;
-        }
-
-        SubscribeToEvent(subprocess, E_SUBPROCESSCOMPLETE, HANDLER(NETCompile, HandleCompileProcessComplete));
-        SubscribeToEvent(subprocess, E_SUBPROCESSOUTPUT, HANDLER(NETCompile, HandleSubprocessOutput));
-        
-        return true;
-    }
-
-
-}

+ 93 - 0
Source/ToolCore/NETTools/NETProjectGen.cpp

@@ -271,6 +271,7 @@ namespace ToolCore
         pgroup.CreateChild("DebugType").SetValue("full");
         pgroup.CreateChild("Optimize").SetValue("true");
         pgroup.CreateChild("OutputPath").SetValue(assemblyOutputPath_ + "Release\\");
+
         pgroup.CreateChild("DefineConstants").SetValue("TRACE");
         pgroup.CreateChild("ErrorReport").SetValue("prompt");
         pgroup.CreateChild("WarningLevel").SetValue("4");
@@ -400,6 +401,15 @@ namespace ToolCore
 
         project.CreateChild("Import").SetAttribute("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets");
 
+        if (name_ == "AtomicProject")
+        {
+            XMLElement afterBuild = project.CreateChild("Target");
+            afterBuild.SetAttribute("Name", "AfterBuild");
+            XMLElement copy = afterBuild.CreateChild("Copy");
+            copy.SetAttribute("SourceFiles", "$(TargetPath)");
+            copy.SetAttribute("DestinationFolder", projectPath_ + "../../../Resources/");
+        }
+
         String projectSource = xmlFile_->ToString();
 
         SharedPtr<File> output(new File(context_, projectPath_ + name_ + ".csproj", FILE_WRITE));
@@ -439,6 +449,15 @@ namespace ToolCore
         for (unsigned i = 0; i < packages.Size(); i++)
         {
             String package = packages[i].GetString();
+
+            if (packages_.Find(package) != packages_.End())
+            {
+                LOGERRORF("Duplicate package found %s", package.CString());
+                continue;
+            }
+
+            projectGen_->GetSolution()->RegisterPackage(package);
+
             packages_.Push(package);
         }
 
@@ -557,6 +576,16 @@ namespace ToolCore
         return true;
     }
 
+    bool NETSolution::RegisterPackage(const String& package)
+    {
+        if (packages_.Find(package) != packages_.End())
+            return false;
+        
+        packages_.Push(package);
+
+        return true;
+    }
+
     NETProjectGen::NETProjectGen(Context* context) : Object(context)
     {
 
@@ -666,6 +695,70 @@ namespace ToolCore
         return LoadProject(jvalue);
     }
 
+    bool NETProjectGen::LoadProject(Project* project)
+    {
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+        ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
+
+        JSONValue root;
+
+        JSONValue solution;
+
+        solution["name"] = "AtomicProject";
+        solution["outputPath"] = AddTrailingSlash(project->GetProjectPath()) + "AtomicNET/Solution/";
+
+        JSONArray projects;
+
+        JSONObject jproject;
+        jproject["name"] = "AtomicProject";
+        jproject["outputType"] = "Library";
+        jproject["assemblyName"] = "AtomicProject";
+        jproject["assemblyOutputPath"] = AddTrailingSlash(project->GetProjectPath()) + "AtomicNET/Bin/";
+
+        JSONArray references;
+        references.Push(JSONValue("System"));
+        references.Push(JSONValue("System.Core"));
+        references.Push(JSONValue("System.Xml.Linq"));
+        references.Push(JSONValue("System.XML"));
+
+        String atomicNETAssembly = tenv->GetAtomicNETCoreAssemblyDir() + "AtomicNET.dll";
+
+        if (!fileSystem->FileExists(atomicNETAssembly))
+        {
+            LOGERRORF("NETProjectGen::LoadProject - AtomicNET assembly does not exist: %s", atomicNETAssembly.CString());
+            return false;
+        }
+
+        references.Push(JSONValue(atomicNETAssembly));        
+
+        jproject["references"] = references;
+
+        JSONArray sources;
+        sources.Push(JSONValue(ToString("%s", project->GetResourcePath().CString())));
+
+        jproject["sources"] = sources;
+
+        projects.Push(jproject);
+
+        root["projects"] = projects;
+        root["solution"] = solution;
+        
+        return LoadProject(root);
+    }
+
+    bool NETProjectGen::GetRequiresNuGet()
+    {
+        if (solution_.Null())
+        {
+            LOGERROR("NETProjectGen::GetRequiresNuGet() - called without a solution loaded");
+            return false;
+        }
+
+        return solution_->GetPackages().Size() != 0;
+
+    }
+
+
     String NETProjectGen::GenerateUUID()
     {
         Poco::UUIDGenerator& generator = Poco::UUIDGenerator::defaultGenerator();

+ 16 - 4
Source/ToolCore/NETTools/NETProjectGen.h

@@ -36,7 +36,7 @@ namespace ToolCore
 
     class NETProjectBase : public Object
     {
-        OBJECT(NETProjectBase);
+        OBJECT(NETProjectBase)
 
     public:
 
@@ -56,7 +56,7 @@ namespace ToolCore
 
     class NETCSProject : public NETProjectBase
     {
-        OBJECT(NETCSProject);
+        OBJECT(NETCSProject)
 
     public:
 
@@ -69,6 +69,7 @@ namespace ToolCore
         const String& GetProjectGUID() { return projectGuid_; }
 
         const Vector<String>& GetReferences() const { return references_; }
+        const Vector<String>& GetPackages() const { return packages_; }
 
         bool Generate();
 
@@ -106,7 +107,7 @@ namespace ToolCore
 
     class NETSolution : public NETProjectBase
     {
-        OBJECT(NETSolution);
+        OBJECT(NETSolution)
 
     public:
 
@@ -118,6 +119,12 @@ namespace ToolCore
         bool Generate();
 
         const String& GetOutputPath() { return outputPath_; }
+        String GetOutputFilename() { return outputPath_ + name_ + ".sln"; }
+
+        Vector<String>& GetPackages() { return packages_;  }
+
+        // Registers a NuGet package, returns true if the package hasn't been previously registered
+        bool RegisterPackage(const String& package);
 
     private:
 
@@ -126,12 +133,13 @@ namespace ToolCore
         String name_;
         String outputPath_;
         String solutionGUID_;
+        Vector<String> packages_;
 
     };
 
     class NETProjectGen : public Object
     {
-        OBJECT(NETProjectGen);
+        OBJECT(NETProjectGen)
 
     public:
 
@@ -152,10 +160,14 @@ namespace ToolCore
 
         bool Generate();
 
+        /// Returns true if the generated solution requires NuGet
+        bool GetRequiresNuGet();
+
         String GenerateUUID();
 
         bool LoadProject(const JSONValue& root);
         bool LoadProject(const String& projectPath);
+        bool LoadProject(Project* project);
 
     private:
 

+ 15 - 1
Source/ToolCore/ToolEnvironment.cpp

@@ -156,8 +156,22 @@ void ToolEnvironment::SetRootSourceDir(const String& sourceDir)
     resourceCoreDataDir_ = rootSourceDir_ + "Resources/CoreData";
     resourcePlayerDataDir_ = rootSourceDir_ + "Resources/PlayerData";
     resourceEditorDataDir_ = rootSourceDir_ + "Resources/EditorData";
-
     toolDataDir_ = rootSourceDir_ + "Data/AtomicEditor/";
+
+    // AtomicNET
+
+#ifdef _DEBUG
+    String config = "Debug";
+#else
+    String config = "Release";
+#endif
+
+    atomicNETNuGetBinary_ = ToString("%sBuild/Managed/nuget/nuget.exe", rootSourceDir_.CString());
+
+    atomicNETCoreAssemblyDir_ = rootSourceDir_ + "Artifacts/AtomicNET/" + config + "/";
+
+    atomicNETManagedPlayerBinary_ = atomicNETCoreAssemblyDir_ + "AtomicPlayer.exe";
+
 }
 
 void ToolEnvironment::SetRootBuildDir(const String& buildDir, bool setBinaryPaths)

+ 12 - 0
Source/ToolCore/ToolEnvironment.h

@@ -79,6 +79,13 @@ public:
 
     const String& GetDevConfigFilename();
 
+    // AtomicNET
+
+    const String& GetAtomicNETCoreAssemblyDir() { return atomicNETCoreAssemblyDir_; }
+    const String& GetAtomicNETNuGetBinary() { return atomicNETNuGetBinary_; }
+    const String& GetAtomicNETManagedPlayerBinary() { return atomicNETManagedPlayerBinary_; }
+
+
     // OSX
     const String& GetPlayerAppFolder() { return playerAppFolder_; }
 
@@ -132,6 +139,11 @@ private:
 
     String devConfigFilename_;
 
+    // AtomicNET
+    String atomicNETCoreAssemblyDir_;
+    String atomicNETNuGetBinary_;
+    String atomicNETManagedPlayerBinary_;
+
     SharedPtr<ToolPrefs> toolPrefs_;
 };
 

+ 5 - 0
Source/ToolCore/ToolEvents.h

@@ -34,5 +34,10 @@ EVENT(E_PLATFORMCHANGED, PlatformChanged)
     PARAM(P_PLATFORM, Platform);    // Platform Ptr
 }
 
+// called at 2Hz for non-time critical updates
+EVENT(E_TOOLUPDATE, ToolUpdate)
+{
+}
+
 
 }

+ 19 - 1
Source/ToolCore/ToolSystem.cpp

@@ -21,6 +21,7 @@
 //
 
 #include <Atomic/Core/Context.h>
+#include <Atomic/Core/CoreEvents.h>
 #include <Atomic/IO/FileSystem.h>
 #include <Atomic/Resource/ResourceCache.h>
 
@@ -48,7 +49,8 @@ namespace ToolCore
 {
 
 ToolSystem::ToolSystem(Context* context) : Object(context),
-    cli_(false)
+    cli_(false),
+    updateDelta_(0.0f)
 {
     context_->RegisterSubsystem(new AssetDatabase(context_));
     context_->RegisterSubsystem(new CurlManager(context_));
@@ -62,6 +64,8 @@ ToolSystem::ToolSystem(Context* context) : Object(context),
     RegisterPlatform(new PlatformWindows(context));
     RegisterPlatform(new PlatformIOS(context));
     RegisterPlatform(new PlatformAndroid(context));
+
+    SubscribeToEvent(E_UPDATE, HANDLER(ToolSystem, HandleUpdate));
 }
 
 ToolSystem::~ToolSystem()
@@ -69,6 +73,20 @@ ToolSystem::~ToolSystem()
 
 }
 
+void ToolSystem::HandleUpdate(StringHash eventType, VariantMap& eventData)
+{
+    using namespace Update;
+
+    updateDelta_ += eventData[P_TIMESTEP].GetFloat();
+    
+    if (updateDelta_ >= 0.5f)
+    {
+        updateDelta_ = 0.0f;
+        SendEvent(E_TOOLUPDATE);
+    }
+
+}
+
 bool ToolSystem::LoadProject(const String& fullpath)
 {
 

+ 4 - 0
Source/ToolCore/ToolSystem.h

@@ -63,6 +63,8 @@ public:
 
 private:
 
+    void HandleUpdate(StringHash eventType, VariantMap& eventData);
+
     /// Full path to data files
     String dataPath_;
 
@@ -75,6 +77,8 @@ private:
 
     bool cli_;
 
+    float updateDelta_;
+
 };
 
 }