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:
parent
1100e286a6
commit
70b8dcdc3a
22 changed files with 737 additions and 796 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
348
Aquaria/Game.cpp
348
Aquaria/Game.cpp
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -77,7 +77,7 @@ void Intro::endIntro()
|
|||
|
||||
dsq->sound->clearLocalSounds();
|
||||
|
||||
cachy.clean();
|
||||
cachy.clear();
|
||||
|
||||
dsq->toggleBlackBars(0);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 = "";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
|
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
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
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue