From 90af5d47ab8b1e96f86984cf846fe0a426ee54ae Mon Sep 17 00:00:00 2001 From: Aloshi Date: Fri, 21 Jun 2013 11:49:29 -0500 Subject: [PATCH] Created the ResourceManager, Resource, and TextureResource classes. The ResourceManager provides a unified interface for accessing resource data, embedded or from the filesystem, with initialization/deinitialization handled automatically behind the scenes. It also keeps from creating duplicate resources (e.g. when two ImageComponents use the same image file). Audio still needs to be moved over to it. --- CMakeLists.txt | 6 + src/Window.cpp | 9 +- src/Window.h | 3 + src/components/ImageComponent.cpp | 196 ++++-------------------------- src/components/ImageComponent.h | 17 +-- src/resources/Resource.cpp | 0 src/resources/Resource.h | 14 +++ src/resources/ResourceManager.cpp | 95 +++++++++++++++ src/resources/ResourceManager.h | 34 ++++++ src/resources/TextureResource.cpp | 86 +++++++++++++ src/resources/TextureResource.h | 27 ++++ 11 files changed, 300 insertions(+), 187 deletions(-) create mode 100644 src/resources/Resource.cpp create mode 100644 src/resources/Resource.h create mode 100644 src/resources/ResourceManager.cpp create mode 100644 src/resources/ResourceManager.h create mode 100644 src/resources/TextureResource.cpp create mode 100644 src/resources/TextureResource.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ab88f5c13..67177e5bb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,9 @@ set(ES_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.h ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugiconfig.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Resource.h + ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.h ${CMAKE_CURRENT_SOURCE_DIR}/data/Resources.h ) set(ES_SOURCES @@ -187,6 +190,9 @@ set(ES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/GuiSettingsMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/pugiXML/pugixml.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/ResourceManager.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/Resource.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/resources/TextureResource.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/logo/ES_logo_16.cpp ${CMAKE_CURRENT_SOURCE_DIR}/data/logo/ES_logo_32.cpp ) diff --git a/src/Window.cpp b/src/Window.cpp index 340cd09a3..ccbd9490a 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -53,8 +53,9 @@ void Window::render() void Window::init() { - mInputManager->init(); + mInputManager->init(); //shouldn't this go AFTER renderer initialization? Renderer::init(0, 0); + mResourceManager.init(); for(unsigned int i = 0; i < mGuiStack.size(); i++) { @@ -70,6 +71,7 @@ void Window::deinit() } mInputManager->deinit(); + mResourceManager.deinit(); Renderer::deinit(); } @@ -97,3 +99,8 @@ InputManager* Window::getInputManager() { return mInputManager; } + +ResourceManager* Window::getResourceManager() +{ + return &mResourceManager; +} diff --git a/src/Window.h b/src/Window.h index 6319bb68c..9e38b03cb 100644 --- a/src/Window.h +++ b/src/Window.h @@ -3,6 +3,7 @@ #include "GuiComponent.h" #include "InputManager.h" +#include "resources/ResourceManager.h" #include class Window @@ -23,9 +24,11 @@ public: void deinit(); InputManager* getInputManager(); + ResourceManager* getResourceManager(); private: InputManager* mInputManager; + ResourceManager mResourceManager; std::vector mGuiStack; }; diff --git a/src/components/ImageComponent.cpp b/src/components/ImageComponent.cpp index 238c9019e..f89368eb7 100644 --- a/src/components/ImageComponent.cpp +++ b/src/components/ImageComponent.cpp @@ -4,13 +4,18 @@ #include #include "../Log.h" #include "../Renderer.h" +#include "../Window.h" -Vector2u ImageComponent::getTextureSize() { return mTextureSize; } +Vector2u ImageComponent::getTextureSize() +{ + if(mTexture) + return mTexture->getSize(); + else + return Vector2u(0, 0); +} ImageComponent::ImageComponent(Window* window, int offsetX, int offsetY, std::string path, unsigned int resizeWidth, unsigned int resizeHeight, bool allowUpscale) : GuiComponent(window) { - mTextureID = 0; - setOffset(Vector2i(offsetX, offsetY)); //default origin is the center of image @@ -35,120 +40,15 @@ ImageComponent::ImageComponent(Window* window, int offsetX, int offsetY, std::st ImageComponent::~ImageComponent() { - unloadImage(); -} - -void ImageComponent::loadImage(std::string path) -{ - //make sure the file *exists* - if(!boost::filesystem::exists(path)) - { - LOG(LogError) << "Image \"" << path << "\" not found!"; - return; - } - - //make sure we don't already have an image - unloadImage(); - - - FREE_IMAGE_FORMAT format = FIF_UNKNOWN; - FIBITMAP* image = NULL; - BYTE* imageData = NULL; - unsigned int width, height; - - //detect the filetype - format = FreeImage_GetFileType(path.c_str(), 0); - if(format == FIF_UNKNOWN) - format = FreeImage_GetFIFFromFilename(path.c_str()); - if(format == FIF_UNKNOWN) - { - LOG(LogError) << "Error - could not detect filetype for image \"" << path << "\"!"; - return; - } - - - //make sure we can read this filetype first, then load it - if(FreeImage_FIFSupportsReading(format)) - { - image = FreeImage_Load(format, path.c_str()); - }else{ - LOG(LogError) << "Error - file format reading not supported for image \"" << path << "\"!"; - return; - } - - //make sure it loaded properly - if(!image) - { - LOG(LogError) << "Error loading image \"" << path << "\"!"; - return; - } - - //convert to 32bit - FIBITMAP* imgConv = FreeImage_ConvertTo32Bits(image); - FreeImage_Unload(image); - image = imgConv; - - //get a pointer to the image data as BGRA - imageData = FreeImage_GetBits(image); - if(!imageData) - { - LOG(LogError) << "Error retriving bits from image \"" << path << "\"!"; - return; - } - - - - width = FreeImage_GetWidth(image); - height = FreeImage_GetHeight(image); - - //if width or height are zero then something is clearly wrong - if(!width || !height) - { - LOG(LogError) << "Width or height are zero for image \"" << path << "\"!"; - FreeImage_Unload(image); - return; - } - - //convert from BGRA to RGBA - GLubyte* imageRGBA = new GLubyte[4*width*height]; - for(unsigned int i = 0; i < width*height; i++) - { - imageRGBA[i*4+0] = imageData[i*4+2]; - imageRGBA[i*4+1] = imageData[i*4+1]; - imageRGBA[i*4+2] = imageData[i*4+0]; - imageRGBA[i*4+3] = imageData[i*4+3]; - } - - - - //now for the openGL texture stuff - glGenTextures(1, &mTextureID); - glBindTexture(GL_TEXTURE_2D, mTextureID); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageRGBA); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - mTextureSize.x = width; - mTextureSize.y = height; - - //free the image data - FreeImage_Unload(image); - - //free the memory from that pointer - delete[] imageRGBA; - - resize(); } void ImageComponent::resize() { - mSize.x = mTextureSize.x; - mSize.y = mTextureSize.y; + if(!mTexture) + return; + + mSize.x = getTextureSize().x; + mSize.y = getTextureSize().y; //(we don't resize tiled images) if(!mTiled && (mTargetSize.x || mTargetSize.y)) @@ -176,19 +76,7 @@ void ImageComponent::resize() } if(mTiled) - { mSize = mTargetSize; - } -} - -void ImageComponent::unloadImage() -{ - if(mTextureID) - { - glDeleteTextures(1, &mTextureID); - - mTextureID = 0; - } } void ImageComponent::setImage(std::string path) @@ -198,10 +86,8 @@ void ImageComponent::setImage(std::string path) mPath = path; - unloadImage(); - if(!path.empty()) - loadImage(path); - + mTexture = mWindow->getResourceManager()->getTexture(path); + resize(); } void ImageComponent::setOrigin(float originX, float originY) @@ -240,15 +126,15 @@ void ImageComponent::setFlipY(bool flip) void ImageComponent::onRender() { - if(mTextureID && getOpacity() > 0) + if(mTexture && getOpacity() > 0) { GLfloat points[12], texs[12]; GLubyte colors[6*4]; if(mTiled) { - float xCount = (float)mSize.x / mTextureSize.x; - float yCount = (float)mSize.y / mTextureSize.y; + float xCount = (float)mSize.x / getTextureSize().x; + float yCount = (float)mSize.y / getTextureSize().y; Renderer::buildGLColorArray(colors, 0xFFFFFF00 | (getOpacity()), 6); buildImageArray(0, 0, points, texs, xCount, yCount); @@ -303,7 +189,8 @@ void ImageComponent::buildImageArray(int posX, int posY, GLfloat* points, GLfloa void ImageComponent::drawImageArray(GLfloat* points, GLfloat* texs, GLubyte* colors, unsigned int numArrays) { - glBindTexture(GL_TEXTURE_2D, mTextureID); + mTexture->bind(); + glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -332,21 +219,6 @@ void ImageComponent::drawImageArray(GLfloat* points, GLfloat* texs, GLubyte* col glDisable(GL_BLEND); } -void ImageComponent::init() -{ - if(!mPath.empty()) - loadImage(mPath); - - GuiComponent::init(); -} - -void ImageComponent::deinit() -{ - unloadImage(); - - GuiComponent::deinit(); -} - bool ImageComponent::hasImage() { return !mPath.empty(); @@ -357,32 +229,10 @@ void ImageComponent::setOpacity(unsigned char opacity) { mOpacity = opacity; } void ImageComponent::copyScreen() { - unloadImage(); - - int width = Renderer::getScreenWidth(); - int height = Renderer::getScreenHeight(); + mTexture.reset(); - //glReadBuffer(GL_FRONT); - - /*char* data = new char[width*height*3]; - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, data);*/ - - glGenTextures(1, &mTextureID); - glBindTexture(GL_TEXTURE_2D, mTextureID); - - //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); - //delete[] data; - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, width, height, 0); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - - mTextureSize.x = height; - mTextureSize.y = height; + mTexture = std::make_shared(); + mTexture->initFromScreen(); resize(); } diff --git a/src/components/ImageComponent.h b/src/components/ImageComponent.h index 3cf0204b1..12318c413 100644 --- a/src/components/ImageComponent.h +++ b/src/components/ImageComponent.h @@ -6,8 +6,8 @@ #include "../GuiComponent.h" #include -#include - +#include +#include "../resources/TextureResource.h" class ImageComponent : public GuiComponent { @@ -18,8 +18,7 @@ public: ImageComponent(Window* window, int offsetX = 0, int offsetY = 0, std::string path = "", unsigned int maxWidth = 0, unsigned int maxHeight = 0, bool allowUpscale = false); virtual ~ImageComponent(); - //Copy the entire screen into a texture for us to use. - void copyScreen(); + void copyScreen(); //Copy the entire screen into a texture for us to use. void setImage(std::string path); //Loads the image at the given filepath. void setOrigin(float originX, float originY); //Sets the origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center) void setTiling(bool tile); //Enables or disables tiling. Must be called before loading an image or resizing will be weird. @@ -31,13 +30,8 @@ public: //You can get the rendered size of the ImageComponent with getSize(). Vector2u getTextureSize(); - bool hasImage(); - //Image textures will be deleted on renderer deinitialization, and recreated on reinitialization (if mPath is not empty). - void init(); - void deinit(); - unsigned char getOpacity(); void setOpacity(unsigned char opacity); @@ -46,22 +40,19 @@ protected: private: Vector2u mTargetSize; - Vector2u mTextureSize; Vector2f mOrigin; bool mAllowUpscale, mTiled, mFlipX, mFlipY; unsigned char mOpacity; - void loadImage(std::string path); void resize(); void buildImageArray(int x, int y, GLfloat* points, GLfloat* texs, float percentageX = 1, float percentageY = 1); //writes 12 GLfloat points and 12 GLfloat texture coordinates to a given array at a given position void drawImageArray(GLfloat* points, GLfloat* texs, GLubyte* colors, unsigned int count = 6); //draws the given set of points and texture coordinates, number of coordinate pairs may be specified (default 6) - void unloadImage(); std::string mPath; - GLuint mTextureID; + std::shared_ptr mTexture; }; #endif diff --git a/src/resources/Resource.cpp b/src/resources/Resource.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/resources/Resource.h b/src/resources/Resource.h new file mode 100644 index 000000000..178453a0c --- /dev/null +++ b/src/resources/Resource.h @@ -0,0 +1,14 @@ +#pragma once + +struct ResourceData +{ + const unsigned char* ptr; + const size_t length; +}; + +class Resource +{ +public: + virtual void init(ResourceData data) = 0; + virtual void deinit() = 0; +}; diff --git a/src/resources/ResourceManager.cpp b/src/resources/ResourceManager.cpp new file mode 100644 index 000000000..eed1666bc --- /dev/null +++ b/src/resources/ResourceManager.cpp @@ -0,0 +1,95 @@ +#include "ResourceManager.h" +#include "../Log.h" +#include + +//from ES_logo_16.cpp +extern const size_t es_logo_16_data_len; +extern const unsigned char es_logo_16_data[]; + +//from ES_logo_32.cpp +extern const size_t es_logo_32_data_len; +extern const unsigned char es_logo_32_data[]; + +struct EmbeddedResource +{ + const char* internal_path; + ResourceData resourceData; +}; + +static const int embedded_resource_count = 2; +static const EmbeddedResource embedded_resources[embedded_resource_count] = { + { "internal://es_logo_16.png", {es_logo_16_data, es_logo_16_data_len} }, + { "internal://es_logo_32.png", {es_logo_32_data, es_logo_32_data_len} } +}; + +std::shared_ptr ResourceManager::getTexture(const std::string& path) +{ + if(path.empty()) + return std::shared_ptr(); //NULL pointer + + if(mTextureMap[path].expired()) + { + std::shared_ptr ret(new TextureResource()); + mTextureMap[path] = std::weak_ptr(ret); + + initializeResource(path, ret); + + return ret; + } + + return mTextureMap[path].lock(); +} + +void ResourceManager::init() +{ + for(auto iter = mTextureMap.begin(); iter != mTextureMap.end(); iter++) + { + if(!iter->second.expired()) + { + initializeResource(iter->first, iter->second.lock()); + } + } +} + +void ResourceManager::deinit() +{ + for(auto iter = mTextureMap.begin(); iter != mTextureMap.end(); iter++) + { + if(!iter->second.expired()) + iter->second.lock()->deinit(); + } +} + +void ResourceManager::initializeResource(const std::string& path, std::shared_ptr resource) +{ + for(int i = 0; i < embedded_resource_count; i++) + { + if(strcmp(embedded_resources[i].internal_path, path.c_str()) == 0) + { + //this embedded resource matches the filepath; use it + resource->init(embedded_resources[i].resourceData); + return; + } + } + + //it's not embedded; load the file, initialize with it, then free the file + ResourceData data = loadFile(path); + resource->init(data); + delete[] data.ptr; +} + +ResourceData ResourceManager::loadFile(const std::string& path) +{ + std::ifstream stream(path, std::ios::binary); + + stream.seekg(0, stream.end); + size_t size = (size_t)stream.tellg(); + stream.seekg(0, stream.beg); + + unsigned char* data = new unsigned char[size]; + stream.read((char*)data, size); + stream.close(); + + ResourceData ret = {data, size}; + return ret; +} diff --git a/src/resources/ResourceManager.h b/src/resources/ResourceManager.h new file mode 100644 index 000000000..435aa221b --- /dev/null +++ b/src/resources/ResourceManager.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include "Resource.h" +#include "TextureResource.h" + + +//The ResourceManager exists to: +//1. Automatically deal with initializing/deinitializing various resources (OpenGL textures, SDL audio) +//2. Allow loading resources embedded into the executable like an actual file. +// a. Allow embedded resources to be optionally remapped to actual files for further customization. +//3. Keep from creating duplicate resources. + +//The ResourceManager returns resources as std::shared_ptrs. +//When the resource no longer has any references, it will be automatically freed. +//If ES launches a game, all resources will have deinit() called on them and SDL will deinitialize. +//Once the game exits and ES returns, resources will have init() called on them. + +class ResourceManager +{ +public: + std::shared_ptr getTexture(const std::string& path); + + void init(); + void deinit(); + +private: + void initializeResource(const std::string& path, std::shared_ptr resource); + ResourceData loadFile(const std::string& path); + + std::map< std::string, std::weak_ptr > mTextureMap; +}; diff --git a/src/resources/TextureResource.cpp b/src/resources/TextureResource.cpp new file mode 100644 index 000000000..13bf7c593 --- /dev/null +++ b/src/resources/TextureResource.cpp @@ -0,0 +1,86 @@ +#include "TextureResource.h" +#include "../Log.h" +#include "../platform.h" +#include GLHEADER +#include "../ImageIO.h" +#include "../Renderer.h" + +TextureResource::TextureResource() : mTextureID(0) +{ +} + +TextureResource::~TextureResource() +{ + deinit(); +} + +void TextureResource::init(ResourceData data) +{ + //make sure we aren't going to leak an old texture + deinit(); + + size_t width, height; + std::vector imageRGBA = ImageIO::loadFromMemoryRGBA32(data.ptr, data.length, width, height); + + if(imageRGBA.size() == 0) + { + LOG(LogError) << "Could not initialize texture!"; + return; + } + + //now for the openGL texture stuff + glGenTextures(1, &mTextureID); + glBindTexture(GL_TEXTURE_2D, mTextureID); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageRGBA.data()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + mTextureSize.x = width; + mTextureSize.y = height; +} + +void TextureResource::deinit() +{ + if(mTextureID != 0) + { + glDeleteTextures(1, &mTextureID); + mTextureID = 0; + } +} + +Vector2u TextureResource::getSize() +{ + return mTextureSize; +} + +void TextureResource::bind() +{ + glBindTexture(GL_TEXTURE_2D, mTextureID); +} + +void TextureResource::initFromScreen() +{ + deinit(); + + int width = Renderer::getScreenWidth(); + int height = Renderer::getScreenHeight(); + + glGenTextures(1, &mTextureID); + glBindTexture(GL_TEXTURE_2D, mTextureID); + + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, width, height, 0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + mTextureSize.x = height; + mTextureSize.y = height; +} diff --git a/src/resources/TextureResource.h b/src/resources/TextureResource.h new file mode 100644 index 000000000..5137fb80c --- /dev/null +++ b/src/resources/TextureResource.h @@ -0,0 +1,27 @@ +#pragma once + +#include "Resource.h" + +#include +#include "../Vector2.h" +#include "../platform.h" +#include GLHEADER + +class TextureResource : public Resource +{ +public: + TextureResource(); + ~TextureResource(); + + void init(ResourceData data) override; + void deinit() override; + + Vector2u getSize(); + void bind(); + + void initFromScreen(); + +private: + Vector2u mTextureSize; + GLuint mTextureID; +};