From 51b082226794a2b12ba7f1792539f3c9deff980e Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Tue, 31 Jan 2012 15:06:55 -0200 Subject: [PATCH] zoom out much more smoother --- src/framework/core/eventdispatcher.cpp | 6 +- src/framework/math/size.h | 8 +- src/otclient/core/map.cpp | 10 ++ src/otclient/core/map.h | 2 +- src/otclient/core/mapview.cpp | 209 +++++++++++++++++-------- src/otclient/core/mapview.h | 35 +++-- src/otclient/core/tile.cpp | 8 + 7 files changed, 194 insertions(+), 84 deletions(-) diff --git a/src/framework/core/eventdispatcher.cpp b/src/framework/core/eventdispatcher.cpp index 92bdb989..599115f1 100644 --- a/src/framework/core/eventdispatcher.cpp +++ b/src/framework/core/eventdispatcher.cpp @@ -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;iexecute(); diff --git a/src/framework/math/size.h b/src/framework/math/size.h index f87523ea..e286697c 100644 --- a/src/framework/math/size.h +++ b/src/framework/math/size.h @@ -61,10 +61,10 @@ public: TSize operator/(const float v) const { return TSize((T)wd/v, (T)ht/v); } TSize& operator/=(const float v) { wd/=v; ht/=v; return *this; } - bool operator<=(const TSize&other) const { return wd<=other.wd || ht<=other.ht; } - bool operator>=(const TSize&other) const { return wd>=other.wd || ht>=other.ht; } - bool operator<(const TSize&other) const { return wd(const TSize&other) const { return wd>other.wd || ht>other.ht; } + bool operator<=(const TSize&other) const { return wd<=other.wd && ht<=other.ht; } + bool operator>=(const TSize&other) const { return wd>=other.wd && ht>=other.ht; } + bool operator<(const TSize&other) const { return wd(const TSize&other) const { return wd>other.wd && ht>other.ht; } TSize& operator=(const TSize& other) { wd = other.wd; ht = other.ht; return *this; } bool operator==(const TSize& other) const { return other.wd==wd && other.ht==ht; } diff --git a/src/otclient/core/map.cpp b/src/otclient/core/map.cpp index 4a42c262..b81b977d 100644 --- a/src/otclient/core/map.cpp +++ b/src/otclient/core/map.cpp @@ -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); } diff --git a/src/otclient/core/map.h b/src/otclient/core/map.h index c7c369cc..469e6788 100644 --- a/src/otclient/core/map.h +++ b/src/otclient/core/map.h @@ -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); diff --git a/src/otclient/core/mapview.cpp b/src/otclient/core/mapview.cpp index 6b9a147c..a527036c 100644 --- a/src/otclient/core/mapview.cpp +++ b/src/otclient/core/mapview.cpp @@ -31,12 +31,14 @@ #include "tile.h" #include "statictext.h" #include "animatedtext.h" +#include 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 diff --git a/src/otclient/core/mapview.h b/src/otclient/core/mapview.h index 470184db..928aaabf 100644 --- a/src/otclient/core/mapview.h +++ b/src/otclient/core/mapview.h @@ -26,14 +26,28 @@ #include "declarations.h" #include #include +#include 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 m_mustUpdateVisibleTilesCache; + Boolean m_mustDrawVisibleTilesCache; + Boolean m_mustCleanFramebuffer; Boolean m_animated; std::vector m_cachedVisibleTiles; std::vector m_cachedFloorVisibleCreatures; + EventPtr m_updateTilesCacheEvent; CreaturePtr m_followingCreature; FrameBufferPtr m_framebuffer; PainterShaderProgramPtr m_shaderProgram; + ViewRange m_viewRange; }; #endif diff --git a/src/otclient/core/tile.cpp b/src/otclient/core/tile.cpp index 963dffd1..624e31a5 100644 --- a/src/otclient/core/tile.cpp +++ b/src/otclient/core/tile.cpp @@ -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) {