zoom out much more smoother

master
Eduardo Bart 12 years ago
parent deedef235d
commit 51b0822267

@ -28,7 +28,8 @@ EventDispatcher g_dispatcher;
void EventDispatcher::flush()
{
poll();
while(!m_eventList.empty())
poll();
while(!m_scheduledEventList.empty())
m_scheduledEventList.pop();
@ -44,7 +45,8 @@ void EventDispatcher::poll()
scheduledEvent->execute();
}
while(!m_eventList.empty()) {
int maxEvents = m_eventList.size();
for(int i=0;i<maxEvents;++i) {
EventPtr event = m_eventList.front();
m_eventList.pop_front();
event->execute();

@ -61,10 +61,10 @@ public:
TSize<T> operator/(const float v) const { return TSize<T>((T)wd/v, (T)ht/v); }
TSize<T>& operator/=(const float v) { wd/=v; ht/=v; return *this; }
bool operator<=(const TSize<T>&other) const { return wd<=other.wd || ht<=other.ht; }
bool operator>=(const TSize<T>&other) const { return wd>=other.wd || ht>=other.ht; }
bool operator<(const TSize<T>&other) const { return wd<other.wd || ht<other.ht; }
bool operator>(const TSize<T>&other) const { return wd>other.wd || ht>other.ht; }
bool operator<=(const TSize<T>&other) const { return wd<=other.wd && ht<=other.ht; }
bool operator>=(const TSize<T>&other) const { return wd>=other.wd && ht>=other.ht; }
bool operator<(const TSize<T>&other) const { return wd<other.wd && ht<other.ht; }
bool operator>(const TSize<T>&other) const { return wd>other.wd && ht>other.ht; }
TSize<T>& operator=(const TSize<T>& other) { wd = other.wd; ht = other.ht; return *this; }
bool operator==(const TSize<T>& other) const { return other.wd==wd && other.ht==ht; }

@ -216,10 +216,20 @@ TilePtr Map::createTile(const Position& pos)
return tile;
}
const TilePtr& Map::getTile(const Position& pos)
{
auto it = m_tiles.find(pos);
if(it != m_tiles.end())
return it->second;
static TilePtr nulltile;
return nulltile;
}
void Map::cleanTile(const Position& pos)
{
if(TilePtr tile = getTile(pos)) {
tile->clean();
m_tiles.erase(m_tiles.find(pos));
notificateTileUpdateToMapViews(pos);
}

@ -45,7 +45,7 @@ public:
// tile related
TilePtr createTile(const Position& pos);
const TilePtr& getTile(const Position& pos) { return m_tiles[pos]; }
const TilePtr& getTile(const Position& pos);
void cleanTile(const Position& pos);
bool removeThing(const ThingPtr& thing);

@ -31,12 +31,14 @@
#include "tile.h"
#include "statictext.h"
#include "animatedtext.h"
#include <framework/core/eventdispatcher.h>
MapView::MapView()
{
int frameBufferSize = std::min(g_graphics.getMaxTextureSize(), (int)DEFAULT_FRAMBUFFER_SIZE);
Size frameBufferSize(std::min(g_graphics.getMaxTextureSize(), (int)DEFAULT_FRAMBUFFER_WIDTH),
std::min(g_graphics.getMaxTextureSize(), (int)DEFAULT_FRAMBUFFER_HEIGHT));
m_framebuffer = FrameBufferPtr(new FrameBuffer(Size(frameBufferSize, frameBufferSize)));
m_framebuffer = FrameBufferPtr(new FrameBuffer(frameBufferSize));
m_framebuffer->setClearColor(Fw::black);
m_lockedFirstVisibleFloor = -1;
setVisibleDimension(Size(15, 11));
@ -50,42 +52,47 @@ MapView::MapView()
void MapView::draw(const Rect& rect)
{
// update visible tiles cache when needed
bool updated = updateVisibleTilesCache();
if(m_mustUpdateVisibleTilesCache)
updateVisibleTilesCache();
float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS;
Position cameraPosition = getCameraPosition();
int tileDrawFlags = 0;
if(isNearView())
if(m_viewRange == NEAR_VIEW)
tileDrawFlags = Otc::DrawGround | Otc::DrawWalls | Otc::DrawCommonItems | Otc::DrawCreatures | Otc::DrawEffects;
else if(isMidView())
else if(m_viewRange == MID_VIEW)
tileDrawFlags = Otc::DrawGround | Otc::DrawWalls | Otc::DrawCommonItems;
else if(isFarView())
tileDrawFlags = Otc::DrawGround | Otc::DrawWalls;
else // huge far view
else if(m_viewRange == FAR_VIEW)
tileDrawFlags = Otc::DrawGround | Otc::DrawWalls | Otc::DrawCommonItems;
else // HUGE_VIEW
tileDrawFlags = Otc::DrawGround;
bool animate = m_animated;
// avoid animation of mid/far views
if(!isNearView())
// only animate in near views
if(m_viewRange != NEAR_VIEW)
animate = false;
if(updated || animate) {
m_framebuffer->bind();
if(m_mustDrawVisibleTilesCache || animate) {
m_framebuffer->bind(m_mustCleanFramebuffer);
if(m_mustCleanFramebuffer)
m_mustCleanFramebuffer = false;
for(const TilePtr& tile : m_cachedVisibleTiles) {
tile->draw(transformPositionTo2D(tile->getPosition()), scaleFactor, tileDrawFlags);
//TODO: restore missiles
}
m_framebuffer->generateMipmaps();
m_framebuffer->release();
m_mustDrawVisibleTilesCache = false;
}
g_painter.setCustomProgram(m_shaderProgram);
g_painter.setColor(Fw::white);
Point drawOffset(m_tileSize, m_tileSize);
Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1,1)).toPoint()/2) * m_tileSize;
if(m_followingCreature)
drawOffset += m_followingCreature->getWalkOffset() * scaleFactor;
Rect srcRect = Rect(drawOffset, m_visibleDimension * m_tileSize);
@ -150,89 +157,104 @@ void MapView::draw(const Rect& rect)
}
// draw a arrow for position the center in non near views
if(!isNearView()) {
if(m_viewRange != NEAR_VIEW) {
g_painter.setColor(Fw::red);
g_painter.drawFilledRect(Rect(rect.center(), 4, 4));
}
}
bool MapView::updateVisibleTilesCache()
void MapView::updateVisibleTilesCache(int start)
{
// update only when needed
if(!m_mustUpdateVisibleTilesCache)
return false;
if(start == 0) {
if(m_updateTilesCacheEvent) {
m_updateTilesCacheEvent->cancel();
m_updateTilesCacheEvent = nullptr;
}
int firstFloor = getFirstVisibleFloor();
int lastFloor = getLastVisibleFloor();
m_cachedFirstVisibleFloor = getFirstVisibleFloor();
m_cachedLastVisibleFloor = getLastVisibleFloor();
if(lastFloor < firstFloor)
lastFloor = firstFloor;
if(m_cachedLastVisibleFloor < m_cachedFirstVisibleFloor)
m_cachedLastVisibleFloor = m_cachedFirstVisibleFloor;
m_cachedFirstVisibleFloor = firstFloor;
m_cachedLastVisibleFloor = lastFloor;
Position cameraPosition = getCameraPosition();
m_cachedFloorVisibleCreatures.clear();
m_cachedVisibleTiles.clear();
// clear current visible tiles cache
m_cachedVisibleTiles.clear();
m_cachedFloorVisibleCreatures.clear();
m_mustCleanFramebuffer = true;
m_mustDrawVisibleTilesCache = true;
m_mustUpdateVisibleTilesCache = false;
}
// there is no tile to render on invalid positions
Position cameraPosition = getCameraPosition();
if(!cameraPosition.isValid())
return true;
return;
int count = 0;
bool stop = false;
// clear current visible tiles cache
m_cachedVisibleTiles.clear();
m_mustDrawVisibleTilesCache = true;
// cache visible tiles in draw order
// draw from last floor (the lower) to first floor (the higher)
for(int iz = m_cachedLastVisibleFloor; iz >= m_cachedFirstVisibleFloor; --iz) {
for(int iz = m_cachedLastVisibleFloor; iz >= m_cachedFirstVisibleFloor && !stop; --iz) {
// draw tiles like linus pauling's rule order
const int numDiagonals = m_drawDimension.width() + m_drawDimension.height() - 1;
for(int diagonal = 0; diagonal < numDiagonals; ++diagonal) {
for(int diagonal = 0; diagonal < numDiagonals && !stop; ++diagonal) {
// loop through / diagonal tiles
for(int ix = std::min(diagonal, m_drawDimension.width() - 1), iy = std::max(diagonal - m_drawDimension.width() + 1, 0); ix >= 0 && iy < m_drawDimension.height(); --ix, ++iy) {
for(int ix = std::min(diagonal, m_drawDimension.width() - 1), iy = std::max(diagonal - m_drawDimension.width() + 1, 0); ix >= 0 && iy < m_drawDimension.height() && !stop; --ix, ++iy) {
// only start really looking tiles in the desired start
if(count < start) {
count++;
continue;
}
// avoid rendering too much tiles at once on far views
if(count - start + 1 > MAX_TILE_UPDATES && m_viewRange >= FAR_VIEW) {
stop = true;
break;
}
// position on current floor
//TODO: check position limits
Position tilePos = cameraPosition.translated(ix - m_virtualCenterOffset.x, iy - m_virtualCenterOffset.y);
// adjust tilePos to the wanted floor
tilePos.coveredUp(cameraPosition.z - iz);
if(const TilePtr& tile = g_map.getTile(tilePos)) {
// skip tiles that have nothing
if(tile->getThingCount() == 0)
if(tile->isEmpty())
continue;
// skip tiles that are completely behind another tile
if(g_map.isCompletelyCovered(tilePos, firstFloor))
if(g_map.isCompletelyCovered(tilePos, m_cachedLastVisibleFloor))
continue;
m_cachedVisibleTiles.push_back(tile);
}
count++;
}
}
}
m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false);
m_mustUpdateVisibleTilesCache = false;
return true;
}
void MapView::recalculateTileSize()
{
int possiblesTileSizes[] = {32,16,8,4,2,1};
/*
for(int range=0; range <= m_drawDimension.width() || range <= m_drawDimension.height(); ++range) {
for(int ix=0;ix<=range;++ix) {
int foundSize = 0;
for(int candidateTileSize : possiblesTileSizes) {
Size candidateFramebufferSize = m_drawDimension * candidateTileSize;
}
}*/
}
// found a valid size
if(candidateFramebufferSize.width() <= m_framebuffer->getSize().width() && candidateFramebufferSize.height() <= m_framebuffer->getSize().height()) {
foundSize = candidateTileSize;
break;
}
if(stop) {
// schedule next update continuation
m_updateTilesCacheEvent = g_dispatcher.addEvent(std::bind(&MapView::updateVisibleTilesCache, asMapView(), count));
}
assert(foundSize > 0);
m_tileSize = foundSize;
if(start == 0)
m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false);
}
void MapView::onTileUpdate(const Position& pos)
{
requestVisibleTilesCacheUpdate();
//if(m_viewRange == NEAR_VIEW)
requestVisibleTilesCacheUpdate();
}
void MapView::lockFirstVisibleFloor(int firstVisibleFloor)
@ -269,16 +291,72 @@ void MapView::setVisibleDimension(const Size& visibleDimension)
}
if(visibleDimension.width() <= 3 || visibleDimension.height() <= 3) {
logTraceError("cannot render less than 3x3 tiles");
logTraceError("reach max zoom in");
return;
}
int possiblesTileSizes[] = {32,16,8,4,2,1};
int tileSize = 0;
Size drawDimension = visibleDimension + Size(3,3);
for(int candidateTileSize : possiblesTileSizes) {
Size candidateDrawSize = drawDimension * candidateTileSize;
// found a valid size
if(candidateDrawSize <= m_framebuffer->getSize()) {
tileSize = candidateTileSize;
break;
}
}
if(tileSize == 0) {
logTraceError("reached max zoom out");
return;
}
if(tileSize != m_tileSize) {
dump << "tile size =" << tileSize;
}
Point virtualCenterOffset = (drawDimension/2 - Size(1,1)).toPoint();
ViewRange viewRange;
if(visibleDimension.area() <= NEAR_VIEW_AREA)
viewRange = NEAR_VIEW;
else if(visibleDimension.area() <= MID_VIEW_AREA)
viewRange = MID_VIEW;
else if(visibleDimension.area() <= FAR_VIEW_AREA)
viewRange = FAR_VIEW;
else
viewRange = HUGE_VIEW;
dump << visibleDimension;
if(m_viewRange != viewRange) {
if(viewRange == NEAR_VIEW) dump << "near view";
else if(viewRange == MID_VIEW) dump << "mid view";
else if(viewRange == FAR_VIEW) dump << "far view";
else if(viewRange == HUGE_VIEW) dump << "huge view";
}
// draw actually more than what is needed to avoid massive recalculations on far views
if(viewRange >= FAR_VIEW) {
Size oldDimension = drawDimension;
drawDimension = (m_framebuffer->getSize() / tileSize);
virtualCenterOffset += (drawDimension - oldDimension).toPoint() / 2;
}
bool mustUpdate = (m_drawDimension != drawDimension ||
m_viewRange != viewRange ||
m_virtualCenterOffset != virtualCenterOffset ||
m_tileSize != tileSize);
m_visibleDimension = visibleDimension;
m_drawDimension = visibleDimension + Size(3,3);
m_virtualCenterOffset = (m_drawDimension/2 - Size(1,1)).toPoint();
recalculateTileSize();
m_drawDimension = drawDimension;
m_tileSize = tileSize;
m_viewRange = viewRange;
m_virtualCenterOffset = virtualCenterOffset;
requestVisibleTilesCacheUpdate();
if(mustUpdate)
requestVisibleTilesCacheUpdate();
}
int MapView::getFirstVisibleFloor()
@ -290,7 +368,7 @@ int MapView::getFirstVisibleFloor()
Position cameraPosition = getCameraPosition();
// avoid rendering multile floors on far views
if(!isNearView() && !isMidView())
if(m_viewRange >= FAR_VIEW)
return cameraPosition.z;
// if nothing is limiting the view, the first visible floor is 0
@ -324,9 +402,6 @@ int MapView::getFirstVisibleFloor()
firstFloor = coveredPos.z + 1;
break;
}
if(upperPos.z == 0)
break;
}
}
}
@ -340,7 +415,7 @@ int MapView::getLastVisibleFloor()
Position cameraPosition = getCameraPosition();
// avoid rendering multile floors on far views
if(!isNearView() && !isMidView())
if(m_viewRange >= FAR_VIEW)
return cameraPosition.z;
// view only underground floors when below sea level

@ -26,14 +26,28 @@
#include "declarations.h"
#include <framework/graphics/declarations.h>
#include <framework/luascript/luaobject.h>
#include <framework/core/declarations.h>
class MapView : public LuaObject
{
enum {
DEFAULT_FRAMBUFFER_SIZE = 3840,
NEAR_VIEW_AREA = 64*64,
MID_VIEW_AREA = 128*128,
FAR_VIEW_AREA = 256*256
// 3840x2160 => 1080p optimized
// 2560x1440 => 720p optimized
// 1728x972 => 480p optimized
DEFAULT_FRAMBUFFER_WIDTH = 3840,
DEFAULT_FRAMBUFFER_HEIGHT = 2160,
NEAR_VIEW_AREA = 48*48,
MID_VIEW_AREA = 96*96,
FAR_VIEW_AREA = 384*384,
MAX_TILE_UPDATES = NEAR_VIEW_AREA*7
};
enum ViewRange {
NEAR_VIEW,
MID_VIEW,
FAR_VIEW,
HUGE_VIEW
};
public:
@ -41,8 +55,7 @@ public:
void draw(const Rect& rect);
private:
void recalculateTileSize();
bool updateVisibleTilesCache();
void updateVisibleTilesCache(int start = 0);
void requestVisibleTilesCacheUpdate() { m_mustUpdateVisibleTilesCache = true; }
protected:
@ -71,10 +84,7 @@ public:
Size getVisibleSize() { return m_visibleDimension * m_tileSize; }
CreaturePtr getFollowingCreature() { return m_followingCreature; }
bool isNearView() { return m_drawDimension.area() <= NEAR_VIEW_AREA; }
bool isMidView() { return m_drawDimension.area() > NEAR_VIEW_AREA && m_drawDimension.area() <= MID_VIEW_AREA; }
bool isFarView() { return m_drawDimension.area() > MID_VIEW_AREA && m_drawDimension.area() <= FAR_VIEW_AREA; }
bool isHugeFarView() { return m_drawDimension.area() > FAR_VIEW_AREA; }
bool getViewRange() { return m_viewRange; }
bool isAnimated() { return m_animated; }
Point transformPositionTo2D(const Position& position);
@ -91,13 +101,18 @@ private:
Size m_visibleDimension;
Point m_virtualCenterOffset;
Position m_customCameraPosition;
Position m_framebufferCenterPosition;
Boolean<true> m_mustUpdateVisibleTilesCache;
Boolean<true> m_mustDrawVisibleTilesCache;
Boolean<true> m_mustCleanFramebuffer;
Boolean<true> m_animated;
std::vector<TilePtr> m_cachedVisibleTiles;
std::vector<CreaturePtr> m_cachedFloorVisibleCreatures;
EventPtr m_updateTilesCacheEvent;
CreaturePtr m_followingCreature;
FrameBufferPtr m_framebuffer;
PainterShaderProgramPtr m_shaderProgram;
ViewRange m_viewRange;
};
#endif

@ -40,6 +40,14 @@ void Tile::draw(const Point& dest, float scaleFactor, int drawFlags)
{
int drawElevation = 0;
// optimization far far views
if(drawFlags == Otc::DrawGround) {
const ThingPtr& thing = m_things.front();
if(thing)
thing->draw(dest, scaleFactor);
return;
}
// first bottom items
if(drawFlags & Otc::DrawGround || drawFlags & Otc::DrawWalls) {
for(const ThingPtr& thing : m_things) {

Loading…
Cancel
Save