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.
This commit is contained in:
Aloshi 2013-06-21 11:49:29 -05:00
parent ae50cc82ad
commit 90af5d47ab
11 changed files with 300 additions and 187 deletions

View file

@ -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
)

View file

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

View file

@ -3,6 +3,7 @@
#include "GuiComponent.h"
#include "InputManager.h"
#include "resources/ResourceManager.h"
#include <vector>
class Window
@ -23,9 +24,11 @@ public:
void deinit();
InputManager* getInputManager();
ResourceManager* getResourceManager();
private:
InputManager* mInputManager;
ResourceManager mResourceManager;
std::vector<GuiComponent*> mGuiStack;
};

View file

@ -4,13 +4,18 @@
#include <math.h>
#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,20 +76,8 @@ 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();
mTexture.reset();
int width = Renderer::getScreenWidth();
int height = Renderer::getScreenHeight();
//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<TextureResource>();
mTexture->initFromScreen();
resize();
}

View file

@ -6,8 +6,8 @@
#include "../GuiComponent.h"
#include <string>
#include <FreeImage.h>
#include <memory>
#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<TextureResource> mTexture;
};
#endif

View file

14
src/resources/Resource.h Normal file
View file

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

View file

@ -0,0 +1,95 @@
#include "ResourceManager.h"
#include "../Log.h"
#include <fstream>
//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<TextureResource> ResourceManager::getTexture(const std::string& path)
{
if(path.empty())
return std::shared_ptr<TextureResource>(); //NULL pointer
if(mTextureMap[path].expired())
{
std::shared_ptr<TextureResource> ret(new TextureResource());
mTextureMap[path] = std::weak_ptr<TextureResource>(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> 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;
}

View file

@ -0,0 +1,34 @@
#pragma once
#include <stddef.h>
#include <memory>
#include <map>
#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<TextureResource> getTexture(const std::string& path);
void init();
void deinit();
private:
void initializeResource(const std::string& path, std::shared_ptr<Resource> resource);
ResourceData loadFile(const std::string& path);
std::map< std::string, std::weak_ptr<TextureResource> > mTextureMap;
};

View file

@ -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<unsigned char> 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;
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "Resource.h"
#include <string>
#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;
};