mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2025-07-03 14:34:34 +00:00
Rework texture loading, part 1
Major code/logic cleanups + it has a multithreaded batch mode now. The VFS code doesn't like multiple threads at all, so for now there's a big lock in front of where it matters. Changed precacher, map/tileset, and worldmap loading to batched mode. Still TODO: - fix broken mod preview images - reloading resources on debug key - make mod recache entirely unnecessary - actually drop resources when no longer needed
This commit is contained in:
parent
1100e286a6
commit
70b8dcdc3a
22 changed files with 758 additions and 817 deletions
|
@ -232,10 +232,14 @@ bool exists(const std::string &f, bool makeFatal, bool skipVFS)
|
|||
#ifdef BBGE_BUILD_VFS
|
||||
if (!skipVFS)
|
||||
{
|
||||
e = !!vfs.GetFile(f.c_str());
|
||||
e = !!vfgetfile(f.c_str());
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
std::string tmp = adjustFilenameCase(f);
|
||||
e = ttvfs::FileExists(tmp.c_str());
|
||||
}
|
||||
#else
|
||||
if (!e)
|
||||
{
|
||||
std::string tmp = adjustFilenameCase(f);
|
||||
|
@ -246,6 +250,7 @@ bool exists(const std::string &f, bool makeFatal, bool skipVFS)
|
|||
fclose(file);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (makeFatal && !e)
|
||||
{
|
||||
|
|
|
@ -105,6 +105,8 @@ set(BBGE_SRCS
|
|||
StringBank.h
|
||||
Texture.cpp
|
||||
Texture.h
|
||||
TextureMgr.cpp
|
||||
TextureMgr.h
|
||||
TTFFont.cpp
|
||||
TTFFont.h
|
||||
Vector.cpp
|
||||
|
|
178
BBGE/Core.cpp
178
BBGE/Core.cpp
|
@ -350,8 +350,6 @@ Core::Core(const std::string &filesystem, const std::string& extraDataDir, int n
|
|||
debugLogActive = true;
|
||||
debugOutputActive = false;
|
||||
|
||||
debugLogTextures = true;
|
||||
|
||||
grabInput = false;
|
||||
|
||||
srand(time(NULL));
|
||||
|
@ -398,6 +396,8 @@ Core::Core(const std::string &filesystem, const std::string& extraDataDir, int n
|
|||
initRenderObjectLayers(numRenderLayers);
|
||||
|
||||
initPlatform(filesystem);
|
||||
|
||||
texmgr.spawnThreads(3);
|
||||
}
|
||||
|
||||
void Core::initPlatform(const std::string &filesystem)
|
||||
|
@ -1824,20 +1824,6 @@ void Core::showBuffer()
|
|||
window->present();
|
||||
}
|
||||
|
||||
// WARNING: only for use during shutdown
|
||||
// otherwise, textures will try to remove themselves
|
||||
// when destroy is called on them
|
||||
void Core::clearResources()
|
||||
{
|
||||
if(resources.size())
|
||||
{
|
||||
debugLog("Warning: The following resources were not cleared:");
|
||||
for(size_t i = 0; i < resources.size(); ++i)
|
||||
debugLog(resources[i]->name);
|
||||
resources.clear(); // nothing we can do; refcounting is messed up
|
||||
}
|
||||
}
|
||||
|
||||
void Core::shutdownInputLibrary()
|
||||
{
|
||||
}
|
||||
|
@ -1897,7 +1883,7 @@ void Core::shutdown()
|
|||
debugLog("OK");
|
||||
|
||||
debugLog("Clear All Resources...");
|
||||
clearResources();
|
||||
texmgr.shutdown();
|
||||
debugLog("OK");
|
||||
|
||||
|
||||
|
@ -1956,118 +1942,9 @@ bool Core::exists(const std::string &filename)
|
|||
return ::exists(filename, false); // defined in Base.cpp
|
||||
}
|
||||
|
||||
CountedPtr<Texture> Core::findTexture(const std::string &name)
|
||||
CountedPtr<Texture> Core::getTexture(const std::string &name)
|
||||
{
|
||||
int sz = resources.size();
|
||||
for (int i = 0; i < sz; i++)
|
||||
{
|
||||
//out << resources[i]->name << " is " << name << " ?" << std::endl;
|
||||
//NOTE: ensure all names are lowercase before this point
|
||||
if (resources[i]->name == name)
|
||||
{
|
||||
return resources[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// This handles unix/win32 relative paths: ./rel/path
|
||||
// Unix abs paths: /home/user/...
|
||||
// Win32 abs paths: C:/Stuff/.. and also C:\Stuff\...
|
||||
#define ISPATHROOT(x) (x[0] == '.' || x[0] == '/' || ((x).length() > 1 && x[1] == ':'))
|
||||
|
||||
std::string Core::getTextureLoadName(const std::string &texture)
|
||||
{
|
||||
std::string loadName = texture;
|
||||
|
||||
if (texture.empty() || !ISPATHROOT(texture))
|
||||
{
|
||||
if (texture.find(baseTextureDirectory) == std::string::npos)
|
||||
loadName = baseTextureDirectory + texture;
|
||||
}
|
||||
return loadName;
|
||||
}
|
||||
|
||||
CountedPtr<Texture> Core::doTextureAdd(const std::string &texture, const std::string &loadName, std::string internalTextureName)
|
||||
{
|
||||
if (texture.empty() || !ISPATHROOT(texture))
|
||||
{
|
||||
if (texture.find(baseTextureDirectory) != std::string::npos)
|
||||
internalTextureName = internalTextureName.substr(baseTextureDirectory.size(), internalTextureName.size());
|
||||
}
|
||||
|
||||
if (internalTextureName.size() > 4)
|
||||
{
|
||||
if (internalTextureName[internalTextureName.size()-4] == '.')
|
||||
{
|
||||
internalTextureName = internalTextureName.substr(0, internalTextureName.size()-4);
|
||||
}
|
||||
}
|
||||
|
||||
stringToLowerUserData(internalTextureName);
|
||||
CountedPtr<Texture> t = core->findTexture(internalTextureName);
|
||||
if (t)
|
||||
return t;
|
||||
|
||||
t = new Texture;
|
||||
t->name = internalTextureName;
|
||||
|
||||
if(t->load(loadName, true))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "LOADED TEXTURE FROM DISK: [" << internalTextureName << "] idx: " << resources.size()-1;
|
||||
debugLog(os.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
t->width = 64;
|
||||
t->height = 64;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
CountedPtr<Texture> Core::addTexture(const std::string &textureName)
|
||||
{
|
||||
if (textureName.empty())
|
||||
return NULL;
|
||||
|
||||
CountedPtr<Texture> ptex;
|
||||
std::string texture = textureName;
|
||||
stringToLowerUserData(texture);
|
||||
std::string internalTextureName = texture;
|
||||
std::string loadName = getTextureLoadName(texture);
|
||||
|
||||
if (!texture.empty() && texture[0] == '@')
|
||||
{
|
||||
texture = secondaryTexturePath + texture.substr(1, texture.size());
|
||||
loadName = texture;
|
||||
}
|
||||
else if (!secondaryTexturePath.empty() && texture[0] != '.' && texture[0] != '/')
|
||||
{
|
||||
std::string t = texture;
|
||||
std::string ln = loadName;
|
||||
texture = secondaryTexturePath + texture;
|
||||
loadName = texture;
|
||||
ptex = doTextureAdd(texture, loadName, internalTextureName);
|
||||
if (!ptex || ptex->getLoadResult() == TEX_FAILED)
|
||||
ptex = doTextureAdd(t, ln, internalTextureName);
|
||||
}
|
||||
else
|
||||
ptex = doTextureAdd(texture, loadName, internalTextureName);
|
||||
|
||||
addTexture(ptex.content());
|
||||
|
||||
if(debugLogTextures)
|
||||
{
|
||||
if(!ptex || ptex->getLoadResult() != TEX_SUCCESS)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "FAILED TO LOAD TEXTURE: [" << internalTextureName << "] idx: " << resources.size()-1;
|
||||
debugLog(os.str());
|
||||
}
|
||||
}
|
||||
return ptex;
|
||||
return texmgr.getOrLoad(name);
|
||||
}
|
||||
|
||||
void Core::addRenderObject(RenderObject *o, unsigned layer)
|
||||
|
@ -2089,10 +1966,10 @@ void Core::switchRenderObjectLayer(RenderObject *o, unsigned toLayer)
|
|||
|
||||
void Core::unloadResources()
|
||||
{
|
||||
for (size_t i = 0; i < resources.size(); i++)
|
||||
/*for (size_t i = 0; i < resources.size(); i++)
|
||||
{
|
||||
resources[i]->unload();
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
void Core::onReloadResources()
|
||||
|
@ -2101,43 +1978,30 @@ void Core::onReloadResources()
|
|||
|
||||
void Core::reloadResources()
|
||||
{
|
||||
for (size_t i = 0; i < resources.size(); i++)
|
||||
/*for (size_t i = 0; i < resources.size(); i++)
|
||||
{
|
||||
resources[i]->reload();
|
||||
}
|
||||
onReloadResources();
|
||||
onReloadResources();*/
|
||||
}
|
||||
|
||||
void Core::addTexture(Texture *r)
|
||||
const std::string & Core::getBaseTexturePath() const
|
||||
{
|
||||
for(size_t i = 0; i < resources.size(); ++i)
|
||||
if(resources[i] == r)
|
||||
return;
|
||||
|
||||
resources.push_back(r);
|
||||
if (r->name.empty())
|
||||
{
|
||||
debugLog("Empty name resource added");
|
||||
}
|
||||
return texmgr.loadFromPaths.back();
|
||||
}
|
||||
|
||||
void Core::removeTexture(Texture *res)
|
||||
void Core::setExtraTexturePath(const char * dir)
|
||||
{
|
||||
std::vector<Texture*> copy;
|
||||
copy.swap(resources);
|
||||
texmgr.loadFromPaths.resize(size_t(1) + !!dir);
|
||||
size_t w = 0;
|
||||
if(dir)
|
||||
texmgr.loadFromPaths[w++] = dir;
|
||||
texmgr.loadFromPaths[w] = "gfx/";
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < copy.size(); ++i)
|
||||
{
|
||||
if (copy[i] == res)
|
||||
{
|
||||
copy[i]->destroy();
|
||||
copy[i] = copy.back();
|
||||
copy.pop_back();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
resources.swap(copy);
|
||||
const char *Core::getExtraTexturePath() const
|
||||
{
|
||||
return texmgr.loadFromPaths.size() > 1 ? texmgr.loadFromPaths[0].c_str() : NULL;
|
||||
}
|
||||
|
||||
void Core::removeRenderObject(RenderObject *r, RemoveRenderObjectFlag flag)
|
||||
|
|
33
BBGE/Core.h
33
BBGE/Core.h
|
@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
#include "StateManager.h"
|
||||
#include "Localization.h"
|
||||
#include "Window.h"
|
||||
#include "TextureMgr.h"
|
||||
|
||||
#include "DarkLayer.h"
|
||||
|
||||
|
@ -231,8 +232,6 @@ public:
|
|||
|
||||
// state functions
|
||||
|
||||
std::string getTextureLoadName(const std::string &texture);
|
||||
|
||||
void setMousePosition(const Vector &p);
|
||||
|
||||
void setFullscreen(bool full);
|
||||
|
@ -240,12 +239,7 @@ public:
|
|||
void enable2D(int pixelScaleX, int pixelScaleY);
|
||||
void addRenderObject(RenderObject *o, unsigned layer);
|
||||
void switchRenderObjectLayer(RenderObject *o, unsigned toLayer);
|
||||
void addTexture(Texture *r);
|
||||
CountedPtr<Texture> findTexture(const std::string &name);
|
||||
void removeTexture(Texture *res);
|
||||
void clearResources();
|
||||
|
||||
CountedPtr<Texture> addTexture(const std::string &texture);
|
||||
CountedPtr<Texture> getTexture(const std::string &name);
|
||||
|
||||
enum RemoveRenderObjectFlag { DESTROY_RENDER_OBJECT=0, DO_NOT_DESTROY_RENDER_OBJECT };
|
||||
void removeRenderObject(RenderObject *r, RemoveRenderObjectFlag flag = DESTROY_RENDER_OBJECT);
|
||||
|
@ -279,8 +273,6 @@ public:
|
|||
|
||||
void print(int x, int y, const char *str, float sz=1);
|
||||
|
||||
std::vector<Texture*> resources;
|
||||
|
||||
RenderObjectLayer *getRenderObjectLayer(int i);
|
||||
std::vector <int> renderObjectLayerOrder;
|
||||
|
||||
|
@ -311,15 +303,9 @@ public:
|
|||
|
||||
ParticleManager *particleManager;
|
||||
|
||||
|
||||
|
||||
void setBaseTextureDirectory(const std::string &newBaseTextureDirectory)
|
||||
{ this->baseTextureDirectory = newBaseTextureDirectory; }
|
||||
std::string getBaseTextureDirectory()
|
||||
{
|
||||
return baseTextureDirectory;
|
||||
}
|
||||
|
||||
void setExtraTexturePath(const char *dir); // pass NULL to disable secondary
|
||||
const char *getExtraTexturePath() const; // NULL when no secondary
|
||||
const std::string& getBaseTexturePath() const;
|
||||
|
||||
virtual bool canChangeState();
|
||||
void resetTimer();
|
||||
|
@ -376,8 +362,6 @@ public:
|
|||
|
||||
bool joystickEnabled;
|
||||
|
||||
bool debugLogTextures;
|
||||
|
||||
void setup_opengl();
|
||||
void setClearColor(const Vector &c);
|
||||
int flipMouseButtons;
|
||||
|
@ -388,8 +372,6 @@ public:
|
|||
|
||||
ParticleEffect* createParticleEffect(const std::string &name, const Vector &position, int layer, float rotz=0);
|
||||
|
||||
std::string secondaryTexturePath;
|
||||
|
||||
float get_old_dt() { return old_dt; }
|
||||
float get_current_dt() { return current_dt; }
|
||||
|
||||
|
@ -431,6 +413,8 @@ public:
|
|||
|
||||
void initLocalization();
|
||||
|
||||
TextureMgr texmgr;
|
||||
|
||||
protected:
|
||||
|
||||
CoreWindow *window;
|
||||
|
@ -450,8 +434,6 @@ protected:
|
|||
|
||||
virtual void onReloadResources();
|
||||
|
||||
CountedPtr<Texture> doTextureAdd(const std::string &texture, const std::string &name, std::string internalTextureName);
|
||||
|
||||
bool lib_graphics, lib_sound, lib_input;
|
||||
Vector clearColor;
|
||||
virtual void unloadDevice();
|
||||
|
@ -495,7 +477,6 @@ protected:
|
|||
bool shuttingDown;
|
||||
bool quitNestedMainFlag;
|
||||
int nestedMains;
|
||||
std::string baseTextureDirectory;
|
||||
|
||||
int nowTicks, thenTicks;
|
||||
|
||||
|
|
|
@ -25,129 +25,104 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
Precacher::Precacher()
|
||||
{
|
||||
loadProgressCallback = NULL;
|
||||
cleaned = true;
|
||||
}
|
||||
|
||||
Precacher::~Precacher()
|
||||
{
|
||||
if (!cleaned)
|
||||
errorLog ("Precacher shutdown unclean");
|
||||
}
|
||||
|
||||
void Precacher::setBaseDir(const std::string& dir)
|
||||
{
|
||||
basedirOverride = dir;
|
||||
basedir = dir;
|
||||
}
|
||||
|
||||
void Precacher::clean()
|
||||
void Precacher::clear()
|
||||
{
|
||||
for (unsigned int i = 0; i < renderObjects.size(); i++)
|
||||
{
|
||||
RenderObject *r = renderObjects[i];
|
||||
r->destroy();
|
||||
delete r;
|
||||
}
|
||||
renderObjects.clear();
|
||||
cleaned = true;
|
||||
texkeep.clear();
|
||||
}
|
||||
|
||||
void Precacher::loadTextureRange(const std::string &file, const std::string &type, int start, int end)
|
||||
{
|
||||
for (int t = start; t < end; t++)
|
||||
{
|
||||
|
||||
std::ostringstream num_os;
|
||||
num_os << t;
|
||||
|
||||
std::ostringstream os;
|
||||
os << file;
|
||||
|
||||
if(num_os.str().size() <= 4) {
|
||||
for (size_t j = 0; j < 4 - num_os.str().size(); j++) {
|
||||
os << "0";
|
||||
}
|
||||
}
|
||||
|
||||
os << t;
|
||||
|
||||
os << type;
|
||||
|
||||
precacheTex(os.str());
|
||||
}
|
||||
}
|
||||
|
||||
void precacherCallback(const std::string &file, void *param)
|
||||
void Precacher::_Callback(const std::string &file, void *param)
|
||||
{
|
||||
Precacher *p = (Precacher*)param;
|
||||
p->precacheTex(file);
|
||||
p->_precacheTex(file);
|
||||
}
|
||||
|
||||
// precacheTex
|
||||
// caches one texture
|
||||
// also support simple wildcard to cache multiple textures
|
||||
// e.g. naija/*.png
|
||||
void Precacher::precacheTex(const std::string &tex)
|
||||
void Precacher::_precacheTex(const std::string &tex)
|
||||
{
|
||||
if (tex.find("txt") != std::string::npos)
|
||||
{
|
||||
errorLog("Call precacheList to precache a text file of gfx names, not precacheTex!");
|
||||
}
|
||||
assert(!basedir.empty());
|
||||
if (tex.empty()) return;
|
||||
|
||||
std::string basedir = basedirOverride.empty() ? core->getBaseTextureDirectory() : basedirOverride;
|
||||
|
||||
if (core->debugLogTextures)
|
||||
debugLog("PRECACHING: " + tex);
|
||||
|
||||
if (tex.find('*')!=std::string::npos)
|
||||
{
|
||||
if (core->debugLogTextures)
|
||||
debugLog("searching directory");
|
||||
|
||||
int loc = tex.find('*');
|
||||
size_t loc = tex.find('*');
|
||||
std::string path = tex.substr(0, loc);
|
||||
std::string type = tex.substr(loc+1, tex.size());
|
||||
path = basedir + path;
|
||||
forEachFile(path, type, precacherCallback, this);
|
||||
return;
|
||||
forEachFile(path, type, _Callback, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (loadProgressCallback)
|
||||
loadProgressCallback();
|
||||
std::string t = tex;
|
||||
if (tex.find(basedir) != std::string::npos)
|
||||
{
|
||||
t = tex.substr(basedir.size(), tex.size());
|
||||
}
|
||||
Quad *q = new Quad;
|
||||
q->setTexture(t);
|
||||
q->alpha = 0;
|
||||
renderObjects.push_back(q);
|
||||
cleaned = false;
|
||||
todo.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
void Precacher::precacheList(const std::string &list, void progressCallback())
|
||||
static void texLoadProgressCallback(size_t done, void *ud)
|
||||
{
|
||||
loadProgressCallback = progressCallback;
|
||||
Precacher::ProgressCallback cb = (Precacher::ProgressCallback)(ud);
|
||||
cb();
|
||||
}
|
||||
|
||||
void Precacher::doCache(ProgressCallback progress)
|
||||
{
|
||||
if(!todo.empty())
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Precacher: Batch-loading " << todo.size() << " textures...";
|
||||
debugLog(os.str());
|
||||
std::vector<Texture*> tmp(todo.size());
|
||||
core->texmgr.loadBatch(&tmp[0], &todo[0], todo.size(), TextureMgr::KEEP,
|
||||
progress ? texLoadProgressCallback : NULL, progress);
|
||||
todo.clear();
|
||||
texkeep.reserve(texkeep.size() + tmp.size());
|
||||
for(size_t i = 0; i < tmp.size(); ++i)
|
||||
texkeep.push_back(tmp[i]);
|
||||
}
|
||||
debugLog("Precacher: done");
|
||||
}
|
||||
|
||||
void Precacher::precacheList(const std::string &list, ProgressCallback progress)
|
||||
{
|
||||
assert(todo.empty());
|
||||
InStream in(list.c_str());
|
||||
std::string t;
|
||||
while (std::getline(in, t))
|
||||
{
|
||||
if (!t.empty())
|
||||
while (!t.empty())
|
||||
{
|
||||
#if defined(BBGE_BUILD_UNIX)
|
||||
|
||||
t = t.substr(0,t.size()-1);
|
||||
debugLog("precache["+t+"]");
|
||||
#endif
|
||||
stringToLower(t);
|
||||
precacheTex(t);
|
||||
if(t.back() == '\r' || t.back() == '\n') // linux doesn't like CRLF, make sure to trim that off
|
||||
t.pop_back();
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if(!t.empty())
|
||||
_precacheTex(t);
|
||||
}
|
||||
in.close();
|
||||
loadProgressCallback = NULL;
|
||||
doCache(progress);
|
||||
}
|
||||
|
||||
void Precacher::precacheTex(const std::string& tex, ProgressCallback progress)
|
||||
{
|
||||
_precacheTex(tex);
|
||||
doCache(progress);
|
||||
}
|
||||
|
|
|
@ -23,25 +23,28 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "Texture.h"
|
||||
|
||||
class RenderObject;
|
||||
|
||||
class Precacher
|
||||
{
|
||||
public:
|
||||
typedef void (*ProgressCallback)(void);
|
||||
|
||||
Precacher();
|
||||
~Precacher();
|
||||
void precacheTex(const std::string &tex);
|
||||
void precacheList(const std::string &list, void progressCallback() = NULL);
|
||||
void clean();
|
||||
void loadTextureRange(const std::string &file, const std::string &type, int start, int end);
|
||||
void precacheList(const std::string &list, ProgressCallback progress = NULL);
|
||||
void precacheTex(const std::string &tex, ProgressCallback progress = NULL);
|
||||
void clear();
|
||||
void setBaseDir(const std::string& dir);
|
||||
|
||||
std::vector<RenderObject*> renderObjects;
|
||||
private:
|
||||
bool cleaned;
|
||||
void (*loadProgressCallback)();
|
||||
std::string basedirOverride;
|
||||
static void _Callback(const std::string &file, void *param);
|
||||
void _precacheTex(const std::string &tex);
|
||||
std::string basedir;
|
||||
std::vector<CountedPtr<Texture> > texkeep;
|
||||
std::vector<std::string> todo;
|
||||
void doCache(ProgressCallback progress = NULL);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -349,8 +349,7 @@ void Quad::renderGrid(const RenderState& rs) const
|
|||
}
|
||||
}
|
||||
glEnd();
|
||||
if (texture)
|
||||
glBindTexture(GL_TEXTURE_2D, texture->textures[0]);
|
||||
RenderObject::lastTextureApplied = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -491,11 +491,11 @@ nofollow:
|
|||
{
|
||||
if (texture)
|
||||
{
|
||||
if (texture->textures[0] != lastTextureApplied || repeatTexture != lastTextureRepeat)
|
||||
if (texture->gltexid != lastTextureApplied || repeatTexture != lastTextureRepeat)
|
||||
{
|
||||
texture->apply(repeatTexture);
|
||||
lastTextureRepeat = repeatTexture;
|
||||
lastTextureApplied = texture->textures[0];
|
||||
lastTextureApplied = texture->gltexid;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -866,21 +866,9 @@ void RenderObject::freeMotionBlur()
|
|||
|
||||
bool RenderObject::setTexture(const std::string &n)
|
||||
{
|
||||
std::string name = n;
|
||||
stringToLowerUserData(name);
|
||||
|
||||
if (name.empty())
|
||||
{
|
||||
setTexturePointer(NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(texture && texture->getLoadResult() == TEX_SUCCESS && name == texture->name)
|
||||
return true; // no texture change
|
||||
|
||||
CountedPtr<Texture> tex = core->addTexture(name);
|
||||
CountedPtr<Texture> tex = core->getTexture(n);
|
||||
setTexturePointer(tex);
|
||||
return tex && tex->getLoadResult() == TEX_SUCCESS;
|
||||
return tex->success;
|
||||
}
|
||||
|
||||
void RenderObject::addChild(RenderObject *r, ParentManaged pm, RenderBeforeParent rbp, ChildOrder order)
|
||||
|
|
256
BBGE/Texture.cpp
256
BBGE/Texture.cpp
|
@ -18,8 +18,10 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include "Base.h"
|
||||
#include "Texture.h"
|
||||
#include "Core.h"
|
||||
#include "Image.h"
|
||||
#include "ByteBuffer.h"
|
||||
#include "RenderBase.h"
|
||||
|
@ -31,41 +33,31 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|||
|
||||
Texture::Texture()
|
||||
{
|
||||
textures[0] = 0;
|
||||
gltexid = 0;
|
||||
width = height = 0;
|
||||
|
||||
_repeating = false;
|
||||
ow = oh = -1;
|
||||
loadResult = TEX_FAILED;
|
||||
_mipmap = false;
|
||||
success = false;
|
||||
}
|
||||
|
||||
Texture::~Texture()
|
||||
{
|
||||
destroy();
|
||||
unload();
|
||||
}
|
||||
|
||||
void Texture::read(int tx, int ty, int w, int h, unsigned char *pixels)
|
||||
void Texture::readRGBA(unsigned char *pixels)
|
||||
{
|
||||
if (tx == 0 && ty == 0 && w == this->width && h == this->height)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Unable to read a texture subimage (size = "
|
||||
<< this->width << "x" << this->height << ", requested = "
|
||||
<< tx << "," << ty << "+" << w << "x" << h << ")";
|
||||
debugLog(os.str());
|
||||
}
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, gltexid);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Texture::write(int tx, int ty, int w, int h, const unsigned char *pixels)
|
||||
void Texture::writeRGBA(int tx, int ty, int w, int h, const unsigned char *pixels)
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
||||
glBindTexture(GL_TEXTURE_2D, gltexid);
|
||||
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
||||
tx,
|
||||
|
@ -78,158 +70,24 @@ void Texture::write(int tx, int ty, int w, int h, const unsigned char *pixels)
|
|||
);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
/*
|
||||
target Specifies the target texture. Must be
|
||||
GL_TEXTURE_2D.
|
||||
|
||||
level Specifies the level-of-detail number. Level 0 is
|
||||
the base image level. Level n is the nth mipmap
|
||||
reduction image.
|
||||
|
||||
xoffset Specifies a texel offset in the x direction within
|
||||
the texture array.
|
||||
|
||||
yoffset Specifies a texel offset in the y direction within
|
||||
the texture array.
|
||||
|
||||
width Specifies the width of the texture subimage.
|
||||
|
||||
height Specifies the height of the texture subimage.
|
||||
|
||||
format Specifies the format of the pixel data. The
|
||||
following symbolic values are accepted:
|
||||
GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE,
|
||||
GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, and
|
||||
GL_LUMINANCE_ALPHA.
|
||||
|
||||
type Specifies the data type of the pixel data. The
|
||||
following symbolic values are accepted:
|
||||
GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP,
|
||||
GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT,
|
||||
GL_INT, and GL_FLOAT.
|
||||
|
||||
pixels Specifies a pointer to the image data in memory.
|
||||
*/
|
||||
}
|
||||
|
||||
void Texture::unload()
|
||||
{
|
||||
if (textures[0])
|
||||
if (gltexid)
|
||||
{
|
||||
ow = width;
|
||||
oh = height;
|
||||
|
||||
if (core->debugLogTextures)
|
||||
{
|
||||
debugLog("UNLOADING TEXTURE: " + name);
|
||||
}
|
||||
|
||||
|
||||
glDeleteTextures(1, &textures[0]);
|
||||
textures[0] = 0;
|
||||
glDeleteTextures(1, &gltexid);
|
||||
gltexid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Texture::destroy()
|
||||
{
|
||||
unload();
|
||||
|
||||
core->removeTexture(this);
|
||||
}
|
||||
|
||||
void Texture::reload()
|
||||
{
|
||||
debugLog("RELOADING TEXTURE: " + name + " with loadName " + loadName + "...");
|
||||
|
||||
unload();
|
||||
load(loadName, _mipmap);
|
||||
|
||||
|
||||
debugLog("DONE");
|
||||
}
|
||||
|
||||
bool Texture::load(std::string file, bool mipmap)
|
||||
{
|
||||
loadResult = TEX_FAILED;
|
||||
if (file.size()<4)
|
||||
{
|
||||
errorLog("Texture Name is Empty or Too Short");
|
||||
return false;
|
||||
}
|
||||
|
||||
stringToLowerUserData(file);
|
||||
file = adjustFilenameCase(file);
|
||||
|
||||
loadName = file;
|
||||
_mipmap = mipmap;
|
||||
|
||||
size_t pos = file.find_last_of('.');
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
// make sure this didn't catch the '.' in /home/username/.Aquaria/* --ryan.
|
||||
const std::string userdata = core->getUserDataFolder();
|
||||
const size_t len = userdata.length();
|
||||
if (pos < len)
|
||||
pos = std::string::npos;
|
||||
}
|
||||
|
||||
bool found = exists(file);
|
||||
|
||||
if(!found && exists(file + ".png"))
|
||||
{
|
||||
found = true;
|
||||
file += ".png";
|
||||
}
|
||||
|
||||
// .tga/.zga are never used as game graphics anywhere except save slot thumbnails.
|
||||
// if so, their file names are passed exact, not with a missing extension
|
||||
|
||||
bool ok = false;
|
||||
if (found)
|
||||
{
|
||||
file = localisePathInternalModpath(file);
|
||||
file = adjustFilenameCase(file);
|
||||
|
||||
|
||||
std::string post = file.substr(file.size()-3, 3);
|
||||
stringToLower(post);
|
||||
|
||||
ImageData img = {};
|
||||
if (post == "zga")
|
||||
{
|
||||
img = imageLoadZGA(file.c_str());
|
||||
if(img.pixels)
|
||||
mipmap = false;
|
||||
else
|
||||
debugLog("Can't load ZGA File: " + file);
|
||||
}
|
||||
else
|
||||
{
|
||||
img = imageLoadGeneric(file.c_str(), false);
|
||||
if(!img.pixels)
|
||||
debugLog("unknown image file type: " + file);
|
||||
}
|
||||
|
||||
if(img.pixels)
|
||||
{
|
||||
ok = loadInternal(img, mipmap);
|
||||
free(img.pixels);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// load default image / leave white
|
||||
if (core->debugLogTextures)
|
||||
debugLog("***Could not find texture: " + file);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static const GLenum repeatLUT[] = { GL_CLAMP_TO_EDGE, GL_REPEAT };
|
||||
void Texture::apply(bool repeat) const
|
||||
{
|
||||
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
||||
glBindTexture(GL_TEXTURE_2D, gltexid);
|
||||
if(repeat != _repeating)
|
||||
{
|
||||
_repeating = repeat;
|
||||
|
@ -252,7 +110,7 @@ static const GlTexFormat formatLUT[] =
|
|||
{ GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, 3 }
|
||||
};
|
||||
|
||||
bool Texture::loadInternal(const ImageData& img, bool mipmap)
|
||||
bool Texture::upload(const ImageData& img, bool mipmap)
|
||||
{
|
||||
if(!img.pixels || !img.channels || img.channels > 4 || !img.w || !img.h)
|
||||
return false;
|
||||
|
@ -266,8 +124,9 @@ bool Texture::loadInternal(const ImageData& img, bool mipmap)
|
|||
// no padding
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
glGenTextures(1, &textures[0]);
|
||||
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
||||
if(!gltexid)
|
||||
glGenTextures(1, &gltexid);
|
||||
glBindTexture(GL_TEXTURE_2D, gltexid);
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
_repeating = false;
|
||||
|
@ -345,79 +204,24 @@ bool Texture::loadInternal(const ImageData& img, bool mipmap)
|
|||
|
||||
width = img.w;
|
||||
height = img.h;
|
||||
loadResult = TEX_SUCCESS;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, unsigned int *sizeparam)
|
||||
unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, size_t *sizeparam)
|
||||
{
|
||||
unsigned char *data = NULL;
|
||||
unsigned int size = 0;
|
||||
int tw = 0, th = 0;
|
||||
int w = 0, h = 0;
|
||||
|
||||
// This can't happen. If it does we're doomed.
|
||||
if(width <= 0 || height <= 0)
|
||||
goto fail;
|
||||
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, textures[0]);
|
||||
|
||||
// As returned by graphics driver
|
||||
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &w);
|
||||
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &h);
|
||||
|
||||
// As we know it - but round to nearest power of 2 - OpenGL does this internally anyways.
|
||||
tw = bithacks::clp2(width); // known to be > 0.
|
||||
th = bithacks::clp2(height);
|
||||
|
||||
if (w != tw || h != th)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Texture::getBufferAndSize() WARNING: width/height disagree: ";
|
||||
os << "Driver says (" << w << ", " << h << "); ";
|
||||
os << "Texture says (" << width << ", " << height << "); ";
|
||||
os << "Rounded to (" << tw << ", " << th << ")";
|
||||
debugLog(os.str());
|
||||
// choose max. for size calculation
|
||||
w = w > tw ? w : tw;
|
||||
h = h > th ? h : th;
|
||||
}
|
||||
|
||||
size = w * h * 4;
|
||||
if (!size)
|
||||
goto fail;
|
||||
|
||||
data = (unsigned char*)malloc(size + 32);
|
||||
const size_t bytes = size_t(width) * size_t(height) * 4;
|
||||
unsigned char *data = (unsigned char*)malloc(bytes);
|
||||
if (!data)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Game::fillGridFromQuad allocation failure, size = " << size;
|
||||
os << "Game::getBufferAndSize allocation failure, bytes = " << bytes;
|
||||
errorLog(os.str());
|
||||
goto fail;
|
||||
return NULL;
|
||||
}
|
||||
memcpy(data + size, "SAFE", 5);
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
this->readRGBA(data);
|
||||
|
||||
// Not sure but this might be the case with nouveau drivers on linux... still investigating. -- fg
|
||||
if(memcmp(data + size, "SAFE", 5))
|
||||
{
|
||||
errorLog("Texture::getBufferAndSize(): Broken graphics driver! Wrote past end of buffer!");
|
||||
free(data); // in case we are here, this will most likely cause a crash.
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*wparam = w;
|
||||
*hparam = h;
|
||||
*sizeparam = size;
|
||||
*wparam = width;
|
||||
*hparam = height;
|
||||
*sizeparam = bytes;
|
||||
return data;
|
||||
|
||||
|
||||
fail:
|
||||
*wparam = 0;
|
||||
*hparam = 0;
|
||||
*sizeparam = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -39,39 +39,30 @@ public:
|
|||
Texture();
|
||||
~Texture();
|
||||
|
||||
bool load(std::string file, bool mipmap);
|
||||
void apply(bool repeat = false) const;
|
||||
void unload();
|
||||
|
||||
void destroy();
|
||||
|
||||
unsigned gltexid;
|
||||
int width, height;
|
||||
|
||||
unsigned textures[1];
|
||||
void writeRGBA(int tx, int ty, int w, int h, const unsigned char *pixels);
|
||||
void readRGBA(unsigned char *pixels);
|
||||
|
||||
void reload();
|
||||
unsigned char *getBufferAndSize(int *w, int *h, size_t *size); // returned memory must be free()'d
|
||||
|
||||
void write(int tx, int ty, int w, int h, const unsigned char *pixels);
|
||||
void read(int tx, int ty, int w, int h, unsigned char *pixels);
|
||||
std::string name, filename;
|
||||
bool upload(const ImageData& img, bool mipmap);
|
||||
|
||||
unsigned char *getBufferAndSize(int *w, int *h, unsigned int *size); // returned memory must be free()'d
|
||||
|
||||
std::string name;
|
||||
|
||||
TextureLoadResult getLoadResult() const { return loadResult; }
|
||||
bool success;
|
||||
|
||||
protected:
|
||||
std::string loadName;
|
||||
|
||||
bool loadInternal(const ImageData& img, bool mipmap);
|
||||
|
||||
int ow, oh;
|
||||
TextureLoadResult loadResult;
|
||||
bool _mipmap;
|
||||
private:
|
||||
mutable bool _repeating; // modified during rendering
|
||||
};
|
||||
|
||||
#define UNREFTEX(x) if (x) {x = NULL;}
|
||||
#define UNREFTEX(x) {x = NULL;}
|
||||
|
||||
#endif
|
||||
|
|
318
BBGE/TextureMgr.cpp
Normal file
318
BBGE/TextureMgr.cpp
Normal file
|
@ -0,0 +1,318 @@
|
|||
#include "TextureMgr.h"
|
||||
#include <sstream>
|
||||
#include <SDL.h>
|
||||
#include "MT.h"
|
||||
#include "Image.h"
|
||||
#include "Base.h"
|
||||
#include "Localization.h"
|
||||
|
||||
struct TexLoadTmp
|
||||
{
|
||||
TexLoadTmp() : curTex(NULL), success(false) { img.pixels = NULL; }
|
||||
std::string name, filename;
|
||||
ImageData img;
|
||||
TextureMgr::LoadMode loadmode;
|
||||
Texture *curTex; // immutable
|
||||
bool success; // if this is true and img.pixels is NULL, don't change anything
|
||||
//bool mipmap;
|
||||
};
|
||||
|
||||
typedef ImageData (*ImageLoadFunc)(const char *fn);
|
||||
|
||||
struct TexLoader
|
||||
{
|
||||
TexLoader(ImageLoadFunc f, const std::string& s, const std::string& name = std::string())
|
||||
: loader(f), fn(s), name(name) {}
|
||||
ImageLoadFunc loader;
|
||||
std::string fn;
|
||||
std::string name; // usually empty, but may specify tex name in case cleanups had to be done. always lowercase.
|
||||
ImageData load() const { return loader(fn.c_str()); }
|
||||
};
|
||||
|
||||
static ImageData loadGeneric(const char *fn)
|
||||
{
|
||||
return imageLoadGeneric(fn, false);
|
||||
}
|
||||
|
||||
static const std::string fixup(const std::string& fn)
|
||||
{
|
||||
std::string file = localisePathInternalModpath(fn);
|
||||
file = adjustFilenameCase(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
struct ExtAndLoader
|
||||
{
|
||||
const char *ext;
|
||||
ImageLoadFunc func;
|
||||
};
|
||||
|
||||
static const ExtAndLoader s_extAndLoader[] =
|
||||
{
|
||||
{ "png", loadGeneric },
|
||||
{ "jpg", loadGeneric },
|
||||
{ "zga", imageLoadZGA },
|
||||
{ "tga", loadGeneric },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static TexLoader getFullnameAndLoader(const std::string& name, const std::string& basedir)
|
||||
{
|
||||
// TODO: use localisePath()
|
||||
// TODO: assert that we don't start with . or /
|
||||
|
||||
if(exists(name))
|
||||
{
|
||||
// check first if name exists and has a known extension, if so, use correct loader
|
||||
size_t lastdot = name.rfind('.');
|
||||
size_t lastslash = name.rfind('/');
|
||||
if(lastdot != std::string::npos && (lastslash == std::string::npos || lastslash < lastdot))
|
||||
{
|
||||
std::string ext = name.substr(lastdot + 1);
|
||||
for(const ExtAndLoader *exl = &s_extAndLoader[0]; exl->func; ++exl)
|
||||
{
|
||||
if(ext == exl->ext)
|
||||
{
|
||||
// remove basedir and extension
|
||||
std::string texname = name.substr(basedir.length(), name.length() - (basedir.length() + ext.length() + 1)); // strip extension
|
||||
return TexLoader(exl->func, fixup(name), texname );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string fn = name;
|
||||
for(const ExtAndLoader *exl = &s_extAndLoader[0]; exl->func; ++exl)
|
||||
{
|
||||
fn.resize(name.length());
|
||||
fn += '.';
|
||||
fn += exl->ext;
|
||||
if(exists(fn))
|
||||
return TexLoader(exl->func, fixup(fn));
|
||||
}
|
||||
|
||||
return TexLoader(NULL, std::string());
|
||||
}
|
||||
|
||||
void TextureMgr::th_loadFromFile(TexLoadTmp& tt) const
|
||||
{
|
||||
std::string withoutExt;
|
||||
for(size_t i = 0; i < loadFromPaths.size(); ++i)
|
||||
{
|
||||
withoutExt = loadFromPaths[i] + tt.name;
|
||||
TexLoader ldr = getFullnameAndLoader(withoutExt, loadFromPaths[i]);
|
||||
if(!ldr.name.empty()) // name was cleaned up, use the updated one
|
||||
tt.name = ldr.name;
|
||||
if(ldr.loader)
|
||||
{
|
||||
if(tt.loadmode < OVERWRITE && tt.curTex && tt.curTex->success && tt.curTex->filename == ldr.fn)
|
||||
{
|
||||
tt.success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
tt.filename = ldr.fn;
|
||||
tt.img = ldr.load();
|
||||
tt.success = !!tt.img.pixels;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tt.img.pixels = NULL;
|
||||
}
|
||||
|
||||
|
||||
TextureMgr::TextureMgr()
|
||||
: sem(SDL_CreateSemaphore(0))
|
||||
{
|
||||
}
|
||||
|
||||
TextureMgr::~TextureMgr()
|
||||
{
|
||||
for(size_t i = 0; i < threads.size(); ++i)
|
||||
worktodo.push(NULL); // signal all threads to exit
|
||||
for(size_t i = 0; i < threads.size(); ++i)
|
||||
SDL_WaitThread((SDL_Thread*)threads[i], NULL);
|
||||
|
||||
SDL_DestroySemaphore((SDL_sem*)sem);
|
||||
}
|
||||
|
||||
size_t TextureMgr::spawnThreads(size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
{
|
||||
SDL_Thread *worker;
|
||||
#if SDL_VERSION_ATLEAST(2,0,0)
|
||||
worker = SDL_CreateThread(Th_Main, "texldr", this);
|
||||
#else
|
||||
worker = SDL_CreateThread(Th_Main, this);
|
||||
#endif
|
||||
if(!worker)
|
||||
return i;
|
||||
|
||||
threads.push_back(worker);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t TextureMgr::getNumLoaded() const
|
||||
{
|
||||
return cache.size();
|
||||
}
|
||||
|
||||
Texture* TextureMgr::getOrLoad(const std::string& name)
|
||||
{
|
||||
return load(name, KEEP);
|
||||
}
|
||||
|
||||
void TextureMgr::shutdown()
|
||||
{
|
||||
for(TexCache::iterator it = cache.begin(); it != cache.end(); ++it)
|
||||
it->second->unload();
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
int TextureMgr::Th_Main(void* ud)
|
||||
{
|
||||
TextureMgr *self = (TextureMgr*)ud;
|
||||
self->thMain();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TextureMgr::thMain()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
void *p;
|
||||
worktodo.pop(p);
|
||||
if(!p) // a pushed NULL is the signal to exit
|
||||
break;
|
||||
|
||||
TexLoadTmp *tt = (TexLoadTmp*)p;
|
||||
th_loadFromFile(*tt);
|
||||
|
||||
workdone.push(p);
|
||||
}
|
||||
}
|
||||
|
||||
Texture *TextureMgr::finalize(const TexLoadTmp& tt)
|
||||
{
|
||||
Texture *tex = tt.curTex;
|
||||
if(!tex)
|
||||
{
|
||||
tex = new Texture; // always make sure a valid Texture object comes out
|
||||
tex->name = tt.name; // this doesn't ever change
|
||||
cache[tt.name] = tex; // didn't exist, cache now
|
||||
}
|
||||
|
||||
tex->filename = tt.filename;
|
||||
tex->success = tt.success;
|
||||
|
||||
if(!tt.success)
|
||||
{
|
||||
debugLog("FAILED TO LOAD TEXTURE: [" + tt.name + "]");
|
||||
tex->unload();
|
||||
tex->width = 64;
|
||||
tex->height = 64;
|
||||
}
|
||||
if(tt.img.pixels)
|
||||
{
|
||||
//debugLog("LOADED TEXTURE FROM DISK: [" + tt.name + "]");
|
||||
tex->upload(tt.img, /*tt.mipmap*/ true);
|
||||
free(tt.img.pixels);
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
|
||||
void TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_t n, LoadMode mode, ProgressCallback cb, void *cbUD)
|
||||
{
|
||||
if(threads.empty())
|
||||
{
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
{
|
||||
Texture *tex = load(texnames[i], mode);
|
||||
if(pdst)
|
||||
pdst[i] = tex;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Important that this is pre-allocated. We store pointers to elements and
|
||||
// send them to threads, so this must never reallocate.
|
||||
std::vector<TexLoadTmp> tmp(n);
|
||||
|
||||
size_t inprogress = 0, doneCB = 0;
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
{
|
||||
TexLoadTmp& tt = tmp[i];
|
||||
tt.name = texnames[i];
|
||||
stringToLower(tt.name);
|
||||
TexCache::iterator it = cache.find(tt.name);
|
||||
tt.curTex = it != cache.end() ? it->second.content() : NULL;
|
||||
if(mode == KEEP && tt.curTex && tt.curTex->success)
|
||||
{
|
||||
if(pdst)
|
||||
pdst[i] = tt.curTex;
|
||||
if(cb)
|
||||
cb(++doneCB, cbUD);
|
||||
continue;
|
||||
}
|
||||
|
||||
tt.loadmode = mode;
|
||||
|
||||
worktodo.push(&tt);
|
||||
++inprogress;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < inprogress; ++i)
|
||||
{
|
||||
void *p;
|
||||
workdone.pop(p);
|
||||
const TexLoadTmp& tt = *(const TexLoadTmp*)p;
|
||||
Texture *tex = finalize(tt);
|
||||
if(pdst)
|
||||
pdst[i] = tex;
|
||||
if(cb)
|
||||
cb(++doneCB, cbUD);
|
||||
}
|
||||
}
|
||||
|
||||
Texture* TextureMgr::load(const std::string& texname, LoadMode mode)
|
||||
{
|
||||
TexLoadTmp tt;
|
||||
tt.name = texname;
|
||||
stringToLower(tt.name);
|
||||
TexCache::iterator it = cache.find(tt.name);
|
||||
tt.curTex = it != cache.end() ? it->second.content() : NULL;
|
||||
|
||||
// texname "" will never load, so don't even try once we have a valid object
|
||||
if(tt.curTex && (texname.empty() || (mode == KEEP && tt.curTex->success)))
|
||||
return tt.curTex;
|
||||
|
||||
tt.loadmode = mode;
|
||||
th_loadFromFile(tt);
|
||||
return finalize(tt);
|
||||
}
|
||||
|
||||
void TextureMgr::reloadAll(LoadMode mode)
|
||||
{
|
||||
std::vector<std::string> todo;
|
||||
for(TexCache::iterator it = cache.begin(); it != cache.end(); ++it)
|
||||
{
|
||||
Texture *tex = it->second.content();
|
||||
if(mode == KEEP && tex->success)
|
||||
continue;
|
||||
|
||||
todo.push_back(tex->name);
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "TextureMgr: Potentially reloading up to " << todo.size() << " textures...";
|
||||
debugLog(os.str());
|
||||
|
||||
if(!todo.empty())
|
||||
loadBatch(NULL, &todo[0],todo.size(), mode);
|
||||
}
|
||||
|
||||
|
53
BBGE/TextureMgr.h
Normal file
53
BBGE/TextureMgr.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
#ifndef BBGE_TEXTUREMGR_H
|
||||
#define BBGE_TEXTUREMGR_H
|
||||
|
||||
#include <map>
|
||||
#include "Texture.h"
|
||||
#include "MT.h"
|
||||
|
||||
|
||||
struct TexLoadTmp;
|
||||
|
||||
class TextureMgr
|
||||
{
|
||||
public:
|
||||
TextureMgr();
|
||||
~TextureMgr();
|
||||
|
||||
typedef void (*ProgressCallback)(size_t done, void*);
|
||||
|
||||
std::vector<std::string> loadFromPaths;
|
||||
size_t spawnThreads(size_t n);
|
||||
size_t getNumLoaded() const;
|
||||
Texture *getOrLoad(const std::string& name);
|
||||
void clearUnused(); // clear everything whose refcount is 1
|
||||
void shutdown();
|
||||
|
||||
enum LoadMode
|
||||
{
|
||||
KEEP, // if already exists, keep unchanged
|
||||
KEEP_IF_SAME, // load if we resolve to a different file than the texture that's already there, if any.
|
||||
OVERWRITE, // always overwrite
|
||||
};
|
||||
|
||||
void loadBatch(Texture *pdst[], const std::string texnames[], size_t n, LoadMode mode = KEEP, ProgressCallback cb = 0, void *cbUD = 0);
|
||||
Texture *load(const std::string& texname, LoadMode mode);
|
||||
void reloadAll(LoadMode mode);
|
||||
|
||||
private:
|
||||
typedef std::map<std::string, CountedPtr<Texture> > TexCache;
|
||||
TexCache cache;
|
||||
|
||||
BlockingQueue<void*> worktodo;
|
||||
BlockingQueue<void*> workdone;
|
||||
std::vector<void*> threads;
|
||||
void *sem; // SDL_sem*
|
||||
|
||||
static int Th_Main(void *self);
|
||||
void thMain(); // for int
|
||||
|
||||
void th_loadFromFile(TexLoadTmp& tt) const;
|
||||
Texture *finalize(const TexLoadTmp& tt);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue