1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-01-24 17:26:41 +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:
fgenesis 2023-05-31 00:55:16 +02:00
parent 1100e286a6
commit 70b8dcdc3a
22 changed files with 737 additions and 796 deletions

View file

@ -1153,7 +1153,7 @@ void AnimationEditor::clearRot()
if (editingBone)
{
if(core->getCtrlState())
editingBone->texture->reload();
core->texmgr.load(editingBone->texture->name, TextureMgr::OVERWRITE);
else if(splinegrid)
splinegrid->resetControlPoints();
else

View file

@ -441,27 +441,27 @@ void DSQ::loadFonts()
font.load(file, 1, false);
font.fontTopColor = Vector(0.9f,0.9f,1);
font.fontBtmColor = Vector(0.5f,0.8f,1);
font.overrideTexture = core->addTexture("font");
font.overrideTexture = this->getTexture("font");
smallFont.load(file, 0.6f, false);
smallFont.fontTopColor = Vector(0.9f,0.9f,1);
smallFont.fontBtmColor = Vector(0.5f,0.8f,1);
smallFont.overrideTexture = core->addTexture("font");
smallFont.overrideTexture = this->getTexture("font");
smallFontRed.load(file, 0.6f, false);
smallFontRed.fontTopColor = Vector(1,0.9f,0.9f);
smallFontRed.fontBtmColor = Vector(1,0.8f,0.5f);
smallFontRed.overrideTexture = core->addTexture("font");
smallFontRed.overrideTexture = this->getTexture("font");
subsFont.load(file, 0.5f, false);
subsFont.fontTopColor = Vector(1,1,1);
subsFont.fontBtmColor = Vector(0.5f,0.8f,1);
subsFont.overrideTexture = core->addTexture("font");
subsFont.overrideTexture = this->getTexture("font");
goldFont.load(file, 1, false);
goldFont.fontTopColor = Vector(1,0.9f,0.5f);
goldFont.fontBtmColor = Vector(0.6f,0.5f,0.25f);
goldFont.overrideTexture = core->addTexture("font");
goldFont.overrideTexture = this->getTexture("font");
file = localisePath("data/font.ttf");
@ -844,11 +844,10 @@ void DSQ::init()
addStateInstance(new Nag);
this->setBaseTextureDirectory("gfx/");
voiceOversEnabled = true;
this->setExtraTexturePath(NULL);
if(!user.load(false))
@ -1255,6 +1254,7 @@ void DSQ::init()
}
addRenderObject(fpsText, LR_DEBUG_TEXT);
precacher.setBaseDir(this->getBaseTexturePath());
precacher.precacheList("data/precache.txt", loadBitForTexPrecache);
setTexturePointers();
@ -1899,20 +1899,10 @@ void DSQ::refreshResourcesForPatch(const std::string& name)
int reloaded = 0;
if(files.size())
{
for(size_t i = 0; i < dsq->resources.size(); ++i)
{
Texture *r = dsq->resources[i];
for(size_t i = 0; i < files.size(); ++i)
if(files[i] == r->name)
{
++reloaded;
r->reload();
break;
}
}
texmgr.loadBatch(NULL, &files[0], files.size(), TextureMgr::OVERWRITE);
}
os.str("");
os << "refreshResourcesForPatch - " << reloaded << " textures reloaded";
os << "refreshResourcesForPatch - " << files.size() << " textures reloaded";
debugLog(os.str());
}
#else
@ -1995,7 +1985,7 @@ void DSQ::shutdown()
Network::shutdown();
scriptInterface.shutdown();
precacher.clean();
precacher.clear();
core->particleManager->clearParticleBank();
@ -2086,11 +2076,11 @@ void DSQ::shutdown()
void DSQ::setTexturePointers()
{
texCursor = core->addTexture("cursor");
texCursorLook = core->addTexture("cursor-look");
texCursorBurst = core->addTexture("cursor-burst");
texCursorSwim = core->addTexture("cursor-swim");
texCursorSing = core->addTexture("cursor-sing");
texCursor = this->getTexture("cursor");
texCursorLook = this->getTexture("cursor-look");
texCursorBurst = this->getTexture("cursor-burst");
texCursorSwim = this->getTexture("cursor-swim");
texCursorSing = this->getTexture("cursor-sing");
if (cursor)
cursor->setTexturePointer(texCursor);
@ -3928,7 +3918,7 @@ void DSQ::onUpdate(float dt)
if (isDeveloperKeys() && fpsText && cmDebug && cmDebug->alpha == 1)
{
std::ostringstream os;
os << "FPS: " << core->fps << " | ROC: " << core->renderObjectCount << " | RC: " << g_dbg_numRenderCalls << " | RES: " << core->resources.size();
os << "FPS: " << core->fps << " | ROC: " << core->renderObjectCount << " | RC: " << g_dbg_numRenderCalls << " | RES: " << texmgr.getNumLoaded();
os << " | p: " << core->processedRenderObjectCount << " | t: " << core->totalRenderObjectCount;
os << " | s: " << dsq->continuity.seconds;
os << " | sndQ: " << core->dbg_numThreadDecoders;

View file

@ -231,7 +231,6 @@ Game::Game() : StateObject()
Game::~Game()
{
delete themenu;
tileCache.clean();
game = 0;
}
@ -410,7 +409,7 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
tpos.y -= h2;
int w = 0, h = 0;
unsigned int size = 0;
size_t size = 0;
unsigned char *data = q->texture->getBufferAndSize(&w, &h, &size);
if (!data)
{
@ -1082,7 +1081,7 @@ void Game::generateCollisionMask(Bone *q, float overrideCollideRadius /* = 0 */)
tpos.y -= h2;
int w = 0, h = 0;
unsigned int size = 0;
size_t size = 0;
unsigned char *data = q->texture->getBufferAndSize(&w, &h, &size);
if (!data)
{
@ -1285,7 +1284,6 @@ bool Game::loadSceneXML(std::string scene)
{
bgSfxLoop = "";
airSfxLoop = "";
elementTemplatePack = "Main";
entitySaveData.clear();
std::string fn = getSceneFilename(scene);
if (!exists(fn))
@ -1317,17 +1315,13 @@ bool Game::loadSceneXML(std::string scene)
if (level)
{
XMLElement *levelSF = saveFile->NewElement("Level");
if (level->Attribute("tileset"))
const char *tileset = level->Attribute("tileset");
if(!tileset)
tileset = level->Attribute("elementTemplatePack"); // legacy, still present in some very old maps
if (tileset)
{
elementTemplatePack = level->Attribute("tileset");
loadElementTemplates(elementTemplatePack);
levelSF->SetAttribute("tileset", elementTemplatePack.c_str());
}
else if (level->Attribute("elementTemplatePack"))
{
elementTemplatePack = level->Attribute("elementTemplatePack");
loadElementTemplates(elementTemplatePack);
levelSF->SetAttribute("tileset", elementTemplatePack.c_str());
loadElementTemplates(tileset);
levelSF->SetAttribute("tileset", tileset);
}
else
return false;
@ -1729,206 +1723,195 @@ bool Game::loadSceneXML(std::string scene)
saveFile->InsertEndChild(newSF);
}
std::vector<Element*> loadedElements;
loadedElements.reserve(200);
struct ElementDef
{
ElementDef(int lr)
: layer(lr), idx(0), x(0), y(0), rot(0), fh(0), fv(0), flags(0), efxIdx(0), repeat(0)
, tag(0), sx(1), sy(1), rsx(1), rsy(1)
{}
int layer, idx, x, y, rot, fh, fv, flags, efxIdx, repeat, tag;
float sx, sy, rsx, rsy;
};
std::vector<ElementDef> elemsDefs;
elemsDefs.reserve(256);
XMLElement *simpleElements = doc.FirstChildElement("SE");
while (simpleElements)
{
int idx, x, y, rot;
float sz,sz2;
loadedElements.clear();
if (simpleElements->Attribute("d"))
const size_t defsBeginIdx = elemsDefs.size();
const int layer = atoi(simpleElements->Attribute("l"));
if (const char *attr = simpleElements->Attribute("d"))
{
SimpleIStringStream is(simpleElements->Attribute("d"));
while (is >> idx)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(4); // legacy crap
while (is >> d.idx)
{
is >> x >> y >> rot;
Element *e = createElement(idx, Vector(x,y), 4);
e->rotation.z = rot;
loadedElements.push_back(e);
is >> d.x >> d.y >> d.rot;
elemsDefs.push_back(d);
}
}
if (simpleElements->Attribute("e"))
if (const char *attr = simpleElements->Attribute("e"))
{
SimpleIStringStream is2(simpleElements->Attribute("e"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
is2 >> x >> y >> rot;
Element *e = createElement(idx, Vector(x,y), l);
e->rotation.z = rot;
loadedElements.push_back(e);
is >> d.x >> d.y >> d.rot;
elemsDefs.push_back(d);
}
}
if (simpleElements->Attribute("f"))
if (const char *attr = simpleElements->Attribute("f"))
{
SimpleIStringStream is2(simpleElements->Attribute("f"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
is2 >> x >> y >> rot >> sz;
Element *e = createElement(idx, Vector(x,y), l);
e->scale = Vector(sz,sz);
e->rotation.z = rot;
loadedElements.push_back(e);
is >> d.x >> d.y >> d.rot >> d.sx;
d.sy = d.sx;
elemsDefs.push_back(d);
}
}
if (simpleElements->Attribute("g"))
if (const char *attr = simpleElements->Attribute("g"))
{
SimpleIStringStream is2(simpleElements->Attribute("g"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
int fh, fv;
is2 >> x >> y >> rot >> sz >> fh >> fv;
Element *e = createElement(idx, Vector(x,y), l);
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz);
e->rotation.z = rot;
loadedElements.push_back(e);
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv;
d.sy = d.sx;
elemsDefs.push_back(d);
}
}
if (simpleElements->Attribute("h"))
if (const char *attr = simpleElements->Attribute("h"))
{
SimpleIStringStream is2(simpleElements->Attribute("h"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
int fh, fv;
int flags;
is2 >> x >> y >> rot >> sz >> fh >> fv >> flags;
Element *e = createElement(idx, Vector(x,y), l);
e->elementFlag = (ElementFlag)flags;
if (e->elementFlag >= EF_MAX || e->elementFlag < EF_NONE)
e->elementFlag = EF_NONE;
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz);
e->rotation.z = rot;
loadedElements.push_back(e);
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags;
d.sy = d.sx;
elemsDefs.push_back(d);
}
}
if (simpleElements->Attribute("i"))
if (const char *attr = simpleElements->Attribute("i"))
{
SimpleIStringStream is2(simpleElements->Attribute("i"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
{
int fh, fv;
int flags;
int efxIdx;
is2 >> x >> y >> rot >> sz >> fh >> fv >> flags >> efxIdx;
if (sz < MIN_SIZE)
sz = MIN_SIZE;
Element *e = createElement(idx, Vector(x,y), l);
e->elementFlag = (ElementFlag)flags;
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz);
e->rotation.z = rot;
e->setElementEffectByIndex(efxIdx);
loadedElements.push_back(e);
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags >> d.efxIdx;
d.sy = d.sx;
elemsDefs.push_back(d);
}
}
if (simpleElements->Attribute("j"))
if (const char *attr = simpleElements->Attribute("j"))
{
SimpleIStringStream is2(simpleElements->Attribute("j"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
int fh, fv;
int flags;
int efxIdx;
int repeat;
is2 >> x >> y >> rot >> sz >> fh >> fv >> flags >> efxIdx >> repeat;
if (sz < MIN_SIZE)
sz = MIN_SIZE;
Element *e = createElement(idx, Vector(x,y), l);
e->elementFlag = (ElementFlag)flags;
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz);
e->rotation.z = rot;
e->setElementEffectByIndex(efxIdx);
if (repeat)
e->repeatTextureToFill(true);
loadedElements.push_back(e);
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags >> d.efxIdx >> d.repeat;
d.sy = d.sx;
elemsDefs.push_back(d);
}
}
if (simpleElements->Attribute("k"))
if (const char *attr = simpleElements->Attribute("k"))
{
SimpleIStringStream is2(simpleElements->Attribute("k"));
int l = atoi(simpleElements->Attribute("l"));
while(is2 >> idx)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
while(is >> d.idx)
{
int fh, fv;
int flags;
int efxIdx;
int repeat;
is2 >> x >> y >> rot >> sz >> sz2 >> fh >> fv >> flags >> efxIdx >> repeat;
if (sz < MIN_SIZE)
sz = MIN_SIZE;
if (sz2 < MIN_SIZE)
sz2 = MIN_SIZE;
Element *e = createElement(idx, Vector(x,y), l);
e->elementFlag = (ElementFlag)flags;
if (fh)
e->flipHorizontal();
if (fv)
e->flipVertical();
e->scale = Vector(sz,sz2);
e->rotation.z = rot;
e->setElementEffectByIndex(efxIdx);
if (repeat)
e->repeatTextureToFill(true);
loadedElements.push_back(e);
is >> d.x >> d.y >> d.rot >> d.sx >> d.sy >> d.fh >> d.fv >> d.flags >> d.efxIdx >> d.repeat;
elemsDefs.push_back(d);
}
}
if (simpleElements->Attribute("repeatScale"))
// done loading raw data, now for some possible extensions added later
if (const char *attr = simpleElements->Attribute("repeatScale"))
{
SimpleIStringStream is2(simpleElements->Attribute("repeatScale"), SimpleIStringStream::REUSE);
for(size_t i = 0; i < loadedElements.size(); ++i)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
for(size_t i = defsBeginIdx; i < elemsDefs.size(); ++i)
{
Element *e = loadedElements[i];
if(e->isRepeatingTextureToFill())
ElementDef& d = elemsDefs[i];
if(d.repeat)
{
float repeatScaleX = 1, repeatScaleY = 1;
if(!(is2 >> repeatScaleX >> repeatScaleY))
if(!(is >> d.rsx >> d.rsx))
break;
e->repeatToFillScale.x = repeatScaleX;
e->repeatToFillScale.y = repeatScaleY;
e->refreshRepeatTextureToFill();
}
}
}
if (simpleElements->Attribute("tag"))
if (const char *attr = simpleElements->Attribute("tag"))
{
SimpleIStringStream is2(simpleElements->Attribute("tag"), SimpleIStringStream::REUSE);
for(size_t i = 0; i < loadedElements.size(); ++i)
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
for(size_t i = defsBeginIdx; i < elemsDefs.size(); ++i)
{
Element *e = loadedElements[i];
int tag = 0;
if(!(is2 >> tag))
ElementDef& d = elemsDefs[i];
if(!(is >> d.tag))
break;
e->setTag(tag);
}
}
simpleElements = simpleElements->NextSiblingElement("SE");
}
// figure out which textures in the tileset are used and preload those that are actually used
{
unsigned char usedIdx[1024] = {0};
for(size_t i = 0; i < elemsDefs.size(); ++i)
{
unsigned idx = elemsDefs[i].idx;
if(idx < Countof(usedIdx))
usedIdx[idx] = 1;
}
std::vector<std::string> usedTex;
usedTex.reserve(elementTemplates.size()); // optimistically assume all textures in the tileset are used
for (size_t i = 0; i < elementTemplates.size(); i++)
{
unsigned idx = elementTemplates[i].idx;
if (idx < Countof(usedIdx) && usedIdx[idx])
usedTex.push_back(elementTemplates[i].gfx);
}
std::sort(usedTex.begin(), usedTex.end());
// drop duplicates
usedTex.resize(std::distance(usedTex.begin(), std::unique(usedTex.begin(), usedTex.end())));
std::ostringstream os;
os << "Scene has " << elemsDefs.size() << " elements that use " << usedTex.size()
<< " distinct textures out of the " << elementTemplates.size() << " entries in the tileset";
debugLog(os.str());
// preload all used textures
if(usedTex.size())
dsq->texmgr.loadBatch(NULL, &usedTex[0], usedTex.size());
}
// Now that all SE tags have been processed, spawn them
for(size_t i = 0; i < elemsDefs.size(); ++i)
{
const ElementDef& d = elemsDefs[i];
Element *e = createElement(d.idx, Vector(d.x,d.y), d.layer);
e->elementFlag = (ElementFlag)d.flags;
if (d.fh)
e->flipHorizontal();
if (d.fv)
e->flipVertical();
e->scale = Vector(d.sx, d.sy);
e->rotation.z = d.rot;
e->repeatToFillScale.x = d.rsx;
e->repeatToFillScale.y = d.rsy;
if(d.efxIdx)
e->setElementEffectByIndex(d.efxIdx);
if (d.repeat)
e->repeatTextureToFill(true); // also applies repeatToFillScale
e->setTag(d.tag);
}
XMLElement *element = doc.FirstChildElement("Element");
while (element)
{
@ -2783,7 +2766,6 @@ void Game::applyState()
//sceneColor = Vector(0.75, 0.75, 0.8);
sceneColor = Vector(1,1,1);
sceneName = "";
elementTemplatePack ="";
clearGrid();
bg = 0;
bg2 = 0;
@ -2950,9 +2932,9 @@ void Game::applyState()
bindInput();
if (verbose) debugLog("Loading Scene");
if (!loadScene(sceneToLoad))
loadScene(sceneToLoad);
{
loadElementTemplates(elementTemplatePack);
debugLog("Failed to load scene [" + sceneToLoad + "]");
}
if (verbose) debugLog("...Done");
@ -4934,33 +4916,18 @@ void Game::loadElementTemplates(std::string pack)
elementTemplates.clear();
// HACK: need to uncache things! causes memory leak currently
bool doPrecache=false;
std::string fn;
if (dsq->mod.isActive())
fn = dsq->mod.getPath() + "tilesets/" + pack + ".txt";
else
fn = "data/tilesets/" + pack + ".txt";
if (lastTileset == fn)
{
doPrecache=false;
}
lastTileset = fn;
if (!exists(fn))
{
errorLog ("Could not open element template pack [" + fn + "]");
errorLog ("Could not open tileset [" + fn + "]");
return;
}
if (doPrecache)
{
tileCache.clean();
}
InStream in(fn.c_str());
std::string line;
while (std::getline(in, line))
@ -4977,21 +4944,12 @@ void Game::loadElementTemplates(std::string pack)
t.w = w;
t.h = h;
elementTemplates.push_back(t);
if (doPrecache)
tileCache.precacheTex(gfx);
}
in.close();
for (size_t i = 0; i < elementTemplates.size(); i++)
{
for (size_t j = i; j < elementTemplates.size(); j++)
{
if (elementTemplates[i].idx > elementTemplates[j].idx)
{
std::swap(elementTemplates[i], elementTemplates[j]);
}
}
}
std::sort(elementTemplates.begin(), elementTemplates.end());
for (int i = 0; i < 27; i++)
{
elementTemplates.push_back(getElementTemplateForLetter(i));

View file

@ -88,6 +88,7 @@ class ElementTemplate
{
public:
ElementTemplate() { alpha = 1; cull = true; w=-1; h=-1; idx=-1; tu1=tu2=tv1=tv2=0; }
inline bool operator<(const ElementTemplate& o) const { return idx < o.idx; }
std::string gfx;
std::vector <TileVector> grid;
int w,h;
@ -293,8 +294,6 @@ public:
std::vector<EntitySaveData> entitySaveData;
int getIdxForEntityType(std::string type);
Precacher tileCache;
void setCameraFollow(Vector *position);
Shot *fireShot(const std::string &bankShot, Entity *firer, Entity *target=0, const Vector &pos=Vector(0,0,0), const Vector &aim=Vector(0,0,0), bool playSfx=true);
void playBurstSound(bool wallJump=false);
@ -474,9 +473,6 @@ protected:
bool controlHint_ignoreClear;
BitmapText *controlHint_text;
std::string lastTileset;
void createLi();
void createPets();
Quad *backdropQuad;
@ -498,9 +494,6 @@ protected:
Vector getClosestPointOnTriangle(Vector a, Vector b, Vector c, Vector p);
Vector getClosestPointOnLine(Vector a, Vector b, Vector p);
std::string elementTemplatePack;
Vector *cameraFollow;
RenderObject *cameraFollowObject;
Entity *cameraFollowEntity;

View file

@ -77,7 +77,7 @@ void Intro::endIntro()
dsq->sound->clearLocalSounds();
cachy.clean();
cachy.clear();
dsq->toggleBlackBars(0);

View file

@ -106,19 +106,14 @@ void MinimapIcon::update(float dt)
// pretty much copied from RenderObject::setTexture()
static bool _setTex(CountedPtr<Texture> &tex, std::string name)
{
stringToLowerUserData(name);
if (name.empty())
{
tex = NULL;
return false;
}
if(tex && tex->getLoadResult() == TEX_SUCCESS && name == tex->name)
return true; // no texture change
tex = core->addTexture(name);
return tex && tex->getLoadResult() == TEX_SUCCESS;
tex = core->getTexture(name);
return tex->success;
}
bool MinimapIcon::setTexture(std::string name)
@ -168,12 +163,12 @@ MiniMapRender::MiniMapRender() : RenderObject()
cull = false;
lightLevel = 1.0;
texWaterBit = core->addTexture("gui/minimap/waterbit");
texMinimapBtm = core->addTexture("gui/minimap/btm");
texMinimapTop = core->addTexture("gui/minimap/top");
texNaija = core->addTexture("gems/naija-token");
texHealthBar = core->addTexture("particles/glow-masked");
texMarker = core->addTexture("gui/minimap/marker");
texWaterBit = core->getTexture("gui/minimap/waterbit");
texMinimapBtm = core->getTexture("gui/minimap/btm");
texMinimapTop = core->getTexture("gui/minimap/top");
texNaija = core->getTexture("gems/naija-token");
texHealthBar = core->getTexture("particles/glow-masked");
texMarker = core->getTexture("gui/minimap/marker");
buttons.clear();

View file

@ -43,7 +43,7 @@ Mod::Mod()
Mod::~Mod()
{
modcache.clean();
modcache.clear();
}
/*
@ -152,7 +152,7 @@ void Mod::load(const std::string &p)
}
}
dsq->secondaryTexturePath = path + "graphics/";
dsq->setExtraTexturePath((path + "graphics/").c_str());
dsq->sound->audioPath2 = path + "audio/";
dsq->sound->setVoicePath2(path + "audio/");
@ -180,13 +180,13 @@ void Mod::recache()
{
if(doRecache)
{
dsq->precacher.clean();
core->texmgr.reloadAll(TextureMgr::KEEP_IF_SAME);
dsq->unloadResources();
}
if(active)
{
modcache.setBaseDir(dsq->secondaryTexturePath);
modcache.setBaseDir(dsq->getExtraTexturePath());
std::string fname = path;
if(fname[fname.length() - 1] != '/')
fname += '/';
@ -200,7 +200,7 @@ void Mod::recache()
}
else
{
modcache.clean();
modcache.clear();
}
if(doRecache)
@ -292,7 +292,7 @@ void Mod::setActive(bool a)
mapRevealMethod = REVEAL_UNSPECIFIED;
setLocalisationModPath("");
name = path = "";
dsq->secondaryTexturePath = "";
dsq->setExtraTexturePath(NULL);
dsq->sound->audioPath2 = "";
dsq->sound->setVoicePath2("");
SkeletalSprite::secondaryAnimationPath = "";

View file

@ -494,7 +494,7 @@ static unsigned char *tileDataToAlpha(WorldMapTile *tile)
const unsigned int scaleY = texHeight / MAPVIS_SUBDIV;
unsigned char *savedTexData = new unsigned char[texWidth * texHeight * 4];
tile->q->texture->read(0, 0, texWidth, texHeight, savedTexData);
tile->q->texture->readRGBA(savedTexData);
unsigned char *texData = new unsigned char[texWidth * texHeight * 4];
memcpy(texData, savedTexData, texWidth * texHeight * 4);
@ -538,7 +538,7 @@ static unsigned char *tileDataToAlpha(WorldMapTile *tile)
}
}
tile->q->texture->write(0, 0, texWidth, texHeight, texData);
tile->q->texture->writeRGBA(0, 0, texWidth, texHeight, texData);
delete[] texData;
return savedTexData;
@ -546,7 +546,7 @@ static unsigned char *tileDataToAlpha(WorldMapTile *tile)
static void resetTileAlpha(WorldMapTile *tile, const unsigned char *savedTexData)
{
tile->q->texture->write(0, 0, tile->q->texture->width, tile->q->texture->height, savedTexData);
tile->q->texture->writeRGBA(0, 0, tile->q->texture->width, tile->q->texture->height, savedTexData);
}
@ -629,25 +629,29 @@ WorldMapRender::WorldMapRender() : RenderObject(), ActionMapper()
int num = dsq->continuity.worldMap.getNumWorldMapTiles();
const size_t num = dsq->continuity.worldMap.getNumWorldMapTiles();
std::string n = dsq->game->sceneName;
stringToUpper(n);
for (int i = 0; i < num; i++)
std::vector<std::string> textodo(num);
std::vector<Texture*> texs(num, NULL);
textodo.reserve(num);
for (size_t i = 0; i < num; i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile)
{
if (tile->name == n)
{
activeTile = tile;
break;
}
textodo[i] = "gui/worldmap/" + tile->name;
}
}
tiles.clear();
for (int i = 0; i < num; i++)
if(num)
dsq->texmgr.loadBatch(&texs[0], &textodo[0], num);
for (size_t i = 0; i < num; i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile)
@ -655,8 +659,7 @@ WorldMapRender::WorldMapRender() : RenderObject(), ActionMapper()
Vector pos(tile->gridPos.x, tile->gridPos.y);
Quad *q = new Quad;
std::string tn = "Gui/WorldMap/" + tile->name;
q->setTexture(tn);
q->setTexturePointer(texs[i]);
q->position = pos;
q->alphaMod = 0;
q->drawOrder = Quad::GRID_DRAW_WORLDMAP;

View file

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

View file

@ -105,6 +105,8 @@ set(BBGE_SRCS
StringBank.h
Texture.cpp
Texture.h
TextureMgr.cpp
TextureMgr.h
TTFFont.cpp
TTFFont.h
Vector.cpp

View file

@ -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)
{
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)
CountedPtr<Texture> Core::getTexture(const std::string &name)
{
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);
for (size_t i = 0; i < copy.size(); ++i)
{
if (copy[i] == res)
{
copy[i]->destroy();
copy[i] = copy.back();
copy.pop_back();
break;
}
}
texmgr.loadFromPaths.resize(size_t(1) + !!dir);
size_t w = 0;
if(dir)
texmgr.loadFromPaths[w++] = dir;
texmgr.loadFromPaths[w] = "gfx/";
}
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)

View file

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

View file

@ -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);
}
}
static void texLoadProgressCallback(size_t done, void *ud)
{
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, void progressCallback())
void Precacher::precacheList(const std::string &list, ProgressCallback progress)
{
loadProgressCallback = progressCallback;
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);
}

View file

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

View file

@ -349,8 +349,7 @@ void Quad::renderGrid(const RenderState& rs) const
}
}
glEnd();
if (texture)
glBindTexture(GL_TEXTURE_2D, texture->textures[0]);
RenderObject::lastTextureApplied = 0;
}
}

View file

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

View file

@ -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;
}
memcpy(data + size, "SAFE", 5);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
glBindTexture(GL_TEXTURE_2D, 0);
// 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;
return NULL;
}
this->readRGBA(data);
*wparam = w;
*hparam = h;
*sizeparam = size;
*wparam = width;
*hparam = height;
*sizeparam = bytes;
return data;
fail:
*wparam = 0;
*hparam = 0;
*sizeparam = 0;
return NULL;
}

View file

@ -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 reload();
void writeRGBA(int tx, int ty, int w, int h, const unsigned char *pixels);
void readRGBA(unsigned char *pixels);
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);
unsigned char *getBufferAndSize(int *w, int *h, size_t *size); // returned memory must be free()'d
unsigned char *getBufferAndSize(int *w, int *h, unsigned int *size); // returned memory must be free()'d
std::string name, filename;
bool upload(const ImageData& img, bool mipmap);
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
View 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
View 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

View file

@ -6,6 +6,10 @@
#include <sstream>
#include <stdio.h>
// HACK: add a big lock to make this thing not crash when multiple threads are active
#include "../../BBGE/MT.h"
static Lockable lock;
static ttvfs::Root *vfs = NULL;
@ -14,8 +18,15 @@ void ttvfs_setroot(ttvfs::Root *root)
vfs = root;
}
VFILE* vfgetfile(const char* fn)
{
MTGuard g(lock);
return vfs->GetFile(fn);
}
VFILE *vfopen(const char *fn, const char *mode)
{
MTGuard g(lock);
VFILE *vf = vfs->GetFile(fn);
if (!vf || !vf->open(mode))
return NULL;
@ -30,6 +41,7 @@ size_t vfread(void *ptr, size_t size, size_t count, VFILE *vf)
int vfclose(VFILE *vf)
{
MTGuard g(lock);
vf->close();
vf->decref();
return 0;
@ -105,11 +117,15 @@ InStream::InStream(const char *fn)
bool InStream::open(const char *fn)
{
ttvfs::File *vf = vfs->GetFile(fn);
if(!vf || !vf->open("r"))
ttvfs::File *vf = NULL;
{
setstate(std::ios::failbit);
return false;
MTGuard g(lock);
vf = vfs->GetFile(fn);
if(!vf || !vf->open("r"))
{
setstate(std::ios::failbit);
return false;
}
}
size_t sz = (size_t)vf->size();
std::string s;

View file

@ -36,6 +36,9 @@ typedef ttvfs::File VFILE;
void ttvfs_setroot(ttvfs::Root *root);
// HACK
VFILE *vfgetfile(const char *fn);
// Note that vfopen() returns the same pointer for the same file name,
// so effectively a file is a singleton object.
VFILE *vfopen(const char *fn, const char *mode);