Browse Source

Merge pull request #1672 from LumaDigital/MB_LumaChanges

Various small changes
Matt Benic 8 years ago
parent
commit
397ce661af
37 changed files with 1032 additions and 60 deletions
  1. 63 8
      Script/AtomicNET/AtomicNET/Application/Application.cs
  2. 33 0
      Script/AtomicNET/AtomicNET/Application/NETIPCServerApp.cs
  3. 4 2
      Script/AtomicNET/AtomicNET/Core/AtomicNET.cs
  4. 7 6
      Script/AtomicNET/AtomicNET/Core/NativeCore.cs
  5. 29 0
      Script/AtomicNET/AtomicNET/IO/FileNameIterator.cs
  6. 1 1
      Script/Packages/Atomic/IO.json
  7. 3 2
      Script/Packages/AtomicApp/AtomicApp.json
  8. 2 1
      Script/Packages/AtomicNETNative/AtomicNETNative.json
  9. 24 5
      Source/Atomic/Engine/Engine.cpp
  10. 4 0
      Source/Atomic/Engine/EngineConfig.cpp
  11. 15 1
      Source/Atomic/Graphics/Animation.cpp
  12. 4 0
      Source/Atomic/Graphics/Animation.h
  13. 32 0
      Source/Atomic/IO/FileSystem.cpp
  14. 22 0
      Source/Atomic/IO/FileSystem.h
  15. 1 1
      Source/Atomic/Network/MasterServerClient.cpp
  16. 6 6
      Source/Atomic/Network/Network.cpp
  17. 9 4
      Source/Atomic/Network/Network.h
  18. 74 0
      Source/Atomic/UI/UISection.cpp
  19. 12 0
      Source/Atomic/UI/UISection.h
  20. 7 7
      Source/Atomic/UI/UIWidget.h
  21. 10 0
      Source/AtomicApp/AppBase.cpp
  22. 6 0
      Source/AtomicApp/AppBase.h
  23. 261 0
      Source/AtomicApp/IPCServerApp.cpp
  24. 43 0
      Source/AtomicApp/IPCServerApp.h
  25. 4 1
      Source/AtomicNET/NETNative/CMakeLists.txt
  26. 99 0
      Source/AtomicNET/NETNative/Desktop/NETIPCServerApp.cpp
  27. 51 0
      Source/AtomicNET/NETNative/Desktop/NETIPCServerApp.h
  28. 30 1
      Source/AtomicTool/AtomicTool.cpp
  29. 48 0
      Source/ThirdParty/TurboBadger/tb_toggle_container.cpp
  30. 13 0
      Source/ThirdParty/TurboBadger/tb_toggle_container.h
  31. 9 0
      Source/ToolCore/Assets/AssetDatabase.cpp
  32. 4 3
      Source/ToolCore/Assets/TextureImporter.cpp
  33. 88 5
      Source/ToolCore/Build/BuildBase.cpp
  34. 8 5
      Source/ToolCore/Command/BuildCmd.cpp
  35. 2 1
      Source/ToolCore/Command/BuildCmd.h
  36. 2 0
      Source/ToolCore/Command/CacheCmd.h
  37. 2 0
      Source/ToolCore/Command/Command.h

+ 63 - 8
Script/AtomicNET/AtomicNET/Application/Application.cs

@@ -12,6 +12,8 @@ namespace AtomicEngine
 
     public partial class Application : AObject
     {
+        public static AppBase AppBase { get; private set; }
+
 
         public static int Run<T>(string[] args) where T : AppDelegate, new()
         {
@@ -22,10 +24,8 @@ namespace AtomicEngine
         {
             // Create the Application
 
-            AppBase app = null;
-
 #if ATOMIC_DESKTOP
-            app = NETIPCPlayerApp.Create(args);
+            AppBase = NETIPCPlayerApp.Create(args);
 #endif
 
 #if ATOMIC_IOS
@@ -36,7 +36,7 @@ namespace AtomicEngine
 
 #if ATOMIC_MOBILE
 
-            app = NETAtomicPlayer.Create(args);
+            AppBase = NETAtomicPlayer.Create(args);
             
             var renderer = AtomicNET.GetSubsystem<Renderer>();
             renderer.ReuseShadowMaps = false;
@@ -54,7 +54,7 @@ namespace AtomicEngine
                 appDelegate.Start();
 
                 // Managed code in charge of main loop
-                while (app.RunFrame())
+                while (AppBase.RunFrame())
                 {
                     appDelegate.PostFrame();
                 }
@@ -62,7 +62,7 @@ namespace AtomicEngine
                 appDelegate.Shutdown();
 
                 // Shut 'er down
-                app.Shutdown();
+                AppBase.Shutdown();
             }
             catch (Exception e)
             {
@@ -76,16 +76,71 @@ namespace AtomicEngine
                 {
                     Log.Error(e.StackTrace);
                     // rethrow
-                    throw e;
+                    throw;
                 }
             }
+            finally
+            {
+                AppBase = null;
+            }
 
             return 0;
         }
 
 
+#if ATOMIC_DESKTOP
+        public static int RunServer<T>(string[] args) where T : AppDelegate, new()
+        {
+            return RunServer(typeof(T), args);
+        }
+
+        public static int RunServer(Type appDelegateType, string[] args)
+        {
+            // Create the Application
+            AppBase = NETIPCServerApp.Create(args);
+
+            appDelegate = (AppDelegate)Activator.CreateInstance(appDelegateType);
+
+            try
+            {
+                appDelegate.Start();
+
+                // Managed code in charge of main loop
+                while (AppBase.RunFrame())
+                {
+                    appDelegate.PostFrame();
+                }
+
+                appDelegate.Shutdown();
+
+                // Shut 'er down
+                AppBase.Shutdown();
+            }
+            catch (Exception e)
+            {
+                if (e.InnerException != null)
+                {
+                    Log.Error(e.InnerException.StackTrace);
+                    // rethrow inner exception
+                    throw e.InnerException;
+                }
+                else
+                {
+                    Log.Error(e.StackTrace);
+                    // rethrow
+                    throw e;
+                }
+            }
+            finally
+            {
+                AppBase = null;
+            }
+
+
+            return 0;
+        }
+#endif
         internal static AppDelegate appDelegate;
 
     }
-
 }

+ 33 - 0
Script/AtomicNET/AtomicNET/Application/NETIPCServerApp.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Reflection;
+
+#if ATOMIC_DESKTOP
+
+namespace AtomicEngine
+{
+    public partial class NETIPCServerApp : IPCServerApp
+    {
+        /// <summary>
+        ///  IPC Player App used with the Atomic Editor
+        /// </summary>
+        /// <param name="args"></param>
+        /// <param name="headless"></param>
+        /// <returns></returns>
+        public static NETIPCServerApp Create(string[] args, bool headless = false)
+        {
+            // Initialize AtomicNET
+            AtomicNET.Initialize();
+
+            var app = CreateInternal();
+
+            app.Initialize();
+
+            return app;
+        }
+
+
+    }
+
+}
+
+#endif

+ 4 - 2
Script/AtomicNET/AtomicNET/Core/AtomicNET.cs

@@ -9,7 +9,9 @@ namespace AtomicEngine
 
     public static class AtomicNET
     {
-        public static Context Context => context;        
+        public static Context Context => context;
+
+        public static string GetCacheStatus() => NativeCore.GetCacheStatus();
 
         public static T GetSubsystem<T>() where T : AObject
         {
@@ -28,7 +30,7 @@ namespace AtomicEngine
                 throw new System.InvalidOperationException($"AtomicNET.GetSubsystem<T> - Attempting to get null subsystem: {type.Name}");
             }
 
-            // Look up possible native subsystem 
+            // Look up possible native subsystem
             subsystem = AtomicNET.Context.GetSubsystem(type.Name);
 
             // If we didn't find one, this is an error

+ 7 - 6
Script/AtomicNET/AtomicNET/Core/NativeCore.cs

@@ -45,10 +45,11 @@ namespace AtomicEngine
     }
 
     /// <summary>
-    ///  Internal class for native interop 
+    ///  Internal class for native interop
     /// </summary>
     internal static class NativeCore
     {
+        public static string GetCacheStatus() => refCountedCache.GetCacheStatus();
 
         static internal void SubscribeToEvent(AObject receiver, uint eventType)
         {
@@ -215,16 +216,16 @@ namespace AtomicEngine
         }
 
         /// <summary>
-        ///  Runs a GC collection, waits for any finalizers, and then expires any natives collected 
+        ///  Runs a GC collection, waits for any finalizers, and then expires any natives collected
         /// </summary>
         public static void RunGC()
         {
             // run a GC collection
             GC.Collect();
             // finalizers can run on any thread, we're explicitly running a GC here
-            // so wait for all the finalizers to finish            
+            // so wait for all the finalizers to finish
             GC.WaitForPendingFinalizers();
-            // Anything finalized on another thread will now be available to release 
+            // Anything finalized on another thread will now be available to release
             // in main thread
             RefCounted.ReleaseFinalized();
 
@@ -270,7 +271,7 @@ namespace AtomicEngine
         #endif
         public static void RefCountedDeleted(IntPtr refCounted)
         {
-            
+
         }
 
         // Called to throw a managed exception from native code
@@ -286,7 +287,7 @@ namespace AtomicEngine
         {
             if (refCounted == IntPtr.Zero)
                 return;
-           
+
             RemoveEventSender(refCounted);
 
             refCountedCache.Remove(refCounted);

+ 29 - 0
Script/AtomicNET/AtomicNET/IO/FileNameIterator.cs

@@ -0,0 +1,29 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+namespace AtomicEngine
+{
+
+    public partial class FileNameIterator : IEnumerable<string>
+    {
+        public IEnumerator<string> GetEnumerator()
+        {
+            Reset();
+            while (MoveNext())
+            {
+                yield return GetCurrent();
+            }
+        }
+
+        IEnumerator IEnumerable.GetEnumerator()
+        {
+            Reset();
+            while (MoveNext())
+            {
+                yield return GetCurrent();
+            }
+        }
+    }
+
+
+}

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

@@ -1,7 +1,7 @@
 {
 	"name" : "IO",
 	"sources" : ["Source/Atomic/IO"],
-	"classes" : ["Log", "File", "FileSystem", "FileWatcher", "BufferQueue", "PackageFile"],
+	"classes" : ["Log", "File", "FileSystem", "FileWatcher", "BufferQueue", "PackageFile", "FileNameIterator"],
 	"interfaces" : ["Serializer", "Deserializer", "AbstractFile"],
 	"overloads" : {
 		"File" : {

+ 3 - 2
Script/Packages/AtomicApp/AtomicApp.json

@@ -1,9 +1,10 @@
 {
 	"name" : "AtomicApp",
 	"sources" : ["Source/AtomicApp", "Source/AtomicApp/Player"],
-	"classes" : ["AppBase", "PlayerApp", "IPCClientApp", "IPCPlayerApp"],
+	"classes" : ["AppBase", "PlayerApp", "IPCClientApp", "IPCPlayerApp", "IPCServerApp"],
 	"classExcludes" : {
 		"IPCClientApp" : ["ANDROID", "IOS", "WEB"],
-		"IPCPlayerApp" : ["ANDROID", "IOS", "WEB"]
+		"IPCPlayerApp" : ["ANDROID", "IOS", "WEB"],
+		"IPCServerApp" : ["ANDROID", "IOS", "WEB"]
 	}
 }

+ 2 - 1
Script/Packages/AtomicNETNative/AtomicNETNative.json

@@ -1,9 +1,10 @@
 {
 	"name" : "AtomicNETNative",
 	"sources" : ["Source/AtomicNET/NETNative", "Source/AtomicNET/NETNative/Desktop"],
-	"classes" : ["NETCore", "NETAtomicPlayer","NETIPCPlayerApp", "NETServiceApplication"],
+	"classes" : ["NETCore", "NETAtomicPlayer","NETIPCPlayerApp", "NETIPCServerApp", "NETServiceApplication"],
 	"classExcludes" : {
 		"NETIPCPlayerApp" : ["ANDROID", "IOS", "WEB"],
+		"NETIPCServerApp" : ["ANDROID", "IOS", "WEB"],
 		"NETServiceApplication" : ["ANDROID", "IOS", "WEB"]
 	}
 

+ 24 - 5
Source/Atomic/Engine/Engine.cpp

@@ -131,7 +131,7 @@ Engine::Engine(Context* context) :
     exiting_(false),
     headless_(false),
     audioPaused_(false),
-    // ATOMIC BEGIN    
+    // ATOMIC BEGIN
     paused_(false),
     runNextPausedFrame_(false),
     fpsTimeSinceUpdate_(ENGINE_FPS_UPDATE_INTERVAL),
@@ -185,7 +185,7 @@ Engine::Engine(Context* context) :
 #endif
 
     SubscribeToEvent(E_EXITREQUESTED, ATOMIC_HANDLER(Engine, HandleExitRequested));
-    // ATOMIC BEGIN        
+    // ATOMIC BEGIN
     SubscribeToEvent(E_PAUSERESUMEREQUESTED, ATOMIC_HANDLER(Engine, HandlePauseResumeRequested));
     SubscribeToEvent(E_PAUSESTEPREQUESTED, ATOMIC_HANDLER(Engine, HandlePauseStepRequested));
 
@@ -264,9 +264,18 @@ bool Engine::Initialize(const VariantMap& parameters)
     // Set maximally accurate low res timer
     GetSubsystem<Time>()->SetTimerPeriod(1);
 
-    // Configure max FPS
+    // Configure FPS limits
     if (GetParameter(parameters, EP_FRAME_LIMITER, true) == false)
+    {
         SetMaxFps(0);
+    }
+    else
+    {
+        if (HasParameter(parameters, "MaxFps"))
+            SetMaxFps(GetParameter(parameters, "MaxFps").GetInt());
+        if (HasParameter(parameters, "MinFps"))
+            SetMinFps(GetParameter(parameters, "MinFps").GetInt());
+    }
 
     // Set amount of worker threads according to the available physical CPU cores. Using also hyperthreaded cores results in
     // unpredictable extra synchronization overhead. Also reserve one core for the main thread
@@ -582,7 +591,7 @@ void Engine::RunFrame()
     ATOMIC_PROFILE(DoFrame);
     time->BeginFrame(timeStep_);
     // check for exit again that comes in thru an event handler
-    if ( exiting_ ) // needed to prevent scripts running the 
+    if ( exiting_ ) // needed to prevent scripts running the
         return;     // current frame update with null objects
 
     // If paused, or pause when minimized -mode is in use, stop updates and audio as necessary
@@ -603,7 +612,7 @@ void Engine::RunFrame()
             audio->Play();
             audioPaused_ = false;
         }
-        
+
         // Only run one frame when stepping
         runNextPausedFrame_ = false;
 
@@ -887,6 +896,16 @@ VariantMap Engine::ParseParameters(const Vector<String>& arguments)
                 ret[EP_HEADLESS] = true;
             else if (argument == "nolimit")
                 ret[EP_FRAME_LIMITER] = false;
+            else if (argument == "maxfps" && !value.Empty())
+            {
+                ret["MaxFps"] = ToInt(value);
+                i++;
+            }
+            else if (argument == "minfps" && !value.Empty())
+            {
+                ret["MinFps"] = ToInt(value);
+                i++;
+            }
             else if (argument == "flushgpu")
                 ret[EP_FLUSH_GPU] = true;
             else if (argument == "gl2")

+ 4 - 0
Source/Atomic/Engine/EngineConfig.cpp

@@ -68,6 +68,10 @@ bool EngineConfig::LoadGraphicsConfig(const JSONValue& jgraphics)
             valueMap_["Headless"] = GetBoolValue(jvalue, false);
         else if (key == "framelimiter")
             valueMap_["FrameLimiter"] = GetBoolValue(jvalue, true);
+        else if (key == "maxfps")
+            valueMap_["MaxFps"] = GetIntValue(jvalue, 0);
+        else if (key == "minfps")
+            valueMap_["MinFps"] = GetIntValue(jvalue, 0);
         else if (key == "flushgpu")
             valueMap_["FlushGPU"] = GetBoolValue(jvalue, false);
         else if (key == "forcegl2")

+ 15 - 1
Source/Atomic/Graphics/Animation.cpp

@@ -432,7 +432,21 @@ void Animation::SetTracks(const Vector<AnimationTrack>& tracks)
 
 }
 
-// ATOMIC END
 
+Vector3 Animation::GetKeyFramePositionAtIndex(const String & name, unsigned keyIndex)
+{
+    for (HashMap<StringHash, AnimationTrack>::ConstIterator i = tracks_.Begin(); i != tracks_.End(); ++i)
+    {
+        const AnimationTrack& track = i->second_;
 
+        if (track.name_ == name)
+        {
+            const AnimationKeyFrame& key = track.keyFrames_.At(keyIndex);
+            return key.position_;
+        }
+    }
+    return Vector3();
+}
+
+// ATOMIC END
 }

+ 4 - 0
Source/Atomic/Graphics/Animation.h

@@ -186,8 +186,12 @@ public:
     /// Set all animation tracks.
     void SetTracks(const Vector<AnimationTrack>& tracks);
 
+    /// Return position of an animation track's keyframe at index.
+    Vector3 GetKeyFramePositionAtIndex(const String& name, unsigned keyIndex);
+
     // ATOMIC END
 
+
 private:
     /// Animation name.
     String animationName_;

+ 32 - 0
Source/Atomic/IO/FileSystem.cpp

@@ -1081,6 +1081,8 @@ bool IsAbsolutePath(const String& pathName)
 }
 
 // ATOMIC BEGIN
+
+
 bool FileSystem::CreateDirs(const String& root, const String& subdirectory)
 {
     String folder = AddTrailingSlash(GetInternalPath(root));
@@ -1335,6 +1337,36 @@ bool GetRelativePath(const String& fromPath, const String& toPath, String& outpu
 
 }
 
+FileNameIterator::FileNameIterator()
+{
+    Reset();
+}
+
+const String& FileNameIterator::GetCurrent()
+{
+    return (index_ < filenames_.Size()) ?
+        filenames_[index_] :
+        String::EMPTY;
+}
+
+bool FileNameIterator::MoveNext()
+{
+    return ++index_ < filenames_.Size();
+}
+
+void FileNameIterator::Reset()
+{
+    index_ = -1;
+}
+
+SharedPtr<FileNameIterator> FileSystem::ScanDir(const String& pathName, const String& filter, unsigned flags, bool recursive) const
+{
+    SharedPtr<FileNameIterator> enumerator(new FileNameIterator());
+    ScanDir(enumerator->filenames_, pathName, filter, flags, recursive);
+
+    return enumerator;
+}
+
 // ATOMIC END
 
 }

+ 22 - 0
Source/Atomic/IO/FileSystem.h

@@ -38,6 +38,26 @@ static const unsigned SCAN_DIRS = 0x2;
 /// Return also hidden files.
 static const unsigned SCAN_HIDDEN = 0x4;
 
+// ATOMIC BEGIN
+
+/// Helper class to expose resource iteration to script
+class FileNameIterator : public RefCounted
+{
+    ATOMIC_REFCOUNTED(FileNameIterator);
+public:
+    FileNameIterator();
+
+    const String& GetCurrent();
+    bool MoveNext();
+    void Reset();
+
+    Vector<String> filenames_;
+private:
+    unsigned index_;
+};
+
+// ATOMIC END
+
 /// Subsystem for file and directory operations and access control.
 class ATOMIC_API FileSystem : public Object
 {
@@ -103,6 +123,8 @@ public:
     String GetAppPreferencesDir(const String& org, const String& app) const;
 
 // ATOMIC BEGIN
+    /// Scan specified files, returning them as an iterator
+    SharedPtr<FileNameIterator> ScanDir(const String& pathName, const String& filter, unsigned flags, bool recursive) const;
 
     /// Check if a file or directory exists at the specified path
     bool Exists(const String& pathName) const { return FileExists(pathName) || DirExists(pathName); }

+ 1 - 1
Source/Atomic/Network/MasterServerClient.cpp

@@ -576,7 +576,7 @@ bool MasterServerClient::StartServerAndRegisterWithMaster(unsigned short serverP
     }
 
     // First start the server
-    bool rc = network->StartServer(serverPort);
+    bool rc = network->StartServer(serverPort, kNet::SocketOverUDP);
 
     if (!rc)
     {

+ 6 - 6
Source/Atomic/Network/Network.cpp

@@ -206,7 +206,7 @@ void Network::ClientDisconnected(kNet::MessageConnection* connection)
     }
 }
 
-bool Network::Connect(const String& address, unsigned short port, Scene* scene, const VariantMap& identity)
+bool Network::Connect(const String& address, unsigned short port, kNet::SocketTransportLayer transport, Scene* scene, const VariantMap& identity)
 {
     ATOMIC_PROFILE(Connect);
 
@@ -217,7 +217,7 @@ bool Network::Connect(const String& address, unsigned short port, Scene* scene,
         OnServerDisconnected();
     }
 
-    kNet::SharedPtr<kNet::MessageConnection> connection = network_->Connect(address.CString(), port, kNet::SocketOverUDP, this);
+    kNet::SharedPtr<kNet::MessageConnection> connection = network_->Connect(address.CString(), port, transport, this);
     if (connection)
     {
         serverConnection_ = new Connection(context_, false, connection);
@@ -246,7 +246,7 @@ void Network::Disconnect(int waitMSec)
     serverConnection_->Disconnect(waitMSec);
 }
 
-bool Network::StartServer(unsigned short port)
+bool Network::StartServer(unsigned short port, kNet::SocketTransportLayer transport)
 {
     if (IsServerRunning())
         return true;
@@ -257,14 +257,14 @@ bool Network::StartServer(unsigned short port)
     serverPort_ = port;
 // ATOMIC END
 
-    if (network_->StartServer(port, kNet::SocketOverUDP, this, true) != 0)
+    if (network_->StartServer(port, transport, this, true) != 0)
     {
-        ATOMIC_LOGINFO("Started server on port " + String(port));
+        ATOMIC_LOGINFOF("Started %s server on port %i", (transport == kNet::SocketOverTCP ? "TCP" : "UDP"), port);
         return true;
     }
     else
     {
-        ATOMIC_LOGERROR("Failed to start server on port " + String(port));
+        ATOMIC_LOGERRORF("Failed to start %s server on port %i", (transport == kNet::SocketOverTCP ? "TCP" : "UDP"), port);
         return false;
     }
 }

+ 9 - 4
Source/Atomic/Network/Network.h

@@ -29,6 +29,11 @@
 
 #include <kNet/IMessageHandler.h>
 #include <kNet/INetworkServerListener.h>
+#include <kNet/Socket.h>
+
+#ifdef SendMessage
+#undef SendMessage
+#endif
 
 namespace Atomic
 {
@@ -64,12 +69,12 @@ public:
     /// Handle a client disconnection.
     virtual void ClientDisconnected(kNet::MessageConnection* connection);
 
-    /// Connect to a server using UDP protocol. Return true if connection process successfully started.
-    bool Connect(const String& address, unsigned short port, Scene* scene, const VariantMap& identity = Variant::emptyVariantMap);
+    /// Connect to a server using specified protocol. Return true if connection process successfully started.
+    bool Connect(const String& address, unsigned short port, kNet::SocketTransportLayer transport, Scene* scene, const VariantMap& identity = Variant::emptyVariantMap);
     /// Disconnect the connection to the server. If wait time is non-zero, will block while waiting for disconnect to finish.
     void Disconnect(int waitMSec = 0);
-    /// Start a server on a port using UDP protocol. Return true if successful.
-    bool StartServer(unsigned short port);
+    /// Start a server on a port using specified protocol. Return true if successful.
+    bool StartServer(unsigned short port, kNet::SocketTransportLayer transport);
     /// Stop the server.
     void StopServer();
     /// Broadcast a message with content ID to all client connections.

+ 74 - 0
Source/Atomic/UI/UISection.cpp

@@ -55,4 +55,78 @@ bool UISection::OnEvent(const tb::TBWidgetEvent &ev)
     return UIWidget::OnEvent(ev);
 }
 
+void UISection::AddChild(UIWidget* child)
+{
+    if (!widget_ || !child || !child->GetInternalWidget())
+        return;
+
+    TBSafeCast<TBSection>(widget_)->AddToggleChild(child->GetInternalWidget());
+}
+
+void UISection::AddChildAfter(UIWidget* child, UIWidget* otherChild)
+{
+    if (!widget_ || !child || !child->GetInternalWidget() || !otherChild || !otherChild->GetInternalWidget())
+        return;
+
+    TBSafeCast<TBSection>(widget_)->AddToggleChildRelative(child->GetInternalWidget(), tb::WIDGET_Z_REL_AFTER, otherChild->GetInternalWidget());
+}
+
+void UISection::AddChildBefore(UIWidget* child, UIWidget* otherChild)
+{
+    if (!widget_ || !child || !child->GetInternalWidget() || !otherChild || !otherChild->GetInternalWidget())
+        return;
+
+    TBSafeCast<TBSection>(widget_)->AddToggleChildRelative(child->GetInternalWidget(), tb::WIDGET_Z_REL_BEFORE, otherChild->GetInternalWidget());
+}
+
+void UISection::AddChildRelative(UIWidget* child, UI_WIDGET_Z_REL z, UIWidget* reference)
+{
+    if (!widget_ || !child || !child->GetInternalWidget() || !reference || !reference->GetInternalWidget())
+        return;
+
+    TBSafeCast<TBSection>(widget_)->AddToggleChildRelative(child->GetInternalWidget(), (WIDGET_Z_REL)z, reference->GetInternalWidget());
+}
+
+void UISection::RemoveChild(UIWidget* child, bool cleanup)
+{
+    if (!widget_ || !child)
+        return;
+
+    TBWidget* childw = child->GetInternalWidget();
+
+    if (!childw)
+        return;
+
+    TBSafeCast<TBSection>(widget_)->RemoveToggleChild(childw);
+}
+
+void UISection::DeleteAllChildren()
+{
+    if (!widget_)
+        return;
+
+    TBSafeCast<TBSection>(widget_)->DeleteAllChildren();
+}
+
+UIWidget* UISection::GetFirstChild()
+{
+    if (!widget_)
+        return NULL;
+
+    return GetSubsystem<UI>()->WrapWidget(TBSafeCast<TBSection>(widget_)->GetFirstToggleChild());
+}
+
+UIWidget* UISection::GetWidget(const String& id)
+{
+    if (!widget_)
+        return 0;
+
+    TBWidget* child = TBSafeCast<TBSection>(widget_)->GetToggleWidgetById(TBID(id.CString()));
+
+    if (!child)
+        return 0;
+
+    UI* ui = GetSubsystem<UI>();
+    return ui->WrapWidget(child);
+}
 }

+ 12 - 0
Source/Atomic/UI/UISection.h

@@ -37,6 +37,18 @@ public:
     UISection(Context* context, bool createWidget = true);
     virtual ~UISection();
 
+    void AddChild(UIWidget* child);
+    void AddChildAfter(UIWidget* child, UIWidget* otherChild);
+    void AddChildBefore(UIWidget* child, UIWidget* otherChild);
+    void AddChildRelative(UIWidget* child, UI_WIDGET_Z_REL z, UIWidget* reference);
+
+    void RemoveChild(UIWidget* child, bool cleanup = true);
+    void DeleteAllChildren();
+
+    UIWidget* GetFirstChild();
+    UIWidget* GetWidget(const String& id);
+
+
 protected:
 
     virtual bool OnEvent(const tb::TBWidgetEvent &ev);

+ 7 - 7
Source/Atomic/UI/UIWidget.h

@@ -200,9 +200,9 @@ class ATOMIC_API UIWidget : public Object, public tb::TBWidgetDelegate
     void SetFontDescription(UIFontDescription* fd);
 
     virtual void Remove();
-    void RemoveChild(UIWidget* child, bool cleanup = true);
+    virtual void RemoveChild(UIWidget* child, bool cleanup = true);
 
-    void DeleteAllChildren();
+    virtual void DeleteAllChildren();
 
     /// searches for specified widget ID from the top of the widget tree, returns the 1st one found.
     virtual UIWidget *FindWidget ( const String& searchid );
@@ -247,7 +247,7 @@ class ATOMIC_API UIWidget : public Object, public tb::TBWidgetDelegate
     void SetDragObject(UIDragObject* object) { dragObject_ = object; }
     UIDragObject* GetDragObject() { return dragObject_; }
 
-    UIWidget* GetFirstChild();
+    virtual UIWidget* GetFirstChild();
     UIWidget* GetNext();
 
     bool IsAncestorOf(UIWidget* widget);
@@ -255,18 +255,18 @@ class ATOMIC_API UIWidget : public Object, public tb::TBWidgetDelegate
     void SetIsFocusable(bool value);
 
     // get this or child widget with id
-    UIWidget* GetWidget(const String& id);
+    virtual UIWidget* GetWidget(const String& id);
 
     UIView* GetView();
 
     virtual void AddChild(UIWidget* child);
 
-    void AddChildAfter(UIWidget* child, UIWidget* otherChild);
-    void AddChildBefore(UIWidget* child, UIWidget* otherChild);
+    virtual void AddChildAfter(UIWidget* child, UIWidget* otherChild);
+    virtual void AddChildBefore(UIWidget* child, UIWidget* otherChild);
 
     /// Add the child to this widget. See AddChild for adding a child to the top or bottom.
     /// This takes a relative Z and insert the child before or after the given reference widget.
-    void AddChildRelative(UIWidget* child, UI_WIDGET_Z_REL z, UIWidget* reference);
+    virtual void AddChildRelative(UIWidget* child, UI_WIDGET_Z_REL z, UIWidget* reference);
 
     void InvalidateLayout();
 

+ 10 - 0
Source/AtomicApp/AppBase.cpp

@@ -79,6 +79,16 @@ namespace Atomic
         arguments_.Push(argument);
     }
 
+    unsigned AppBase::GetNumArguments()
+    {
+        return arguments_.Size();
+    }
+
+    String AppBase::GetArgument(unsigned index)
+    {
+        return arguments_[index];
+    }
+
     void AppBase::ProcessArguments()
     {
         for (unsigned i = 0; i < arguments_.Size(); ++i)

+ 6 - 0
Source/AtomicApp/AppBase.h

@@ -53,6 +53,12 @@ namespace Atomic
         /// Called before initializing application for inserting arguments
         static void AddArgument(const String& argument);
 
+        /// Returns the number of arguments specified on the commandline and added with AddArgument
+        static unsigned GetNumArguments();
+
+        /// Returns an argument from the list of arguments specified on the commandline or added with AddArgument
+        static String GetArgument(unsigned index);
+
         virtual void ProcessArguments();
 
         static void AddEngineConfigSearchPath(const String& path) { engineConfigSearchPaths_.Push(path); }

+ 261 - 0
Source/AtomicApp/IPCServerApp.cpp

@@ -0,0 +1,261 @@
+#include <Atomic/IO/Log.h>
+#include <Atomic/IO/FileSystem.h>
+
+#include <Atomic/IO/IOEvents.h>
+#include <Atomic/Core/ProcessUtils.h>
+#include <Atomic/Core/CoreEvents.h>
+
+#include <Atomic/IPC/IPC.h>
+#include <Atomic/IPC/IPCEvents.h>
+#include <Atomic/IPC/IPCWorker.h>
+#include <Atomic/IPC/IPCBroker.h>
+
+#include <ToolCore/ToolSystem.h>
+#include <ToolCore/ToolEnvironment.h>
+#include <ToolCore/Project/Project.h>
+#include <ToolCore/Project/ProjectSettings.h>
+
+#include <Atomic/UI/SystemUI/DebugHud.h>
+#include <AtomicApp/Player/IPCPlayerAppEvents.h>
+
+#include <AtomicJS/Javascript/JSIPCEvents.h>
+
+#include <Atomic/Input/InputEvents.h>
+
+#include "IPCServerApp.h"
+
+#pragma push_macro("PostMessage")
+#undef PostMessage
+
+using namespace ToolCore;
+
+namespace Atomic
+{
+
+    IPCServerApp::IPCServerApp(Context* context) :
+        AppBase(context),
+        playerEnabled_(false)
+    {
+    }
+
+    IPCServerApp::~IPCServerApp()
+    {
+    }
+
+    void IPCServerApp::Setup()
+    {
+        AppBase::Setup();
+
+        // Register IPC system
+        context_->RegisterSubsystem(new IPC(context_));
+
+        ToolEnvironment* env = new ToolEnvironment(context_);
+        context_->RegisterSubsystem(env);
+
+        env->Initialize();
+
+        ToolSystem* system = new ToolSystem(context_);
+        context_->RegisterSubsystem(system);
+
+        engineParameters_["Headless"] = true;
+        engineParameters_["LogLevel"] = LOG_INFO;
+    }
+
+    void IPCServerApp::Stop()
+    {
+        IPC* ipc = GetSubsystem<IPC>();
+
+        if (ipc)
+        {
+            ipc->Shutdown();
+        }
+
+        AppBase::Stop();
+    }
+
+    bool IPCServerApp::RunIPCPlayer(const String& projectName, const String& projectPath, const String &addArgs)
+    {
+        /* Params to pass in (possibly) from PE
+        --windowposx 244
+        --windowposy 157
+        --windowwidth 1440
+        --windowheight 806
+        --resizable
+
+        ALSO STUFF TO TELL VSEATOMIC WHICH CUTSCENE TO LAUNCH..app delegate will need to parse run params I think
+
+        */
+
+        if (playerBroker_.NotNull())
+            return false;
+
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+        ToolSystem* tsystem = GetSubsystem<ToolSystem>();
+        ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
+
+        String projectAssembly = projectName + ".dll";
+        String projectExe = projectName + ".exe";
+
+        String playerBinary = "";
+
+        String resourcePath = projectPath + "Resources/" + projectAssembly;
+
+        // TODO: We need to configure project as managed
+        if (fileSystem->FileExists(resourcePath))
+        {
+#ifdef ATOMIC_DEV_BUILD
+
+#ifdef ATOMIC_DEBUG        
+            playerBinary = projectPath + "AtomicNET/Debug/Bin/Desktop/" + projectExe;
+#else
+            playerBinary = projectPath + "AtomicNET/Release/Bin/Desktop/" + projectExe;
+#endif
+
+#else
+            // TODO: We are using the release build of the managed project here, how and when to use debug?
+            playerBinary = projectPath + "AtomicNET/Release/Bin/Desktop/" + projectExe;
+#endif
+
+
+            if (!fileSystem->FileExists(playerBinary))
+            {
+                ATOMIC_LOGERRORF("Managed player: %s does not exist", playerBinary.CString());
+                return false;
+            }
+
+        }
+        else
+        {
+            return false;
+        }
+
+        Vector<String> vargs;
+
+        String args = ToString("--player --project \"%s\"", projectPath.CString());
+
+        vargs = args.Split(' ');
+
+#ifdef ATOMIC_DEV_BUILD
+        vargs.Insert(0, ToString("\"%s/Resources/\"", tenv->GetRootSourceDir().CString()));
+#else
+
+#ifdef ATOMIC_PLATFORM_OSX
+        vargs.Insert(0, ToString("\"%s\"", (fileSystem->GetProgramDir() + "../Resources/").CString()));
+#else
+        vargs.Insert(0, ToString("\"%s\"", (fileSystem->GetProgramDir() + "Resources/").CString()));
+#endif
+
+#endif
+        vargs.Insert(0, "--resourcePrefix");
+
+        if (addArgs.Length() > 0)
+            vargs.Insert(0, addArgs.Split(' '));
+
+
+        String dump;
+        dump.Join(vargs, " ");
+        ATOMIC_LOGINFOF("Launching Broker %s %s", playerBinary.CString(), dump.CString());
+
+        IPC* ipc = GetSubsystem<IPC>();
+        playerBroker_ = ipc->SpawnWorker(playerBinary, vargs);
+
+        if (playerBroker_)
+        {
+            SubscribeToEvent(playerBroker_, E_IPCWORKERSTART, ATOMIC_HANDLER(IPCServerApp, HandleIPCWorkerStarted));
+            SubscribeToEvent(playerBroker_, E_IPCJSERROR, ATOMIC_HANDLER(IPCServerApp, HandleIPCJSError));
+            SubscribeToEvent(playerBroker_, E_IPCWORKEREXIT, ATOMIC_HANDLER(IPCServerApp, HandleIPCWorkerExit));
+            SubscribeToEvent(playerBroker_, E_IPCWORKERLOG, ATOMIC_HANDLER(IPCServerApp, HandleIPCWorkerLog));
+        }
+
+        return playerBroker_.NotNull();
+    }
+
+    void IPCServerApp::HandleIPCWorkerStarted(StringHash eventType, VariantMap& eventData)
+    {
+        VariantMap startupData;
+        DebugHud* debugHud = GetSubsystem<DebugHud>();
+
+        startupData["debugHudMode"] = debugHud ? debugHud->GetMode() : (unsigned)0;
+        startupData["debugHudProfilerMode"] = (unsigned)(debugHud ? debugHud->GetProfilerMode() : DEBUG_HUD_PROFILE_PERFORMANCE);
+
+        playerBroker_->PostMessage(E_IPCINITIALIZE, startupData);
+
+        playerEnabled_ = true;
+    }
+
+
+    void IPCServerApp::HandleIPCWorkerExit(StringHash eventType, VariantMap& eventData)
+    {
+        if (eventData[IPCWorkerExit::P_BROKER] == playerBroker_)
+        {
+            playerBroker_ = 0;
+            playerEnabled_ = false;
+
+            UnsubscribeFromEvent(E_IPCWORKERSTART);
+            UnsubscribeFromEvent(E_IPCPLAYERPAUSERESUMEREQUEST);
+            UnsubscribeFromEvent(E_IPCPLAYERUPDATESPAUSEDRESUMED);
+            UnsubscribeFromEvent(E_IPCPLAYERPAUSESTEPREQUEST);
+            UnsubscribeFromEvent(E_IPCPLAYEREXITREQUEST);
+        }
+        else
+        {
+            ATOMIC_LOGERROR("IPCServerApp::HandleIPCWorkerExit - Unknown Broker");
+        }
+    }
+
+    void IPCServerApp::HandleIPCWorkerLog(StringHash eventType, VariantMap& eventData)
+    {
+        using namespace IPCWorkerLog;
+
+        // convert to a player log
+
+        VariantMap playerLogData;
+
+        playerLogData["message"] = eventData[P_MESSAGE].GetString();
+        playerLogData["level"] = eventData[P_LEVEL].GetInt();
+
+        //SendEvent("EditorPlayerLog", playerLogData);
+
+    }
+
+    void IPCServerApp::HandleIPCJSError(StringHash eventType, VariantMap& eventData)
+    {
+
+    }
+    
+    void IPCServerApp::RequestTogglePlayerUpdatesPaused()
+    {
+        if (!playerBroker_)
+        {
+            return;
+        }
+
+        VariantMap noEventData;
+        playerBroker_->PostMessage(E_PAUSERESUMEREQUESTED, noEventData);
+    }
+
+    void IPCServerApp::RequestPlayerPauseStep()
+    {
+        if (!playerBroker_)
+        {
+            return;
+        }
+
+        VariantMap noEventData;
+        playerBroker_->PostMessage(E_PAUSESTEPREQUESTED, noEventData);
+    }
+
+    void IPCServerApp::RequestPlayerExit()
+    {
+        if (!playerBroker_)
+        {
+            return;
+        }
+
+        VariantMap noEventData;
+        playerBroker_->PostMessage(E_EXITREQUESTED, noEventData);
+    }
+
+}
+
+#pragma pop_macro("PostMessage")

+ 43 - 0
Source/AtomicApp/IPCServerApp.h

@@ -0,0 +1,43 @@
+#pragma once
+
+#include <Atomic/IPC/IPC.h>
+#include "AppBase.h"
+
+namespace Atomic
+{
+    
+    class IPCServerApp : public AppBase
+    {
+
+        ATOMIC_OBJECT(IPCServerApp, AppBase)
+
+    public:
+        /// Construct.
+        IPCServerApp(Context* context);
+        /// Destruct.
+        virtual ~IPCServerApp();
+
+        virtual void Setup();
+        virtual void Stop();
+
+        bool RunIPCPlayer(const String& projectName, const String& projectPath, const String &addArgs);
+
+        void RequestTogglePlayerUpdatesPaused();
+        void RequestPlayerPauseStep();
+        void RequestPlayerExit();
+
+
+    private:
+
+        void HandleIPCWorkerStarted(StringHash eventType, VariantMap& eventData);
+        void HandleIPCJSError(StringHash eventType, VariantMap& eventData);
+        void HandleIPCWorkerLog(StringHash eventType, VariantMap& eventData);
+        void HandleIPCWorkerExit(StringHash eventType, VariantMap& eventData);        
+        
+        SharedPtr<IPCBroker> playerBroker_;
+
+        bool playerEnabled_;
+
+    };
+
+}

+ 4 - 1
Source/AtomicNET/NETNative/CMakeLists.txt

@@ -27,7 +27,10 @@ add_library(AtomicNETNative SHARED ${SOURCE_FILES} ${CSHARP_BINDINGS_SOURCE})
 if (ATOMIC_DESKTOP)
     add_dependencies(AtomicNETNative AtomicToolCheckScripts)
 endif ()
-target_link_libraries(AtomicNETNative AtomicApp AtomicNETScriptBindings AtomicNETScript AtomicJS AtomicPlayerLib AtomicPlayerJS Atomic)
+target_link_libraries(AtomicNETNative AtomicApp AtomicNETScriptBindings AtomicNETScript AtomicJS AtomicPlayerLib AtomicPlayerJS Atomic ToolCore Poco)
+if (WIN32)
+target_link_libraries(AtomicNETNative Iphlpapi)
+endif ()
 target_include_directories(AtomicNETNative PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
 
 if (APPLE)

+ 99 - 0
Source/AtomicNET/NETNative/Desktop/NETIPCServerApp.cpp

@@ -0,0 +1,99 @@
+//
+// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+//
+// 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/Engine/Engine.h>
+#include <Atomic/IO/FileSystem.h>
+#include "NETCore.h"
+#include <AtomicNET/NETScript/NETScript.h>
+#include <AtomicNET/NETScript/CSComponentAssembly.h>
+
+#include "NETIPCServerApp.h"
+
+#ifdef ATOMIC_PLATFORM_OSX
+#include <unistd.h>
+#endif
+
+#ifdef ATOMIC_PLATFORM_WINDOWS
+#include <stdio.h>
+#endif
+
+namespace Atomic
+{
+
+    NETIPCServerApp::NETIPCServerApp(Context* context) :
+        IPCServerApp(context)
+    {
+
+    }
+
+    void NETIPCServerApp::Setup()
+    {
+        IPCServerApp::Setup();
+
+        // TODO: we should always have a --project for IPCPlayer, however it is doing 
+        // double duty right now as managed player
+        StringVector args = GetArguments();
+        if (!args.Contains("--project"))
+        {
+            engineParameters_["ResourcePrefixPaths"] = "AtomicPlayer_Resources";
+            engineParameters_["ResourcePaths"] = "AtomicResources";
+        }
+    }
+
+    int NETIPCServerApp::Initialize()
+    {
+        Setup();
+
+        RegisterNETScriptLibrary(context_);
+
+        if (exitCode_)
+            return exitCode_;
+
+        if (!engine_->Initialize(engineParameters_))
+        {
+            ErrorExit();
+            return exitCode_;
+        }
+
+        // TODO: Proper CSComponent assembly preload
+        // For now, user must ensure that assemblies are loaded via
+        // referencing their types or load assembly before initializing
+        // AtomicNET in application.  This is commented out as it
+        // causes an issue with double loading referenced assemblies
+        // and the ones in the project file
+        // CSComponentAssembly::PreloadClassAssemblies();
+
+        Start();
+
+        if (exitCode_)
+            return exitCode_;
+
+        return 0;
+    }
+    
+    NETIPCServerApp* NETIPCServerApp::CreateInternal()
+    {
+        return new NETIPCServerApp(NETCore::GetContext());
+    }
+
+}

+ 51 - 0
Source/AtomicNET/NETNative/Desktop/NETIPCServerApp.h

@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2014-2015, THUNDERBEAST GAMES LLC All rights reserved
+//
+// 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 <AtomicApp/IPCServerApp.h>
+
+namespace Atomic
+{
+
+    class NETIPCServerApp : public IPCServerApp
+    {
+        ATOMIC_OBJECT(NETIPCServerApp,IPCServerApp)
+
+    public:
+
+        static NETIPCServerApp* CreateInternal();
+
+        int Initialize();
+
+    private:
+
+        /// Construct.
+        NETIPCServerApp(Context* context);
+
+        /// Setup before engine initialization.
+        virtual void Setup();
+
+    };
+
+}

+ 30 - 1
Source/AtomicTool/AtomicTool.cpp

@@ -34,6 +34,10 @@
 #include <ToolCore/Command/Command.h>
 #include <ToolCore/Command/CommandParser.h>
 
+#include <Atomic/IPC/IPC.h>
+#include <AtomicNET/NETScript/NETScript.h>
+#include <ToolCore/NETTools/AtomicNETService.h>
+
 #include "AtomicTool.h"
 
 ATOMIC_DEFINE_APPLICATION_MAIN(AtomicTool::AtomicTool);
@@ -81,6 +85,11 @@ void AtomicTool::Setup()
                 engineParameters_["LogLevel"] = Variant(VariantType::VAR_INT, value);
                 i++;
             }
+            else if (argument == "logname")
+            {
+                engineParameters_["LogName"] = Variant(VariantType::VAR_STRING, value);
+                i++;
+            }
         }
     }
 
@@ -249,6 +258,22 @@ void AtomicTool::Start()
 
     }
 
+    if (cmd->RequiresNETService())
+    {
+        context_->RegisterSubsystem(new IPC(context_));
+        RegisterNETScriptLibrary(context_);
+        SharedPtr<AtomicNETService> netService(new AtomicNETService(context_));
+
+        if (!netService->Start())
+        {
+            netService = nullptr;
+            ErrorExit(ToString("Unable to start AtomicNETService"));
+            return;
+        }
+
+        context_->RegisterSubsystem(netService);
+    }
+
     command_ = cmd;
 
     // BEGIN LICENSE MANAGEMENT
@@ -272,7 +297,11 @@ void AtomicTool::Start()
 
 void AtomicTool::Stop()
 {
-
+    IPC* ipc = GetSubsystem<IPC>();
+    if (ipc)
+    {
+        ipc->Shutdown();
+    }
 }
 
 void AtomicTool::ErrorExit(const String& message)

+ 48 - 0
Source/ThirdParty/TurboBadger/tb_toggle_container.cpp

@@ -50,6 +50,15 @@ TBSection::TBSection()
     m_layout.SetGravity(WIDGET_GRAVITY_ALL);
     m_layout.SetLayoutSize(LAYOUT_SIZE_AVAILABLE);
 
+    // ATOMIC BEGIN
+    m_toggle_container_layout.SetSkinBg(TBIDC("TBSection.layout"), WIDGET_INVOKE_INFO_NO_CALLBACKS);
+    m_toggle_container_layout.SetAxis(AXIS_Y);
+    m_toggle_container_layout.SetGravity(WIDGET_GRAVITY_ALL);
+    m_toggle_container_layout.SetLayoutSize(LAYOUT_SIZE_AVAILABLE);
+
+    m_toggle_container.AddChild(&m_toggle_container_layout);
+    // ATOMIC END
+
     AddChild(&m_layout);
     m_layout.AddChild(&m_header);
     m_layout.AddChild(&m_toggle_container);
@@ -57,6 +66,13 @@ TBSection::TBSection()
 
 TBSection::~TBSection()
 {
+    // ATOMIC BEGIN
+    while (TBWidget* child = m_toggle_container_layout.GetFirstChild())
+    {
+        m_toggle_container_layout.RemoveChild(child);
+    }
+    m_toggle_container.RemoveChild(&m_toggle_container_layout);
+    // ATOMIC END
     m_layout.RemoveChild(&m_toggle_container);
     m_layout.RemoveChild(&m_header);
     RemoveChild(&m_layout);
@@ -68,6 +84,38 @@ void TBSection::SetValue(int value)
     m_toggle_container.SetValue(value);
 }
 
+// ATOMIC BEGIN
+void TBSection::AddToggleChild(TBWidget *child, WIDGET_Z z, WIDGET_INVOKE_INFO info)
+{
+    m_toggle_container_layout.AddChild(child, z, info);
+}
+
+void TBSection::AddToggleChildRelative(TBWidget *child, WIDGET_Z_REL z, TBWidget *reference, WIDGET_INVOKE_INFO info)
+{
+    m_toggle_container_layout.AddChildRelative(child, z, reference, info);
+}
+
+void TBSection::RemoveToggleChild(TBWidget* child)
+{
+    m_toggle_container_layout.RemoveChild(child);
+}
+
+void TBSection::DeleteAllToggleChildren()
+{
+    m_toggle_container_layout.DeleteAllChildren();
+}
+
+TBWidget* TBSection::GetFirstToggleChild()
+{
+    return m_toggle_container_layout.GetFirstChild();
+}
+
+TBWidget* TBSection::GetToggleWidgetById(const TBID &id)
+{
+    return m_toggle_container_layout.GetWidgetByTouchId(id);
+}
+// ATOMIC END
+
 void TBSection::OnProcessAfterChildren()
 {
     if (m_pending_scroll)

+ 13 - 0
Source/ThirdParty/TurboBadger/tb_toggle_container.h

@@ -104,6 +104,15 @@ public:
     virtual void SetValue(int value);
     virtual int GetValue() { return m_toggle_container.GetValue(); }
 
+    // ATOMIC BEGIN
+    virtual void AddToggleChild(TBWidget *child, WIDGET_Z z = WIDGET_Z_TOP, WIDGET_INVOKE_INFO info = WIDGET_INVOKE_INFO_NORMAL);
+    virtual void AddToggleChildRelative(TBWidget *child, WIDGET_Z_REL z, TBWidget *reference, WIDGET_INVOKE_INFO info = WIDGET_INVOKE_INFO_NORMAL);
+    virtual void RemoveToggleChild(TBWidget* child);
+    virtual void DeleteAllToggleChildren();
+    virtual TBWidget* GetFirstToggleChild();
+    virtual TBWidget* GetToggleWidgetById(const TBID &id);
+    // ATOMIC END
+
     virtual TBWidget *GetContentRoot() { return m_toggle_container.GetContentRoot(); }
     virtual void OnProcessAfterChildren();
 
@@ -113,6 +122,10 @@ private:
     TBSectionHeader m_header;
     TBToggleContainer m_toggle_container;
     bool m_pending_scroll;
+
+    // ATOMIC BEGIN
+    TBLayout m_toggle_container_layout;
+    // ATOMIC END
 };
 
 }; // namespace tb

+ 9 - 0
Source/ToolCore/Assets/AssetDatabase.cpp

@@ -754,6 +754,15 @@ bool AssetDatabase::GenerateCache(bool clean)
         if (!CleanCache())
             return false;
     }
+    else
+    {
+        FileSystem* fileSystem = GetSubsystem<FileSystem>();
+        String cachePath = GetCachePath();
+        if (!fileSystem->DirExists(cachePath))
+        {
+            fileSystem->CreateDir(cachePath);
+        }
+    }
 
     ReimportAllAssets();
 

+ 4 - 3
Source/ToolCore/Assets/TextureImporter.cpp

@@ -97,9 +97,10 @@ bool TextureImporter::Import()
         }
 
         if (image->SaveDDS(compressedPath))
-        {            
-            Renderer * renderer = GetSubsystem<Renderer>();
-            renderer->ReloadTextures();
+        {
+            Renderer* renderer = GetSubsystem<Renderer>();
+            if (renderer != NULL) // May be importing through headless process
+                renderer->ReloadTextures();
         }
     }
 

+ 88 - 5
Source/ToolCore/Build/BuildBase.cpp

@@ -30,6 +30,7 @@
 #include "../ToolEnvironment.h"
 #include "../Assets/Asset.h"
 #include "../Assets/AssetDatabase.h"
+#include "../Import/ImportConfig.h"
 
 #include "BuildSystem.h"
 #include "BuildEvents.h"
@@ -384,19 +385,101 @@ void BuildBase::BuildAllProjectResourceEntries()
         String projectResourceDir = projectResourceDir_[i];
         fileIncludedResourcesLog_->WriteLine("\nBuildBase::BuildAllProjectResourceEntries - Project resources being included from: " + projectResourceDir);
 
-        Vector<String> fileNamesInProject;
         FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
+        bool compressTextures = false;
+
+        if (ImportConfig::IsLoaded())
+        {
+            VariantMap tiParameters;
+            ImportConfig::ApplyConfig(tiParameters);
+            VariantMap::ConstIterator itr = tiParameters.Begin();
+
+            for (; itr != tiParameters.End(); itr++)
+            {
+                if (itr->first_ == "tiProcess_CompressTextures")
+                    compressTextures = itr->second_.GetBool();
+            }
+        }
+
+        Vector<String> assetFileNamesInProject;
+        fileSystem->ScanDir(assetFileNamesInProject, projectResourceDir, "*.asset", SCAN_FILES, true);
+
+        Vector<String> cachedFileNames;
+
+        AssetDatabase* db = GetSubsystem<AssetDatabase>();
+        String cachePath = db->GetCachePath();
+        Vector<String> filesInCacheFolder;
+        Vector<String> fileNamesInCacheFolder;
+
+        Vector<String> compressedResources;
+
+        if (assetFileNamesInProject.Size() > 0)
+        {
+            fileSystem->ScanDir(filesInCacheFolder, cachePath, "*.*", SCAN_FILES, false);
+
+            for (unsigned j = 0; j < filesInCacheFolder.Size(); ++j)
+            {
+
+                const String &fileName = GetFileNameAndExtension(filesInCacheFolder[j]);
+                fileNamesInCacheFolder.Push(fileName);
+            }
+        }
+
+
+        for (unsigned i = 0; i < assetFileNamesInProject.Size(); i++)
+        {
+            unsigned assetSubStrPos = assetFileNamesInProject[i].Find(".asset");
+
+            String baseFileName = assetFileNamesInProject[i].Substring(0, assetSubStrPos);
+
+            String extension = GetExtension(baseFileName);
+
+            if (compressTextures && (extension == ".jpg" || extension == ".png" || extension == ".tga"))
+            {
+                SharedPtr<File> file(new File(context_, project_->GetResourcePath() + assetFileNamesInProject[i]));
+                SharedPtr<JSONFile> json(new JSONFile(context_));
+                json->Load(*file);
+                file->Close();
+
+                JSONValue& root = json->GetRoot();
+                int test = root.Get("version").GetInt();
+                assert(root.Get("version").GetInt() == ASSET_VERSION);
+
+                String guid = root.Get("guid").GetString();
+
+                String dds = guid + ".dds";
+
+                if (fileNamesInCacheFolder.Contains(dds))
+                {
+                    compressedResources.Push(baseFileName);
+                }
+            }
+        }
+
+        Vector<String> fileNamesInProject;        
         fileSystem->ScanDir(fileNamesInProject, projectResourceDir, "*.*", SCAN_FILES, true);
 
         for (unsigned i = 0; i < fileNamesInProject.Size(); i++)
         {
-            AddToResourcePackager(fileNamesInProject[i], projectResourceDir);
+            String fName = fileNamesInProject[i];
 
-            if (verbose_)
+            if (compressedResources.Contains(fName))
+            {
+                if (autoLog_)
+                {
+                    ATOMIC_LOGINFO(ToString("Project Resource not included because compressed version exists: %s%s", projectResourceDir.CString(), fileNamesInProject[i].CString()));
+                }
+            }
+            else
             {
-                ATOMIC_LOGINFO(ToString("Project Resource Added: %s%s", projectResourceDir.CString(), fileNamesInProject[i].CString()));
+                AddToResourcePackager(fileNamesInProject[i], projectResourceDir);
+                
+                if (verbose_)
+                {
+                    ATOMIC_LOGINFO(ToString("Project Resource Added: %s%s", projectResourceDir.CString(), fileNamesInProject[i].CString()));
+                }
             }
-
         }
     }
 }

+ 8 - 5
Source/ToolCore/Command/BuildCmd.cpp

@@ -36,7 +36,10 @@
 namespace ToolCore
 {
 
-BuildCmd::BuildCmd(Context* context) : Command(context)
+BuildCmd::BuildCmd(Context* context) :
+    Command(context),
+    autoLog_(false),
+    verbose_(false)
 {
 
 }
@@ -68,7 +71,7 @@ bool BuildCmd::ParseInternal(const Vector<String>& arguments, unsigned startInde
     for (int i = startIndex + 2; i < arguments.Size(); ++i)
     {
         String option = arguments[i].ToLower();
-        
+
         if (option == "-tag")
         {
             if (arguments.Size() == i + 1)
@@ -81,10 +84,9 @@ bool BuildCmd::ParseInternal(const Vector<String>& arguments, unsigned startInde
         {
             autoLog_ = true;
         }
-        else
+        else if (option == "-verbose")
         {
-            errorMsg = "Invalid option: " + option;
-            return false;
+            verbose_ = true;
         }
     }
 
@@ -132,6 +134,7 @@ void BuildCmd::Run()
         buildBase->SetAssetBuildTag(assetsBuildTag_);
     }
     buildBase->SetAutoLog(autoLog_);
+    buildBase->SetVerbose(verbose_);
 
     // add it to the build system
     BuildSystem* buildSystem = GetSubsystem<BuildSystem>();

+ 2 - 1
Source/ToolCore/Command/BuildCmd.h

@@ -47,11 +47,12 @@ protected:
 private:
 
     void HandleBuildComplete(StringHash eventType, VariantMap& eventData);
-    
+
 
     String buildPlatform_;
     String assetsBuildTag_;
     bool autoLog_;
+    bool verbose_;
 
 };
 

+ 2 - 0
Source/ToolCore/Command/CacheCmd.h

@@ -48,6 +48,8 @@ public:
 
     void Run();
 
+    bool RequiresNETService() { return true; }
+
 protected:
 
     bool ParseInternal(const Vector<String>& arguments, unsigned startIndex, String& errorMsg);

+ 2 - 0
Source/ToolCore/Command/Command.h

@@ -62,6 +62,8 @@ public:
 
     virtual bool RequiresProjectLoad() { return true; }
 
+    virtual bool RequiresNETService() { return false; }
+
     /// Loads project into ToolSystem returning true on success
     bool LoadProject();