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

Worldmap overhaul, part 1

In short:
- No more grid-for-alpha; everything uses generated textures now
  (With proper bilinear filtering so it looks like the old method)
- All tiles are now shown partially uncovered at the same time;
  selecting one is no longer needed
- Gems can now be local (associated to a tile) or global.
  Local games move with their tile, global ones stay where they were placed

Background:
Originally there were two possible implementations of how to render the world map:
- One used write-alpha-to-texture to implement graual uncovering.
- The other (permanently enabled) used the DrawGrid to render the map tiles
  as a fine grid, each little square having its own alpha value
The downside of the first method was that it didn't look as good as the
second, so i guess that's why it was never fully finished.
The main downside of the second method was that it burned a lot of vertices
just to do alpha, so only one tile at a time could show the detailed grid.

I also never liked how an entire tile was effectively fully uncovered once
the map was first entered, taking away a lot of the exploration feeling
that could have been there if everything that hasn't been explored would be
completely invisible.
I've added this worldmap uncovering method as an optional config param,
<WorldMap revealMethod="1"/> but i've decided to fully switch over now.

Things left to be done:
- create a WorldMapRender instance only once and keep the tiles across map loads
- add debug option to reload/recreate worldmap at runtime
- cleanup gem storage and carry over the player gem properly (ged rid of std::list)
- remove "worldmap" grid render type
- Add more user "pyramid" gems as world map markers. More colors!
- check that gems and beacons still work as they should
This commit is contained in:
fgenesis 2024-11-15 03:12:14 +01:00
parent 97cea29235
commit c44c67a063
19 changed files with 811 additions and 717 deletions

View file

@ -5012,6 +5012,11 @@ void Avatar::setCollisionAvoidanceData(int range, float mod)
_collisionAvoidMod = mod;
}
Vector Avatar::getPositionForMap() const
{
return warpInLocal.isZero() ? position : warpInLocal;
}
bool Avatar::canQuickSong()
{
return !isSinging() && !isEntityDead() && isInputEnabled() && quickSongCastDelay <= 0;

View file

@ -320,6 +320,7 @@ public:
void setSeeMapMode(SeeMapMode mode) { _seeMapMode = mode; }
SeeMapMode getSeeMapMode() const { return _seeMapMode; }
Vector getPositionForMap() const; // same as position, but special when inside a local warp
int leaches;
float shieldPoints;

View file

@ -105,6 +105,7 @@ SET(AQUARIA_SRCS
Web.h
WorldMap.h
WorldMapRender.cpp
WorldMapRender.h
WorldMapTiles.cpp
)

View file

@ -27,6 +27,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "ttvfs_stdio.h"
#include "ReadXML.h"
#include "Web.h"
#include "WorldMap.h"
#include "WorldMapRender.h"
#include <tinyxml2.h>
using namespace tinyxml2;
@ -2368,33 +2370,33 @@ void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData,
}
doc.InsertEndChild(gems);
XMLElement *worldMap = doc.NewElement("WorldMap");
XMLElement *wmap = doc.NewElement("WorldMap");
{
std::ostringstream os;
for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
for (size_t i = 0; i < worldMap.worldMapTiles.size(); i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile->revealed)
const WorldMapTile& tile = worldMap.worldMapTiles[i];
if (tile.revealed)
{
os << tile->index << " ";
os << tile.index << " ";
}
}
worldMap->SetAttribute("b", os.str().c_str());
wmap->SetAttribute("b", os.str().c_str());
if (game->worldMapRender)
{
std::ostringstream os;
for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
for (size_t i = 0; i < worldMap.worldMapTiles.size(); i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
os << tile->index << " ";
tile->dataToString(os);
const WorldMapTile& tile = worldMap.worldMapTiles[i];
os << tile.index << " ";
tile.dataToString(os);
os << " ";
}
worldMap->SetAttribute("va", os.str().c_str());
wmap->SetAttribute("va", os.str().c_str());
}
}
doc.InsertEndChild(worldMap);
doc.InsertEndChild(wmap);
XMLElement *vox = doc.NewElement("VO");
{
@ -2895,24 +2897,24 @@ bool Continuity::loadFile(int slot)
}
}
XMLElement *worldMap = doc.FirstChildElement("WorldMap");
if (worldMap)
XMLElement *wmap = doc.FirstChildElement("WorldMap");
if (wmap)
{
if (worldMap->Attribute("b"))
if (wmap->Attribute("b"))
{
std::string s = worldMap->Attribute("b");
std::string s = wmap->Attribute("b");
std::istringstream is(s);
int idx;
while (is >> idx)
{
dsq->continuity.worldMap.revealMapIndex(idx);
worldMap.revealMapIndex(idx);
}
}
if (worldMap->Attribute("va") && dsq->continuity.worldMap.getNumWorldMapTiles())
if (const char *va = wmap->Attribute("va"))
{
std::istringstream is(worldMap->Attribute("va"));
SimpleIStringStream is(va, SimpleIStringStream::REUSE);
WorldMapTile dummy;
@ -2922,7 +2924,7 @@ bool Continuity::loadFile(int slot)
while (is >> idx)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(idx);
WorldMapTile *tile = worldMap.getWorldMapTileByIndex(idx);
if (!tile)
{
@ -3343,24 +3345,28 @@ void Continuity::removeGemData(GemData *gemData)
}
}
GemData *Continuity::pickupGem(std::string name, bool effects)
GemData *Continuity::pickupGem(const std::string& name, bool effects)
{
GemData g;
g.name = name;
g.mapName = game->sceneName;
int sz = gems.size();
//HACK: (hacky) using effects to determine the starting position of the gem
if (!effects)
// HACK: First gem silently picked up ever with this texture needs special treatment
bool isPlayerGem = !effects && gems.empty() && !nocasecmp(name, "Naija-Token");
if(isPlayerGem)
{
g.pos = game->worldMapRender->getAvatarWorldMapPosition() + Vector(sz*16-64, -64);
g.global = false; // the player is always on a map
g.blink = true;
}
else
g.isplayer = isPlayerGem;
Vector avatarPos = game->avatar->getPositionForMap();
if(!game->worldMapRender->getWorldToPlayerTile(g.pos, avatarPos, g.global))
{
if (!gems.empty())
g.pos = game->worldMapRender->getAvatarWorldMapPosition();
else
g.pos = Vector(0,0);
debugLog("pickupgem failed, no worldmap tile for current map");
return NULL;
}
gems.push_back(g);
@ -3372,21 +3378,32 @@ GemData *Continuity::pickupGem(std::string name, bool effects)
GemGet *gg = new GemGet(g.name);
game->addRenderObject(gg, LR_MINIMAP);
if (!getFlag("tokenHint"))
{
setFlag("tokenHint", 1);
game->setControlHint(stringbank.get(4), false, false, false, 8);
}
}
// return the last one
return &gems.back();
}
void Continuity::setCurrentMap(const std::string& mapname)
{
worldMap.revealMap(mapname);
for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++)
{
GemData& g = *i;
if(g.isplayer)
{
g.mapName = mapname;
game->worldMapRender->updateGem(&g);
}
}
}
void Continuity::entityDied(Entity *eDead)
{
if (statsAndAchievements)

View file

@ -162,9 +162,11 @@ public:
typedef std::list<BeaconData> Beacons;
Beacons beacons;
GemData *pickupGem(std::string name, bool effects = true);
GemData *pickupGem(const std::string& name, bool effects = true);
void removeGemData(GemData *gemData);
void setCurrentMap(const std::string& mapname);
typedef std::vector<std::string> VoiceOversPlayed;
VoiceOversPlayed voiceOversPlayed;

View file

@ -48,6 +48,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "Ingredient.h"
#include "Beam.h"
#include "Hair.h"
#include "WorldMapRender.h"
#ifdef BBGE_USE_GLM
@ -2183,11 +2184,28 @@ void Game::createGradient()
}
}
bool Game::isInGameMenu()
bool Game::isInGameMenu() const
{
return themenu->isInGameMenu();
}
bool Game::isOnWorldMap() const
{
return worldMapRender->isOn();
}
void Game::toggleWorldMap(bool on)
{
if (dsq->continuity.gems.empty())
dsq->continuity.pickupGem("Naija-Token", false);
worldMapRender->toggle(on);
}
void Game::toggleWorldMap()
{
toggleWorldMap(!isOnWorldMap());
}
bool Game::isValidTarget(Entity *e, Entity *me)
{
return (e != me && e->isNormalLayer() && e->isPresent() && e->getEntityType() == ET_ENEMY && e->isAvatarAttackTarget());
@ -2443,14 +2461,6 @@ void Game::action(int id, int state, int source, InputDevice device)
}
}
void Game::toggleWorldMap()
{
if (worldMapRender)
{
worldMapRender->toggle(!worldMapRender->isOn());
}
}
void Game::applyState()
{
bool verbose = true;
@ -2762,7 +2772,7 @@ void Game::applyState()
// ----------------- SCENE IS LOADED BELOW HERE -------------------
dsq->continuity.worldMap.revealMap(sceneName);
dsq->continuity.setCurrentMap(sceneName);
if (verbose) debugLog("Adding Avatar");
addRenderObject(avatar, LR_ENTITIES);
@ -2794,9 +2804,9 @@ void Game::applyState()
timerText->followCamera = 1;
addRenderObject(timerText, LR_MINIMAP);
worldMapRender = 0;
worldMapRender = new WorldMapRender;
worldMapRender = new WorldMapRender(dsq->continuity.worldMap);
worldMapRender->init();
worldMapRender->setCurrentMap(sceneName.c_str());
addRenderObject(worldMapRender, LR_WORLDMAP);
sceneToLoad="";
@ -4251,6 +4261,11 @@ bool Game::isControlHint()
return controlHint_bg->alpha.x != 0;
}
WorldMapTileContainer* Game::getCurrentWorldMapTile() const
{
return worldMapRender ? worldMapRender->getCurrentTile() : NULL;
}
bool Game::trace(Vector start, Vector target)
{
/*

View file

@ -65,6 +65,7 @@ class ToolTip;
class Ingredient;
class ManaBall;
class Beam;
class WorldMapTileContainer;
#include "Path.h"
@ -137,8 +138,6 @@ public:
void clearGrid(int v = 0);
void clearDynamicGrid(unsigned char maskbyte = OT_MASK_BLACK);
void toggleWorldMap();
void action(int id, int state, int source, InputDevice device);
InGameMenu *getInGameMenu() { return themenu; }
@ -254,7 +253,10 @@ public:
SceneEditor sceneEditor;
bool isSceneEditorActive() {return sceneEditor.isOn();}
bool isInGameMenu();
bool isInGameMenu() const;
bool isOnWorldMap() const;
void toggleWorldMap(bool on);
void toggleWorldMap(); // on or off
typedef std::vector<EntityClass> EntityTypeList;
EntityTypeList entityTypeList;
@ -313,6 +315,7 @@ public:
int getNumberOfEntitiesNamed(const std::string &name);
MiniMapRender *miniMapRender;
WorldMapRender *worldMapRender;
WorldMapTileContainer *getCurrentWorldMapTile() const;
bool loadingScene;
bool doScreenTrans;

View file

@ -21,13 +21,15 @@ struct EmoteData
struct GemData
{
GemData() { canMove=false; blink = false; }
GemData() { canMove=false; blink = false; global = false; isplayer = false; }
std::string name;
std::string userString;
std::string mapName;
Vector pos;
bool canMove;
bool blink; // not saved
bool global; // local gems use their parent container's coordinate system, global gems are placed directly on the map screen
bool isplayer;
};
struct BeaconData

View file

@ -23,14 +23,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "GameEnums.h"
#include "GameStructs.h"
#include "../BBGE/Quad.h"
#include "Quad.h"
#include "ActionMapper.h"
#include "VertexBuffer.h"
class GemMover;
struct MinimapIcon;
struct WorldMapTile;
struct GemData;
class AquariaMenuItem;
class BitmapText;
@ -93,43 +91,6 @@ public:
static bool setMaxHealthMarkerTex(const std::string& name);
};
class WorldMapRender : public RenderObject, public ActionMapper
{
public:
WorldMapRender();
void destroy();
void toggle(bool on);
bool isOn();
Vector getAvatarWorldMapPosition();
Vector getWorldToTile(WorldMapTile *tile, Vector position, bool fromCenter, bool tilePos);
void setProperTileColor(WorldMapTile *tile);
void action(int id, int state, int source, InputDevice device);
GemMover* addGem(GemData *gemData);
void bindInput();
void createGemHint(const std::string &gfx);
void addAllGems();
void fixGems();
void removeGem(GemMover *gemMover);
void onToggleHelpScreen();
bool isCursorOffHud();
protected:
Quad *addHintQuad1, *addHintQuad2;
AquariaMenuItem *helpButton;
float doubleClickTimer;
float inputDelay;
BitmapText *areaLabel, *areaLabel2, *areaLabel3;
WorldMapTile *originalActiveTile;
void setVis(WorldMapTile *tile);
void clearVis(WorldMapTile *tile);
bool on;
void onUpdate(float dt);
unsigned char *savedTexData;
bool mb;
Vector lastMousePosition; // See FIXME in WorldMapRender.cpp --achurch
void updateEditor();
};
class PathRender : public RenderObject
{
public:

View file

@ -376,7 +376,7 @@ void MiniMapRender::onUpdate(float dt)
_isCursorIn = false;
if (alpha.x == 1)
{
if (!game->isInGameMenu() && (!game->isPaused() || (game->isPaused() && game->worldMapRender->isOn())))
if (!game->isInGameMenu() && (!game->isPaused() || (game->isPaused() && game->isOnWorldMap())))
{
if (isCursorInButtons())
{
@ -397,7 +397,7 @@ void MiniMapRender::onUpdate(float dt)
bool btn=false;
if (!game->worldMapRender->isOn())
if (!game->isOnWorldMap())
{
for (size_t i = 0; i < buttons.size(); i++)
{
@ -421,20 +421,16 @@ void MiniMapRender::onUpdate(float dt)
if (!btn && !radarHide && (!dsq->mod.isActive() || dsq->mod.hasWorldMap()))
{
if (game->worldMapRender->isOn())
if (game->isOnWorldMap())
{
game->worldMapRender->toggle(false);
game->toggleWorldMap(false);
clickEffect(1);
}
else
{
if (doubleClickDelay > 0 && !core->isStateJumpPending())
{
if (dsq->continuity.gems.empty())
dsq->continuity.pickupGem("Naija-Token");
game->worldMapRender->toggle(true);
game->toggleWorldMap(true);
clickEffect(0);

View file

@ -2357,7 +2357,7 @@ void SceneEditor::toggle(bool on)
if (core->getNestedMains() > 1) return;
if (game->isInGameMenu()) return;
if (!on && editType == ET_SELECTENTITY) return;
if (game->worldMapRender && game->worldMapRender->isOn()) return;
if (game->isOnWorldMap()) return;
this->on = on;
autoSaveTimer = 0;

View file

@ -61,6 +61,7 @@ extern "C" {
#include "Shader.h"
#include "ActionMapper.h"
#include "QuadGrid.h"
#include "WorldMapRender.h"
#include "MathFunctions.h"
@ -8855,16 +8856,15 @@ luaFunc(setGemPosition)
Vector pos(lua_tonumber(L, 2), lua_tonumber(L, 3));
bool result = false;
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(mapname);
if(tile)
WorldMapTileContainer *tc = game->worldMapRender->getTileByName(mapname.c_str());
if(tc)
{
pos = game->worldMapRender->getWorldToTile(tile, pos, true, true);
if(gemId < dsq->continuity.gems.size())
{
Continuity::Gems::iterator it = dsq->continuity.gems.begin();
std::advance(it, gemId);
GemData& gem = *it;
gem.pos = pos;
gem.pos = gem.global ? tc->worldPosToMapPos(pos) : tc->worldPosToTilePos(pos);
gem.mapName = mapname;
result = true;
}
@ -8925,9 +8925,7 @@ luaFunc(removeGem)
{
Continuity::Gems::iterator it = dsq->continuity.gems.begin();
std::advance(it, gemId);
dsq->continuity.removeGemData(&(*it));
if(game->worldMapRender->isOn())
game->worldMapRender->fixGems();
game->worldMapRender->removeGem(&(*it));
}
luaReturnNil();
}

View file

@ -439,10 +439,10 @@ void StatsAndAchievements::EvaluateAchievement( Achievement &achievement )
{
// check world map data somehow
bool hasAllMap = true;
for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
for (size_t i = 0; i < dsq->continuity.worldMap.worldMapTiles.size(); i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (!tile->revealed && (nocasecmp(tile->name, "thirteenlair") != 0)) {
const WorldMapTile& tile = dsq->continuity.worldMap.worldMapTiles[i];
if (!tile.revealed && (nocasecmp(tile.name, "thirteenlair") != 0)) {
hasAllMap = false;
break;

View file

@ -4,6 +4,10 @@
#include <string>
#include "Vector.h"
#include "GameEnums.h"
#include "Image.h"
#include "Refcounted.h"
#include "Texture.h"
#include "SimpleIStringStream.h"
#define MAPVIS_SUBDIV 64
@ -15,21 +19,23 @@ struct WorldMapTile
~WorldMapTile();
void markVisited(int left, int top, int right, int bottom);
void dataToString(std::ostringstream &os);
void stringToData(std::istringstream &is);
const unsigned char *getData() const {return data;}
void dataToString(std::ostringstream &os) const;
void stringToData(SimpleIStringStream &is);
ImageData generateAlphaImage(size_t w, size_t h); // free() data when no longer needed
bool updateDiscoveredTex();
std::string name;
Vector gridPos;
float scale, scale2;
bool revealed, prerevealed;
bool revealed, prerevealed, dirty;
int layer, index;
int stringIndex;
Quad *q;
protected:
unsigned char *data;
Array2d<unsigned char> visited;
CountedPtr<Texture> originalTex, generatedTex;
};
struct WorldMap
@ -37,12 +43,8 @@ struct WorldMap
WorldMap();
void load();
void save();
void hideMap();
void revealMap(const std::string &name);
WorldMapTile *getWorldMapTile(const std::string &name);
size_t getNumWorldMapTiles();
WorldMapTile *getWorldMapTile(size_t index);
WorldMapTile *getWorldMapTileByIndex(int index);
void revealMapIndex(int index);

File diff suppressed because it is too large Load diff

90
Aquaria/WorldMapRender.h Normal file
View file

@ -0,0 +1,90 @@
#ifndef WORLDMAPRENDER_H
#define WORLDMAPRENDER_H
#include "Quad.h"
#include "ActionMapper.h"
class GemMover;
struct WorldMapTile;
struct GemData;
class BitmapText;
class AquariaMenuItem;
struct WorldMap;
class Gradient;
// This is used for properly positioning the tile and gems on top of it.
// Affected by scale2 -- gems also move with scale2.
class WorldMapTileContainer : public RenderObject
{
public:
WorldMapTileContainer(WorldMapTile& tile);
virtual ~WorldMapTileContainer();
virtual void onUpdate(float dt) OVERRIDE;
void refresh(); // Called whenever we need to prepare for rendering
void removeGems();
void addGem(GemMover *gem);
bool removeGem(GemMover *gem);
GemMover *removeGem(const GemData *gem);
GemMover *getGem(const GemData *gemData) const;
void updateGems();
Vector worldPosToTilePos(const Vector& p) const;
Vector worldPosToMapPos(const Vector& p) const;
WorldMapTile& tile;
Quad q; // The actual world map tile, additionally affected by scale (NOT scale2), and always at (0, 0)
std::vector<GemMover*> gems;
};
class WorldMapRender : public RenderObject, public ActionMapper
{
public:
WorldMapRender(WorldMap& wm);
virtual ~WorldMapRender();
void init();
void toggle(bool on);
bool isOn();
void setProperTileColor(WorldMapTileContainer& wt);
void action(int id, int state, int source, InputDevice device);
GemMover* addGem(GemData *gemData);
void updateGem(const GemData *gemData);
void removeGem(GemMover *gemMover);
void removeGem(const GemData *gemData);
GemMover *getGem(const GemData *gemData) const;
WorldMapTileContainer *getTileWithGem(const GemData *gemData) const;
void bindInput();
void createGemHint(const std::string &gfx);
void onToggleHelpScreen();
bool isCursorOffHud();
void updateAllTilesColor();
WorldMapTileContainer *getCurrentTile() { return playerTile; }
bool getWorldToPlayerTile(Vector& dst, const Vector& pos, bool global) const;
WorldMapTileContainer *getTileByName(const char *name) const;
WorldMapTileContainer * setCurrentMap(const char *mapname);
protected:
Quad *addHintQuad1, *addHintQuad2;
AquariaMenuItem *helpButton;
float doubleClickTimer;
float inputDelay;
BitmapText *areaLabel, *areaLabel2, *areaLabel3;
WorldMapTileContainer *playerTile; // tile where the player is located
WorldMapTileContainer *selectedTile; // starts out == playerTile but changed on selection
bool on;
void onUpdate(float dt);
bool mb, wasEditorSaveDown;
Vector lastMousePosition; // See FIXME in WorldMapRender.cpp --achurch
void updateEditor();
std::vector<WorldMapTileContainer*> tiles;
WorldMap& worldmap;
Quad *tophud;
Gradient *underlay;
};
#endif

View file

@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "DSQ.h"
#include "ttvfs_stdio.h"
#include <fstream>
#include "stb_image_resize.h"
#if MAPVIS_SUBDIV % 8 != 0
#error MAPVIS_SUBDIV must be a multiple of 8
@ -39,23 +40,19 @@ WorldMapTile::WorldMapTile()
scale = scale2 = 1;
layer = 0;
index = -1;
data = 0;
q = 0;
stringIndex = 0;
dirty = true;
generatedTex = new Texture;
}
WorldMapTile::~WorldMapTile()
{
delete[] data;
}
void WorldMapTile::markVisited(int left, int top, int right, int bottom)
{
if (data == 0)
{
data = new unsigned char[dataSize];
memset(data, 0, dataSize);
}
if(visited.empty())
visited.init(rowSize, MAPVIS_SUBDIV);
if (left < 0)
left = 0;
@ -66,19 +63,32 @@ void WorldMapTile::markVisited(int left, int top, int right, int bottom)
if (bottom > MAPVIS_SUBDIV - 1)
bottom = MAPVIS_SUBDIV - 1;
// Accumulate bits that were changed
unsigned changed = 0;
for (int y = top; y <= bottom; y++)
{
unsigned char *row = visited.row(y);
for (int x = left; x <= right; x++)
{
data[y*rowSize + x/8] |= 1 << (x%8);
unsigned char& val = row[x/8];
unsigned char add = 1 << (x%8);
changed |= (val ^ add) & add;
val |= add;
}
}
if(changed)
{
//debugLog("worldmap[" + name + "] visited something new");
dirty = true; // Only mark as dirty if we've uncovered something new
}
}
// Convert the data array to string format for saving.
void WorldMapTile::dataToString(std::ostringstream &os)
void WorldMapTile::dataToString(std::ostringstream &os) const
{
if (!data)
if (visited.empty())
{
os << "0 0";
return;
@ -88,6 +98,7 @@ void WorldMapTile::dataToString(std::ostringstream &os)
char *outbuf = new char[((dataSize+2)/3)*4 + 1];
char *ptr = outbuf;
const unsigned char * const data = visited.data();
unsigned int i;
for (i = 0; i+3 <= dataSize; i += 3, ptr += 4)
@ -121,18 +132,21 @@ void WorldMapTile::dataToString(std::ostringstream &os)
unsigned int count = 0;
std::ostringstream tempStream;
unsigned char *ptr = data;
for (unsigned int y = 0; y < MAPVIS_SUBDIV; y++, ptr += rowSize)
for (unsigned int y = 0; y < MAPVIS_SUBDIV; y++)
{
const unsigned char *row = visited.row(y);
for (unsigned int x = 0; x < MAPVIS_SUBDIV; x += 8)
{
unsigned char dataByte = ptr[x/8];
for (unsigned int x2 = 0; x2 < 8; x2++)
if(unsigned char dataByte = row[x/8])
{
if (dataByte & (1 << x2))
for (unsigned int x2 = 0; x2 < 8; x2++)
{
tempStream << " " << (x+x2) << " " << y;
count++;
if (dataByte & (1 << x2))
{
tempStream << " " << (x+x2) << " " << y;
count++;
}
}
}
}
@ -144,10 +158,10 @@ void WorldMapTile::dataToString(std::ostringstream &os)
}
// Parse a string from a save file and store in the data array.
void WorldMapTile::stringToData(std::istringstream &is)
void WorldMapTile::stringToData(SimpleIStringStream &is)
{
delete[] data;
data = 0;
dirty = true;
visited.init(rowSize, MAPVIS_SUBDIV);
int subdiv;
std::string countOrType;
@ -182,15 +196,13 @@ void WorldMapTile::stringToData(std::istringstream &is)
return;
}
data = new unsigned char[dataSize];
memset(data, 0, dataSize);
if (countOrType == "b") // Raw bitmap (base64-encoded)
{
std::string encodedData = "";
is >> encodedData;
const char *in = encodedData.c_str();
unsigned char *out = data;
unsigned char * const top = data + dataSize;
unsigned char *out = visited.data();
unsigned char * const top = out + dataSize;
while (in[0] != 0 && in[1] != 0 && out < top)
{
unsigned char ch0, ch1, ch2, ch3;
@ -222,6 +234,8 @@ void WorldMapTile::stringToData(std::istringstream &is)
}
else // List of coordinate pairs
{
visited.fill(0);
int count = 0;
std::istringstream is2(countOrType);
is2 >> count;
@ -231,11 +245,88 @@ void WorldMapTile::stringToData(std::istringstream &is)
int x, y;
is >> x >> y;
if (x >= 0 && x < MAPVIS_SUBDIV && y >= 0 && y < MAPVIS_SUBDIV)
data[y*rowSize + x/8] |= 1 << (x%8);
visited(x/8, y) |= 1 << (x%8);
}
}
}
ImageData WorldMapTile::generateAlphaImage(size_t w, size_t h)
{
ImageData ret = {0};
if(visited.empty())
return ret;
// stbir can't deal with 1-bit images, so let's make a 1-channel black or white image first
Array2d<unsigned char> tmp(MAPVIS_SUBDIV, MAPVIS_SUBDIV);
unsigned char *dst = tmp.data();
for(size_t y = 0; y < MAPVIS_SUBDIV; ++y)
{
const unsigned char * const src = visited.row(y);
for(size_t x = 0; x < MAPVIS_SUBDIV; x += 8)
{
unsigned c = src[x/8];
for(size_t bit = 0; bit < 8; ++bit)
*dst++ = (c & (1 << bit)) ? 0xff : 0;
}
}
std::ostringstream os;
os << "worldmap[" << name << "] Generate alpha from vis data, resize to (" << w << ", " << h << ")";
debugLog(os.str());
ret.pixels = (unsigned char*)malloc(w * h);
if(ret.pixels)
{
ret.w = w;
ret.h = h;
ret.channels = 1;
stbir_resize_uint8_generic(
tmp.data(), MAPVIS_SUBDIV, MAPVIS_SUBDIV, 0,
ret.pixels, w, h, 0,
1, STBIR_ALPHA_CHANNEL_NONE, 0,
STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, NULL
);
}
return ret;
}
// If there was a shader to use one texture for colors and another for alpha this wouldn't be needed
// TODO: If we ever get this far, do this with shaders. Software resize sucks so bad...
bool WorldMapTile::updateDiscoveredTex()
{
int w, h;
size_t sz;
const unsigned char * texbuf = originalTex->getBufferAndSize(&w, &h, &sz);
std::vector<unsigned char> tmp(sz);
{
const ImageData aimg = generateAlphaImage(w, h);
if(!aimg.pixels)
return false;
const unsigned char *ap = aimg.pixels;
unsigned char *dst = &tmp[0];
for(size_t i = 0; i < sz; i += 4)
{
*dst++ = *texbuf++;
*dst++ = *texbuf++;
*dst++ = *texbuf++;
*dst++ = (*texbuf++ * *ap++) >> 8; // inaccurate fixed-point multiplication; ends up 0xfe as the highest possible value but whatev
}
free(aimg.pixels);
}
ImageData up;
up.pixels = &tmp[0];
up.channels = 4;
up.w = w;
up.h = h;
generatedTex->upload(up, true);
return true;
}
WorldMap::WorldMap()
{
@ -338,26 +429,3 @@ WorldMapTile *WorldMap::getWorldMapTileByIndex(int index)
}
return 0;
}
void WorldMap::hideMap()
{
for (size_t i = 0; i < worldMapTiles.size(); i++)
{
worldMapTiles[i].revealed = false;
}
}
size_t WorldMap::getNumWorldMapTiles()
{
return worldMapTiles.size();
}
WorldMapTile *WorldMap::getWorldMapTile(size_t index)
{
if (index >= worldMapTiles.size()) return 0;
return &worldMapTiles[index];
}

View file

@ -100,11 +100,7 @@ void Texture::unload()
glDeleteTextures(1, &gltexid);
gltexid = 0;
}
if(_pixbuf)
{
free(_pixbuf);
_pixbuf = NULL;
}
_freePixbuf();
}
size_t Texture::sizeBytes() const
@ -112,6 +108,26 @@ size_t Texture::sizeBytes() const
return size_t(width) * size_t(height) * 4;
}
bool Texture::uploadAndKeep(ImageData& img, bool mipmap)
{
bool ok = upload(img, mipmap); // this also clears pixbuf
if(ok)
{
_pixbuf = img.pixels;
img.pixels = NULL;
}
return ok;
}
void Texture::_freePixbuf()
{
if(_pixbuf)
{
free(_pixbuf);
_pixbuf = NULL;
}
}
void Texture::apply() const
{
glBindTexture(GL_TEXTURE_2D, gltexid);
@ -223,6 +239,8 @@ bool Texture::upload(const ImageData& img, bool mipmap)
width = img.w;
height = img.h;
_mipmap = mipmap;
_freePixbuf();
return true;
}

View file

@ -70,10 +70,12 @@ public:
std::string name, filename;
bool upload(const ImageData& img, bool mipmap);
bool uploadAndKeep(ImageData& img, bool mipmap);
bool success;
protected:
void _freePixbuf();
int ow, oh;
bool _mipmap;