diff --git a/assets b/assets index 6cbf2365..a3c92ac8 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 6cbf2365d49e6280750ea3bcd0f9c271779e6f15 +Subproject commit a3c92ac876dd061776c36d1594bd82264372f028 diff --git a/include/Engine/Collision/Collision.h b/include/Engine/Collision/Collision.h index 714cee3f..a441c998 100644 --- a/include/Engine/Collision/Collision.h +++ b/include/Engine/Collision/Collision.h @@ -7,10 +7,10 @@ #include -#include "Core/Ray.h" -#include "Core/AABB.h" -#include "Engine/Rendering/RawModel.h" -#include "Core/Entity.h" +#include "../Core/Ray.h" +#include "../Core/AABB.h" +#include "../Rendering/RawModel.h" +#include "../Core/Entity.h" class World; struct ComponentWrapper; diff --git a/include/Engine/Collision/CollisionSystem.h b/include/Engine/Collision/CollisionSystem.h index 254a2461..f4752ecf 100644 --- a/include/Engine/Collision/CollisionSystem.h +++ b/include/Engine/Collision/CollisionSystem.h @@ -4,10 +4,10 @@ #include #include -#include "Common.h" -#include "Core/System.h" -#include "Core/EventBroker.h" -#include "Core/EKeyUp.h" +#include "../Common.h" +#include "../Core/System.h" +#include "../Core/EventBroker.h" +#include "../Core/EKeyUp.h" class CollisionSystem : public PureSystem { diff --git a/include/Engine/Collision/TriggerSystem.h b/include/Engine/Collision/TriggerSystem.h index 7e6ef008..dfb56a2c 100644 --- a/include/Engine/Collision/TriggerSystem.h +++ b/include/Engine/Collision/TriggerSystem.h @@ -4,8 +4,8 @@ #include #include -#include "Core/System.h" -#include "Core/EventBroker.h" +#include "../Core/System.h" +#include "../Core/EventBroker.h" #include "ETrigger.h" class AABB; diff --git a/include/Engine/Core/Ray.h b/include/Engine/Core/Ray.h index 0fcef01e..a234a488 100644 --- a/include/Engine/Core/Ray.h +++ b/include/Engine/Core/Ray.h @@ -2,7 +2,7 @@ #define Ray_h__ #include "../GLM.h" -#include "Common.h" +#include "../Common.h" class Ray { diff --git a/include/Engine/Core/ResourceManager.h b/include/Engine/Core/ResourceManager.h index 86ed6254..15a551e7 100644 --- a/include/Engine/Core/ResourceManager.h +++ b/include/Engine/Core/ResourceManager.h @@ -12,7 +12,6 @@ /** Base Resource class. Implement this class for every resource to be handled by the resource manager. - Implement Create() to return a new object of that type. */ class Resource { @@ -22,6 +21,23 @@ protected: Resource() { } public: + //Should be thrown in a Resource's constructor if it cannot complete because another resource is still loading. + //Not actually an error, just a message to the ResourceManager. + struct StillLoadingException : public std::exception + { + virtual const char* what() const throw() + { + return "Resource is still loading."; + } + }; + struct FailedLoadingException : public std::exception + { + virtual const char* what() const throw() + { + return "Resource is failed to load."; + } + }; + // Pretend that this is a pure virtual function that you have to implement // FIXME: Why did we do this again instead of just using the constructor? // static Resource* Create(std::string resourceName); @@ -33,6 +49,15 @@ public: unsigned int ResourceID; }; +//Any class inheriting from this class will always be loaded on the master thread, not on a parallel worker thread. +//This is important in case some instructions must be executed on the main thread, e.g. OpenGL commands, like glBindBuffer. +//This resource can still be loaded asyncronously, but it will not be loaded in a thread, instead it's constructor will +//be called once on every ResourceManager::Load, just throw StillLoadingException in the constructor if it is not done yet. +class ThreadUnsafeResource : public Resource +{ + friend class ResourceManager; +}; + /** Singleton resource manager to keep track of and cache any external engine assets */ class ResourceManager { @@ -40,6 +65,7 @@ private: ResourceManager(); public: + static bool UseThreading; /*static ResourceManager& Instance() { static ResourceManager s; @@ -49,15 +75,6 @@ public: template static void RegisterType(std::string typeName); - /** Preloads a resource and caches it for future use - - @tparam T Resource type. - @param resourceName Fully qualified name of the resource to preload. - */ - template - static void Preload(std::string resourceName); - static void Preload(std::string resourceType, std::string resourceName); - /** Checks if a resource is in cache @param resourceType Resource type as string. @@ -65,15 +82,20 @@ public: */ // TODO: Templateify static bool IsResourceLoaded(std::string resourceType, std::string resourceName); + + /** Return value should always be a valid pointer, will throw an exception on error. + If the resource has been loaded already, returns a pointer to it. - /** Hot-loads a resource and caches it for future use + If Async is false: Hot-loads a resource, caches it for future use, and returns a pointer to it. + If Async is true: If the resource is not loaded yet, starts loading the resource + in the background and throws Resource::StillLoadingException immediately. @tparam T Resource type. + @tparam async Set this to true if the resource should be loaded asyncronously. @param resourceName Fully qualified name of the resource to load. */ - template - static T* Load(std::string resourceName, Resource* parent = nullptr); - static Resource* Load(std::string resourceType, std::string resourceName, Resource* parent = nullptr); + template + static T* Load(const std::string& resourceName, Resource* parent = nullptr); /** Reloads an already loaded resource, keeping its resource ID intact. @@ -87,19 +109,31 @@ public: static void Update(); private: + //This is a haxy way to make sure that IsMainThread is run when the program starts, so the master thread id is set. + struct MasterThreadChecker + { + MasterThreadChecker() + { + ResourceManager::IsMainThread(); + } + }; + const static MasterThreadChecker m_Checker; + static std::unordered_map m_CompilerTypenameToResourceType; static std::unordered_map> m_FactoryFunctions; // type -> factory function static std::unordered_map, Resource*> m_ResourceCache; // (type, name) -> resource static std::unordered_map m_ResourceFromName; // name -> resource static std::unordered_map m_ResourceParents; // resource -> parent resource + static std::unordered_map, boost::thread> m_LoadingThreads; // (type, name) -> loading thread + static std::unordered_map, std::exception_ptr> m_LoadingThreadExceptions; // (type, name) -> exceptions + static boost::recursive_mutex m_Mutex; + // TODO: Getters for IDs static unsigned int m_CurrentResourceTypeID; static std::unordered_map m_ResourceTypeIDs; // Number of resources of a type. Doubles as local ID. static std::unordered_map m_ResourceCount; - // Flag to suppress hot-load warnings when a preloading resource chain loads another resource - static bool m_Preloading; static FileWatcher m_FileWatcher; static void fileWatcherCallback(std::string path, FileWatcher::FileEventFlags flags); @@ -108,22 +142,13 @@ private: static unsigned int GetNewResourceID(unsigned int typeID); // Internal: Create a resource and cache it - static Resource* CreateResource(std::string resourceType, std::string resourceName, Resource* parent); + static Resource* createResourceThrowing(const std::string& resourceType, const std::string& resourceName, Resource* parent); + static Resource* createResource(const std::string& resourceType, const std::string& resourceName, Resource* parent, std::exception_ptr& exception); + static Resource* cacheResource(Resource* resource, const std::string& resourceType, const std::string& resourceName, Resource* parent); + + static bool IsMainThread(); }; -template -T* ResourceManager::Load(std::string resourceName, Resource* parent /* = nullptr */) -{ - auto resourceTypename = typeid(T).name(); - auto it = m_CompilerTypenameToResourceType.find(resourceTypename); - if (it == m_CompilerTypenameToResourceType.end()) { - LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename); - return nullptr; - } - - return static_cast(Load(it->second, resourceName, parent)); -} - template void ResourceManager::RegisterType(std::string typeName) { @@ -131,17 +156,85 @@ void ResourceManager::RegisterType(std::string typeName) m_FactoryFunctions[typeName] = [](std::string resourceName) { return new T(resourceName); }; } -template -void ResourceManager::Preload(std::string resourceName) +template +static T* ResourceManager::Load(const std::string& resourceName, Resource* parent /* = nullptr */) { - auto resourceTypename = typeid(T).name(); - auto it = m_CompilerTypenameToResourceType.find(resourceTypename); - if (it == m_CompilerTypenameToResourceType.end()) { - LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename); - return; - } + auto resourceTypename = typeid(T).name(); + auto iter = m_CompilerTypenameToResourceType.find(resourceTypename); + if (iter == m_CompilerTypenameToResourceType.end()) { + LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename); + throw Resource::FailedLoadingException(); + } - Preload(it->second, resourceName); + std::string resourceType = iter->second; + constexpr bool mustNotLoadInThread = std::is_base_of::value; + if (mustNotLoadInThread && !IsMainThread()) { + LOG_ERROR("Failed to Load \"%s\": ThreadUnsafeResource type \"%s\" load in the constructor of another resource that is loaded asyncronously.", resourceName.c_str(), iter->second.c_str()); + throw Resource::FailedLoadingException(); + } + + auto cacheKey = std::make_pair(resourceType, resourceName); + decltype(m_ResourceCache)::iterator it; + //If a thread has already been launched to load this resource. + auto tIt = m_LoadingThreads.find(cacheKey); + if (UseThreading && tIt != m_LoadingThreads.end()) { + if (async) { + //Throw StillLoadingException if the thread is still working. + if (!tIt->second.try_join_for(boost::chrono::nanoseconds(1))) { + throw Resource::StillLoadingException(); + } + //Else we know the thread has completed. + } else { + //Wait for the thread to finish loading. + tIt->second.join(); + } + //When the thread is done, delete the thread. + m_LoadingThreads.erase(tIt); + //Rethrow the thread exception if it threw any. + auto excIt = m_LoadingThreadExceptions.find(cacheKey); + std::exception_ptr exception = excIt->second; + m_LoadingThreadExceptions.erase(excIt); + if (exception) { + std::rethrow_exception(exception); + } + } + + //If resource has already been cached and completely loaded. + it = m_ResourceCache.find(cacheKey); + if (it != m_ResourceCache.end()) { + if (it->second != nullptr) { + return static_cast(it->second); + } else { + //Don't return null on failure, exception instead. + throw Resource::FailedLoadingException(); + } + } + + //If resource is not cached.. + if (UseThreading && async) { + if (mustNotLoadInThread) { + try { + return static_cast(createResourceThrowing(resourceType, resourceName, parent)); + } catch (const Resource::StillLoadingException&) { + throw; + } + } else { + //Create a thread that loads the resource into cache. + m_LoadingThreads[cacheKey] = boost::thread(createResource, resourceType, resourceName, parent, m_LoadingThreadExceptions[cacheKey]); + throw Resource::StillLoadingException(); + } + } else { + //load and return the resource. + while (true) { + try { + return static_cast(createResourceThrowing(resourceType, resourceName, parent)); + } catch (const Resource::StillLoadingException&) { + continue; + } catch (const std::exception&) { + throw; + } + } + } } #endif diff --git a/include/Engine/Rendering/BaseTexture.h b/include/Engine/Rendering/BaseTexture.h index 26ae399d..96df5561 100644 --- a/include/Engine/Rendering/BaseTexture.h +++ b/include/Engine/Rendering/BaseTexture.h @@ -3,7 +3,7 @@ #include "../Core/ResourceManager.h" -class BaseTexture : public Resource +class BaseTexture : public ThreadUnsafeResource { friend class ResourceManager; diff --git a/include/Engine/Rendering/Model.h b/include/Engine/Rendering/Model.h index 9cc145af..280fe5af 100644 --- a/include/Engine/Rendering/Model.h +++ b/include/Engine/Rendering/Model.h @@ -4,7 +4,7 @@ #include "RawModel.h" #include "../OpenGL.h" -class Model : public RawModel +class Model : public ThreadUnsafeResource { friend class ResourceManager; @@ -13,11 +13,15 @@ private: public: ~Model(); + const std::vector& MaterialGroups() const { return m_RawModel->MaterialGroups; } + const glm::mat4& Matrix() const { return m_RawModel->m_Matrix; } + const std::vector& Vertices() const { return m_RawModel->m_Vertices; } GLuint VAO; GLuint ElementBuffer; private: + RawModel* m_RawModel; GLuint VertexBuffer; GLuint DiffuseVertexColorBuffer; GLuint SpecularVertexColorBuffer; diff --git a/include/Engine/Rendering/ModelJob.h b/include/Engine/Rendering/ModelJob.h index d598766f..353d1bbe 100644 --- a/include/Engine/Rendering/ModelJob.h +++ b/include/Engine/Rendering/ModelJob.h @@ -15,16 +15,16 @@ struct ModelJob : RenderJob { - ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::Model::MaterialGroup texGroup, ComponentWrapper modelComponent, World* world) + ModelJob(Model* model, Camera* camera, glm::mat4 matrix, ::RawModel::MaterialGroup matGroup, ComponentWrapper modelComponent, World* world) : RenderJob() { Model = model; - TextureID = (texGroup.Texture) ? texGroup.Texture->ResourceID : 0; - DiffuseTexture = texGroup.Texture.get(); - NormalTexture = texGroup.NormalMap.get(); - SpecularTexture = texGroup.SpecularMap.get(); - StartIndex = texGroup.StartIndex; - EndIndex = texGroup.EndIndex; + TextureID = (matGroup.Texture) ? matGroup.Texture->ResourceID : 0; + DiffuseTexture = matGroup.Texture.get(); + NormalTexture = matGroup.NormalMap.get(); + SpecularTexture = matGroup.SpecularMap.get(); + StartIndex = matGroup.StartIndex; + EndIndex = matGroup.EndIndex; Matrix = matrix; Color = modelComponent["Color"]; Entity = modelComponent.EntityID; diff --git a/include/Engine/Rendering/RawModel.h b/include/Engine/Rendering/RawModel.h index d63e46bb..0477edb1 100644 --- a/include/Engine/Rendering/RawModel.h +++ b/include/Engine/Rendering/RawModel.h @@ -47,14 +47,17 @@ public: { float Shininess; float Transparency; + std::string TexturePath; std::shared_ptr<::Texture> Texture; + std::string NormalMapPath; std::shared_ptr<::Texture> NormalMap; + std::string SpecularMapPath; std::shared_ptr<::Texture> SpecularMap; unsigned int StartIndex; unsigned int EndIndex; }; - std::vector TextureGroups; + std::vector MaterialGroups; std::vector m_Vertices; std::vector m_Indices; diff --git a/resources/DefaultConfig.ini b/resources/DefaultConfig.ini index 3ab402ad..d84b15bb 100644 --- a/resources/DefaultConfig.ini +++ b/resources/DefaultConfig.ini @@ -16,4 +16,7 @@ StartNetwork=false IsServer=false Name=Bob Address=127.0.0.1 -Port=13 \ No newline at end of file +Port=13 + +[Multithreading] +ResourceLoading=true diff --git a/resources/Schema/Entities/CollisionTestLevel.xml b/resources/Schema/Entities/CollisionTestLevel.xml index 99842b88..d5e932c2 100644 --- a/resources/Schema/Entities/CollisionTestLevel.xml +++ b/resources/Schema/Entities/CollisionTestLevel.xml @@ -17,7 +17,7 @@ - Models/ScaleWidget.obj + Models/Core/UnitSphere.obj diff --git a/resources/Schema/Entities/ThreadTestMap.xml b/resources/Schema/Entities/ThreadTestMap.xml new file mode 100644 index 00000000..b4b358c0 --- /dev/null +++ b/resources/Schema/Entities/ThreadTestMap.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Engine/Collision/Collision.cpp b/src/Engine/Collision/Collision.cpp index 620e9639..c74af3ad 100644 --- a/src/Engine/Collision/Collision.cpp +++ b/src/Engine/Collision/Collision.cpp @@ -225,11 +225,11 @@ bool attachAABBComponentFromModel(World* world, EntityID id) return false; } - glm::mat4 modelMatrix = modelRes->m_Matrix; + glm::mat4 modelMatrix = modelRes->Matrix(); glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY); glm::vec3 maxi = glm::vec3(-INFINITY, -INFINITY, -INFINITY); - for (const auto& v : modelRes->m_Vertices) { + for (const auto& v : modelRes->Vertices()) { const auto& wPos = modelMatrix * glm::vec4(v.Position.x, v.Position.y, v.Position.z, 1); maxi.x = std::max(wPos.x, maxi.x); maxi.y = std::max(wPos.y, maxi.y); @@ -255,7 +255,7 @@ bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox) if (modelRes == nullptr) { return false; } - glm::mat4 modelMatrix = modelRes->m_Matrix * + glm::mat4 modelMatrix = modelRes->Matrix() * glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) * glm::scale((glm::vec3)cTrans["Scale"]); diff --git a/src/Engine/Core/ResourceManager.cpp b/src/Engine/Core/ResourceManager.cpp index 62a60f14..54228efa 100644 --- a/src/Engine/Core/ResourceManager.cpp +++ b/src/Engine/Core/ResourceManager.cpp @@ -1,4 +1,7 @@ #include "Core/ResourceManager.h" +#include "boost/thread/thread.hpp" +#include "boost/thread/mutex.hpp" +#include "boost/thread/lock_guard.hpp" std::unordered_map ResourceManager::m_CompilerTypenameToResourceType; std::unordered_map> ResourceManager::m_FactoryFunctions; @@ -6,10 +9,13 @@ std::unordered_map, Resource*> ResourceManag std::unordered_map ResourceManager::m_ResourceFromName; std::unordered_map ResourceManager::m_ResourceParents; unsigned int ResourceManager::m_CurrentResourceTypeID = 0; +bool ResourceManager::UseThreading = false; std::unordered_map ResourceManager::m_ResourceTypeIDs; std::unordered_map ResourceManager::m_ResourceCount; -bool ResourceManager::m_Preloading = false; FileWatcher ResourceManager::m_FileWatcher; +std::unordered_map, boost::thread> ResourceManager::m_LoadingThreads; +std::unordered_map, std::exception_ptr> ResourceManager::m_LoadingThreadExceptions; +boost::recursive_mutex ResourceManager::m_Mutex; unsigned int ResourceManager::GetTypeID(std::string resourceType) { @@ -74,63 +80,65 @@ void ResourceManager::Update() m_FileWatcher.Check(); } -void ResourceManager::Preload(std::string resourceType, std::string resourceName) -{ - if (IsResourceLoaded(resourceType, resourceName)) { - //LOG_WARNING("Attempted to preload resource \"%s\" multiple times!", resourceName.c_str()); - return; - } - - m_Preloading = true; - LOG_INFO("Preloading resource \"%s\"", resourceName.c_str()); - CreateResource(resourceType, resourceName, nullptr); - m_Preloading = false; -} - -Resource* ResourceManager::Load(std::string resourceType, std::string resourceName, Resource* parent /*= nullptr*/) -{ - auto it = m_ResourceCache.find(std::make_pair(resourceType, resourceName)); - if (it != m_ResourceCache.end()) { - return it->second; - } - - if (m_Preloading) { - LOG_INFO("Preloading resource \"%s\"", resourceName.c_str()); - } else { - LOG_WARNING("Hot-loading resource \"%s\"", resourceName.c_str()); - } - - return CreateResource(resourceType, resourceName, parent); -} - -Resource* ResourceManager::CreateResource(std::string resourceType, std::string resourceName, Resource* parent) +Resource* ResourceManager::createResource(const std::string& resourceType, const std::string& resourceName, Resource* parent, std::exception_ptr& exception) { auto facIt = m_FactoryFunctions.find(resourceType); if (facIt == m_FactoryFunctions.end()) { LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceType.c_str()); - return nullptr; + cacheResource(nullptr, resourceType, resourceName, parent); + //This basically throws an exception. + exception = std::make_exception_ptr(Resource::FailedLoadingException()); return nullptr; } // Call the factory function - Resource* resource; try { - resource = facIt->second(resourceName); + return cacheResource(facIt->second(resourceName), resourceType, resourceName, parent); + } catch (const Resource::StillLoadingException&) { + exception = std::current_exception(); return nullptr; + } catch (const std::exception& e) { + LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what()); + cacheResource(nullptr, resourceType, resourceName, parent); + exception = std::current_exception(); return nullptr; + } +} + + +Resource* ResourceManager::createResourceThrowing(const std::string& resourceType, const std::string& resourceName, Resource* parent) +{ + std::exception_ptr exception; + Resource* res = createResource(resourceType, resourceName, parent, exception); + if (exception) { + std::rethrow_exception(exception); + } + return res; +} + +Resource* ResourceManager::cacheResource(Resource* resource, const std::string& resourceType, const std::string& resourceName, Resource* parent) +{ + //Lock the mutex immediately, and unlock it when leaving the code block. + boost::lock_guard guard(m_Mutex); + if (resource != nullptr) { // Store IDs resource->TypeID = GetTypeID(resourceType); resource->ResourceID = GetNewResourceID(resource->TypeID); - } catch (const std::exception& e) { - resource = nullptr; - LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what()); } - // Cache - m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource; - m_ResourceFromName[resourceName] = resource; - if (parent != nullptr) { - m_ResourceParents[resource] = parent; - } - if (!boost::filesystem::is_directory(resourceName)) { - LOG_DEBUG("Adding watch for %s", resourceName.c_str()); - m_FileWatcher.AddWatch(resourceName, fileWatcherCallback); - } - return resource; + + // Cache + m_ResourceCache[std::make_pair(resourceType, resourceName)] = resource; + m_ResourceFromName[resourceName] = resource; + if (parent != nullptr) { + m_ResourceParents[resource] = parent; + } + + //if (!boost::filesystem::is_directory(resourceName)) { + // LOG_DEBUG("Adding watch for %s", resourceName.c_str()); + // m_FileWatcher.AddWatch(resourceName, fileWatcherCallback); + //} + return resource; +} + +bool ResourceManager::IsMainThread() +{ + static boost::thread::id MainThreadId = boost::this_thread::get_id(); + return boost::this_thread::get_id() == MainThreadId; } diff --git a/src/Engine/Rendering/Model.cpp b/src/Engine/Rendering/Model.cpp index f346d9e1..33539a14 100644 --- a/src/Engine/Rendering/Model.cpp +++ b/src/Engine/Rendering/Model.cpp @@ -1,60 +1,74 @@ #include "Rendering/Model.h" Model::Model(std::string fileName) - : RawModel(fileName) { - // Generate GL buffers - GLuint buffer; - glGenBuffers(1, &buffer); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - glBufferData(GL_ARRAY_BUFFER, m_Vertices.size() * sizeof(Vertex), &m_Vertices[0], GL_STATIC_DRAW); + //Try loading the model asyncronously, if it throws any exceptions then let it propagate back to caller. + m_RawModel = ResourceManager::Load(fileName); - glGenBuffers(1, &ElementBuffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Indices.size() * sizeof(unsigned int), &m_Indices[0], GL_STATIC_DRAW); + for (auto& group : m_RawModel->MaterialGroups) { + if (!group.TexturePath.empty()) { + group.Texture = std::shared_ptr(ResourceManager::Load(group.TexturePath)); + } + if (!group.NormalMapPath.empty()) { + group.NormalMap = std::shared_ptr(ResourceManager::Load(group.NormalMapPath)); + } + if (!group.SpecularMapPath.empty()) { + group.SpecularMap = std::shared_ptr(ResourceManager::Load(group.SpecularMapPath)); + } + } - glGenVertexArrays(1, &VAO); - glBindVertexArray(VAO); - GLERROR("GLEW: BufferFail4"); + // Generate GL buffers + GLuint buffer; + glGenBuffers(1, &buffer); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + glBufferData(GL_ARRAY_BUFFER, m_RawModel->m_Vertices.size() * sizeof(RawModel::Vertex), &m_RawModel->m_Vertices[0], GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, buffer); - std::vector structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 }; - int stride = 0; - for (int size : structSizes) { - stride += size; - } - stride *= sizeof(GLfloat); - int offset = 0; - { - int element = 0; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * offset)); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; - } - GLERROR("GLEW: BufferFail5"); + glGenBuffers(1, &ElementBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_RawModel->m_Indices.size() * sizeof(unsigned int), &m_RawModel->m_Indices[0], GL_STATIC_DRAW); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); - glEnableVertexAttribArray(3); - glEnableVertexAttribArray(4); - glEnableVertexAttribArray(5); - glEnableVertexAttribArray(6); - glEnableVertexAttribArray(7); - glEnableVertexAttribArray(8); - glEnableVertexAttribArray(9); - glEnableVertexAttribArray(10); - GLERROR("GLEW: BufferFail5"); + glGenVertexArrays(1, &VAO); + glBindVertexArray(VAO); + GLERROR("GLEW: BufferFail4"); - //CreateBuffers(); + glBindBuffer(GL_ARRAY_BUFFER, buffer); + std::vector structSizes = { 3, 3, 3, 3, 2, 4, 4, 4, 4, 4, 4 }; + int stride = 0; + for (int size : structSizes) { + stride += size; + } + stride *= sizeof(GLfloat); + int offset = 0; + { + int element = 0; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * offset)); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + glVertexAttribPointer(element, structSizes[element], GL_FLOAT, GL_FALSE, stride, (GLvoid*)(sizeof(GLfloat) * (offset += structSizes[element - 1]))); element++; + } + GLERROR("GLEW: BufferFail5"); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glEnableVertexAttribArray(4); + glEnableVertexAttribArray(5); + glEnableVertexAttribArray(6); + glEnableVertexAttribArray(7); + glEnableVertexAttribArray(8); + glEnableVertexAttribArray(9); + glEnableVertexAttribArray(10); + GLERROR("GLEW: BufferFail5"); + + //CreateBuffers(); } Model::~Model() diff --git a/src/Engine/Rendering/RawModel.cpp b/src/Engine/Rendering/RawModel.cpp index d159f271..256346f1 100644 --- a/src/Engine/Rendering/RawModel.cpp +++ b/src/Engine/Rendering/RawModel.cpp @@ -145,9 +145,7 @@ RawModel::RawModel(std::string fileName) aiString path; aiTextureMapping mapping; material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping); - std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - //LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str()); - matGroup.Texture = std::shared_ptr(ResourceManager::Load(absolutePath)); + matGroup.TexturePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); } // Normal map //LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT)); @@ -155,9 +153,7 @@ RawModel::RawModel(std::string fileName) aiString path; aiTextureMapping mapping; material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping); - std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - //LOG_DEBUG("Normal map: %s", absolutePath.c_str()); - matGroup.NormalMap = std::shared_ptr(ResourceManager::Load(absolutePath)); + matGroup.NormalMapPath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); } // Specular map //LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR)); @@ -165,11 +161,9 @@ RawModel::RawModel(std::string fileName) aiString path; aiTextureMapping mapping; material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping); - std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); - //LOG_DEBUG("Specular map: %s", absolutePath.c_str()); - matGroup.SpecularMap = std::shared_ptr(ResourceManager::Load(absolutePath)); + matGroup.SpecularMapPath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); } - TextureGroups.push_back(matGroup); + MaterialGroups.push_back(matGroup); // Bones std::map>> vertexWeights; diff --git a/src/Engine/Rendering/RenderSystem.cpp b/src/Engine/Rendering/RenderSystem.cpp index 1f2bac9d..6431bde2 100644 --- a/src/Engine/Rendering/RenderSystem.cpp +++ b/src/Engine/Rendering/RenderSystem.cpp @@ -84,15 +84,23 @@ void RenderSystem::fillModels(std::list>& jobs, World continue; } - Model* model = ResourceManager::Load<::Model>(resource); - if (model == nullptr) { - model = ResourceManager::Load<::Model>("Models/Core/Error.obj"); + Model* model; + try { + model = ResourceManager::Load<::Model, true>(resource); + } catch (const Resource::StillLoadingException&) { + //continue; + model = ResourceManager::Load<::Model>("Models/Core/UnitRaptor.obj"); + } catch (const std::exception&) { + try { + model = ResourceManager::Load<::Model>("Models/Core/Error.obj"); + } catch (const std::exception&) { + continue; + } } glm::mat4 modelMatrix = Transform::ModelMatrix(modelComponent.EntityID, world); - - for (auto texGroup : model->TextureGroups) { - std::shared_ptr modelJob = std::shared_ptr(new ModelJob(model, m_Camera, modelMatrix, texGroup, modelComponent, world)); + for (auto matGroup : model->MaterialGroups()) { + std::shared_ptr modelJob = std::shared_ptr(new ModelJob(model, m_Camera, modelMatrix, matGroup, modelComponent, world)); jobs.push_back(modelJob); } } diff --git a/src/Engine/Rendering/Renderer.cpp b/src/Engine/Rendering/Renderer.cpp index 51546f5b..867398bd 100644 --- a/src/Engine/Rendering/Renderer.cpp +++ b/src/Engine/Rendering/Renderer.cpp @@ -131,8 +131,8 @@ void Renderer::DrawScreenQuad(GLuint textureToDraw) glBindVertexArray(m_ScreenQuad->VAO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer); - glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->TextureGroups[0].EndIndex - m_ScreenQuad->TextureGroups[0].StartIndex +1 - , GL_UNSIGNED_INT, 0, m_ScreenQuad->TextureGroups[0].StartIndex); + glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->MaterialGroups()[0].EndIndex - m_ScreenQuad->MaterialGroups()[0].StartIndex +1 + , GL_UNSIGNED_INT, 0, m_ScreenQuad->MaterialGroups()[0].StartIndex); } void Renderer::InitializeTextures() diff --git a/src/Engine/Rendering/Texture.cpp b/src/Engine/Rendering/Texture.cpp index 19c829bf..57f3ca36 100644 --- a/src/Engine/Rendering/Texture.cpp +++ b/src/Engine/Rendering/Texture.cpp @@ -2,48 +2,48 @@ Texture::Texture(std::string path) { - PNG image(path); + PNG image(path); - if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) { - image = PNG("Textures/Core/ErrorTexture.png"); - if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) { - LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed."); - return; - } - } + if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) { + image = PNG("Textures/Core/ErrorTexture.png"); + if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) { + LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed."); + return; + } + } - this->Width = image.Width; - this->Height = image.Height; + this->Width = image.Width; + this->Height = image.Height; - GLint format; - switch (image.Format) { - case Image::ImageFormat::RGB: - format = GL_RGB; - break; - case Image::ImageFormat::RGBA: - format = GL_RGBA; - break; - } + GLint format; + switch (image.Format) { + case Image::ImageFormat::RGB: + format = GL_RGB; + break; + case Image::ImageFormat::RGBA: + format = GL_RGBA; + break; + } - // Construct the OpenGL texture - glGenTextures(1, &m_Texture); - glBindTexture(GL_TEXTURE_2D, m_Texture); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - GLERROR("Texture load"); + // Construct the OpenGL texture + glGenTextures(1, &m_Texture); + glBindTexture(GL_TEXTURE_2D, m_Texture); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLERROR("Texture load"); } Texture::~Texture() { - glDeleteTextures(1, &m_Texture); + glDeleteTextures(1, &m_Texture); } void Texture::Bind(GLenum textureUnit /* = GL_TEXTURE0 */) { - glActiveTexture(textureUnit); - glBindTexture(GL_TEXTURE_2D, m_Texture); + glActiveTexture(textureUnit); + glBindTexture(GL_TEXTURE_2D, m_Texture); } diff --git a/src/Game/Game.cpp b/src/Game/Game.cpp index ba660a7d..e6589676 100644 --- a/src/Game/Game.cpp +++ b/src/Game/Game.cpp @@ -8,11 +8,13 @@ Game::Game(int argc, char* argv[]) { ResourceManager::RegisterType("ConfigFile"); ResourceManager::RegisterType("Model"); + ResourceManager::RegisterType("RawModel"); ResourceManager::RegisterType("Texture"); ResourceManager::RegisterType("ShaderProgram"); ResourceManager::RegisterType("EntityFile"); m_Config = ResourceManager::Load("Config.ini"); + ResourceManager::UseThreading = m_Config->Get("Multithreading.ResourceLoading", true); LOG_LEVEL = static_cast<_LOG_LEVEL>(m_Config->Get("Debug.LogLevel", 1)); // Create the core event broker