1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-11-25 09:44:02 +00:00

wip towards Element draw refactor; still using old render path

This commit is contained in:
fgenesis 2023-07-10 02:29:21 +02:00
parent 578c8ecc45
commit c09098e13c
12 changed files with 444 additions and 151 deletions

View file

@ -318,14 +318,7 @@ void Game::transitionToScene(std::string scene)
ElementTemplate *Game::getElementTemplateByIdx(size_t idx)
{
for (size_t i = 0; i < elementTemplates.size(); i++)
{
if (elementTemplates[i].idx == idx)
{
return &elementTemplates[i];
}
}
return 0;
return tileset.getByIdx(idx);
}
Element* Game::createElement(size_t idx, Vector position, size_t bgLayer, RenderObject *copy, ElementTemplate *et)
@ -338,8 +331,7 @@ Element* Game::createElement(size_t idx, Vector position, size_t bgLayer, Render
Element *element = new Element();
if (et)
{
element->setTexture(et->gfx);
element->alpha = et->alpha;
element->setTexturePointer(et->getTexture());
}
element->position = position;
@ -377,7 +369,6 @@ Element* Game::createElement(size_t idx, Vector position, size_t bgLayer, Render
}
addRenderObject(element, LR_ELEMENTS1+bgLayer);
dsq->addElement(element);
//element->updateCullVariables();
return element;
}
@ -1335,6 +1326,8 @@ bool Game::loadSceneXML(std::string scene)
}
clearObsRows();
std::string tilesetToLoad;
XMLElement *level = doc.FirstChildElement("Level");
if (level)
{
@ -1344,7 +1337,7 @@ bool Game::loadSceneXML(std::string scene)
tileset = level->Attribute("elementTemplatePack"); // legacy, still present in some very old maps
if (tileset)
{
loadElementTemplates(tileset);
tilesetToLoad = tileset;
levelSF->SetAttribute("tileset", tileset);
}
else
@ -1756,6 +1749,7 @@ bool Game::loadSceneXML(std::string scene)
if(fullTilesetReload)
{
fullTilesetReload = false;
tileset.clear();
// used by SceneEditor
// no elements exist right now -> textures will be cleared and reloaded
dsq->texmgr.clearUnused();
@ -1763,6 +1757,10 @@ bool Game::loadSceneXML(std::string scene)
// figure out which textures in the tileset are used and preload those that are actually used
{
std::ostringstream os;
os << "Scene has " << elemsDefs.size() << " elements";
debugLog(os.str());
unsigned char usedIdx[1024] = {0};
for(size_t i = 0; i < elemsDefs.size(); ++i)
{
@ -1770,27 +1768,8 @@ bool Game::loadSceneXML(std::string scene)
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());
loadElementTemplates(tilesetToLoad, &usedIdx[0], Countof(usedIdx));
}
// Now that all SE tags have been processed, spawn them
@ -4690,79 +4669,55 @@ void Game::snapCam()
warpCameraTo(*cameraFollow);
}
ElementTemplate Game::getElementTemplateForLetter(int i)
{
float cell = 64.0f/512.0f;
//for (int i = 0; i < 27; i++)
ElementTemplate t;
t.idx = 1024+i;
t.gfx = "Aquarian";
int x = i,y=0;
while (x >= 6)
{
x -= 6;
y++;
}
t.tu1 = x*cell;
t.tv1 = y*cell;
t.tu2 = t.tu1 + cell;
t.tv2 = t.tv1 + cell;
t.tv2 = 1 - t.tv2;
t.tv1 = 1 - t.tv1;
std::swap(t.tv1,t.tv2);
t.w = 512*cell;
t.h = 512*cell;
//elementTemplates.push_back(t);
return t;
}
void Game::loadElementTemplates(std::string pack)
bool Game::loadElementTemplates(std::string pack, const unsigned char *usedIdx, size_t usedIdxLen)
{
stringToLower(pack);
elementTemplates.clear();
std::string fn;
if (dsq->mod.isActive())
fn = dsq->mod.getPath() + "tilesets/" + pack + ".txt";
else
fn = "data/tilesets/" + pack + ".txt";
if (!exists(fn))
if(!tileset.loadFile(fn.c_str(), usedIdx, usedIdxLen))
{
errorLog ("Could not open tileset [" + fn + "]");
return;
errorLog ("Could not load tileset [" + fn + "]");
return false;
}
InStream in(fn.c_str());
std::string line;
while (std::getline(in, line))
// Aquarian alphabet letters
if(const CountedPtr<Texture> aqtex = dsq->getTexture("aquarian"))
{
int idx=-1, w=-1, h=-1;
std::string gfx;
std::istringstream is(line);
is >> idx >> gfx >> w >> h;
ElementTemplate t;
t.idx = idx;
t.gfx = gfx;
if (w==0) w=-1;
if (h==0) h=-1;
t.w = w;
t.h = h;
elementTemplates.push_back(t);
const float cell = 64.0f/512.0f;
for (int i = 0; i < 27; i++)
{
ElementTemplate t;
t.idx = 1024+i;
t.tex = aqtex;
int x = i,y=0;
while (x >= 6)
{
x -= 6;
y++;
}
t.tu1 = x*cell;
t.tv1 = y*cell;
t.tu2 = t.tu1 + cell;
t.tv2 = t.tv1 + cell;
t.tv2 = 1 - t.tv2;
t.tv1 = 1 - t.tv1;
std::swap(t.tv1,t.tv2);
t.w = 512*cell;
t.h = 512*cell;
tileset.elementTemplates.push_back(t);
}
}
in.close();
std::sort(elementTemplates.begin(), elementTemplates.end());
for (int i = 0; i < 27; i++)
{
elementTemplates.push_back(getElementTemplateForLetter(i));
}
return true;
}
void Game::clearGrid(int v)
@ -4778,40 +4733,6 @@ void Game::resetFromTitle()
overrideMusic = "";
}
void Game::setGrid(ElementTemplate *et, Vector position, float rot360)
{
for (size_t i = 0; i < et->grid.size(); i++)
{
TileVector t(position);
int x = et->grid[i].x;
int y = et->grid[i].y;
if (rot360 >= 0 && rot360 < 90)
{
}
else if (rot360 >= 90 && rot360 < 180)
{
int swap = y;
y = x;
x = swap;
x = -x;
}
else if (rot360 >= 180 && rot360 < 270)
{
x = -x;
y = -y;
}
else if (rot360 >= 270 && rot360 < 360)
{
int swap = y;
y = x;
x = swap;
y = -y;
}
TileVector s(t.x+x, t.y+y);
setGrid(s, OT_INVISIBLE);
}
}
void Game::removeState()
{
const float fadeTime = 0.25;

View file

@ -28,6 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "ScriptedEntity.h"
#include "TileVector.h"
#include "SceneEditor.h"
#include "Tileset.h"
#include <tinyxml2.h>
using namespace tinyxml2;
@ -84,20 +85,6 @@ struct MinimapIcon
typedef std::list<Ingredient*> Ingredients;
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;
float tu1, tu2, tv1, tv2;
bool cull;
float alpha;
size_t idx;
};
class ObsRow
{
public:
@ -171,9 +158,10 @@ public:
InGameMenu *getInGameMenu() { return themenu; }
void loadElementTemplates(std::string pack);
// pass usedIdx == NULL to preload all textures from tileset
// pass usedIdx != NULL to preload only textures where usedIdx[i] != 0
bool loadElementTemplates(std::string pack, const unsigned char *usedIdx, size_t usedIdxLen);
Element* createElement(size_t etidx, Vector position, size_t bgLayer=0, RenderObject *copy=0, ElementTemplate *et=0);
void setGrid(ElementTemplate *et, Vector position, float rot360=0);
void updateParticlePause();
@ -196,7 +184,7 @@ public:
void handleShotCollisionsSkeletal(Entity *e);
void handleShotCollisionsHair(Entity *e, int num = 0, float perc = 0);
std::vector<ElementTemplate> elementTemplates;
Tileset tileset;
std::string sceneName, sceneDisplayName;
ElementTemplate *getElementTemplateByIdx(size_t idx);
@ -362,7 +350,6 @@ public:
void setMusicToPlay(const std::string &musicToPlay);
Vector lastCollidePosition;
void switchBgLoop(int v);
ElementTemplate getElementTemplateForLetter(int i);
CurrentRender *currentRender;
SteamRender *steamRender;
SongLineRender *songLineRender;

View file

@ -23,7 +23,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "../BBGE/Vector.h"
const int TILE_SIZE = 20;
enum { TILE_SIZE = 20 };
class TileVector
{

View file

@ -109,6 +109,12 @@ set(BBGE_SRCS
TextureMgr.h
TTFFont.cpp
TTFFont.h
Tile.cpp
Tile.h
TileRender.cpp
TileRender.h
Tileset.cpp
Tileset.h
Vector.cpp
Vector.h
Window.cpp

View file

@ -205,6 +205,7 @@ public:
// Defined in RenderObject_inline.h
inline Vector getFollowCameraPosition() const;
inline Vector getFollowCameraPosition(const Vector& pos) const;
void lookAt(const Vector &pos, float t, float minAngle, float maxAngle, float offset=0);
inline RenderObject *getParent() const {return parent;}

View file

@ -33,19 +33,42 @@ inline bool RenderObject::isOnScreen() const
}
Vector RenderObject::getFollowCameraPosition() const
{
return getFollowCameraPosition(position);
}
Vector RenderObject::getFollowCameraPosition(const Vector& v) const
{
assert(layer != LR_NONE);
assert(!parent); // this makes no sense when we're not a root object
const RenderObjectLayer &rl = core->renderObjectLayers[layer];
Vector mul = rl.followCameraMult;
float f = followCamera;
if(!f)
f = rl.followCamera;
if (f <= 0)
return position;
Vector M = rl.followCameraMult;
float F = followCamera;
if(!F)
F = rl.followCamera;
if (F <= 0)
return v;
const Vector pos = (position - core->screenCenter) * f + core->screenCenter;
return position * (Vector(1,1) - mul) + (pos * mul); // lerp
/* Originally, not accounting for parallax lock on an axis, this was:
pos = v - core->screenCenter;
pos *= F;
pos = core->screenCenter + pos;
*/
// uppercase are effectively constants that are not per-object
// lowercase are per-object
// more concise math:
//const Vector pos = (v - core->screenCenter) * F + core->screenCenter;
//return v * (Vector(1,1) - M) + (pos * M); // lerp
// optimized and rearranged
const Vector C = core->screenCenter;
const Vector M1 = Vector(1,1) - M;
const Vector T = C * (1 - F);
const Vector pos = T + (F * v);
return v * M1 + (pos * M); // lerp, used to select whether to use original v or parallax-corrected v
}
#endif

2
BBGE/Tile.cpp Normal file
View file

@ -0,0 +1,2 @@
#include "Tile.h"

60
BBGE/Tile.h Normal file
View file

@ -0,0 +1,60 @@
#ifndef BBGE_TILE_H
#define BBGE_TILE_H
#include <vector>
#include "Vector.h"
class ElementTemplate;
class Texture;
// A Tile is a very stripped down RenderObject that bypasses the default
// rendering pipeline for efficiency reasons.
// Use a TileRender to draw a list of Tiles.
/* Properties of tiles:
- Lots of these exist. Need to store & render efficiently
- Usually no dynamic behavior, BUT:
* can have a TileEffect that requires updating (sometimes)
* can react to entities nearby (rare)
* may have a draw grid (wobbly plants etc)
- Only modified in the editor -> Can be slow to modify or reorder
- Render order must be strictly followed to get the correct visual overlapping
- Never part of a parent/child hierarchy, all tiles are standalone
- Does not have offset, internalOffset, gravity, etc etc that RenderObject has
- Parallax scroll factor is solely influenced by layer, not individually
- RGB is never tinted, alpha may come from efx
*/
enum TileFlags
{
TILEFLAG_NONE = 0,
TILEFLAG_REPEAT = 0x01, // texture repeats and uses texscale for the repeat factor
TILEFLAG_SOLID = 0x02, // generates OT_INVISIBLE
TILEFLAG_SOLID_THICK = 0x04, // generates more OT_INVISIBLE
TILEFLAG_SOLID_IN = 0x08, // instead of OT_INVISIBLE, generate OT_INVISIBLEIN
TILEFLAG_HURT = 0x10, // always generate OT_HURT
TILEFLAG_FH = 0x20, // flipped horizontally
};
// sort-of-POD
struct TileData
{
float x, y, rotation, texscale;
Vector scale, beforeScaleOffset;
int efx;
unsigned flags; // TileFlags
unsigned tag;
ElementTemplate *et;
};
class TileStorage
{
public:
std::vector<TileData> tiles;
void refresh(); // call when adding/removing/reordering tiles or changing efx
};
#endif // BBGE_TILE_H

135
BBGE/TileRender.cpp Normal file
View file

@ -0,0 +1,135 @@
#include "TileRender.h"
#include "RenderBase.h"
#include "Core.h"
#include "Tileset.h"
TileRender::TileRender(const TileStorage& tiles)
: storage(tiles)
{
}
TileRender::~TileRender()
{
}
void TileRender::onRender(const RenderState& rs) const
{
// prepare. get parallax scroll factors
const RenderObjectLayer& rl = core->renderObjectLayers[this->layer];
Vector M = rl.followCameraMult; // affected by parallaxLock
const float F = rl.followCamera;
// Formula from RenderObject::getFollowCameraPosition() and optimized for speed
const Vector C = core->screenCenter;
const Vector M1 = Vector(1,1) - M;
const Vector T = C * (1 - F);
unsigned lastTexRepeat = false;
unsigned lastTexId = 0;
BlendType blend = BLEND_DEFAULT; // TODO: influenced by efx
const bool renderBorders = true; // TODO: when layer selected in editor
for(size_t i = 0; i < storage.tiles.size(); ++i)
{
const TileData& tile = storage.tiles[i];
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
rs.gpu.setBlend(blend);
ElementTemplate * const et = tile.et;
if(Texture * const tex = et->tex.content())
{
unsigned texid = tex->gltexid;
unsigned rep = tile.flags & TILEFLAG_REPEAT;
if(texid != lastTexId || rep != lastTexRepeat)
{
lastTexId = texid;
lastTexRepeat = rep;
tex->apply(!!rep);
}
}
else
glBindTexture(GL_TEXTURE_2D, 0); // unlikely
glPushMatrix();
glTranslatef(pos.x, pos.y, pos.z);
glRotatef(tile.rotation, 0, 0, 1);
if(tile.flags & TILEFLAG_FH)
glRotatef(180, 0, 1, 0);
// this is only relevant in editor mode and is always 0 otherwise
glTranslatef(tile.beforeScaleOffset.x, tile.beforeScaleOffset.y, tile.beforeScaleOffset.z);
glScalef(tile.scale.x, tile.scale.y, 1);
// TODO: only need to do this when prev. tile had different alpha
{
const float alpha = 1; // TODO: via efx
Vector col = rs.color;
glColor4f(col.x, col.y, col.z, rs.alpha*alpha);
}
const float _w2 = float(int(et->w)) * 0.5f;
const float _h2 = float(int(et->h)) * 0.5f;
// render texture
{
const Vector upperLeftTextureCoordinates(et->tu1, et->tv1);
const Vector lowerRightTextureCoordinates(et->tu2, et->tv2);
glBegin(GL_QUADS);
{
glTexCoord2f(upperLeftTextureCoordinates.x, 1.0f-upperLeftTextureCoordinates.y);
glVertex2f(-_w2, +_h2);
glTexCoord2f(lowerRightTextureCoordinates.x, 1.0f-upperLeftTextureCoordinates.y);
glVertex2f(+_w2, +_h2);
glTexCoord2f(lowerRightTextureCoordinates.x, 1.0f-lowerRightTextureCoordinates.y);
glVertex2f(+_w2, -_h2);
glTexCoord2f(upperLeftTextureCoordinates.x, 1.0f-lowerRightTextureCoordinates.y);
glVertex2f(-_w2, -_h2);
}
glEnd();
}
if(renderBorders)
{
lastTexId = 0;
glBindTexture(GL_TEXTURE_2D, 0);
Vector color(0.5f,0.5f,0.5f); // TODO: (1,1,1) when selected
glColor4f(color.x, color.y, color.z, 1.0f);
glPointSize(16);
glBegin(GL_POINTS);
glVertex2f(0,0);
glEnd();
glLineWidth(2);
glBegin(GL_LINE_STRIP);
glVertex2f(_w2, _h2);
glVertex2f(_w2, -_h2);
glVertex2f(-_w2, -_h2);
glVertex2f(-_w2, _h2);
glVertex2f(_w2, _h2);
glEnd();
}
glPopMatrix();
}
RenderObject::lastTextureApplied = lastTexId;
RenderObject::lastTextureRepeat = !!lastTexRepeat;
}
void TileRender::onUpdate(float dt)
{
}

24
BBGE/TileRender.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef BBGE_TILERENDER_H
#define BBGE_TILERENDER_H
#include "RenderObject.h"
#include "Tile.h"
class Tileset;
class TileRender : public RenderObject
{
private:
const TileStorage& storage;
public:
TileRender(const TileStorage& tiles);
virtual ~TileRender();
virtual void onRender(const RenderState& rs) const;
virtual void onUpdate(float dt);
private:
};
#endif // BBGE_TILERENDER_H

94
BBGE/Tileset.cpp Normal file
View file

@ -0,0 +1,94 @@
#include "Tileset.h"
#include "SimpleIStringStream.h"
#include "Base.h"
#include "ttvfs_stdio.h"
#include "TextureMgr.h"
#include "Core.h"
bool Tileset::loadFile(const char *fn, const unsigned char *usedIdx, size_t usedIdxLen)
{
elementTemplates.clear();
InStream in(fn);
if(!in)
return false;
std::string line, gfx;
while (std::getline(in, line))
{
int idx=-1, w=0, h=0;
SimpleIStringStream is(line.c_str(), SimpleIStringStream::REUSE);
is >> idx >> gfx >> w >> h;
if(idx > 0)
{
ElementTemplate t;
t.idx = idx;
t.gfx = gfx;
t.w = w;
t.h = h;
elementTemplates.push_back(t);
}
}
in.close();
std::sort(elementTemplates.begin(), elementTemplates.end());
// begin preloading textures
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++)
{
size_t idx = elementTemplates[i].idx;
if (!usedIdx || (idx < usedIdxLen && 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 << "Loading " << usedTex.size()
<< " used textures out of the " << elementTemplates.size() << " tileset entries";
debugLog(os.str());
// preload all used textures
if(usedTex.size())
core->texmgr.loadBatch(NULL, &usedTex[0], usedTex.size());
return true;
}
void Tileset::clear()
{
elementTemplates.clear();
}
ElementTemplate *Tileset::getByIdx(size_t idx)
{
for (size_t i = 0; i < elementTemplates.size(); i++)
{
if (elementTemplates[i].idx == idx)
{
return &elementTemplates[i];
}
}
return 0;
}
Texture* ElementTemplate::getTexture()
{
if(tex)
return tex.content();
tex = core->getTexture(gfx);
if(!w)
w = tex->width;
if(!h)
h = tex->height;
return tex.content();
}

40
BBGE/Tileset.h Normal file
View file

@ -0,0 +1,40 @@
#ifndef BBGE_TILESET_H
#define BBGE_TILESET_H
#include "Vector.h"
#include <vector>
#include "Texture.h"
class ElementTemplate
{
public:
ElementTemplate() { w=0; h=0; idx=-1; tu1=tv1=0; tu2=tv2=1; }
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;
unsigned w,h; // custom size if used, otherwise texture size
// fixed
float tu1, tu2, tv1, tv2; // texcoords
size_t idx;
std::string gfx;
};
class Tileset
{
public:
// 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);
std::vector<ElementTemplate> elementTemplates;
};
#endif