Skip to content

Commit

Permalink
Optimize FieldOfView calculations.
Browse files Browse the repository at this point in the history
  • Loading branch information
miki151 committed Jan 28, 2018
1 parent c15e236 commit d6fc9de
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 42 deletions.
53 changes: 23 additions & 30 deletions field_of_view.cpp
Expand Up @@ -21,75 +21,68 @@
#include "level.h"
#include "position.h"

template <class Archive>
template <class Archive>
void FieldOfView::serialize(Archive& ar, const unsigned int) {
if (Archive::is_saving::value) // don't save the visibility values, as they can be easily recomputed
visibility = Table<unique_ptr<Visibility>>(visibility.getBounds());
ar(level, visibility, vision);
ar(level, vision, blocking);
if (Archive::is_loading::value)
visibility = Table<unique_ptr<Visibility>>(level->getBounds());
}

SERIALIZABLE(FieldOfView)

template <class Archive>
void FieldOfView::Visibility::serialize(Archive& ar, const unsigned int) {
ar(visible, visibleTiles, px, py);
}

SERIALIZABLE(FieldOfView::Visibility)
SERIALIZATION_CONSTRUCTOR_IMPL(FieldOfView)
SERIALIZATION_CONSTRUCTOR_IMPL2(FieldOfView::Visibility, Visibility)

FieldOfView::FieldOfView(WLevel l, VisionId v)
: level(l), visibility(l->getBounds()), vision(v) {
: level(l), visibility(l->getBounds()), vision(v), blocking(l->getBounds().minusMargin(-1), true) {
for (auto v : blocking.getBounds())
blocking[v] = !Position(v, level).canSeeThru(vision);
}

bool FieldOfView::canSee(Vec2 from, Vec2 to) {
PROFILE;;
if ((from - to).lengthD() > sightRange)
return false;
if (!visibility[from])
visibility[from].reset(new Visibility(level, vision, from.x, from.y));
visibility[from].reset(new Visibility(level->getBounds(), blocking, from.x, from.y));
return visibility[from]->checkVisible(to.x - from.x, to.y - from.y);
}

void FieldOfView::squareChanged(Vec2 pos) {
blocking[pos] = !Position(pos, level).canSeeThru(vision);
vector<Vec2> updateList;
if (!visibility[pos])
visibility[pos].reset(new Visibility(level, vision, pos.x, pos.y));
visibility[pos].reset(new Visibility(level->getBounds(), blocking, pos.x, pos.y));
vector<Vec2> visible = visibility[pos]->getVisibleTiles();
for (Vec2 v : visible)
if (visibility[v] && visibility[v]->checkVisible(pos.x - v.x, pos.y - v.y)) {
visibility[v].reset();
}
}

void FieldOfView::Visibility::setVisible(WConstLevel level, int x, int y) {
if (level->inBounds(Vec2(px + x, py + y)) &&
void FieldOfView::Visibility::setVisible(Rectangle bounds, int x, int y) {
if (Vec2(px + x, py + y).inRectangle(bounds) &&
!visible[x + sightRange][y + sightRange] && x * x + y * y <= sightRange * sightRange) {
visible[x + sightRange][y + sightRange] = 1;
visibleTiles.push_back(Vec2(px + x, py + y));
}
}

static int totalIter = 0;
static int numSamples = 0;

FieldOfView::Visibility::Visibility(WLevel level, VisionId vision, int x, int y) : px(x), py(y) {
FieldOfView::Visibility::Visibility(Rectangle bounds, const Table<bool>& blocking, int x, int y) : px(x), py(y) {
PROFILE;
memset(visible, 0, (2 * sightRange + 1) * (2 * sightRange + 1));
calculate(2 * sightRange, 2 * sightRange,2 * sightRange, 2,-1,1,1,1,
[&](int px, int py) { return !Position(Vec2(x + px, y + py), level).canSeeThru(vision); },
[&](int px, int py) { setVisible(level, px, py); });
[&](int px, int py) { return blocking[Vec2(x + px, y + py)]; },
[&](int px, int py) { setVisible(bounds, px, py); });
calculate(2 * sightRange, 2 * sightRange,2 * sightRange, 2,-1,1,1,1,
[&](int px, int py) { return !Position(Vec2(x + py, y - px), level).canSeeThru(vision); },
[&](int px, int py) { setVisible(level, py, -px); });
[&](int px, int py) { return blocking[Vec2(x + py, y - px)]; },
[&](int px, int py) { setVisible(bounds, py, -px); });
calculate(2 * sightRange, 2 * sightRange,2 * sightRange,2,-1,1,1,1,
[&](int px, int py) { return !Position(Vec2(x - px, y - py), level).canSeeThru(vision); },
[&](int px, int py) { setVisible(level, -px, -py); });
[&](int px, int py) { return blocking[Vec2(x - px, y - py)]; },
[&](int px, int py) { setVisible(bounds, -px, -py); });
calculate(2 * sightRange, 2 * sightRange,2 * sightRange,2,-1,1,1,1,
[&](int px, int py) { return !Position(Vec2(x - py, y + px), level).canSeeThru(vision); },
[&](int px, int py) { setVisible(level, -py, px); });
setVisible(level, 0, 0);
[&](int px, int py) { return blocking[Vec2(x - py, y + px)]; },
[&](int px, int py) { setVisible(bounds, -py, px); });
setVisible(bounds, 0, 0);
/* ++numSamples;
totalIter += visibleTiles.size();
if (numSamples%100 == 0)
Expand All @@ -102,7 +95,7 @@ const vector<Vec2>& FieldOfView::Visibility::getVisibleTiles() const {

const vector<Vec2>& FieldOfView::getVisibleTiles(Vec2 from) {
if (!visibility[from]) {
visibility[from].reset(new Visibility(level, vision, from.x, from.y));
visibility[from].reset(new Visibility(level->getBounds(), blocking, from.x, from.y));
}
return visibility[from]->getVisibleTiles();
}
Expand Down
19 changes: 8 additions & 11 deletions field_of_view.h
Expand Up @@ -40,26 +40,23 @@ class FieldOfView {
bool checkVisible(int x,int y) const;
const vector<Vec2>& getVisibleTiles() const;

Visibility(WLevel, VisionId, int x, int y);
Visibility(Visibility&&) = default;
Visibility& operator = (Visibility&&) = default;

SERIALIZATION_DECL(Visibility)
Visibility(Rectangle bounds, const Table<bool>& blocking, int x, int y);

private:
char SERIAL(visible)[sightRange * 2 + 1][sightRange * 2 + 1];
vector<Vec2> SERIAL(visibleTiles);
char visible[sightRange * 2 + 1][sightRange * 2 + 1];
vector<Vec2> visibleTiles;
void calculate(int,int,int,int, int, int, int, int,
function<bool (int, int)> isBlocking,
function<void (int, int)> setVisible);
void setVisible(WConstLevel, int, int);
void setVisible(Rectangle bounds, int, int);

int SERIAL(px);
int SERIAL(py);
int px;
int py;
};

WLevel SERIAL(level);
Table<unique_ptr<Visibility>> SERIAL(visibility);
Table<unique_ptr<Visibility>> visibility;
VisionId SERIAL(vision);
Table<bool> SERIAL(blocking);
};

8 changes: 8 additions & 0 deletions map_gui.cpp
Expand Up @@ -508,6 +508,7 @@ void MapGui::drawHealthBar(Renderer& renderer, Vec2 pos, Vec2 size, const ViewOb

void MapGui::drawObjectAbs(Renderer& renderer, Vec2 pos, const ViewObject& object, Vec2 size, Vec2 movement,
Vec2 tilePos, milliseconds curTimeReal) {
PROFILE;
auto id = object.id();
const Tile& tile = Tile::getTile(id, spriteMode);
Color color = colorWoundedRed ? Renderer::getBleedingColor(object) : Color::WHITE;
Expand Down Expand Up @@ -641,6 +642,7 @@ bool MapGui::isFoW(Vec2 pos) const {
}

void MapGui::renderExtraBorders(Renderer& renderer, milliseconds currentTimeReal) {
PROFILE;
extraBorderPos.clear();
for (Vec2 wpos : layout->getAllTiles(getBounds(), levelBounds, getScreenPos()))
if (objects[wpos] && objects[wpos]->hasObject(ViewLayer::FLOOR_BACKGROUND)) {
Expand Down Expand Up @@ -768,6 +770,7 @@ void MapGui::renderHighlight(Renderer& renderer, Vec2 pos, Vec2 size, const View
}

void MapGui::renderHighlights(Renderer& renderer, Vec2 size, milliseconds currentTimeReal, bool lowHighlights) {
PROFILE;
Rectangle allTiles = layout->getAllTiles(getBounds(), levelBounds, getScreenPos());
Vec2 topLeftCorner = projectOnScreen(allTiles.topLeft());
for (Vec2 wpos : allTiles)
Expand All @@ -786,6 +789,7 @@ void MapGui::renderHighlights(Renderer& renderer, Vec2 size, milliseconds curren
}

void MapGui::renderAnimations(Renderer& renderer, milliseconds currentTimeReal) {
PROFILE;
animations = std::move(animations).filter([=](const AnimationInfo& elem)
{ return !elem.animation->isDone(currentTimeReal);});
for (auto& elem : animations)
Expand All @@ -797,6 +801,7 @@ void MapGui::renderAnimations(Renderer& renderer, milliseconds currentTimeReal)
}

MapGui::HighlightedInfo MapGui::getHighlightedInfo(Vec2 size, milliseconds currentTimeReal) {
PROFILE;
HighlightedInfo ret {};
Rectangle allTiles = layout->getAllTiles(getBounds(), levelBounds, getScreenPos());
Vec2 topLeftCorner = projectOnScreen(allTiles.topLeft());
Expand Down Expand Up @@ -906,6 +911,7 @@ void MapGui::considerRedrawingSquareHighlight(Renderer& renderer, milliseconds c
}

void MapGui::processScrolling(milliseconds time) {
PROFILE;
if (!!softCenter && !!lastScrollUpdate) {
double offsetx = softCenter->x - center.x;
double offsety = softCenter->y - center.y;
Expand Down Expand Up @@ -940,6 +946,7 @@ void MapGui::setDraggedCreature(UniqueEntity<Creature>::Id id, ViewId viewId, Ve
}

void MapGui::considerScrollingToCreature() {
PROFILE;
if (auto& info = centeredCreaturePosition) {
Vec2 size = layout->getSquareSize();
Vec2 offset;
Expand All @@ -959,6 +966,7 @@ void MapGui::considerScrollingToCreature() {
}

void MapGui::render(Renderer& renderer) {
PROFILE;
considerScrollingToCreature();
Vec2 size = layout->getSquareSize();
auto currentTimeReal = clock->getRealMillis();
Expand Down
2 changes: 1 addition & 1 deletion model.cpp
Expand Up @@ -132,7 +132,7 @@ bool Model::update(double totalTime) {
return false;
}

void Model::tick(LocalTime time) {
void Model::tick(LocalTime time) { PROFILE
for (WCreature c : timeQueue->getAllCreatures()) {
c->tick();
}
Expand Down

0 comments on commit d6fc9de

Please sign in to comment.