mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2025-07-02 22:14:37 +00:00
Rework + optimize map tile storage & rendering, initial working draft.
class Element is completely gone. (The files are still there but no longer compiled in. Will delete later) Broken still: - support for vertical flip - the editor - culling
This commit is contained in:
parent
e8c405cd9e
commit
bd5b2b3495
31 changed files with 1704 additions and 1422 deletions
|
@ -97,6 +97,7 @@ RenderObject::RenderObject()
|
|||
|
||||
shareAlphaWithChildren = false;
|
||||
shareColorWithChildren = false;
|
||||
neverFollowCamera = false;
|
||||
}
|
||||
|
||||
RenderObject::~RenderObject()
|
||||
|
|
|
@ -242,6 +242,7 @@ public:
|
|||
bool _hidden;
|
||||
bool _fv, _fh;
|
||||
bool _markedForDelete;
|
||||
bool neverFollowCamera;
|
||||
|
||||
unsigned char pm; // unsigned char to save space
|
||||
|
||||
|
|
|
@ -229,6 +229,8 @@ void RenderObjectLayer::prepareRender()
|
|||
toRender.push_back(NULL); // terminate
|
||||
core->totalRenderObjectCount += n;
|
||||
|
||||
// TODO: set followCameraMult = (0,0) when followCamera == 0 ?
|
||||
|
||||
switch(followCameraLock)
|
||||
{
|
||||
default:
|
||||
|
|
|
@ -41,6 +41,8 @@ Vector RenderObject::getFollowCameraPosition(const Vector& v) const
|
|||
{
|
||||
assert(layer != LR_NONE);
|
||||
assert(!parent); // this makes no sense when we're not a root object
|
||||
if(neverFollowCamera)
|
||||
return v;
|
||||
const RenderObjectLayer &rl = core->renderObjectLayers[layer];
|
||||
Vector M = rl.followCameraMult;
|
||||
float F = followCamera;
|
||||
|
|
|
@ -47,7 +47,7 @@ Texture::~Texture()
|
|||
unload();
|
||||
}
|
||||
|
||||
void Texture::readRGBA(unsigned char *pixels)
|
||||
void Texture::readRGBA(unsigned char *pixels) const
|
||||
{
|
||||
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, gltexid);
|
||||
|
@ -207,7 +207,7 @@ bool Texture::upload(const ImageData& img, bool mipmap)
|
|||
return true;
|
||||
}
|
||||
|
||||
unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, size_t *sizeparam)
|
||||
unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, size_t *sizeparam) const
|
||||
{
|
||||
const size_t bytes = size_t(width) * size_t(height) * 4;
|
||||
unsigned char *data = (unsigned char*)malloc(bytes);
|
||||
|
|
|
@ -46,9 +46,9 @@ public:
|
|||
int width, height;
|
||||
|
||||
void writeRGBA(int tx, int ty, int w, int h, const unsigned char *pixels);
|
||||
void readRGBA(unsigned char *pixels);
|
||||
void readRGBA(unsigned char *pixels) const;
|
||||
|
||||
unsigned char *getBufferAndSize(int *w, int *h, size_t *size); // returned memory must be free()'d
|
||||
unsigned char *getBufferAndSize(int *w, int *h, size_t *size) const; // returned memory must be free()'d
|
||||
|
||||
std::string name, filename;
|
||||
bool upload(const ImageData& img, bool mipmap);
|
||||
|
|
|
@ -269,24 +269,31 @@ Texture *TextureMgr::finalize(TexLoadTmp& tt)
|
|||
return tex;
|
||||
}
|
||||
|
||||
void TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_t n, LoadMode mode, ProgressCallback cb, void *cbUD)
|
||||
size_t TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_t n, LoadMode mode, ProgressCallback cb, void *cbUD)
|
||||
{
|
||||
size_t doneCB = 0;
|
||||
|
||||
if(threads.empty())
|
||||
{
|
||||
size_t loaded = 0;
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
{
|
||||
Texture *tex = load(texnames[i], mode);
|
||||
if(pdst)
|
||||
pdst[i] = tex;
|
||||
if(cb)
|
||||
cb(++doneCB, cbUD);
|
||||
loaded += !!tex;
|
||||
}
|
||||
return;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
// 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;
|
||||
size_t inprogress = 0;
|
||||
size_t loaded = 0;
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
{
|
||||
TexLoadTmp& tt = tmp[i];
|
||||
|
@ -301,6 +308,7 @@ void TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_
|
|||
pdst[i] = tt.curTex;
|
||||
if(cb)
|
||||
cb(++doneCB, cbUD);
|
||||
++loaded;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -320,7 +328,9 @@ void TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_
|
|||
pdst[tt.arrayidx] = tex;
|
||||
if(cb)
|
||||
cb(++doneCB, cbUD);
|
||||
loaded += !!tex;
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
Texture* TextureMgr::load(const std::string& texname, LoadMode mode)
|
||||
|
|
|
@ -30,7 +30,7 @@ public:
|
|||
OVERWRITE, // always overwrite
|
||||
};
|
||||
|
||||
void loadBatch(Texture *pdst[], const std::string texnames[], size_t n, LoadMode mode = KEEP, ProgressCallback cb = 0, void *cbUD = 0);
|
||||
size_t 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);
|
||||
|
||||
|
|
203
BBGE/Tile.cpp
203
BBGE/Tile.cpp
|
@ -2,9 +2,9 @@
|
|||
#include "RenderGrid.h"
|
||||
#include "Tileset.h"
|
||||
#include "Base.h"
|
||||
#include <algorithm>
|
||||
|
||||
TileStorage::TileStorage(const TileEffectStorage& eff)
|
||||
: effstore(eff)
|
||||
TileStorage::TileStorage()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -13,16 +13,31 @@ TileStorage::~TileStorage()
|
|||
destroyAll();
|
||||
}
|
||||
|
||||
void TileStorage::moveToFront(size_t idx)
|
||||
TileStorage::Sizes TileStorage::stats() const
|
||||
{
|
||||
_moveToFront(idx);
|
||||
refreshAll();
|
||||
Sizes sz;
|
||||
sz.tiles = tiles.size();
|
||||
sz.update = indicesToUpdate.size();
|
||||
sz.collide = indicesToCollide.size();
|
||||
return sz;
|
||||
}
|
||||
|
||||
void TileStorage::moveToBack(size_t idx)
|
||||
void TileStorage::moveToFront(const size_t *indices, size_t n)
|
||||
{
|
||||
_moveToBack(idx);
|
||||
refreshAll();
|
||||
if(n)
|
||||
{
|
||||
_moveToFront(indices, n);
|
||||
refreshAll();
|
||||
}
|
||||
}
|
||||
|
||||
void TileStorage::moveToBack(const size_t *indices, size_t n)
|
||||
{
|
||||
if(n)
|
||||
{
|
||||
_moveToBack(indices, n);
|
||||
refreshAll();
|
||||
}
|
||||
}
|
||||
|
||||
void TileStorage::update(float dt)
|
||||
|
@ -44,24 +59,58 @@ void TileStorage::doInteraction(const Vector& pos, const Vector& vel, float mult
|
|||
}
|
||||
}
|
||||
|
||||
void TileStorage::_moveToFront(size_t idx)
|
||||
void TileStorage::_moveToFront(const size_t *indices, size_t n)
|
||||
{
|
||||
// move tile to front -> move it to the back of the list, to be rendered last aka on top of everything else
|
||||
TileData tile = tiles[idx];
|
||||
tiles.erase(tiles.begin() + idx);
|
||||
tiles.push_back(tile);
|
||||
|
||||
if(n == 1)
|
||||
{
|
||||
TileData tile = tiles[*indices];
|
||||
tiles.erase(tiles.begin() + *indices);
|
||||
tiles.push_back(tile);
|
||||
return;
|
||||
}
|
||||
|
||||
_moveToPos(size(), indices, n);
|
||||
}
|
||||
|
||||
void TileStorage::_moveToBack(size_t idx)
|
||||
void TileStorage::_moveToBack(const size_t *indices, size_t n)
|
||||
{
|
||||
// move tile to back -> move it to the front of the list, to be rendered first aka underneath everything else
|
||||
TileData tile = tiles[idx];
|
||||
tiles.erase(tiles.begin() + idx);
|
||||
tiles.insert(tiles.begin(), tile);
|
||||
|
||||
if(n == 1)
|
||||
{
|
||||
TileData tile = tiles[*indices];
|
||||
tiles.erase(tiles.begin() + *indices);
|
||||
tiles.insert(tiles.begin(), tile);
|
||||
return;
|
||||
}
|
||||
|
||||
_moveToPos(0, indices, n);
|
||||
}
|
||||
|
||||
void TileStorage::moveToOther(TileStorage& other, const size_t *indices, size_t n)
|
||||
void TileStorage::_moveToPos(size_t where, const size_t * indices, size_t n)
|
||||
{
|
||||
std::vector<size_t> tmp(indices, indices + n);
|
||||
std::sort(tmp.begin(), tmp.end());
|
||||
|
||||
std::vector<TileData> tt(n);
|
||||
|
||||
// sorted indices -> preserve relative order of tiles
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
tt[i] = tiles[tmp[i]];
|
||||
|
||||
// SORTED indices, erasing from the BACK -> we don't get a destructive index shift
|
||||
for(size_t i = tmp.size(); i --> 0; )
|
||||
tiles.erase(tiles.begin() + tmp[i]);
|
||||
|
||||
tiles.insert(tiles.begin() + where, tt.begin(), tt.end());
|
||||
}
|
||||
|
||||
size_t TileStorage::moveToOther(TileStorage& other, const size_t *indices, size_t n)
|
||||
{
|
||||
const size_t firstNewIdx = other.tiles.size();
|
||||
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
other.tiles.push_back(tiles[indices[i]]);
|
||||
|
||||
|
@ -81,6 +130,7 @@ void TileStorage::moveToOther(TileStorage& other, const size_t *indices, size_t
|
|||
|
||||
refreshAll();
|
||||
other.refreshAll();
|
||||
return firstNewIdx;
|
||||
}
|
||||
|
||||
static void dropAttachments(TileData& t)
|
||||
|
@ -133,13 +183,51 @@ void TileStorage::setTag(unsigned tag, const size_t* indices, size_t n)
|
|||
// don't need to refresh here
|
||||
}
|
||||
|
||||
void TileStorage::setEffect(int idx, const size_t* indices, size_t n)
|
||||
void TileStorage::setEffect(const TileEffectStorage& effstore, int idx, const size_t* indices, size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
effstore.assignEffect(tiles[indices[i]], idx);
|
||||
refreshAll();
|
||||
}
|
||||
|
||||
void TileStorage::changeFlags(unsigned flagsToSet, unsigned flagsToUnset, const size_t* indices, size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
{
|
||||
unsigned& f = tiles[indices[i]].flags;
|
||||
unsigned tmp = f & ~flagsToUnset;
|
||||
f = tmp | flagsToSet;
|
||||
}
|
||||
}
|
||||
|
||||
size_t TileStorage::cloneSome(const TileEffectStorage& effstore, const size_t* indices, size_t n)
|
||||
{
|
||||
const size_t ret = tiles.size(); // new starting index of clone tiles
|
||||
|
||||
// cloning tiles is very simple, but owned pointers will be duplicated and need to be fixed up
|
||||
const size_t N = ret + n;
|
||||
tiles.resize(N);
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
tiles[ret + i] = tiles[indices[i]];
|
||||
|
||||
// cleanup pointers
|
||||
for(size_t i = ret; i < N; ++i) // loop only over newly added tiles
|
||||
{
|
||||
TileData& t = tiles[i];
|
||||
if((t.flags & TILEFLAG_OWN_EFFDATA) && t.eff)
|
||||
{
|
||||
int efx = t.eff->efxidx;
|
||||
t.eff = NULL; // not our pointer, just pretend it was never there
|
||||
t.flags &= TILEFLAG_OWN_EFFDATA;
|
||||
effstore.assignEffect(t, efx); // recreate effect properly
|
||||
}
|
||||
}
|
||||
|
||||
refreshAll();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void TileStorage::refreshAll()
|
||||
{
|
||||
indicesToCollide.clear();
|
||||
|
@ -149,13 +237,16 @@ void TileStorage::refreshAll()
|
|||
for(size_t i = 0; i < n; ++i)
|
||||
{
|
||||
const TileData& t = tiles[i];
|
||||
if(const TileEffectData *e = t.eff)
|
||||
if(!(t.flags & TILEFLAG_HIDDEN))
|
||||
{
|
||||
if(t.flags & TILEFLAG_OWN_EFFDATA)
|
||||
if(const TileEffectData *e = t.eff)
|
||||
{
|
||||
indicesToUpdate.push_back(i);
|
||||
if(e->efxtype == EFX_WAVY)
|
||||
indicesToCollide.push_back(i);
|
||||
if(t.flags & TILEFLAG_OWN_EFFDATA)
|
||||
{
|
||||
indicesToUpdate.push_back(i);
|
||||
if(e->efxtype == EFX_WAVY)
|
||||
indicesToCollide.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,10 +261,14 @@ void TileStorage::clearSelection()
|
|||
|
||||
TileEffectData::TileEffectData(const TileEffectConfig& cfg)
|
||||
: efxtype(cfg.type), efxidx(cfg.index)
|
||||
, grid(NULL), blend(BLEND_DEFAULT)
|
||||
, grid(NULL), alpha(1), blend(BLEND_DEFAULT)
|
||||
{
|
||||
switch(cfg.type)
|
||||
{
|
||||
case EFX_NONE:
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
case EFX_WAVY:
|
||||
{
|
||||
float bity = 20; // FIXME
|
||||
|
@ -184,7 +279,7 @@ TileEffectData::TileEffectData(const TileEffectConfig& cfg)
|
|||
|
||||
RenderGrid *g = new RenderGrid(2, cfg.u.wavy.segsy);
|
||||
grid = g;
|
||||
g->gridType = GRID_UNDEFINED; // by default it's GRID_WAVY, but that would reset during update
|
||||
g->gridType = GRID_UNDEFINED; // we do the grid update manually
|
||||
|
||||
wavy.angleOffset = 0;
|
||||
wavy.magnitude = 0;
|
||||
|
@ -328,6 +423,15 @@ void TileEffectData::doInteraction(const TileData& t, const Vector& pos, const V
|
|||
}
|
||||
}
|
||||
|
||||
TileEffectStorage::TileEffectStorage()
|
||||
{
|
||||
}
|
||||
|
||||
TileEffectStorage::~TileEffectStorage()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void TileEffectStorage::assignEffect(TileData& t, int index) const
|
||||
{
|
||||
dropAttachments(t);
|
||||
|
@ -343,6 +447,9 @@ void TileEffectStorage::assignEffect(TileData& t, int index) const
|
|||
}
|
||||
else if(idx < configs.size())
|
||||
{
|
||||
if(configs[idx].type == EFX_NONE)
|
||||
return;
|
||||
|
||||
t.eff = new TileEffectData(configs[idx]);
|
||||
t.flags |= TILEFLAG_OWN_EFFDATA;
|
||||
}
|
||||
|
@ -354,3 +461,49 @@ void TileEffectStorage::update(float dt)
|
|||
if(TileEffectData *eff = prepared[i])
|
||||
eff->update(dt, NULL);
|
||||
}
|
||||
|
||||
void TileEffectStorage::clear()
|
||||
{
|
||||
clearPrepared();
|
||||
configs.clear();
|
||||
}
|
||||
|
||||
void TileEffectStorage::clearPrepared()
|
||||
{
|
||||
for(size_t i = 0; i < prepared.size(); ++i)
|
||||
delete prepared[i];
|
||||
prepared.clear();
|
||||
}
|
||||
|
||||
|
||||
void TileEffectStorage::finalize()
|
||||
{
|
||||
clearPrepared();
|
||||
prepared.resize(configs.size(), (TileEffectData*)NULL);
|
||||
|
||||
for(size_t i = 0; i < configs.size(); ++i)
|
||||
{
|
||||
TileEffectConfig& c = configs[i];
|
||||
|
||||
c.index = unsigned(i); // just in case
|
||||
|
||||
// segs and alpha are independent of the tile they are applied to,
|
||||
// so we can create shared instances of the effect.
|
||||
if(c.type == EFX_SEGS || c.type == EFX_ALPHA)
|
||||
prepared[i] = new TileEffectData(c);
|
||||
}
|
||||
}
|
||||
|
||||
bool TileData::isCoordinateInside(float cx, float cy, float minsize) const
|
||||
{
|
||||
|
||||
float hw = fabsf(et->w * scalex)*0.5f;
|
||||
float hh = fabsf(et->h * scaley)*0.5f;
|
||||
if (hw < minsize)
|
||||
hw = minsize;
|
||||
if (hh < minsize)
|
||||
hh = minsize;
|
||||
|
||||
return cx >= x - hw && cx <= x + hw
|
||||
&& cy >= y - hh && cy <= y + hh;
|
||||
}
|
||||
|
|
84
BBGE/Tile.h
84
BBGE/Tile.h
|
@ -28,8 +28,13 @@ Further observations:
|
|||
And on map reload everything is back to the same value for each tile with the same effect and params.
|
||||
So we can totally exclude the editor.
|
||||
|
||||
Assumptions:
|
||||
- Most tiles that exist are going to be rendered
|
||||
- Only few tiles have an effect attached
|
||||
|
||||
Gotaches:
|
||||
- Keeping a pointer to a TileData is not safe.
|
||||
- Tile indexes are not stable. Moving a tile changes the index it can be addressed with
|
||||
*/
|
||||
|
||||
class ElementTemplate;
|
||||
|
@ -39,12 +44,13 @@ class TileRender;
|
|||
|
||||
enum EFXType
|
||||
{
|
||||
EFX_NONE,
|
||||
EFX_SEGS,
|
||||
EFX_ALPHA,
|
||||
EFX_WAVY
|
||||
};
|
||||
|
||||
// static configuration for one effect type
|
||||
// static configuration for one effect type. POD.
|
||||
struct TileEffectConfig
|
||||
{
|
||||
public:
|
||||
|
@ -85,9 +91,10 @@ enum TileFlags
|
|||
TILEFLAG_SOLID_IN = 0x08, // instead of OT_INVISIBLE, generate OT_INVISIBLEIN
|
||||
TILEFLAG_HURT = 0x10, // always generate OT_HURT
|
||||
TILEFLAG_FH = 0x20, // flipped horizontally
|
||||
TILEFLAG_OWN_EFFDATA = 0x40, // tile owns its TileEffectData, can modify & must delete
|
||||
TILEFLAG_OWN_EFFDATA = 0x40, // tile owns its TileEffectData, can update, must delete
|
||||
TILEFLAG_HIDDEN = 0x80, // don't render tile
|
||||
TILEFLAG_SELECTED = 0x100
|
||||
TILEFLAG_SELECTED = 0x100, // ephemeral: selected in editor
|
||||
TILEFLAG_EDITOR_HIDDEN = 0x200 // tile is hidden for editor reasons. temporarily set when multi-selecting and moving. doesn't count as hidden externally and is only for rendering.
|
||||
};
|
||||
|
||||
struct TileData;
|
||||
|
@ -100,7 +107,7 @@ struct TileEffectData
|
|||
void doInteraction(const TileData& t, const Vector& pos, const Vector& vel, float mult, float touchWidth);
|
||||
|
||||
const EFXType efxtype;
|
||||
const unsigned efxidx; // index to ElementEffect
|
||||
const unsigned efxidx; // index of TileEffect
|
||||
RenderGrid *grid;
|
||||
InterpolatedVector alpha;
|
||||
BlendType blend;
|
||||
|
@ -116,46 +123,70 @@ struct TileEffectData
|
|||
void update(float dt);
|
||||
};
|
||||
Wavy wavy;
|
||||
|
||||
private:
|
||||
TileEffectData(const TileEffectData&); // no-copy
|
||||
};
|
||||
|
||||
// POD and as compact as possible
|
||||
// POD and as compact as possible. Intended for rendering as quickly as possible.
|
||||
// the idea is that these are linearly adjacent in memory in the order they are rendered,
|
||||
// to maximize cache & prefetch efficiency
|
||||
struct TileData
|
||||
{
|
||||
float x, y, rotation, texscale;
|
||||
float scalex, scaley;
|
||||
float beforeScaleOffsetX, beforeScaleOffsetY;
|
||||
float x, y, scalex, scaley, texscaleX, texscaleY;
|
||||
float beforeScaleOffsetX, beforeScaleOffsetY; // almost always 0. // TODO: this is nasty, ideally get rid of this
|
||||
float rotation;
|
||||
unsigned flags; // TileFlags
|
||||
unsigned tag;
|
||||
ElementTemplate *et; // texture, texcoords, etc is here
|
||||
TileEffectData *eff;
|
||||
unsigned tag; // FIXME: make this int
|
||||
const ElementTemplate *et; // never NULL. texture, texcoords, etc is here. // TODO: maybe replace with unsigned tilesetID? but that's an extra indirection or two during rendering...
|
||||
TileEffectData *eff; // mostly NULL
|
||||
|
||||
// helpers for external access
|
||||
inline void setVisible(bool on) { if(on) flags &= ~TILEFLAG_HIDDEN; else flags |= TILEFLAG_HIDDEN; }
|
||||
inline bool isVisible() const { return !(flags & TILEFLAG_HIDDEN); }
|
||||
bool isCoordinateInside(float cx, float cy, float minsize = 0) const;
|
||||
};
|
||||
|
||||
class TileEffectStorage
|
||||
{
|
||||
public:
|
||||
TileEffectStorage();
|
||||
~TileEffectStorage();
|
||||
void finalize(); // first fill configs[], then call this
|
||||
void assignEffect(TileData& t, int index) const;
|
||||
void update(float dt);
|
||||
void clear(); // do NOT call this while there are tiles that may reference one in prepared[]
|
||||
|
||||
std::vector<TileEffectData*> prepared;
|
||||
std::vector<TileEffectConfig> configs;
|
||||
|
||||
private:
|
||||
void clearPrepared();
|
||||
std::vector<TileEffectData*> prepared;
|
||||
|
||||
TileEffectStorage(const TileEffectStorage&); // no-copy
|
||||
};
|
||||
|
||||
class TileStorage
|
||||
{
|
||||
friend class TileRender;
|
||||
public:
|
||||
TileStorage(const TileEffectStorage& eff);
|
||||
TileStorage();
|
||||
~TileStorage();
|
||||
|
||||
void moveToFront(size_t idx);
|
||||
void moveToBack(size_t idx);
|
||||
void moveToOther(TileStorage& other, const size_t *indices, size_t n);
|
||||
void moveToFront(const size_t *indices, size_t n);
|
||||
void moveToBack(const size_t *indices, size_t n);
|
||||
|
||||
// returns starting index of new tiles. Since new tiles are always appended at the end,
|
||||
// the new indices corresponding to the moved tiles are [retn .. retn+n)
|
||||
size_t moveToOther(TileStorage& other, const size_t *indices, size_t n);
|
||||
size_t cloneSome(const TileEffectStorage& effstore, const size_t *indices, size_t n);
|
||||
|
||||
void deleteSome(const size_t *indices, size_t n);
|
||||
|
||||
void setTag(unsigned tag, const size_t *indices, size_t n);
|
||||
void setEffect(int idx, const size_t *indices, size_t n);
|
||||
void setEffect(const TileEffectStorage& effstore, int idx, const size_t *indices, size_t n);
|
||||
|
||||
void changeFlags(unsigned flagsToSet, unsigned flagsToUnset, const size_t *indices, size_t n);
|
||||
|
||||
void update(float dt);
|
||||
void doInteraction(const Vector& pos, const Vector& vel, float mult, float touchWidth);
|
||||
|
@ -164,16 +195,27 @@ public:
|
|||
|
||||
void clearSelection();
|
||||
|
||||
struct Sizes
|
||||
{
|
||||
size_t tiles, update, collide;
|
||||
};
|
||||
Sizes stats() const;
|
||||
size_t size() const { return tiles.size(); }
|
||||
|
||||
|
||||
std::vector<TileData> tiles; // must call refreshAll() after changing this
|
||||
|
||||
private:
|
||||
|
||||
std::vector<TileData> tiles;
|
||||
std::vector<size_t> indicesToUpdate;
|
||||
std::vector<size_t> indicesToCollide;
|
||||
const TileEffectStorage& effstore;
|
||||
|
||||
void _refreshTile(const TileData& t);
|
||||
void _moveToFront(size_t idx);
|
||||
void _moveToBack(size_t idx);
|
||||
void _moveToFront(const size_t *indices, size_t n);
|
||||
void _moveToBack(const size_t *indices, size_t n);
|
||||
void _moveToPos(size_t where, const size_t *indices, size_t n);
|
||||
|
||||
TileStorage(const TileStorage&); // no-copy
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
TileRender::TileRender(const TileStorage& tiles)
|
||||
: storage(tiles), renderBorders(false)
|
||||
{
|
||||
this->cull = false;
|
||||
this->neverFollowCamera = true;
|
||||
}
|
||||
|
||||
TileRender::~TileRender()
|
||||
|
@ -46,6 +48,7 @@ void TileRender::onRender(const RenderState& rs) const
|
|||
const RenderObjectLayer& rl = core->renderObjectLayers[this->layer];
|
||||
const Vector M = rl.followCameraMult; // affected by parallaxLock
|
||||
const float F = rl.followCamera;
|
||||
const bool parallax = rl.followCamera > 0;
|
||||
|
||||
// Formula from RenderObject::getFollowCameraPosition() and optimized for speed
|
||||
const Vector C = core->screenCenter;
|
||||
|
@ -61,15 +64,18 @@ void TileRender::onRender(const RenderState& rs) const
|
|||
for(size_t i = 0; i < storage.tiles.size(); ++i)
|
||||
{
|
||||
const TileData& tile = storage.tiles[i];
|
||||
if(tile.flags & TILEFLAG_HIDDEN)
|
||||
if(tile.flags & (TILEFLAG_HIDDEN | TILEFLAG_EDITOR_HIDDEN))
|
||||
continue;
|
||||
|
||||
const Vector tilepos(tile.x, tile.y);
|
||||
const Vector tmp = T + (F * tilepos);
|
||||
const Vector pos = tilepos * M1 + (tmp * M); // lerp, used to select whether to use original v or parallax-corrected v
|
||||
Vector pos(tile.x, tile.y);
|
||||
if(parallax)
|
||||
{
|
||||
const Vector tmp = T + (F * pos);
|
||||
pos = pos * M1 + (tmp * M); // lerp, used to select whether to use original v or parallax-corrected v
|
||||
}
|
||||
|
||||
ElementTemplate * const et = tile.et;
|
||||
if(Texture * const tex = et->tex.content())
|
||||
const ElementTemplate * const et = tile.et;
|
||||
if(const Texture * const tex = et->tex.content())
|
||||
{
|
||||
unsigned texid = tex->gltexid;
|
||||
unsigned rep = tile.flags & TILEFLAG_REPEAT;
|
||||
|
@ -96,7 +102,7 @@ void TileRender::onRender(const RenderState& rs) const
|
|||
// this is only relevant in editor mode and is always 0 otherwise
|
||||
glTranslatef(tile.beforeScaleOffsetX, tile.beforeScaleOffsetY, 0);
|
||||
|
||||
glScalef(tile.scalex * et->w, tile.scaley * et->h, 1);
|
||||
glScalef(tile.scalex, tile.scaley, 1);
|
||||
//glScalef(tile.scalex * et->w, tile.scaley * et->h, 1); // TODO use this + fixed verts
|
||||
|
||||
BlendType blend = BLEND_DEFAULT;
|
||||
|
@ -141,6 +147,9 @@ void TileRender::onRender(const RenderState& rs) const
|
|||
}
|
||||
else
|
||||
{
|
||||
glPushMatrix();
|
||||
glScalef(et->w, et->h, 1);
|
||||
|
||||
RenderState rx(rs);
|
||||
rx.alpha = alpha;
|
||||
grid->render(rx, upperLeftTextureCoordinates, lowerRightTextureCoordinates);
|
||||
|
@ -150,6 +159,8 @@ void TileRender::onRender(const RenderState& rs) const
|
|||
grid->renderDebugPoints(rs);
|
||||
lastTexId = 0;
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
if(renderBorders)
|
||||
|
@ -183,7 +194,11 @@ void TileRender::onRender(const RenderState& rs) const
|
|||
glPopMatrix();
|
||||
}
|
||||
|
||||
|
||||
RenderObject::lastTextureApplied = lastTexId;
|
||||
RenderObject::lastTextureRepeat = !!lastTexRepeat;
|
||||
}
|
||||
|
||||
void TileRender::onUpdate(float dt)
|
||||
{
|
||||
//this->position = core->screenCenter;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ public:
|
|||
|
||||
TileRender(const TileStorage& tiles);
|
||||
virtual ~TileRender();
|
||||
virtual void onRender(const RenderState& rs) const;
|
||||
virtual void onRender(const RenderState& rs) const OVERRIDE;
|
||||
virtual void onUpdate(float dt) OVERRIDE;
|
||||
|
||||
bool renderBorders;
|
||||
|
||||
|
|
190
BBGE/Tileset.cpp
190
BBGE/Tileset.cpp
|
@ -5,6 +5,15 @@
|
|||
#include "TextureMgr.h"
|
||||
#include "Core.h"
|
||||
|
||||
Tileset::Tileset()
|
||||
{
|
||||
}
|
||||
|
||||
Tileset::~Tileset()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
bool Tileset::loadFile(const char *fn, const unsigned char *usedIdx, size_t usedIdxLen)
|
||||
{
|
||||
elementTemplates.clear();
|
||||
|
@ -13,24 +22,34 @@ bool Tileset::loadFile(const char *fn, const unsigned char *usedIdx, size_t used
|
|||
if(!in)
|
||||
return false;
|
||||
|
||||
bool warn = false;
|
||||
std::string line, gfx;
|
||||
while (std::getline(in, line))
|
||||
{
|
||||
gfx.clear();
|
||||
int idx=-1, w=0, h=0;
|
||||
SimpleIStringStream is(line.c_str(), SimpleIStringStream::REUSE);
|
||||
is >> idx >> gfx >> w >> h;
|
||||
if(idx >= 0)
|
||||
if(idx >= 0 && !gfx.empty())
|
||||
{
|
||||
ElementTemplate t;
|
||||
t.idx = idx;
|
||||
t.gfx = gfx;
|
||||
t.w = w;
|
||||
t.h = h;
|
||||
elementTemplates.push_back(t);
|
||||
if(idx < 1024)
|
||||
{
|
||||
ElementTemplate t;
|
||||
t.idx = idx;
|
||||
t.gfx = gfx;
|
||||
t.w = w;
|
||||
t.h = h;
|
||||
elementTemplates.push_back(t);
|
||||
}
|
||||
else
|
||||
warn = true;
|
||||
}
|
||||
}
|
||||
in.close();
|
||||
|
||||
if(warn)
|
||||
errorLog("Tileset indices of 1024 and above are reserved; ignored during load");
|
||||
|
||||
std::sort(elementTemplates.begin(), elementTemplates.end());
|
||||
|
||||
// begin preloading textures
|
||||
|
@ -48,47 +67,174 @@ bool Tileset::loadFile(const char *fn, const unsigned char *usedIdx, size_t used
|
|||
// drop duplicates
|
||||
usedTex.resize(std::distance(usedTex.begin(), std::unique(usedTex.begin(), usedTex.end())));
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Loading " << usedTex.size()
|
||||
<< " used textures out of the " << elementTemplates.size() << " tileset entries";
|
||||
debugLog(os.str());
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Loading " << usedTex.size()
|
||||
<< " used textures out of the " << elementTemplates.size() << " tileset entries";
|
||||
debugLog(os.str());
|
||||
}
|
||||
|
||||
// preload all used textures
|
||||
size_t loaded = 0;
|
||||
if(usedTex.size())
|
||||
core->texmgr.loadBatch(NULL, &usedTex[0], usedTex.size());
|
||||
loaded = core->texmgr.loadBatch(NULL, &usedTex[0], usedTex.size());
|
||||
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Loaded " << loaded << " textures successfully";
|
||||
debugLog(os.str());
|
||||
}
|
||||
|
||||
// finalize
|
||||
size_t nfailed = 0;
|
||||
std::ostringstream failed;
|
||||
for (size_t i = 0; i < elementTemplates.size(); i++)
|
||||
{
|
||||
ElementTemplate& et = elementTemplates[i];
|
||||
// only check those that are actualy loaded; otherwise this would load in textures
|
||||
// that we didn't bother to batch-load above
|
||||
if(!usedIdx || (et.idx < usedIdxLen && usedIdx[et.idx]))
|
||||
{
|
||||
if(!et.getTexture()) // assigns width/height and caches texture pointer
|
||||
{
|
||||
++nfailed;
|
||||
failed << et.gfx << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(nfailed)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "The following " << nfailed << " textures failed to load and would be used by tiles:";
|
||||
debugLog(os.str());
|
||||
debugLog(failed.str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tileset::clear()
|
||||
{
|
||||
for(size_t i = 0; i < dummies.size(); ++i)
|
||||
delete dummies[i];
|
||||
dummies.clear();
|
||||
elementTemplates.clear();
|
||||
}
|
||||
|
||||
ElementTemplate *Tileset::getByIdx(size_t idx)
|
||||
const ElementTemplate *Tileset::getByIdx(size_t idx)
|
||||
{
|
||||
for (size_t i = 0; i < elementTemplates.size(); i++)
|
||||
{
|
||||
if (elementTemplates[i].idx == idx)
|
||||
ElementTemplate& et = elementTemplates[i];
|
||||
if (et.idx == idx)
|
||||
{
|
||||
return &elementTemplates[i];
|
||||
et.getTexture(); // HACK: make sure the texture is loaded before this gets used
|
||||
return &et;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
// a tile that gets an ET attached must remember its tileset id even if the entry is not present
|
||||
// in the tileset. since the tile does not store the idx as an integer, we need to return a dummy element.
|
||||
for (size_t i = 0; i < dummies.size(); i++)
|
||||
{
|
||||
ElementTemplate *et = dummies[i];
|
||||
if (et->idx == idx)
|
||||
return et;
|
||||
}
|
||||
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Tileset idx " << idx << " not found, creating dummy";
|
||||
debugLog(os.str());
|
||||
}
|
||||
|
||||
ElementTemplate *dummy = new ElementTemplate;
|
||||
dummy->idx = idx;
|
||||
dummies.push_back(dummy);
|
||||
|
||||
return dummy;
|
||||
}
|
||||
|
||||
const ElementTemplate* Tileset::getAdjacent(size_t idx, int direction, bool wraparound)
|
||||
{
|
||||
ElementTemplate *et = _getAdjacent(idx, direction, wraparound);
|
||||
if(et)
|
||||
et->getTexture(); // load just in case
|
||||
return et;
|
||||
}
|
||||
|
||||
Texture* ElementTemplate::getTexture()
|
||||
{
|
||||
if(tex)
|
||||
if(loaded)
|
||||
return tex.content();
|
||||
|
||||
tex = core->getTexture(gfx);
|
||||
if(!w)
|
||||
w = tex->width;
|
||||
if(!h)
|
||||
h = tex->height;
|
||||
loaded = true;
|
||||
tex = core->getTexture(gfx); // may end up NULL
|
||||
if(tex)
|
||||
{
|
||||
if(!w)
|
||||
w = tex->width;
|
||||
if(!h)
|
||||
h = tex->height;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!w)
|
||||
w = 64;
|
||||
if(!h)
|
||||
h = 64;
|
||||
}
|
||||
|
||||
return tex.content();
|
||||
|
||||
}
|
||||
|
||||
ElementTemplate * Tileset::_getAdjacent(size_t idx, int direction, bool wraparound)
|
||||
{
|
||||
assert(direction == 1 || direction == -1);
|
||||
const size_t maxn = elementTemplates.size();
|
||||
size_t closest = 0;
|
||||
int mindiff = 0;
|
||||
for (size_t i = 0; i < maxn; i++)
|
||||
{
|
||||
if (elementTemplates[i].idx == idx)
|
||||
{
|
||||
if(wraparound)
|
||||
{
|
||||
if(!i && direction < 0)
|
||||
return &elementTemplates.back();
|
||||
if(i + direction >= maxn)
|
||||
return &elementTemplates[0];
|
||||
}
|
||||
else
|
||||
|
||||
i += direction; // may underflow
|
||||
return i < maxn ? &elementTemplates[i] : NULL;
|
||||
}
|
||||
int diff = labs((int)elementTemplates[i].idx - (int)idx);
|
||||
if(diff < mindiff || !mindiff)
|
||||
{
|
||||
mindiff = diff;
|
||||
closest = i;
|
||||
}
|
||||
}
|
||||
|
||||
// not found? pick whatever was closest to the non-existing idx, and go back/forward from there
|
||||
|
||||
// avoid going "twice" in the given direction
|
||||
if(closest < idx && direction < 0)
|
||||
direction = 0; // this is already a step back, don't step again
|
||||
else if(closest > idx && direction > 0)
|
||||
direction = 0; // this is already a step forward, don't step again
|
||||
else if(wraparound)
|
||||
{
|
||||
if(!closest && direction < 0)
|
||||
return &elementTemplates.back();
|
||||
if(closest + direction >= maxn)
|
||||
return &elementTemplates[0];
|
||||
}
|
||||
|
||||
size_t i = closest + direction;
|
||||
return i < maxn ? &elementTemplates[i] : NULL;
|
||||
}
|
||||
|
|
|
@ -8,32 +8,47 @@
|
|||
class ElementTemplate
|
||||
{
|
||||
public:
|
||||
ElementTemplate() { w=0; h=0; idx=-1; tu1=tv1=0; tu2=tv2=1; }
|
||||
ElementTemplate() { w=0; h=0; idx=-1; tu1=tv1=0; tu2=tv2=1; loaded=false; }
|
||||
inline bool operator<(const ElementTemplate& o) const { return idx < o.idx; }
|
||||
|
||||
Texture *getTexture(); // loads if not already loaded
|
||||
|
||||
// lazily assigned when tex is loaded
|
||||
CountedPtr<Texture> tex;
|
||||
CountedPtr<Texture> tex; // NULL if failed to load or not yet loaded
|
||||
float w,h; // custom size if used, otherwise texture size
|
||||
|
||||
// fixed
|
||||
float tu1, tu2, tv1, tv2; // texcoords
|
||||
size_t idx;
|
||||
std::string gfx;
|
||||
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
class Tileset
|
||||
{
|
||||
public:
|
||||
Tileset();
|
||||
~Tileset();
|
||||
|
||||
// pass usedIdx == NULL to preload all textures from tileset
|
||||
// pass usedIdx != NULL to preload only textures where usedIdx[i] != 0
|
||||
bool loadFile(const char *fn, const unsigned char *usedIdx, size_t usedIdxLen);
|
||||
void clear();
|
||||
|
||||
ElementTemplate *getByIdx(size_t idx);
|
||||
// return valid ET if found, or creates a dummy if not. never returns NULL.
|
||||
const ElementTemplate *getByIdx(size_t idx);
|
||||
|
||||
// search for non-dummy ET in a given direction. used to cycle through ETs.
|
||||
// never returns dummy ET. May return NULL.
|
||||
const ElementTemplate *getAdjacent(size_t idx, int direction, bool wraparound);
|
||||
|
||||
std::vector<ElementTemplate> elementTemplates;
|
||||
|
||||
private:
|
||||
ElementTemplate *_getAdjacent(size_t idx, int direction, bool wraparound);
|
||||
|
||||
std::vector<ElementTemplate*> dummies;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue