From 7295d4712c94194eaa7d2ce38dedaaf4c3b4fa61 Mon Sep 17 00:00:00 2001 From: Doyle Thai Date: Sat, 4 Jun 2016 22:42:22 +1000 Subject: [PATCH] Abstract asset loading to asset manager --- Dengine.vcxproj | 4 + Dengine.vcxproj.filters | 12 +++ src/AssetManager.cpp | 118 +++++++++++++++++++++++++++++ src/Shader.cpp | 63 +++------------ src/Texture.cpp | 42 ++++++++++ src/build.bat | 3 +- src/dengine.cpp | 97 ++++++++++++++---------- src/include/Dengine/AssetManager.h | 34 +++++++++ src/include/Dengine/Shader.h | 17 +++-- src/include/Dengine/Texture.h | 43 +++++++++++ 10 files changed, 331 insertions(+), 102 deletions(-) create mode 100644 src/AssetManager.cpp create mode 100644 src/Texture.cpp create mode 100644 src/include/Dengine/AssetManager.h create mode 100644 src/include/Dengine/Texture.h diff --git a/Dengine.vcxproj b/Dengine.vcxproj index e8d995e..24fa02b 100644 --- a/Dengine.vcxproj +++ b/Dengine.vcxproj @@ -120,17 +120,21 @@ + + + + diff --git a/Dengine.vcxproj.filters b/Dengine.vcxproj.filters index 552a6bd..4129db9 100644 --- a/Dengine.vcxproj.filters +++ b/Dengine.vcxproj.filters @@ -21,6 +21,12 @@ Source Files + + Source Files + + + Source Files + @@ -36,5 +42,11 @@ Header Files + + Header Files + + + Header Files + \ No newline at end of file diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp new file mode 100644 index 0000000..c2e90d7 --- /dev/null +++ b/src/AssetManager.cpp @@ -0,0 +1,118 @@ +#include + +#define STBI_FAILURE_USERMSG +#define STB_IMAGE_IMPLEMENTATION +#include +#include + +namespace Dengine +{ + + AssetManager::AssetManager(){} + AssetManager::~AssetManager(){} + + Texture *AssetManager::getTexture(std::string name) + { + // NOTE(doyle): Since we're using a map, the count of an object can + // only be 1 or 0 + if (mTextures.count(name) == 1) + return &mTextures[name]; + + return nullptr; + } + + i32 AssetManager::loadTextureImage(std::string path, std::string name) + { + /* Open the texture image */ + i32 imgWidth, imgHeight, bytesPerPixel; + stbi_set_flip_vertically_on_load(TRUE); + u8 *image = + stbi_load(path.c_str(), &imgWidth, &imgHeight, &bytesPerPixel, 0); + + if (!image) + { + std::cerr << "stdbi_load() failed: " << stbi_failure_reason() + << std::endl; + return -1; + } + + Texture tex; + tex.generate(imgWidth, imgHeight, image); + stbi_image_free(image); + + mTextures[name] = tex; + return 0; + } + + Shader *AssetManager::getShader(std::string name) + { + if (mShaders.count(name) == 1) + return &mShaders[name]; + + return nullptr; + } + + INTERNAL std::string stringFromFile(const std::string &filename) + { + std::ifstream file; + file.open(filename.c_str(), std::ios::in | std::ios::binary); + + std::string output; + std::string line; + + if (!file.is_open()) + { + std::runtime_error(std::string("Failed to open file: ") + filename); + } + else + { + while (file.good()) + { + std::getline(file, line); + output.append(line + "\n"); + } + } + file.close(); + return output; + } + + INTERNAL GLuint createShaderFromPath(std::string path, GLuint shadertype) + { + std::string shaderSource = stringFromFile(path); + const GLchar *source = shaderSource.c_str(); + + GLuint result = glCreateShader(shadertype); + glShaderSource(result, 1, &source, NULL); + glCompileShader(result); + + GLint success; + GLchar infoLog[512]; + glGetShaderiv(result, GL_COMPILE_STATUS, &success); + if (!success) + { + glGetShaderInfoLog(result, 512, NULL, infoLog); + std::cout << "glCompileShader failed: " << infoLog << std::endl; + } + + return result; + } + + i32 AssetManager::loadShaderFiles(std::string vertexPath, + std::string fragmentPath, std::string name) + { + + GLuint vertexShader = + createShaderFromPath(vertexPath, GL_VERTEX_SHADER); + GLuint fragmentShader = + createShaderFromPath(fragmentPath, GL_FRAGMENT_SHADER); + + Shader shader; + i32 result = shader.loadProgram(vertexShader, fragmentShader); + if (result) + return result; + + mShaders[name] = shader; + return 0; + } + +} diff --git a/src/Shader.cpp b/src/Shader.cpp index 9701c30..21ae18f 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -5,63 +5,23 @@ namespace Dengine { -INTERNAL std::string stringFromFile(const std::string &filename) +Shader::Shader() +: mProgram(0) { - std::ifstream file; - file.open(filename.c_str(), std::ios::in | std::ios::binary); - - std::string output; - std::string line; - - if (!file.is_open()) - { - std::runtime_error(std::string("Failed to open file: ") + filename); - } - else - { - while (file.good()) - { - std::getline(file, line); - output.append(line + "\n"); - } - } - file.close(); - return output; } -INTERNAL GLuint createShaderFromPath(std::string path, GLuint shadertype) +Shader::~Shader() {} + +i32 Shader::loadProgram(GLuint vertexShader, GLuint fragmentShader) { - std::string shaderSource = stringFromFile(path); - const GLchar *source = shaderSource.c_str(); - - GLuint result = glCreateShader(shadertype); - glShaderSource(result, 1, &source, NULL); - glCompileShader(result); - - GLint success; - GLchar infoLog[512]; - glGetShaderiv(result, GL_COMPILE_STATUS, &success); - if (!success) - { - glGetShaderInfoLog(result, 512, NULL, infoLog); - std::cout << "glCompileShader failed: " << infoLog << std::endl; - } - - return result; -} - -Shader::Shader(std::string vertexPath, std::string fragmentPath) -{ - - GLuint vertexShader = createShaderFromPath(vertexPath, GL_VERTEX_SHADER); - GLuint fragmentShader = - createShaderFromPath(fragmentPath, GL_FRAGMENT_SHADER); - mProgram = glCreateProgram(); glAttachShader(mProgram, vertexShader); glAttachShader(mProgram, fragmentShader); glLinkProgram(mProgram); + glDeleteShader(fragmentShader); + glDeleteShader(vertexShader); + GLint success; GLchar infoLog[512]; glGetProgramiv(mProgram, GL_LINK_STATUS, &success); @@ -69,13 +29,12 @@ Shader::Shader(std::string vertexPath, std::string fragmentPath) { glGetProgramInfoLog(mProgram, 512, NULL, infoLog); std::cout << "glLinkProgram failed: " << infoLog << std::endl; + return -1; } - glDeleteShader(fragmentShader); - glDeleteShader(vertexShader); -} + return 0; -Shader::~Shader() {} +} void Shader::use() { glUseProgram(mProgram); } } diff --git a/src/Texture.cpp b/src/Texture.cpp new file mode 100644 index 0000000..6043846 --- /dev/null +++ b/src/Texture.cpp @@ -0,0 +1,42 @@ +#include + +namespace Dengine +{ +Texture::Texture() +: mId(0) +, mWidth(0) +, mHeight(0) +, mInternalFormat(GL_RGB) +, mImageFormat(GL_RGB) +, mWrapS(GL_REPEAT) +, mWrapT(GL_REPEAT) +, mFilterMinification(GL_LINEAR) +, mFilterMagnification(GL_LINEAR) +{ + glGenTextures(1, &mId); +} + +void Texture::generate(GLuint width, GLuint height, u8 *image) +{ + mWidth = width; + mHeight = height; + + glBindTexture(GL_TEXTURE_2D, mId); + + /* Load image into texture */ + glTexImage2D(GL_TEXTURE_2D, 0, mInternalFormat, mWidth, mHeight, 0, + mImageFormat, GL_UNSIGNED_BYTE, image); + glGenerateMipmap(GL_TEXTURE_2D); + + /* Set parameter of currently bound texture */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mWrapS); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mWrapT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, mFilterMinification); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mFilterMagnification); + + /* Unbind and clean up */ + glBindTexture(GL_TEXTURE_2D, 0); +} + +void Texture::bind() const { glBindTexture(GL_TEXTURE_2D, mId); } +} diff --git a/src/build.bat b/src/build.bat index b645bf5..3cb7e2f 100644 --- a/src/build.bat +++ b/src/build.bat @@ -18,8 +18,9 @@ set includeFlags=/I ..\src\include /I %GLEW%\include /I %GLFW%\include /I %STB%\ REM Link libraries set linkLibraries=/link opengl32.lib %GLFW%\lib-vc2015\glfw3.lib %GLEW%\lib\Release\Win32\glew32s.lib gdi32.lib user32.lib shell32.lib +set ignoreLibraries=/NODEFAULTLIB:"libc.lib" /NODEFAULTLIB:"libcmt.lib" /NODEFAULTLIB:"libcd.lib" /NODEFAULTLIB:"libcmtd.lib" /NODEFAULTLIB:"msvcrtd.lib" -cl %compileFlags% ..\src\*.cpp %includeFlags% %linkLibraries% +cl %compileFlags% ..\src\*.cpp %includeFlags% %linkLibraries% %ignoreLibraries% /OUT:"Dengine.exe" REM /SUBSYSTEM:CONSOLE popd diff --git a/src/dengine.cpp b/src/dengine.cpp index 8a318b8..09e899d 100644 --- a/src/dengine.cpp +++ b/src/dengine.cpp @@ -1,10 +1,8 @@ #include #include #include +#include -#define STBI_FAILURE_USERMSG -#define STB_IMAGE_IMPLEMENTATION -#include #include #include #include @@ -130,6 +128,42 @@ void scroll_callback(GLFWwindow *window, double xOffset, double yOffset) fov = 45.0f; } +GLenum glCheckError_(const char *file, int line) +{ + GLenum errorCode; + while ((errorCode = glGetError()) != GL_NO_ERROR) + { + std::string error; + switch (errorCode) + { + case GL_INVALID_ENUM: + error = "INVALID_ENUM"; + break; + case GL_INVALID_VALUE: + error = "INVALID_VALUE"; + break; + case GL_INVALID_OPERATION: + error = "INVALID_OPERATION"; + break; + case GL_STACK_OVERFLOW: + error = "STACK_OVERFLOW"; + break; + case GL_STACK_UNDERFLOW: + error = "STACK_UNDERFLOW"; + break; + case GL_OUT_OF_MEMORY: + error = "OUT_OF_MEMORY"; + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + error = "INVALID_FRAMEBUFFER_OPERATION"; + break; + } + std::cout << error << " | " << file << " (" << line << ")" << std::endl; + } + return errorCode; +} +#define glCheckError() glCheckError_(__FILE__, __LINE__) + int main() { glfwInit(); @@ -173,41 +207,22 @@ int main() glEnable(GL_DEPTH_TEST); /* Initialise shaders */ - Dengine::Shader shader = Dengine::Shader("data/shaders/default.vert.glsl", - "data/shaders/default.frag.glsl"); - GLuint texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); + Dengine::AssetManager assetManager; + i32 result = 0; - // Set texture wrapping parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - // Set texture filtering parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + result = assetManager.loadShaderFiles("data/shaders/default.vert.glsl", + "data/shaders/default.frag.glsl", + "default"); + if (result) return result; /* Load a texture */ - i32 imgWidth, imgHeight, bytesPerPixel; - stbi_set_flip_vertically_on_load(TRUE); - u8 *image = stbi_load("data/textures/container.jpg", &imgWidth, &imgHeight, - &bytesPerPixel, 0); + result = assetManager.loadTextureImage("data/textures/container.jpg", + "container"); + if (result) return result; - if (!image) - { - std::cerr << "stdbi_load() failed: " << stbi_failure_reason() - << std::endl; - } - - glTexImage2D(GL_TEXTURE_2D, 0, - getGLFormat((BytesPerPixel)bytesPerPixel, FALSE), imgWidth, - imgHeight, 0, getGLFormat((BytesPerPixel)bytesPerPixel, FALSE), - GL_UNSIGNED_BYTE, image); - glGenerateMipmap(GL_TEXTURE_2D); - - // Unbind texture - glBindTexture(GL_TEXTURE_2D, 0); - stbi_image_free(image); + Dengine::Texture *containerTex = assetManager.getTexture("container"); + Dengine::Shader *shader = assetManager.getShader("default"); + //if (!containerTex) return -1; /* Create OGL Vertex objects */ GLfloat vertices[] = { @@ -275,11 +290,11 @@ int main() -0.5f, -0.5f, +0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // Bottom left -0.5f, +0.5f, +0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, // Top left }; -#endif GLuint indices[] = { 0, 1, 3, // First triangle 1, 2, 3, // First triangle }; +#endif GLuint vbo, vao; @@ -355,10 +370,10 @@ int main() /* Bind textures */ glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, texture); - glUniform1i(glGetUniformLocation(shader.mProgram, "ourTexture"), 0); + glBindTexture(GL_TEXTURE_2D, containerTex->mId); + glUniform1i(glGetUniformLocation(shader->mProgram, "ourTexture"), 0); - shader.use(); + shader->use(); /* Camera/View transformation */ glm::mat4 view; @@ -372,9 +387,9 @@ int main() 0.1f, 100.0f); /* Get shader uniform locations */ - GLuint modelLoc = glGetUniformLocation(shader.mProgram, "model"); - GLuint viewLoc = glGetUniformLocation(shader.mProgram, "view"); - GLuint projectionLoc = glGetUniformLocation(shader.mProgram, "projection"); + GLuint modelLoc = glGetUniformLocation(shader->mProgram, "model"); + GLuint viewLoc = glGetUniformLocation(shader->mProgram, "view"); + GLuint projectionLoc = glGetUniformLocation(shader->mProgram, "projection"); /* Pass matrices to the shader */ glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); diff --git a/src/include/Dengine/AssetManager.h b/src/include/Dengine/AssetManager.h new file mode 100644 index 0000000..429c059 --- /dev/null +++ b/src/include/Dengine/AssetManager.h @@ -0,0 +1,34 @@ +#ifndef DENGINE_ASSET_MANAGER_H +#define DENGINE_ASSET_MANAGER_H + +#include +#include +#include + +#include +#include +#include + +namespace Dengine +{ +class AssetManager +{ +public: + AssetManager(); + ~AssetManager(); + + /* Texture */ + Texture *getTexture(std::string name); + i32 loadTextureImage(std::string path, std::string name); + + /* Shaders */ + Shader *getShader(std::string name); + i32 loadShaderFiles(std::string vertexPath, std::string fragmentPath, + std::string name); + +private: + std::map mTextures; + std::map mShaders; +}; +} +#endif diff --git a/src/include/Dengine/Shader.h b/src/include/Dengine/Shader.h index 376ddb9..10d30f1 100644 --- a/src/include/Dengine/Shader.h +++ b/src/include/Dengine/Shader.h @@ -7,17 +7,18 @@ namespace Dengine { - class Shader - { - public: - GLuint mProgram; +class Shader +{ +public: + GLuint mProgram; - Shader(std::string vertexPath, std::string fragmentPath); - ~Shader(); + Shader(); + ~Shader(); - void use(); + i32 loadProgram(GLuint vertexShader, GLuint fragmentShader); - }; + void use(); +}; } #endif diff --git a/src/include/Dengine/Texture.h b/src/include/Dengine/Texture.h new file mode 100644 index 0000000..c6cbb3d --- /dev/null +++ b/src/include/Dengine/Texture.h @@ -0,0 +1,43 @@ +#ifndef DENGINE_TEXTURE_H +#define DENGINE_TEXTURE_H + +#include +#include + +namespace Dengine +{ +class Texture +{ +public: + // Holds the ID of the texture object, used for all texture operations to + // reference to this particlar texture + GLuint mId; + + // Texture image dimensions + GLuint mWidth; + GLuint mHeight; + + // Texture Format + GLuint mInternalFormat; // Format of texture object + GLuint mImageFormat; // Format of loaded image + + // Texture configuration + GLuint mWrapS; // Wrapping mode on S axis + GLuint mWrapT; // Wrapping mode on T axis + + // Filtering mode if texture pixels < screen pixels + GLuint mFilterMinification; + // Filtering mode if texture pixels > screen pixels + GLuint mFilterMagnification; + + // Constructor (sets default texture modes) + Texture(); + + // Generates texture from image data + void generate(GLuint width, GLuint height, unsigned char *data); + + // Binds the texture as the current active GL_TEXTURE_2D texture object + void bind() const; +}; +} +#endif