zoom out much more smoother
This commit is contained in:
parent
deedef235d
commit
51b0822267
|
@ -28,6 +28,7 @@ EventDispatcher g_dispatcher;
|
|||
|
||||
void EventDispatcher::flush()
|
||||
{
|
||||
while(!m_eventList.empty())
|
||||
poll();
|
||||
|
||||
while(!m_scheduledEventList.empty())
|
||||
|
@ -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,88 +157,103 @@ 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;
|
||||
m_cachedFloorVisibleCreatures.clear();
|
||||
m_cachedVisibleTiles.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;
|
||||
|
||||
int count = 0;
|
||||
bool stop = false;
|
||||
|
||||
// clear current visible tiles cache
|
||||
m_cachedVisibleTiles.clear();
|
||||
m_cachedFloorVisibleCreatures.clear();
|
||||
|
||||
// there is no tile to render on invalid positions
|
||||
if(!cameraPosition.isValid())
|
||||
return true;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
/*
|
||||
for(int range=0; range <= m_drawDimension.width() || range <= m_drawDimension.height(); ++range) {
|
||||
for(int ix=0;ix<=range;++ix) {
|
||||
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if(stop) {
|
||||
// schedule next update continuation
|
||||
m_updateTilesCacheEvent = g_dispatcher.addEvent(std::bind(&MapView::updateVisibleTilesCache, asMapView(), count));
|
||||
}
|
||||
|
||||
if(start == 0)
|
||||
m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false);
|
||||
|
||||
m_mustUpdateVisibleTilesCache = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MapView::recalculateTileSize()
|
||||
{
|
||||
int possiblesTileSizes[] = {32,16,8,4,2,1};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
assert(foundSize > 0);
|
||||
m_tileSize = foundSize;
|
||||
}
|
||||
|
||||
void MapView::onTileUpdate(const Position& pos)
|
||||
{
|
||||
//if(m_viewRange == NEAR_VIEW)
|
||||
requestVisibleTilesCacheUpdate();
|
||||
}
|
||||
|
||||
|
@ -269,15 +291,71 @@ 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;
|
||||
}
|
||||
|
||||
m_visibleDimension = visibleDimension;
|
||||
m_drawDimension = visibleDimension + Size(3,3);
|
||||
m_virtualCenterOffset = (m_drawDimension/2 - Size(1,1)).toPoint();
|
||||
recalculateTileSize();
|
||||
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 = drawDimension;
|
||||
m_tileSize = tileSize;
|
||||
m_viewRange = viewRange;
|
||||
m_virtualCenterOffset = virtualCenterOffset;
|
||||
|
||||
if(mustUpdate)
|
||||
requestVisibleTilesCacheUpdate();
|
||||
}
|
||||
|
||||
|
@ -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…
Reference in New Issue