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; +};