Josh Engebretson 8 years ago
parent
commit
752ee61c13

+ 6 - 2
Source/Atomic/Graphics/Batch.cpp

@@ -186,6 +186,10 @@ void Batch::Prepare(View* view, Camera* camera, bool setModelTransform, bool all
     Light* light = lightQueue_ ? lightQueue_->light_ : 0;
     Texture2D* shadowMap = lightQueue_ ? lightQueue_->shadowMap_ : 0;
 
+    // ATOMIC BEGIN
+    Scene* scene = view->GetScene();
+    // ATOMIC END
+
     // Set shaders first. The available shader parameters and their register/uniform positions depend on the currently set shaders
     graphics->SetShaders(vertexShader_, pixelShader_);
 
@@ -624,9 +628,9 @@ void Batch::Prepare(View* view, Camera* camera, bool setModelTransform, bool all
             }
         }
 
-        if (lightmapTilingOffset_)
+        if (lightmapTilingOffset_ && scene)
         {
-            graphics->SetLightmapTexture(lightmapTextureID_);
+            scene->SetLightmapTexture(lightmapTextureID_);
         }
 
         // ATOMIC END

+ 0 - 43
Source/Atomic/Graphics/Graphics.cpp

@@ -449,49 +449,6 @@ void Graphics::RaiseWindow()
     if (window_)
         SDL_RaiseWindow(window_);
 }
-
-// FIXME: Hack
-#include "./Resource/ResourceCache.h"
-static PODVector<Texture2D*> lightmapTextures;
-
-void Graphics::SetLightmapTexture(unsigned id)
-{
-    const unsigned numLightmaps = 25;
-    static bool initialized = false;
-
-    if (!lightmapTextures.Size() && !initialized)
-    {
-        initialized = true;
-
-        ResourceCache* cache = GetSubsystem<ResourceCache>();
-
-        for (unsigned i = 0; i < numLightmaps; i++)
-        {
-            Texture2D* texture = cache->GetResource<Texture2D>(ToString("Textures/Scene_Lightmap%u.png", i));
-
-            if (!texture)
-                break;
-
-            // FILTER_NEAREST is good for testing lightmap, without bilinear artifacts
-            // texture->SetFilterMode(FILTER_NEAREST);
-            texture->SetNumLevels(1); // No mipmaps
-
-            texture->SetAddressMode(COORD_U, ADDRESS_CLAMP);
-            texture->SetAddressMode(COORD_V, ADDRESS_CLAMP);
-
-            lightmapTextures.Push(texture);
-
-        }
-
-    }
-
-    if (id >= lightmapTextures.Size())
-        return;
-
-    SetTexture(TU_EMISSIVE, lightmapTextures[id]);
-
-}
-
 // ATOMIC END
 
 }

+ 0 - 2
Source/Atomic/Graphics/Graphics.h

@@ -582,8 +582,6 @@ public:
     static unsigned GetSinglePassPrimitives() { return numSinglePassPrimitives_; }
     /// Set number of single render pass primitives drawn this frame (D3D9 Only)
     static void SetSinglePassPrimitives(unsigned value) { numSinglePassPrimitives_ = value; }
-
-    void SetLightmapTexture(unsigned id);
   
     // ATOMIC END
 

+ 98 - 0
Source/Atomic/Scene/Scene.cpp

@@ -46,6 +46,10 @@
 #include "../DebugNew.h"
 
 // ATOMIC BEGIN
+#include "../Graphics/Graphics.h"
+#include "../Graphics/Texture2D.h"
+#include "../Graphics/StaticModel.h"
+#include "../IO/FileSystem.h"
 #include "PrefabComponent.h"
 // ATOMIC END
 
@@ -1281,6 +1285,13 @@ void Scene::FinishLoading(Deserializer* source)
         fileName_ = source->GetName();
         checksum_ = source->GetChecksum();
     }
+
+    // ATOMIC BEGIN
+    if (fileName_.Length())
+    {
+        LoadLightmaps();
+    }
+    // ATOMIC END
 }
 
 void Scene::FinishSaving(Serializer* dest) const
@@ -1547,4 +1558,91 @@ void RegisterSceneLibrary(Context* context)
     // ATOMIC END
 }
 
+// ATOMIC BEGIN
+void Scene::LoadLightmaps(bool reload)
+{
+    // If we're running headless, don't load lightmap textures
+    if (!GetSubsystem<Graphics>())
+    {
+        return;
+    }
+
+    if (lightmaps_.Size() && !reload)
+    {
+        return;
+    }
+
+    lightmaps_.Clear();
+
+    PODVector<StaticModel*> staticModels;
+    GetComponents<StaticModel>(staticModels, true);
+
+    int maxLightMap = -1;
+
+    for (int i = 0; i < (int) staticModels.Size(); i++)
+    {
+        StaticModel* staticModel = staticModels[i];
+
+        if (!staticModel->GetLightmap())
+            continue;
+
+        int lightmapIndex = (int) staticModel->GetLightmapIndex();
+
+        if (lightmapIndex > maxLightMap)
+        {
+            maxLightMap = lightmapIndex;
+        }
+    }
+
+    if (maxLightMap < 0)
+        return;
+
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
+
+    String sceneName = Atomic::GetFileName(GetFileName());
+    String lightmapFolder = ToString("AtomicGlow/Scenes/%s/Lightmaps/", sceneName.CString());
+
+    for (int i = 0; i < maxLightMap + 1; i++)
+    {
+        String textureName = ToString("%sLightmap%i.png", lightmapFolder.CString(), i);
+        Texture2D* texture = cache->GetResource<Texture2D>(textureName);
+
+        if (!texture)
+        {
+            ATOMIC_LOGWARNINGF("Scene::PreloadLightmaps() - Unable to load texture %s", textureName.CString());
+            lightmaps_.Push(SharedPtr<Texture2D>((Texture2D*)0));
+            continue;
+        }
+
+        // FILTER_NEAREST is good for testing lightmap, without bilinear artifacts
+        // texture->SetFilterMode(FILTER_NEAREST);
+        texture->SetNumLevels(1); // No mipmaps
+
+        texture->SetAddressMode(COORD_U, ADDRESS_CLAMP);
+        texture->SetAddressMode(COORD_V, ADDRESS_CLAMP);
+
+        lightmaps_.Push(SharedPtr<Texture2D>(texture));
+    }
+
+}
+
+void Scene::SetLightmapTexture(unsigned id)
+{
+    // Store graphics subsystem into static variable for speed
+    // NOTE: If Atomic needs to support changing graphics subsystem on the fly
+    // this will need to be changed
+    static Graphics* graphics = GetSubsystem<Graphics>();
+
+    if (id >= lightmaps_.Size())
+    {
+        graphics->SetTexture(TU_EMISSIVE, 0);
+        return;
+    }
+
+    graphics->SetTexture(TU_EMISSIVE, lightmaps_[id]);
+
+}
+
+// ATOMIC END
+
 }

+ 13 - 0
Source/Atomic/Scene/Scene.h

@@ -35,6 +35,10 @@ namespace Atomic
 class File;
 class PackageFile;
 
+// ATOMIC BEGIN
+class Texture2D;
+// ATOMIC END
+
 static const unsigned FIRST_REPLICATED_ID = 0x1;
 static const unsigned LAST_REPLICATED_ID = 0xffffff;
 static const unsigned FIRST_LOCAL_ID = 0x01000000;
@@ -256,6 +260,11 @@ public:
     /// Mark a node dirty in scene replication states. The node does not need to have own replication state yet.
     void MarkReplicationDirty(Node* node);
 
+    // ATOMIC BEGIN
+    void LoadLightmaps(bool reload = false);
+    void SetLightmapTexture(unsigned id);
+    // ATOMIC END
+
 private:
     /// Handle the logic update event to update the scene, if active.
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
@@ -276,6 +285,10 @@ private:
     /// Preload resources from a JSON scene or object prefab file.
     void PreloadResourcesJSON(const JSONValue& value);
 
+    // ATOMIC BEGIN
+    Vector<SharedPtr<Texture2D>> lightmaps_;
+    // ATOMIC END
+
     /// Replicated scene nodes by ID.
     HashMap<unsigned, Node*> replicatedNodes_;
     /// Local scene nodes by ID.

+ 14 - 1
Source/AtomicEditor/Components/GlowComponent.cpp

@@ -23,9 +23,13 @@
 #include <Atomic/IO/Log.h>
 #include <Atomic/Core/Context.h>
 
+#include <ToolCore/ToolSystem.h>
+#include <ToolCore/Project/Project.h>
+
 #include <AtomicGlow/GlowService/GlowService.h>
 #include "GlowComponent.h"
 
+using namespace ToolCore;
 using namespace AtomicGlow;
 
 namespace AtomicEditor
@@ -95,7 +99,16 @@ bool GlowComponent::Bake()
     CopyToGlowSettings(settings);
     settings.Validate();
     SetFromGlowSettings(settings);
-    return glowService->Bake(GetScene(), settings);
+
+    ToolSystem* toolSystem = GetSubsystem<ToolSystem>();
+    if (!toolSystem)
+        return false;
+
+    Project* project = toolSystem->GetProject();
+    if (!project)
+        return false;
+
+    return glowService->Bake(project->GetProjectPath(), GetScene(), settings);
 }
 
 void GlowComponent::HandleAtomicGlowBakeCancel(StringHash eventType, VariantMap& eventData)

+ 0 - 2
Source/AtomicEditor/Components/GlowComponent.h

@@ -96,8 +96,6 @@ private:
     float aoMin_;
     float aoMultiply_;
 
-    WeakPtr<SceneEditor3D> sceneEditor_;
-
 };
 
 

+ 0 - 6
Source/AtomicGlow/Common/GlowSettings.h

@@ -67,9 +67,6 @@ namespace AtomicGlow
         float aoMin_;
         float aoMultiply_;
 
-
-        String projectPath_;
-
         GlowSettings()
         {
             SetDefaults();
@@ -136,9 +133,6 @@ namespace AtomicGlow
 
         void SetDefaults(GlowPreset preset = GLOW_PRESET_FAST_LOW_QUALITY)
         {
-            // fix me
-            projectPath_ = "/Users/jenge/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1/";
-
             // common settings
 
             // lightmap size

+ 42 - 18
Source/AtomicGlow/GlowApplication/AtomicGlowApp.cpp

@@ -86,17 +86,18 @@ namespace AtomicGlow
 
         if (cmd == "bake")
         {
-
             timer_.Reset();
 
+            String sceneName = eventData["scenename"].GetString();
+
             // settings
             VectorBuffer settingsBuffer = eventData["settings"].GetVectorBuffer();
             GlobalGlowSettings.Unpack(settingsBuffer);
 
-            ATOMIC_LOGINFO("AtomicGlow bake received, baking");
+            ATOMIC_LOGINFOF("AtomicGlow baking scene: %s", sceneName.CString());
 
             sceneBaker_->SetStandaloneMode(false);
-            sceneBaker_->LoadScene("Scenes/Scene.scene");
+            sceneBaker_->LoadScene(sceneName);
 
             using namespace IPCCmdResult;
             result["result"] = "success";
@@ -209,12 +210,14 @@ namespace AtomicGlow
 
     void AtomicGlowApp::Start()
     {
+        // IMPORTANT!!!
+        // This needs to be refactored, see // ATOMIC GLOW HACK in AssetDatabase.cpp
+        // SharedPtr<ResourceMapRouter> router(new ResourceMapRouter(context_, "__atomic_ResourceCacheMap.json"));
+        // IMPORTANT!!!
+
         if (exitCode_)
             return;
 
-        // Initialize resource mapper
-        SharedPtr<ResourceMapRouter> router(new ResourceMapRouter(context_, "__atomic_ResourceCacheMap.json"));
-
         if (!engine_->Initialize(engineParameters_))
         {
             ErrorExit();
@@ -231,12 +234,26 @@ namespace AtomicGlow
 
         bool ipc = IPCClientApp::Initialize(arguments_);
 
-        sceneBaker_ = new SceneBaker(context_);
+        ToolSystem* tsystem = GetSubsystem<ToolSystem>();
+
+        // Load project, in read only mode
+        if (!tsystem->LoadProject(projectPath_, true))
+        {
+            ErrorExit(ToString("Unable to load project %s", projectPath_.CString()));
+            return;
+        }
+
+        sceneBaker_ = new SceneBaker(context_, projectPath_);
 
         if (!ipc)
         {
-            // TODO: proper standalone scene arg
-            sceneBaker_->LoadScene("Scenes/Scene.scene");
+            if (!sceneBaker_->LoadScene(scenePath_))
+            {
+                String message = scenePath_.Length() ? "AtomicGlowApp::Start() - standalone mode unable to load scene: " + scenePath_:
+                                                       "AtomicGlowApp::Start() - standalone mode scene not specified";
+
+                ErrorExit(message);
+            }
         }
 
     }
@@ -261,8 +278,6 @@ namespace AtomicGlow
             return;
         }
 
-        String projectPath;
-
         for (unsigned i = 0; i < arguments_.Size(); ++i)
         {
             if (arguments_[i].Length() > 1)
@@ -277,26 +292,35 @@ namespace AtomicGlow
                         value = GetPath(value);
                     }
 
-                    if (fileSystem->DirExists(value))
-                    {
-
-                    }
-                    else
+                    if (!fileSystem->DirExists(value))
                     {
                         ErrorExit(ToString("%s project path does not exist", value.CString()));
+                        return;
                     }
 
-                    projectPath = AddTrailingSlash(value);
+                    projectPath_ = AddTrailingSlash(value);
+
+                }
+                else if (argument == "--scene" && value.Length())
+                {
+                    scenePath_ = value;
 
                 }
+
             }
         }
 
+        if (!projectPath_.Length())
+        {
+            ErrorExit(ToString("%s project path not specified"));
+            return;
+        }
+
         engineParameters_.InsertNew("LogName", fileSystem->GetAppPreferencesDir("AtomicEditor", "Logs") + "AtomicGlow.log");
 
     #ifdef ATOMIC_DEV_BUILD
         engineParameters_["ResourcePrefixPaths"] = env->GetRootSourceDir() + "/Resources/";
-        engineParameters_["ResourcePaths"] = ToString("CoreData;EditorData;%sResources;%sCache", projectPath.CString(), projectPath.CString());
+        engineParameters_["ResourcePaths"] = ToString("CoreData;EditorData;%sResources;%sCache", projectPath_.CString(), projectPath_.CString());
     #endif
 
         // TODO: change to using new workerthreadcount command line arg

+ 3 - 0
Source/AtomicGlow/GlowApplication/AtomicGlowApp.h

@@ -56,6 +56,9 @@ namespace AtomicGlow
 
         Timer timer_;
 
+        String projectPath_;
+        String scenePath_;
+
         SharedPtr<SceneBaker> sceneBaker_;
 
     };

+ 33 - 6
Source/AtomicGlow/GlowService/GlowService.cpp

@@ -58,6 +58,12 @@ void GlowService::OnGlowProcessExited()
         }
     }
 
+    if (scene_)
+    {
+        scene_->LoadLightmaps(true);
+    }
+
+    projectPath_ = String::EMPTY;
     glowProcess_ = 0;
     scene_ = 0;
 
@@ -85,7 +91,7 @@ void GlowService::OnBakeSuccess()
 
     VariantMap eventData;
     eventData[P_RESULT] = "success";
-    eventData[P_SUCCESS] = true;
+    eventData[P_SUCCESS] = true;    
 
     SendEvent(E_ATOMICGLOWSERVICEBAKERESULT, eventData);
 
@@ -148,7 +154,7 @@ void GlowService::ProcessBakeData(VectorBuffer& bakeData)
 
 }
 
-bool GlowService::Bake(Scene* scene, const GlowSettings& settings)
+bool GlowService::Bake(const String& projectPath, Scene* scene, const GlowSettings& settings)
 {
     if (!scene)
     {
@@ -156,6 +162,21 @@ bool GlowService::Bake(Scene* scene, const GlowSettings& settings)
         return false;
     }
 
+    String sceneName = scene->GetFileName();
+
+    if (!sceneName.Length())
+    {
+        ATOMIC_LOGERROR("GlowService::Bake() - Called with unnamed scene");
+        return false;
+    }
+
+    if (!projectPath.Length())
+    {
+        ATOMIC_LOGERROR("GlowService::Bake() - zero length projectPath");
+        return false;
+    }
+
+
     if (glowProcess_.NotNull())
     {
         ATOMIC_LOGERROR("GlowService::Bake() - Called with existing glow process");
@@ -167,17 +188,26 @@ bool GlowService::Bake(Scene* scene, const GlowSettings& settings)
 
     glowProcess_ = new GlowProcess(context_);
 
-    if (!glowProcess_->Start(glowBinaryPath_, glowBaseArgs_))
+    projectPath_ = projectPath;
+
+    StringVector args;
+    args.Push("--project");
+    args.Push(projectPath_);
+    args += glowBaseArgs_;
+
+    if (!glowProcess_->Start(glowBinaryPath_, args))
     {
         ATOMIC_LOGERROR("GlowService::Bake() - Glow process failed to start");
         return false;
     }
 
     scene_ = scene;
+    projectPath_ = projectPath;
 
     using namespace IPCCmd;
     VariantMap cmdMap;
     cmdMap[P_COMMAND] = "bake";
+    cmdMap["scenename"] = sceneName;
 
     // settings
     VectorBuffer settingsBuffer;
@@ -224,9 +254,6 @@ bool GlowService::Start()
     glowBinaryPath_ = "/Users/jenge/Dev/atomic/build-AtomicGameEngine-Desktop-Release/Source/AtomicGlow/AtomicGlow";
 #endif
 
-    glowBaseArgs_.Push("--project");
-    glowBaseArgs_.Push("/Users/jenge/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1");
-
     return true;
 
 }

+ 2 - 1
Source/AtomicGlow/GlowService/GlowService.h

@@ -48,7 +48,7 @@ public:
 
     bool Start();
 
-    bool Bake(Scene* scene, const GlowSettings& settings);
+    bool Bake(const String& projectPath, Scene* scene, const GlowSettings& settings);
 
     void CancelBake();
 
@@ -69,6 +69,7 @@ private:
 
     String result_;
     bool bakeCanceled_;
+    String projectPath_;
 
 };
 

+ 28 - 8
Source/AtomicGlow/Kernel/LightMapPacker.cpp

@@ -22,6 +22,7 @@
 #include <ThirdParty/STB/stb_rect_pack.h>
 
 #include <Atomic/IO/Log.h>
+#include <Atomic/IO/FileSystem.h>
 #include <Atomic/Resource/Image.h>
 
 #include "BakeMesh.h"
@@ -246,25 +247,44 @@ void LightMapPacker::EmitLightmap(unsigned lightMapID)
 
 }
 
-void LightMapPacker::SaveLightmaps()
-{
+bool LightMapPacker::SaveLightmaps(const String &projectPath, const String &scenePath)
+{    
+    FileSystem* fileSystem = GetSubsystem<FileSystem>();
+
     for (unsigned i = 0; i < lightMaps_.Size(); i++)
     {
         LightMap* lightmap = lightMaps_[i];
 
-#ifdef ATOMIC_PLATFORM_WINDOWS
-        String filename = ToString("C:/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1/Resources/Textures/Scene_Lightmap%u.png", lightmap->GetID());
-#else
         const char* format = GlobalGlowSettings.outputFormat_ == GLOW_OUTPUT_PNG ? "png" : "dds";
-        String filename = ToString("%s/Resources/Textures/Scene_Lightmap%u.%s", GlobalGlowSettings.projectPath_.CString(), lightmap->GetID(), format);
-#endif
 
-        //ATOMIC_LOGINFOF("Saving Lightmap: %s", filename.CString());
+        // Note: 2 scenes with the same name in project will collide for lightmap storage
+        // this shouldn't be a general issue, and will be addressed once lightmaps are processed
+        // to Cache with GUID
+        String sceneName = GetFileName(scenePath);
+
+        String folder = ToString("%sResources/AtomicGlow/Scenes/%s/Lightmaps/",  projectPath.CString(), sceneName.CString());
+
+        if (!fileSystem->DirExists(folder))
+        {
+            fileSystem->CreateDirsRecursive(folder);
+        }
+
+        if (!fileSystem->DirExists(folder))
+        {
+            ATOMIC_LOGERRORF("LightMapPacker::SaveLightmaps - Unable to create folder: %s", folder.CString());
+            return false;
+        }
+
+        String filename = ToString("%sLightmap%u.%s", folder.CString(), lightmap->GetID(), format);
+
+        ATOMIC_LOGINFOF("Saving Lightmap: %s", filename.CString());
 
         GlobalGlowSettings.outputFormat_ == GLOW_OUTPUT_PNG ? lightmap->GetImage()->SavePNG(filename) : lightmap->GetImage()->SaveDDS(filename);
 
     }
 
+    return true;
+
 }
 
 static bool CompareRadianceMap(RadianceMap* lhs, RadianceMap* rhs)

+ 1 - 1
Source/AtomicGlow/Kernel/LightMapPacker.h

@@ -54,7 +54,7 @@ class LightMapPacker : public Object
 
     void Pack();
 
-    void SaveLightmaps();
+    bool SaveLightmaps(const String& projectPath, const String& scenePath);
 
 private:
 

+ 60 - 8
Source/AtomicGlow/Kernel/SceneBaker.cpp

@@ -28,6 +28,7 @@
 
 #include <Atomic/Core/WorkQueue.h>
 #include <Atomic/IO/Log.h>
+#include <Atomic/IO/FileSystem.h>
 #include <Atomic/Resource/ResourceCache.h>
 #include <Atomic/Graphics/Zone.h>
 #include <Atomic/Graphics/Light.h>
@@ -43,9 +44,10 @@
 namespace AtomicGlow
 {
 
-SceneBaker::SceneBaker(Context* context) : Object(context),
+SceneBaker::SceneBaker(Context* context, const String &projectPath) : Object(context),
     currentLightMode_(GLOW_LIGHTMODE_UNDEFINED),
     currentGIPass_(0),
+    projectPath_(AddTrailingSlash(projectPath)),
     standaloneMode_(true)
 {
     embreeScene_ = new EmbreeScene(context_);
@@ -64,13 +66,9 @@ bool SceneBaker::SaveLitScene()
         return false;
     }
 
-#ifdef ATOMIC_PLATFORM_WINDOWS
-    String scenefilename = ToString("C:/Dev/atomic/AtomicExamplesPrivate/AtomicGlowTests/TestScene1/Resources/Scenes/LitScene.scene");
-#else
-    String scenefilename = ToString("%s/Resources/Scenes/LitScene.scene", GlobalGlowSettings.projectPath_.CString());
-#endif
+    String sceneFilename = AddTrailingSlash(projectPath_) + "Resources/" + scene_->GetFileName();
 
-    File saveFile(context_, scenefilename, FILE_WRITE);
+    File saveFile(context_, sceneFilename, FILE_WRITE);
     return scene_->SaveXML(saveFile);
 }
 
@@ -125,7 +123,16 @@ bool SceneBaker::GenerateLightmaps()
 
     packer->Pack();
 
-    packer->SaveLightmaps();
+    if (!packer->SaveLightmaps(projectPath_, scene_->GetFileName()))
+    {
+        return false;
+    }
+
+    if (standaloneMode_)
+    {
+        if (!SaveLitScene())
+            return false;
+    }
 
     return WriteBakeData(bakeData_);
 
@@ -391,6 +398,51 @@ bool SceneBaker::LoadScene(const String& filename)
 
         if (staticModel->GetModel() && (staticModel->GetLightmap() ||staticModel->GetCastShadows()))
         {
+            Model* model = staticModel->GetModel();
+
+            for (unsigned i = 0; i < model->GetNumGeometries(); i++)
+            {
+                Geometry* geo = model->GetGeometry(i, 0);
+
+                if (!geo)
+                {
+                    ATOMIC_LOGERRORF("SceneBaker::LoadScene - model without geometry: %s", model->GetName().CString());
+                    return false;
+                }
+
+                const unsigned char* indexData = 0;
+                unsigned indexSize = 0;
+                unsigned vertexSize = 0;
+                const unsigned char* vertexData = 0;
+                const PODVector<VertexElement>* elements = 0;
+
+                geo->GetRawData(vertexData, vertexSize, indexData, indexSize, elements);
+
+                if (!indexData || !indexSize || !vertexData || !vertexSize || !elements)
+                {
+                    ATOMIC_LOGERRORF("SceneBaker::LoadScene - Unable to inspect geometry elements: %s",  model->GetName().CString());
+                    return false;
+                }
+
+                int texcoords = 0;
+                for (unsigned i = 0; i < elements->Size(); i++)
+                {
+                    const VertexElement& element = elements->At(i);
+
+                    if (element.type_ == TYPE_VECTOR2 && element.semantic_ == SEM_TEXCOORD)
+                    {
+                        texcoords++;
+                    }
+                }
+
+                if (texcoords < 2)
+                {
+                    ATOMIC_LOGERRORF("SceneBaker::LoadScene - Model without lightmap UV set, skipping: %s",  model->GetName().CString());
+                    continue;
+                }
+
+            }
+
             SharedPtr<BakeMesh> meshMap (new BakeMesh(context_, this));
             meshMap->SetStaticModel(staticModel);
             bakeMeshes_.Push(meshMap);

+ 4 - 3
Source/AtomicGlow/Kernel/SceneBaker.h

@@ -40,7 +40,7 @@ class SceneBaker : public Object
 
     public:
 
-    SceneBaker(Context* context);
+    SceneBaker(Context* context, const String& projectPath);
     virtual ~SceneBaker();    
 
     bool Light(const GlowLightMode lightMode);
@@ -64,8 +64,6 @@ class SceneBaker : public Object
 
     int GetCurrentGIBounce() const {return currentGIPass_; }
 
-    bool SaveLitScene();
-
     bool WriteBakeData(VectorBuffer& buffer);
 
     bool GetStandaloneMode() const { return standaloneMode_; }
@@ -88,6 +86,8 @@ private:
     bool LightGI();
     void LightGIFinish();
 
+    bool SaveLitScene();
+
     SharedPtr<Scene> scene_;
     SharedPtr<EmbreeScene> embreeScene_;
 
@@ -101,6 +101,7 @@ private:
 
     int currentGIPass_;
 
+    String projectPath_;
     bool standaloneMode_;
 
 };

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

@@ -183,6 +183,11 @@ bool Asset::Load()
     dirty_ = false;
     if (!CheckCacheFile())
     {
+        if (db->GetReadOnly())
+        {
+            return false;
+        }
+
         dirty_ = true;
     }
 

+ 41 - 5
Source/ToolCore/Assets/AssetDatabase.cpp

@@ -191,6 +191,11 @@ Asset* AssetDatabase::GetAssetByPath(const String& path)
 void AssetDatabase::PruneOrphanedDotAssetFiles()
 {
 
+    if (GetReadOnly())
+    {
+        return;
+    }
+
     if (project_.Null())
     {
         ATOMIC_LOGDEBUG("AssetDatabase::PruneOrphanedDotAssetFiles - called without project loaded");
@@ -335,6 +340,11 @@ void AssetDatabase::UpdateAssetCacheMap()
     if (project_.Null())
         return;
 
+    if (GetReadOnly())
+    {
+        return;
+    }
+
     bool gen = assetScanImport_;
     assetScanImport_ = false;
 
@@ -431,12 +441,15 @@ void AssetDatabase::Scan()
 
         if (!fs->FileExists(dotAssetFilename))
         {
-            // new asset
-            SharedPtr<Asset> asset(new Asset(context_));
-
-            if (asset->SetPath(path))
+            if (!GetReadOnly())
             {
-                AddAsset(asset, true);
+                // new asset
+                SharedPtr<Asset> asset(new Asset(context_));
+
+                if (asset->SetPath(path))
+                {
+                    AddAsset(asset, true);
+                }
             }
         }
         else
@@ -604,6 +617,14 @@ void AssetDatabase::HandleResourceLoadFailed(StringHash eventType, VariantMap& e
 
 void AssetDatabase::HandleFileChanged(StringHash eventType, VariantMap& eventData)
 {
+    // for now, when in read only mode, early exit
+    // in the future, it is possible we'll want to
+    // handle this for read only project loads differently
+    if (GetReadOnly())
+    {
+        return;
+    }
+
     using namespace FileChanged;
     const String& fullPath = eventData[P_FILENAME].GetString();
 
@@ -679,6 +700,11 @@ String AssetDatabase::GetResourceImporterName(const String& resourceTypeName)
 
 void AssetDatabase::ReimportAllAssets()
 {
+    if (GetReadOnly())
+    {
+        return;
+    }
+
     List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
 
     while (itr != assets_.End())
@@ -693,6 +719,11 @@ void AssetDatabase::ReimportAllAssets()
 
 void AssetDatabase::ReimportAllAssetsInDirectory(const String& directoryPath)
 {
+    if (GetReadOnly())
+    {
+        return;
+    }
+
     List<SharedPtr<Asset>>::ConstIterator itr = assets_.Begin();
 
     while (itr != assets_.End())
@@ -778,4 +809,9 @@ void AssetDatabase::SetCacheEnabled(bool cacheEnabled)
     cacheEnabled_ = cacheEnabled;
 }
 
+bool AssetDatabase::GetReadOnly() const
+{
+    return project_.Null() ? false : project_->GetReadOnly();
+}
+
 }

+ 2 - 1
Source/ToolCore/Assets/AssetDatabase.h

@@ -80,6 +80,8 @@ public:
 
     String GetDotAssetFilename(const String& path);
 
+    bool GetReadOnly() const;
+
 private:
 
     void HandleProjectLoaded(StringHash eventType, VariantMap& eventData);
@@ -118,7 +120,6 @@ private:
     bool assetScanImport_;
 
     bool cacheEnabled_;
-
 };
 
 }

+ 4 - 2
Source/ToolCore/Project/Project.cpp

@@ -44,7 +44,8 @@ namespace ToolCore
 Project::Project(Context* context) :
     Object(context),
     loading_(false),
-    dirty_(false)
+    dirty_(false),
+    readOnly_(false)
 {
     version_ = "1.0.0";
 
@@ -107,9 +108,10 @@ bool Project::GetSupportsPlatform(const String& platform) const
     return projectSettings_->GetSupportsPlatform(platform);
 }
 
-bool Project::Load(const String& fullpath)
+bool Project::Load(const String& fullpath, bool readOnly)
 {
     loading_ = true;
+    readOnly_ = readOnly;
 
     if (fullpath.Contains(".atomic")) {
 

+ 6 - 3
Source/ToolCore/Project/Project.h

@@ -46,7 +46,7 @@ public:
     /// Destruct.
     virtual ~Project();
 
-    bool Load(const String& fullpath);
+    bool Load(const String& fullpath, bool readOnly = false);
     void Save(const String& fullpath = "");
 
     /// Paths
@@ -69,8 +69,10 @@ public:
 
     bool GetSupportsPlatform(const String& platform) const;
 
-    bool IsDirty() { return dirty_; }
-    void SetDirty() { if (!loading_) dirty_ = true; }
+    bool GetReadOnly() const { return readOnly_; }
+
+    bool IsDirty() { if (readOnly_) { return false; } return dirty_; }
+    void SetDirty() { if (readOnly_) { return; } if (!loading_) dirty_ = true; }
 
     ProjectBuildSettings* GetBuildSettings() { return buildSettings_; }
     ProjectUserPrefs* GetUserPrefs() { return userPrefs_; }
@@ -106,6 +108,7 @@ private:
 
     bool loading_;
     bool dirty_;
+    bool readOnly_;
 
     SharedPtr<ProjectUserPrefs> userPrefs_;
     SharedPtr<ProjectBuildSettings> buildSettings_;

+ 9 - 2
Source/ToolCore/ToolSystem.cpp

@@ -22,6 +22,7 @@
 
 #include <Atomic/Core/Context.h>
 #include <Atomic/Core/CoreEvents.h>
+#include <Atomic/Core/ProcessUtils.h>
 #include <Atomic/IO/FileSystem.h>
 #include <Atomic/Resource/ResourceCache.h>
 
@@ -51,6 +52,12 @@ namespace ToolCore
 ToolSystem::ToolSystem(Context* context) : Object(context),
     updateDelta_(0.0f)
 {
+    if (context_->GetSubsystem<ToolSystem>())
+    {
+        ErrorExit("ToolSystem::ToolSystem - existing ToolSystem already registered");
+        return;
+    }
+
     context_->RegisterSubsystem(new AssetDatabase(context_));
     context_->RegisterSubsystem(new LicenseSystem(context_));
     context_->RegisterSubsystem(new BuildSystem(context_));
@@ -86,7 +93,7 @@ void ToolSystem::HandleUpdate(StringHash eventType, VariantMap& eventData)
 
 }
 
-bool ToolSystem::LoadProject(const String& fullpath)
+bool ToolSystem::LoadProject(const String& fullpath, bool readOnly)
 {
 
     String pathName, fileName, ext;
@@ -119,7 +126,7 @@ bool ToolSystem::LoadProject(const String& fullpath)
     project_ = new Project(context_);
     project_->SetResourcePath(resourcePath);
 
-    bool result = project_->Load(fullpath);
+    bool result = project_->Load(fullpath, readOnly);
 
     if (result)
     {

+ 1 - 1
Source/ToolCore/ToolSystem.h

@@ -43,7 +43,7 @@ public:
     ToolSystem(Context* context);
     virtual ~ToolSystem();
 
-    bool LoadProject(const String& fullpath);
+    bool LoadProject(const String& fullpath, bool readOnly = false);
     Project* GetProject() { return project_; }
     void CloseProject();