From cbf36d9386a27dca98b3f4824083beff281f510c Mon Sep 17 00:00:00 2001 From: Anselme Date: Sat, 26 Aug 2017 18:41:40 +0200 Subject: [PATCH] added resource loading thread --- deploy/data/woodbox.pack | 38 ++++++++ src/engine.cpp | 4 + src/engine.h | 3 + src/guitools.cpp | 16 ++++ src/guitools.h | 3 + src/sparrowshell/scriptnode.cpp | 5 ++ src/sparrowshell/scriptnode.h | 1 + src/test/potator.cpp | 23 ++++- src/tools/loader.h | 2 +- src/tools/loadingthread.cpp | 113 ++++++++++++++++++++++++ src/tools/loadingthread.h | 44 ++++++++++ src/tools/resourcepack.cpp | 149 ++++++++++++++++++++++++++++++++ src/tools/resourcepack.h | 63 ++++++++++++++ 13 files changed, 460 insertions(+), 4 deletions(-) create mode 100644 deploy/data/woodbox.pack create mode 100644 src/tools/loadingthread.cpp create mode 100644 src/tools/loadingthread.h create mode 100644 src/tools/resourcepack.cpp create mode 100644 src/tools/resourcepack.h diff --git a/deploy/data/woodbox.pack b/deploy/data/woodbox.pack new file mode 100644 index 0000000..c7eb6ca --- /dev/null +++ b/deploy/data/woodbox.pack @@ -0,0 +1,38 @@ +{ + "TextureResource": [ + { + "woodbox albedo", + "woodframe_albedo.png", + 24, + 1, + 1 + } + { + "woodbox roughness", + "woodframe_roughness.png", + 8, + 1, + 1 + } + { + "woodbox metallic", + "woodframe_metallic.png", + 8, + 1, + 1 + } + { + "woodbox normals", + "woodframe_normal.png", + 24, + 1, + 1 + } + ], + "ResourcePack": [ + { + "woodbox", + [{"TextureResource", 0}] + } + ] +} diff --git a/src/engine.cpp b/src/engine.cpp index 5266503..1b84644 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -14,6 +14,7 @@ #include "imgui/imgui.h" #include "tools/loader.h" #include "guitools.h" +#include "tools/loadingthread.h" Engine::Engine() : m_window(nullptr), @@ -34,6 +35,7 @@ Engine::Engine() : Engine::~Engine() { + LoadingThread::destroy(); delete m_clock; delete m_renderer; if(m_window != NULL) @@ -65,6 +67,7 @@ void Engine::createWindow(std::string title, m_renderer->initGL(w, h); m_sparrowshell = new SparrowShell(m_window); m_guiTools = new GuiTools(); + m_loadingThread = LoadingThread::init(); } void Engine::initPhysics() @@ -109,6 +112,7 @@ void Engine::update() bool isMouseVisible = m_mouseVisible; if(ImGui::Checkbox("Mouse cursor ( shortcut : [M] )", &isMouseVisible)) toggleMouseVisibility(); + ImGui::ProgressBar(m_loadingThread->getTotalProgress()); ImGui::End(); } } diff --git a/src/engine.h b/src/engine.h index df0c848..810fe24 100644 --- a/src/engine.h +++ b/src/engine.h @@ -9,6 +9,7 @@ class SceneTree; class SparrowShell; class PhysicsDebugNode; class GuiTools; +class LoadingThread; namespace sf { @@ -52,6 +53,7 @@ public: btDiscreteDynamicsWorld* getPhysics() const {return m_world;} SparrowShell* getShell() const {return m_sparrowshell;} GuiTools* getGuiTools() const {return m_guiTools;} + LoadingThread* getLoadingThread() const {return m_loadingThread;} SceneTree* getScene() const; unsigned int getTime() const; @@ -78,6 +80,7 @@ private: PhysicsDebugNode *m_physicsDebugNode; SparrowRenderer* m_renderer; GuiTools* m_guiTools; + LoadingThread* m_loadingThread; void update(); diff --git a/src/guitools.cpp b/src/guitools.cpp index 3fd8d50..14cc4ca 100644 --- a/src/guitools.cpp +++ b/src/guitools.cpp @@ -3,6 +3,7 @@ #include "engine.h" #include "tools/scenepicker.h" #include "scene/scenetree.h" +#include "tools/resourcepack.h" #include @@ -15,6 +16,7 @@ GuiTools::GuiTools() : m_pickerNode(new ScenePicker()), m_selectedMesh(nullptr), + m_editedResourcePack(nullptr), m_pickerEnabled(false), m_materialEditorEnabled(false) { @@ -37,6 +39,9 @@ void GuiTools::update() if(m_materialEditorEnabled) materialGui(); + + if(m_editedResourcePack != nullptr) + m_editedResourcePack->gui(); } void GuiTools::materialGui() @@ -98,3 +103,14 @@ void GuiTools::toggleRenderingPipelineGui() DeferredPipeline* pipeline = dynamic_cast(m_scene->getPipeline()); pipeline->toggleDebugGui(); } + +void GuiTools::toggleResourcePackGui() +{ + if(m_editedResourcePack == nullptr) + m_editedResourcePack = new ResourcePack(); + else + { + delete m_editedResourcePack; + m_editedResourcePack = nullptr; + } +} diff --git a/src/guitools.h b/src/guitools.h index ded58ef..b9cff1a 100644 --- a/src/guitools.h +++ b/src/guitools.h @@ -5,12 +5,14 @@ class Engine; class ScenePicker; +class ResourcePack; class Mesh; class GuiTools : public ContainerNode { ScenePicker* m_pickerNode; Mesh* m_selectedMesh; + ResourcePack* m_editedResourcePack; bool m_pickerEnabled; bool m_materialEditorEnabled; public: @@ -21,6 +23,7 @@ public: void togglePicker(); void toggleMaterialEditor(); void toggleRenderingPipelineGui(); + void toggleResourcePackGui(); }; #endif // GUITOOLS_H diff --git a/src/sparrowshell/scriptnode.cpp b/src/sparrowshell/scriptnode.cpp index 9214b24..32fda8c 100644 --- a/src/sparrowshell/scriptnode.cpp +++ b/src/sparrowshell/scriptnode.cpp @@ -22,6 +22,7 @@ ScriptNode::ScriptNode() LUA_SET_FUN(picker); LUA_SET_FUN(materialEditor); LUA_SET_FUN(rendering); + LUA_SET_FUN(resourcePackEditor); m_script.new_usertype("Engine", "time",&Engine::getTime @@ -103,3 +104,7 @@ void ScriptNode::materialEditor(){ void ScriptNode::rendering(){ this->getEngine().getGuiTools()->toggleRenderingPipelineGui(); } + +void ScriptNode::resourcePackEditor(){ + this->getEngine().getGuiTools()->toggleResourcePackGui(); +} diff --git a/src/sparrowshell/scriptnode.h b/src/sparrowshell/scriptnode.h index 1f1d48b..5fd80fa 100644 --- a/src/sparrowshell/scriptnode.h +++ b/src/sparrowshell/scriptnode.h @@ -25,6 +25,7 @@ public: void picker(); void materialEditor(); void rendering(); + void resourcePackEditor(); }; #endif // SCRIPTNODE_H diff --git a/src/test/potator.cpp b/src/test/potator.cpp index 40efa6f..e342077 100644 --- a/src/test/potator.cpp +++ b/src/test/potator.cpp @@ -7,8 +7,11 @@ #include "SparrowRenderer/pbrmaterial.h" #include "scene/meshnode.h" #include "tools/loader.h" +#include "tools/loadingthread.h" #include "SparrowRenderer/texture.h" #include "scene/graphicalcontainernode.h" +#include "resourcemanager.h" +#include #define PHYSICS_OFFSET 0.01f @@ -66,7 +69,7 @@ Potator::Potator(PlayerCharacterNode * player, m_cubeMesh->addTriangle(id+7, id+5, id+6); } - PBRMaterial *mat = new PBRMaterial(); + PBRMaterial *mat = new PBRMaterial(); /* Image* img = Loader::loadImage("woodframe_albedo.png", 24); mat->setTexture(PBRMaterial::ALBEDO_SLOT, new Texture(img)); img = Loader::loadImage("woodframe_metallic.png", 8); @@ -74,7 +77,7 @@ Potator::Potator(PlayerCharacterNode * player, img = Loader::loadImage("woodframe_roughness.png", 8); mat->setTexture(PBRMaterial::ROUGHNESS_SLOT, new Texture(img)); img = Loader::loadImage("woodframe_normal.png", 24); - mat->setTexture(PBRMaterial::NORMALS_SLOT, new Texture(img)); + mat->setTexture(PBRMaterial::NORMALS_SLOT, new Texture(img)); */ m_cubeMesh->setMaterial(mat); m_cubeMesh->computeTangents(); @@ -87,7 +90,7 @@ Potator::Potator(PlayerCharacterNode * player, // creating sphere mat = new PBRMaterial(); - img = Loader::loadImage("slipperystonework_albedo.png", 24); + Image* img = Loader::loadImage("slipperystonework_albedo.png", 24); mat->setTexture(PBRMaterial::ALBEDO_SLOT, new Texture(img)); img = Loader::loadImage("slipperystonework_metallic.png", 8); mat->setTexture(PBRMaterial::METALLIC_SLOT, new Texture(img)); @@ -188,4 +191,18 @@ void Potator::update() throwSword(); } } + + ImGui::Begin("Potator"); + if(ImGui::Button("Load pack")) + LoadingThread::get()->loadResourcePack("woodbox"); + if(ImGui::Button("apply textures")) + { + PBRMaterial* mat = dynamic_cast(m_cubeMesh->getMaterial()); + mat->setTexture(PBRMaterial::ALBEDO_SLOT, RESOURCE_GET(Texture, "woodbox albedo")); + mat->setTexture(PBRMaterial::METALLIC_SLOT, RESOURCE_GET(Texture, "woodbox metallic")); + mat->setTexture(PBRMaterial::ROUGHNESS_SLOT, RESOURCE_GET(Texture, "woodbox roughness")); + mat->setTexture(PBRMaterial::NORMALS_SLOT, RESOURCE_GET(Texture, "woodbox normals")); + m_scene->registerMeshType(m_cubeMesh->getFlags()); + } + ImGui::End(); } diff --git a/src/tools/loader.h b/src/tools/loader.h index 59d6f18..a8b9a02 100644 --- a/src/tools/loader.h +++ b/src/tools/loader.h @@ -19,7 +19,7 @@ class Loader public: static std::string* loadTextFile(const std::string &filename); static std::unordered_map* loadConfigFile(const std::string &filename); - static Image* loadImage(const std::string &filename, int depth = 32, bool reversed = true); + static Image* loadImage(const std::string &filename, int depth = 24, bool reversed = true); static std::vector loadMesh(const std::string &filename); static Font* loadFont(const std::string &texture, const std::string &description); static bool loadMTL(const std::string &filename); diff --git a/src/tools/loadingthread.cpp b/src/tools/loadingthread.cpp new file mode 100644 index 0000000..86b5c83 --- /dev/null +++ b/src/tools/loadingthread.cpp @@ -0,0 +1,113 @@ +#include "loadingthread.h" + +#include +#include + +#include +#include + +#include + +#include + +LoadingThread* LoadingThread::singleton = nullptr; +std::thread* LoadingThread::thread = nullptr; + +LoadingThread::LoadingThread() : + m_currentTask(nullptr), + m_running(true), + m_taskProgress(0), + m_nbTaskProcessed(0) +{ +} + +LoadingThread* LoadingThread::init() +{ + if(singleton == nullptr) + { + singleton = new LoadingThread(); + thread = new std::thread(LoadingThread::run); + } + return singleton; +} + +void LoadingThread::destroy() +{ + if(singleton != nullptr) + { + singleton->m_running = false; + thread->join(); + delete singleton; + singleton = nullptr; + thread = nullptr; + } +} + +void LoadingThread::run() +{ + sf::Context openglContext; + singleton->processTasks(); +} + +void LoadingThread::processTasks() +{ + while(m_running) + { + if(m_resourceQueue.empty()) + sf::sleep(sf::milliseconds(500)); // wait for half a second + else + { + ResourceInterface* res = m_resourceQueue.front(); + m_resourceQueue.pop_front(); + res->load(m_taskProgress); + glFlush(); + if(m_taskProgress < 1.0) + fprintf(stderr, "failed to process the following task : %s\n", res->m_name.c_str()); + else + ++m_nbTaskProcessed; + } + } +} + +const std::string& LoadingThread::getCurrentTaskName() +{ + const static std::string noTask = "No current task"; + if(m_currentTask != nullptr) + return m_currentTask->m_name; + else + return noTask; +} + +void LoadingThread::loadResourcePack(const std::string& name) +{ + std::fstream file; + file.open("data/" + name + ".pack", std::ios_base::in); + ObjectLoader loader; + loader.loadAscii(file); + file.close(); + const std::vector& packVec = loader.getObjects(); + ResourcePack* pack = packVec[0]; + m_packs[name] = pack; + for(ResourceInterface* res : pack->m_resources) + { + if((m_references[res->m_name] += 1) == 1) + m_resourceQueue.push_back(res); + else + delete res; + } +} + +void LoadingThread::freeResourcePack(const std::string& name) +{ + ResourcePack* pack = m_packs[name]; + for(ResourceInterface* res : pack->m_resources) + { + if((m_references[res->m_name] -= 1) == 0) + { + m_references.erase(res->m_name); + delete res; + } + } + m_packs.erase(name); + delete pack; +} diff --git a/src/tools/loadingthread.h b/src/tools/loadingthread.h new file mode 100644 index 0000000..8f92a9a --- /dev/null +++ b/src/tools/loadingthread.h @@ -0,0 +1,44 @@ +#ifndef LOADINGTHREAD_H +#define LOADINGTHREAD_H + +#include +#include +#include + +#include "resourcepack.h" + +class LoadingThread +{ + static LoadingThread* singleton; + static std::thread* thread; + + std::deque m_resourceQueue; + std::unordered_map m_packs; + std::unordered_map m_references; + ResourceInterface* m_currentTask; + bool m_running; + float m_taskProgress; + int m_nbTaskProcessed; + + LoadingThread(); + void processTasks(); + +public: + static LoadingThread *init(); + static void destroy(); + static void run(); + + static LoadingThread* get() { return singleton; } + + float getTotalProgress() { return float(m_nbTaskProcessed)/float(m_resourceQueue.size() + m_nbTaskProcessed); } + void resetTotalProgress() { m_nbTaskProcessed = 0; } + float getCurrentTaskProgress() { return m_taskProgress; } + const std::string& getCurrentTaskName(); + + void loadResourcePack(const std::string& name); + + // please don't call this if the queue isn't empty + void freeResourcePack(const std::string& name); +}; + +#endif // LOADINGTHREAD_H diff --git a/src/tools/resourcepack.cpp b/src/tools/resourcepack.cpp new file mode 100644 index 0000000..8aec89f --- /dev/null +++ b/src/tools/resourcepack.cpp @@ -0,0 +1,149 @@ +#include "resourcepack.h" + +#include "loader.h" +#include "resourcemanager.h" + +#include + +#include +#include + +#include + +#include + +INIT_SERIALIZABLE(ResourcePack) + +void ResourcePack::gui() +{ + static ResourceInterface* currentResource = nullptr; + + ImGui::Begin("ResourcePack"); + char buf[1024] = {0}; + strcpy(buf, m_name.c_str()); + if(ImGui::InputText("Pack name", buf, 1024)) + m_name = buf; + + if(ImGui::Button("Save resource pack")) + { + std::fstream file; + ObjectSaver saver; + saver.addObject(this); + file.open("data/" + m_name + ".pack", std::ios_base::out); + saver.saveAscii(file); + file.close(); + } + if(ImGui::Button("Load resource pack")) + { + std::fstream file; + file.open("data/" + m_name + ".pack", std::ios_base::in); + ObjectLoader loader; + loader.loadAscii(file); + file.close(); + const std::vector& packVec = loader.getObjects(); + for(ResourceInterface* res : m_resources) + delete res; + m_resources = packVec[0]->m_resources; + delete packVec[0]; + } + + if(ImGui::Button("Add a texture")) + { + TextureResource* resource = new TextureResource(); + m_resources.push_back(resource); + } + + for(ResourceInterface* res : m_resources) + { + if(ImGui::Button(res->m_name.c_str())) + currentResource = res; + } + ImGui::End(); + + if(currentResource != nullptr) + { + bool opened = true; + ImGui::Begin("Resource Editor", &opened); + + char resBuf[1024] = {0}; + strcpy(resBuf, currentResource->m_name.c_str()); + if(ImGui::InputText("Name (should be unique)", resBuf, 1024)) + currentResource->m_name = resBuf; + + currentResource->gui(); + if(ImGui::Button("Delete resource")) + { + for(unsigned int i=0; i + +class Texture; + +/** + * @brief The ResourceInterface struct holds a resource, it handles its loading and its destruction + */ +struct ResourceInterface : public Serializable +{ + P_STRING(m_name) + + virtual void load(float & progress) = 0; + + virtual void destroy() = 0; + + virtual void gui() {} +}; + +/** + * @brief The ResourcePack class is just a container of LoadTask, it only exists for serialization purposes + */ +struct ResourcePack : public Serializable +{ + P_STRING(m_name) + P_VECTOR_REFERENCE(ResourceInterface, m_resources) + + ResourcePack() {} + + void gui(); + + SERIALIZABLE(ResourcePack, CAST(m_name), CAST(m_resources)) +}; + +// +// Some LoadTask implementations +// + +class TextureResource : public ResourceInterface +{ + P_STRING(m_path) + P_INT(m_bitsPerPixel) + P_BOOL(m_isVerticallyReversed) + P_BOOL(m_needsMipMaps) + + Texture* m_texture; + +public: + TextureResource(); + virtual ~TextureResource(); + + SERIALIZABLE(TextureResource, CAST(m_name), CAST(m_path), CAST(m_bitsPerPixel), CAST(m_isVerticallyReversed), CAST(m_needsMipMaps)) + + void load(float & progress); + + void destroy(); + + void gui(); +}; + +#endif // RESOURCEPACK_H