Async load works, using exceptions for special resources.

This commit is contained in:
William Moberg
2016-01-14 16:27:45 +01:00
parent 9f185a1e35
commit 71047b08ec
13 changed files with 160 additions and 181 deletions
+83 -94
View File
@@ -12,33 +12,13 @@
/** Base Resource class. /** Base Resource class.
Implement this class for every resource to be handled by the resource manager. Implement this class for every resource to be handled by the resource manager.
If it should be possible to load the resource asyncronously (on a separate thread in the background),
using LoadAsync() then any necessary OpenGL calls (Eg. glGenBuffers(), glBindBuffer(), etc.) must be
made in GlCommands() method, and not inside the constructor (see Texture.cpp for an example).
*/ */
class Resource class Resource
{ {
friend class ResourceManager; friend class ResourceManager;
private:
bool m_FullyConstructed;
protected: protected:
Resource() : m_FullyConstructed(false) { } Resource() { }
//This method only needs to be overridden if:
// 1: a) It should be possible to load the resource with LoadAsync(), or
// b) This resource will be loaded inside the constructor of another resource that can be loaded with LoadAsync().
// c) Same as above but recursively.
// 2: a) The resource needs to make OpenGL calls, or
// b) The resource contains a resource that makes OGL calls, or
// c) The resource contains a resource that contains ... ... a resource that makes OGL calls, or
//
//If 2.a: any calls should be made in the GlCommands.
//If 2.b or 2.c: PostCtorGLCommands() should be called on the contained resource(s).
//If GlCommands needs to be overridden and GlCommands is also overridden by a baseclass then the baseclass
//implementation should be called in from the derived class implementation (see Model.cpp for an example).
virtual void GlCommands() { }
public: public:
// Pretend that this is a pure virtual function that you have to implement // Pretend that this is a pure virtual function that you have to implement
@@ -47,21 +27,30 @@ public:
virtual void Reload() { } virtual void Reload() { }
virtual void OnChildReloaded(Resource* child) { } virtual void OnChildReloaded(Resource* child) { }
//If this resource implements GlCommands and it is loaded in another resource,
//then the containing resource must also implement GlCommands and
//call this method in it (see RawModel.cpp for an example).
void PostCtorGLCommands()
{
if (!m_FullyConstructed) {
GlCommands();
}
m_FullyConstructed = true;
}
unsigned int TypeID; unsigned int TypeID;
unsigned int ResourceID; 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;
protected:
//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.";
}
};
};
/** Singleton resource manager to keep track of and cache any external engine assets */ /** Singleton resource manager to keep track of and cache any external engine assets */
class ResourceManager class ResourceManager
{ {
@@ -85,24 +74,20 @@ public:
*/ */
// TODO: Templateify // TODO: Templateify
static bool IsResourceLoaded(std::string resourceType, std::string resourceName); static bool IsResourceLoaded(std::string resourceType, std::string resourceName);
/** If the resource is not in cache, starts loading the resource and returns nullptr immediately. /** If Async is false: Hot-loads a resource and caches it for future use.
Fairly safe to assume that return value is always a valid pointer, will only return nullptr on error.
If Async is true: If the resource is not loaded yet, starts loading the resource
in the background and returns nullptr immediately.
If the resource has been loaded already, return a pointer to it. If the resource has been loaded already, return a pointer to it.
@tparam T Resource type.
@param resourceName Fully qualified name of the resource to load.
*/
template <typename T>
static T* LoadAsync(std::string resourceName, Resource* parent = nullptr);
/** Hot-loads a resource and caches it for future use.
Fairly safe to assume that return value is a valid pointer, will only return nullptr on error.
@tparam T Resource type. @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. @param resourceName Fully qualified name of the resource to load.
*/ */
template <typename T> template <typename T, bool Async = false>
static T* Load(std::string resourceName, Resource* parent = nullptr); static T* Load(std::string resourceName, Resource* parent = nullptr);
/** Reloads an already loaded resource, keeping its resource ID intact. /** Reloads an already loaded resource, keeping its resource ID intact.
@@ -116,6 +101,22 @@ public:
static void Update(); static void Update();
private: private:
//Represents a pointer value to signify that a resource haven't failed, but is not fully loaded.
class SpecialResourcePointer
{
public:
SpecialResourcePointer()
: m_Val(new Resource())
{}
~SpecialResourcePointer()
{
delete m_Val;
}
//Make this class implicitly convertible to the Resource*.
operator Resource* const() const { return m_Val; }
private:
Resource* const m_Val;
};
//This is a haxy way to make sure that IsMainThread is run when the program starts, so the master thread id is set. //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 struct MasterThreadChecker
{ {
@@ -124,7 +125,8 @@ private:
ResourceManager::IsMainThread(); ResourceManager::IsMainThread();
} }
}; };
static MasterThreadChecker m_Checker; const static SpecialResourcePointer m_StillLoading;
const static MasterThreadChecker m_Checker;
static std::unordered_map<std::string, std::string> m_CompilerTypenameToResourceType; static std::unordered_map<std::string, std::string> m_CompilerTypenameToResourceType;
static std::unordered_map<std::string, std::function<Resource*(std::string)>> m_FactoryFunctions; // type -> factory function static std::unordered_map<std::string, std::function<Resource*(std::string)>> m_FactoryFunctions; // type -> factory function
@@ -151,23 +153,8 @@ private:
static Resource* createResource(std::string resourceType, std::string resourceName, Resource* parent); static Resource* createResource(std::string resourceType, std::string resourceName, Resource* parent);
static bool IsMainThread(); static bool IsMainThread();
template <bool Async>
static Resource* Load(std::string resourceType, std::string resourceName, Resource* parent = nullptr);
}; };
template <typename T>
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<T*>(Load<false>(it->second, resourceName, parent));
}
template <typename T> template <typename T>
void ResourceManager::RegisterType(std::string typeName) void ResourceManager::RegisterType(std::string typeName)
{ {
@@ -175,28 +162,29 @@ void ResourceManager::RegisterType(std::string typeName)
m_FactoryFunctions[typeName] = [](std::string resourceName) { return new T(resourceName); }; m_FactoryFunctions[typeName] = [](std::string resourceName) { return new T(resourceName); };
} }
template <typename T> template <typename T, bool async>
T* ResourceManager::LoadAsync(std::string resourceName, Resource* parent /* = nullptr */) static T* ResourceManager::Load(std::string resourceName, Resource* parent /* = nullptr */)
{ {
auto resourceTypename = typeid(T).name(); auto resourceTypename = typeid(T).name();
auto it = m_CompilerTypenameToResourceType.find(resourceTypename); auto iter = m_CompilerTypenameToResourceType.find(resourceTypename);
if (it == m_CompilerTypenameToResourceType.end()) { if (iter == m_CompilerTypenameToResourceType.end()) {
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename); LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": type not registered", resourceName.c_str(), resourceTypename);
return nullptr; return nullptr;
} }
return static_cast<T*>(Load<true>(it->second, resourceName, parent)); std::string resourceType = iter->second;
} constexpr bool mustNotLoadInThread = std::is_base_of<ThreadUnsafeResource, T>::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());
return nullptr;
}
template <bool Async>
static Resource* ResourceManager::Load(std::string resourceType, std::string resourceName, Resource* parent /* = nullptr */)
{
auto cacheKey = std::make_pair(resourceType, resourceName); auto cacheKey = std::make_pair(resourceType, resourceName);
decltype(m_ResourceCache)::iterator it; decltype(m_ResourceCache)::iterator it;
//If a thread has already been launched to load this resource. //If a thread has already been launched to load this resource.
auto tIt = m_LoadingThreads.find(cacheKey); auto tIt = m_LoadingThreads.find(cacheKey);
if (tIt != m_LoadingThreads.end()) { if (tIt != m_LoadingThreads.end()) {
if (Async) { if (async) {
//Return null if the thread is still working. //Return null if the thread is still working.
if (!tIt->second.try_join_for(boost::chrono::nanoseconds(1))) { if (!tIt->second.try_join_for(boost::chrono::nanoseconds(1))) {
return nullptr; return nullptr;
@@ -211,38 +199,39 @@ static Resource* ResourceManager::Load(std::string resourceType, std::string res
//Find the resource that the thread loaded. //Find the resource that the thread loaded.
it = m_ResourceCache.find(cacheKey); it = m_ResourceCache.find(cacheKey);
if (it != m_ResourceCache.end()) { if (it != m_ResourceCache.end()) {
//At this point the resource may not be completely //Threads should not be able to throw StillLoadingException, so no check should be needed.
//done since the worker threads cannot do opengl commands. return static_cast<T*>(it->second);
if (IsMainThread()) {
//Do the gl commands to complete the resource if we are not a worker thread.
it->second->PostCtorGLCommands();
}
return it->second;
} else { } else {
//If resource is still null at cacheKey after thread finishes, it failed. //If cacheKey does not exist after thread finishes, it failed.
return nullptr; return nullptr;
} }
} }
//If resource has already been loaded and cached. //If resource has already been cached and completely loaded.
it = m_ResourceCache.find(cacheKey); it = m_ResourceCache.find(cacheKey);
if (it != m_ResourceCache.end()) { if (it != m_ResourceCache.end() && it->second != m_StillLoading) {
return it->second; return static_cast<T*>(it->second);
} }
Resource* res = nullptr;
//If resource is not cached.. //If resource is not cached..
if (Async) { if (async) {
//Create a thread that loads the resource into cache. if (mustNotLoadInThread) {
m_LoadingThreads[cacheKey] = boost::thread(createResource, resourceType, resourceName, parent); res = createResource(resourceType, resourceName, parent);
if (res != m_StillLoading) {
return static_cast<T*>(res);
}
} else {
//Create a thread that loads the resource into cache.
m_LoadingThreads[cacheKey] = boost::thread(createResource, resourceType, resourceName, parent);
}
return nullptr; return nullptr;
} else { } else {
//load and return the resource. //load and return the resource.
Resource* res = createResource(resourceType, resourceName, parent); do {
if (IsMainThread() && res != nullptr) { res = createResource(resourceType, resourceName, parent);
//Do the gl commands to complete the resource if we are not a worker thread. } while (res == m_StillLoading);
res->PostCtorGLCommands(); return static_cast<T*>(res);
}
return res;
} }
} }
+1 -1
View File
@@ -3,7 +3,7 @@
#include "../Core/ResourceManager.h" #include "../Core/ResourceManager.h"
class BaseTexture : public Resource class BaseTexture : public ThreadUnsafeResource
{ {
friend class ResourceManager; friend class ResourceManager;
+5 -2
View File
@@ -4,21 +4,24 @@
#include "RawModel.h" #include "RawModel.h"
#include "../OpenGL.h" #include "../OpenGL.h"
class Model : public RawModel class Model : public ThreadUnsafeResource
{ {
friend class ResourceManager; friend class ResourceManager;
private: private:
Model(std::string fileName); Model(std::string fileName);
virtual void GlCommands() override;
public: public:
~Model(); ~Model();
const std::vector<RawModel::MaterialGroup>& TextureGroups() const { return m_RawModel->TextureGroups; }
const glm::mat4& Matrix() const { return m_RawModel->m_Matrix; }
const std::vector<RawModel::Vertex>& Vertices() const { return m_RawModel->m_Vertices; }
GLuint VAO; GLuint VAO;
GLuint ElementBuffer; GLuint ElementBuffer;
private: private:
RawModel* m_RawModel;
GLuint VertexBuffer; GLuint VertexBuffer;
GLuint DiffuseVertexColorBuffer; GLuint DiffuseVertexColorBuffer;
GLuint SpecularVertexColorBuffer; GLuint SpecularVertexColorBuffer;
+3 -1
View File
@@ -24,7 +24,6 @@ class RawModel : public Resource
protected: protected:
RawModel(std::string fileName); RawModel(std::string fileName);
virtual void GlCommands() override;
public: public:
~RawModel(); ~RawModel();
@@ -47,8 +46,11 @@ public:
struct MaterialGroup struct MaterialGroup
{ {
float Shininess; float Shininess;
std::string TexturePath;
std::shared_ptr<::Texture> Texture; std::shared_ptr<::Texture> Texture;
std::string NormalMapPath;
std::shared_ptr<::Texture> NormalMap; std::shared_ptr<::Texture> NormalMap;
std::string SpecularMapPath;
std::shared_ptr<::Texture> SpecularMap; std::shared_ptr<::Texture> SpecularMap;
unsigned int StartIndex; unsigned int StartIndex;
unsigned int EndIndex; unsigned int EndIndex;
-3
View File
@@ -11,10 +11,7 @@ class Texture : public BaseTexture
private: private:
Texture(std::string path); Texture(std::string path);
virtual void GlCommands() override;
GLint m_Format;
Image* m_Image;
public: public:
~Texture(); ~Texture();
+5 -5
View File
@@ -220,16 +220,16 @@ bool attachAABBComponentFromModel(World* world, EntityID id)
} }
ComponentWrapper model = world->GetComponent(id, "Model"); ComponentWrapper model = world->GetComponent(id, "Model");
ComponentWrapper collision = world->AttachComponent(id, "AABB"); ComponentWrapper collision = world->AttachComponent(id, "AABB");
Model* modelRes = ResourceManager::LoadAsync<Model>(model["Resource"]); Model* modelRes = ResourceManager::Load<Model, true>(model["Resource"]);
if (modelRes == nullptr) { if (modelRes == nullptr) {
return false; return false;
} }
glm::mat4 modelMatrix = modelRes->m_Matrix; glm::mat4 modelMatrix = modelRes->Matrix();
glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY); glm::vec3 mini = glm::vec3(INFINITY, INFINITY, INFINITY);
glm::vec3 maxi = 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); 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.x = std::max(wPos.x, maxi.x);
maxi.y = std::max(wPos.y, maxi.y); maxi.y = std::max(wPos.y, maxi.y);
@@ -247,7 +247,7 @@ bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox)
{ {
ComponentWrapper& cTrans = world->GetComponent(AABBComponent.EntityID, "Transform"); ComponentWrapper& cTrans = world->GetComponent(AABBComponent.EntityID, "Transform");
ComponentWrapper model = world->GetComponent(AABBComponent.EntityID, "Model"); ComponentWrapper model = world->GetComponent(AABBComponent.EntityID, "Model");
Model* modelRes = ResourceManager::LoadAsync<Model>(model["Resource"]); Model* modelRes = ResourceManager::Load<Model, true>(model["Resource"]);
outBox.CreateFromCenter(AABBComponent["BoxCenter"], AABBComponent["BoxSize"]); outBox.CreateFromCenter(AABBComponent["BoxCenter"], AABBComponent["BoxSize"]);
glm::vec3 mini = outBox.MinCorner(); glm::vec3 mini = outBox.MinCorner();
glm::vec3 maxi = outBox.MaxCorner(); glm::vec3 maxi = outBox.MaxCorner();
@@ -255,7 +255,7 @@ bool GetEntityBox(World* world, ComponentWrapper& AABBComponent, AABB& outBox)
if (modelRes == nullptr) { if (modelRes == nullptr) {
return false; return false;
} }
glm::mat4 modelMatrix = modelRes->m_Matrix * glm::mat4 modelMatrix = modelRes->Matrix() *
glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) * glm::translate(glm::mat4(), (glm::vec3)cTrans["Position"]) *
glm::scale((glm::vec3)cTrans["Scale"]); glm::scale((glm::vec3)cTrans["Scale"]);
+3
View File
@@ -14,6 +14,7 @@ std::unordered_map<unsigned int, unsigned int> ResourceManager::m_ResourceCount;
FileWatcher ResourceManager::m_FileWatcher; FileWatcher ResourceManager::m_FileWatcher;
std::unordered_map<std::pair<std::string, std::string>, boost::thread> ResourceManager::m_LoadingThreads; std::unordered_map<std::pair<std::string, std::string>, boost::thread> ResourceManager::m_LoadingThreads;
boost::recursive_mutex ResourceManager::m_Mutex; boost::recursive_mutex ResourceManager::m_Mutex;
const ResourceManager::SpecialResourcePointer ResourceManager::m_StillLoading;
unsigned int ResourceManager::GetTypeID(std::string resourceType) unsigned int ResourceManager::GetTypeID(std::string resourceType)
{ {
@@ -92,6 +93,8 @@ Resource* ResourceManager::createResource(std::string resourceType, std::string
Resource* resource = nullptr; Resource* resource = nullptr;
try { try {
resource = facIt->second(resourceName); resource = facIt->second(resourceName);
} catch (const ThreadUnsafeResource::StillLoadingException&) {
resource = m_StillLoading;
} catch (const std::exception& e) { } catch (const std::exception& e) {
LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what()); LOG_ERROR("Failed to load resource \"%s\" of type \"%s\": %s", resourceName.c_str(), resourceType.c_str(), e.what());
} }
+19 -8
View File
@@ -1,24 +1,35 @@
#include "Rendering/Model.h" #include "Rendering/Model.h"
Model::Model(std::string fileName) Model::Model(std::string fileName)
: RawModel(fileName)
{ {
} //Load the RawModel asyncronously, this will be done on a separate thread in the background.
m_RawModel = ResourceManager::Load<RawModel, true>(fileName);
//If it is null, the model is not done yet, so tell resourceManager to try constructing me again later.
if (m_RawModel == nullptr) {
throw StillLoadingException();
}
void Model::GlCommands() for (auto& group : m_RawModel->TextureGroups) {
{ if (!group.TexturePath.empty()) {
//Call the base class method. group.Texture = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.TexturePath));
RawModel::GlCommands(); }
if (!group.NormalMapPath.empty()) {
group.NormalMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.NormalMapPath));
}
if (!group.SpecularMapPath.empty()) {
group.SpecularMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(group.SpecularMapPath));
}
}
// Generate GL buffers // Generate GL buffers
GLuint buffer; GLuint buffer;
glGenBuffers(1, &buffer); glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer); glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, m_Vertices.size() * sizeof(Vertex), &m_Vertices[0], GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, m_RawModel->m_Vertices.size() * sizeof(RawModel::Vertex), &m_RawModel->m_Vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &ElementBuffer); glGenBuffers(1, &ElementBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ElementBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_Indices.size() * sizeof(unsigned int), &m_Indices[0], GL_STATIC_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_RawModel->m_Indices.size() * sizeof(unsigned int), &m_RawModel->m_Indices[0], GL_STATIC_DRAW);
glGenVertexArrays(1, &VAO); glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO); glBindVertexArray(VAO);
+3 -25
View File
@@ -141,9 +141,7 @@ RawModel::RawModel(std::string fileName)
aiString path; aiString path;
aiTextureMapping mapping; aiTextureMapping mapping;
material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping); material->GetTexture(aiTextureType_DIFFUSE, 0, &path, &mapping);
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); matGroup.TexturePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
//LOG_DEBUG("Diffuse texture: %s", absolutePath.c_str());
matGroup.Texture = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
} }
// Normal map // Normal map
//LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT)); //LOG_DEBUG("%i normal maps found", material->GetTextureCount(aiTextureType_HEIGHT));
@@ -151,9 +149,7 @@ RawModel::RawModel(std::string fileName)
aiString path; aiString path;
aiTextureMapping mapping; aiTextureMapping mapping;
material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping); material->GetTexture(aiTextureType_HEIGHT, 0, &path, &mapping);
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); matGroup.NormalMapPath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
//LOG_DEBUG("Normal map: %s", absolutePath.c_str());
matGroup.NormalMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
} }
// Specular map // Specular map
//LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR)); //LOG_DEBUG("%i specular maps found", material->GetTextureCount(aiTextureType_SPECULAR));
@@ -161,9 +157,7 @@ RawModel::RawModel(std::string fileName)
aiString path; aiString path;
aiTextureMapping mapping; aiTextureMapping mapping;
material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping); material->GetTexture(aiTextureType_SPECULAR, 0, &path, &mapping);
std::string absolutePath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string(); matGroup.SpecularMapPath = (boost::filesystem::path(fileName).branch_path() / path.C_Str()).string();
//LOG_DEBUG("Specular map: %s", absolutePath.c_str());
matGroup.SpecularMap = std::shared_ptr<Texture>(ResourceManager::Load<Texture>(absolutePath));
} }
TextureGroups.push_back(matGroup); TextureGroups.push_back(matGroup);
@@ -292,22 +286,6 @@ RawModel::RawModel(std::string fileName)
} }
} }
void RawModel::GlCommands()
{
//Since RawModel contains Textures that was loaded in the constructor, their GlCommands must be run.
for (auto& texGroup : TextureGroups) {
if (texGroup.Texture) {
texGroup.Texture->PostCtorGLCommands();
}
if (texGroup.NormalMap) {
texGroup.NormalMap->PostCtorGLCommands();
}
if (texGroup.SpecularMap) {
texGroup.SpecularMap->PostCtorGLCommands();
}
}
}
RawModel::~RawModel() RawModel::~RawModel()
{ {
if (m_Skeleton) { if (m_Skeleton) {
+3 -3
View File
@@ -84,12 +84,12 @@ void RenderQueueFactory::FillModels(World* world, RenderQueue* renderQueue)
continue; continue;
} }
glm::vec4 color = modelC["Color"]; glm::vec4 color = modelC["Color"];
Model* model = ResourceManager::LoadAsync<Model>(resource); Model* model = ResourceManager::Load<Model, true>(resource);
if (model == nullptr) { if (model == nullptr) {
model = ResourceManager::Load<Model>("Models/Core/Error.obj"); model = ResourceManager::Load<Model>("Models/Core/Error.obj");
} }
for (auto texGroup : model->TextureGroups) { for (auto texGroup : model->TextureGroups()) {
ModelJob job; ModelJob job;
job.TextureID = (texGroup.Texture) ? texGroup.Texture->ResourceID : 0; job.TextureID = (texGroup.Texture) ? texGroup.Texture->ResourceID : 0;
job.DiffuseTexture = texGroup.Texture.get(); job.DiffuseTexture = texGroup.Texture.get();
@@ -98,7 +98,7 @@ void RenderQueueFactory::FillModels(World* world, RenderQueue* renderQueue)
job.Model = model; job.Model = model;
job.StartIndex = texGroup.StartIndex; job.StartIndex = texGroup.StartIndex;
job.EndIndex = texGroup.EndIndex; job.EndIndex = texGroup.EndIndex;
job.ModelMatrix = model->m_Matrix * ModelMatrix(world, modelC.EntityID); job.ModelMatrix = model->Matrix() * ModelMatrix(world, modelC.EntityID);
job.Color = color; job.Color = color;
//TODO: RENDERER: Not sure if the best solution for pickingColor to entity link is this //TODO: RENDERER: Not sure if the best solution for pickingColor to entity link is this
+2 -2
View File
@@ -161,8 +161,8 @@ void Renderer::DrawScreenQuad(GLuint textureToDraw)
glBindVertexArray(m_ScreenQuad->VAO); glBindVertexArray(m_ScreenQuad->VAO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ScreenQuad->ElementBuffer);
glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->TextureGroups[0].EndIndex - m_ScreenQuad->TextureGroups[0].StartIndex +1 glDrawElementsBaseVertex(GL_TRIANGLES, m_ScreenQuad->TextureGroups()[0].EndIndex - m_ScreenQuad->TextureGroups()[0].StartIndex +1
, GL_UNSIGNED_INT, 0, m_ScreenQuad->TextureGroups[0].StartIndex); , GL_UNSIGNED_INT, 0, m_ScreenQuad->TextureGroups()[0].StartIndex);
} }
void Renderer::InitializeTextures() void Renderer::InitializeTextures()
+32 -37
View File
@@ -2,53 +2,48 @@
Texture::Texture(std::string path) Texture::Texture(std::string path)
{ {
m_Image = new PNG(path); PNG image(path);
if (m_Image->Width == 0 && m_Image->Height == 0 || m_Image->Format == Image::ImageFormat::Unknown) { if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
delete m_Image; image = PNG("Textures/Core/ErrorTexture.png");
m_Image = new PNG("Textures/Core/ErrorTexture.png"); if (image.Width == 0 && image.Height == 0 || image.Format == Image::ImageFormat::Unknown) {
if (m_Image->Width == 0 && m_Image->Height == 0 || m_Image->Format == Image::ImageFormat::Unknown) { LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed.");
LOG_ERROR("Couldn't even load the error texture. This is a dark day indeed."); return;
return; }
} }
}
this->Width = m_Image->Width; this->Width = image.Width;
this->Height = m_Image->Height; this->Height = image.Height;
switch (m_Image->Format) { GLint format;
case Image::ImageFormat::RGB: switch (image.Format) {
m_Format = GL_RGB; case Image::ImageFormat::RGB:
break; format = GL_RGB;
case Image::ImageFormat::RGBA: break;
m_Format = GL_RGBA; case Image::ImageFormat::RGBA:
break; format = GL_RGBA;
} break;
} }
void Texture::GlCommands() // Construct the OpenGL texture
{ glGenTextures(1, &m_Texture);
// Construct the OpenGL texture glBindTexture(GL_TEXTURE_2D, m_Texture);
glGenTextures(1, &m_Texture); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, m_Texture); glTexImage2D(GL_TEXTURE_2D, 0, format, image.Width, image.Height, 0, format, GL_UNSIGNED_BYTE, image.Data);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, m_Format, Width, Height, 0, m_Format, GL_UNSIGNED_BYTE, m_Image->Data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); GLERROR("Texture load");
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GLERROR("Texture load");
delete m_Image;
m_Image = nullptr;
} }
Texture::~Texture() Texture::~Texture()
{ {
glDeleteTextures(1, &m_Texture); glDeleteTextures(1, &m_Texture);
} }
void Texture::Bind(GLenum textureUnit /* = GL_TEXTURE0 */) void Texture::Bind(GLenum textureUnit /* = GL_TEXTURE0 */)
{ {
glActiveTexture(textureUnit); glActiveTexture(textureUnit);
glBindTexture(GL_TEXTURE_2D, m_Texture); glBindTexture(GL_TEXTURE_2D, m_Texture);
} }
+1
View File
@@ -6,6 +6,7 @@ Game::Game(int argc, char* argv[])
{ {
ResourceManager::RegisterType<ConfigFile>("ConfigFile"); ResourceManager::RegisterType<ConfigFile>("ConfigFile");
ResourceManager::RegisterType<Model>("Model"); ResourceManager::RegisterType<Model>("Model");
ResourceManager::RegisterType<RawModel>("RawModel");
ResourceManager::RegisterType<Texture>("Texture"); ResourceManager::RegisterType<Texture>("Texture");
ResourceManager::RegisterType<EntityXMLFile>("EntityXMLFile"); ResourceManager::RegisterType<EntityXMLFile>("EntityXMLFile");
ResourceManager::RegisterType<ShaderProgram>("ShaderProgram"); ResourceManager::RegisterType<ShaderProgram>("ShaderProgram");