void MapView::draw(const Rect& rect) { // update visible tiles cache when needed if(m_mustUpdateVisibleTilesCache || m_updateTilesPos > 0) updateVisibleTilesCache(m_mustUpdateVisibleTilesCache ? 0 : m_updateTilesPos); float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS; Position cameraPosition = getCameraPosition(); int drawFlags = 0; if(m_viewMode == NEAR_VIEW) drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems | Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawMissiles | Otc::DrawAnimations; else drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems; Size tileSize = Size(1,1) * m_tileSize; if(m_mustDrawVisibleTilesCache || (drawFlags & Otc::DrawAnimations)) { m_framebuffer->bind(); if(m_mustCleanFramebuffer) { Rect clearRect = Rect(0, 0, m_drawDimension * m_tileSize); g_painter->setColor(Color::black); g_painter->drawFilledRect(clearRect); } g_painter->setColor(Color::white); auto it = m_cachedVisibleTiles.begin(); auto end = m_cachedVisibleTiles.end(); for(int z=m_cachedLastVisibleFloor;z>=m_cachedFirstVisibleFloor;--z) { while(it != end) { const TilePtr& tile = *it; Position tilePos = tile->getPosition(); if(tilePos.z != z) break; else ++it; if(!m_drawMinimapColors) tile->draw(transformPositionTo2D(tilePos, cameraPosition), scaleFactor, drawFlags); else { uint8 c = tile->getMinimapColorByte(); if(c == 0) continue; g_painter->setColor(Color::from8bit(c)); g_painter->drawFilledRect(Rect(transformPositionTo2D(tilePos, cameraPosition), tileSize)); } } if(drawFlags & Otc::DrawMissiles && !m_drawMinimapColors) { for(const MissilePtr& missile : g_map.getFloorMissiles(z)) { missile->draw(transformPositionTo2D(missile->getPosition(), cameraPosition), scaleFactor, drawFlags & Otc::DrawAnimations); } } } m_framebuffer->release(); // generating mipmaps each frame can be slow in older cards //m_framebuffer->getTexture()->buildHardwareMipmaps(); m_mustDrawVisibleTilesCache = false; } Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1,1)).toPoint()/2) * m_tileSize; if(isFollowingCreature()) drawOffset += m_followingCreature->getWalkOffset() * scaleFactor; Size srcSize = rect.size(); Size srcVisible = m_visibleDimension * m_tileSize; srcSize.scale(srcVisible, Fw::KeepAspectRatio); drawOffset.x += (srcVisible.width() - srcSize.width()) / 2; drawOffset.y += (srcVisible.height() - srcSize.height()) / 2; Rect srcRect = Rect(drawOffset, srcSize); g_painter->setColor(Color::white); glDisable(GL_BLEND); g_painter->setShaderProgram(m_shader); #if 0 // debug source area g_painter->saveAndResetState(); m_framebuffer->bind(); g_painter->setColor(Color::green); g_painter->drawBoundingRect(srcRect, 2); m_framebuffer->release(); g_painter->restoreSavedState(); m_framebuffer->draw(rect); #else m_framebuffer->draw(rect, srcRect); #endif g_painter->resetShaderProgram(); glEnable(GL_BLEND); // this could happen if the player position is not known yet if(!cameraPosition.isValid()) return; float horizontalStretchFactor = rect.width() / (float)srcRect.width(); float verticalStretchFactor = rect.height() / (float)srcRect.height(); // avoid drawing texts on map in far zoom outs if(m_viewMode == NEAR_VIEW && m_drawTexts) { for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) { Point creatureOffset = Point(16 - creature->getDisplacementX(), -3 - creature->getDisplacementY()); Position pos = creature->getPosition(); Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset; p += (creature->getDrawOffset() + creatureOffset) * scaleFactor; p.x = p.x * horizontalStretchFactor; p.y = p.y * verticalStretchFactor; p += rect.topLeft(); creature->drawInformation(p, g_map.isCovered(pos, m_cachedFirstVisibleFloor), rect); } for(const StaticTextPtr& staticText : g_map.getStaticTexts()) { Position pos = staticText->getPosition(); // ony draw static texts from current camera floor, unless yells //if(pos.z != cameraPosition.z && !staticText->isYell()) // continue; Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset; p.x = p.x * horizontalStretchFactor; p.y = p.y * verticalStretchFactor; p += rect.topLeft(); staticText->drawText(p, rect); } for(const AnimatedTextPtr& animatedText : g_map.getAnimatedTexts()) { Position pos = animatedText->getPosition(); // only draw animated texts from visible floors if(pos.z < m_cachedFirstVisibleFloor || pos.z > m_cachedLastVisibleFloor) continue; // dont draw animated texts from covered tiles if(pos.z != cameraPosition.z && g_map.isCovered(pos, m_cachedFirstVisibleFloor)) continue; Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset; p.x = p.x * horizontalStretchFactor; p.y = p.y * verticalStretchFactor; p += rect.topLeft(); animatedText->drawText(p, rect); } } else if(m_viewMode > NEAR_VIEW) { // draw a cross in the center instead of our creature /* Known Issue: Changing Z axis causes the cross to go off a little bit. */ Rect vRect(0, 0, 2, 10); Rect hRect(0, 0, 10, 2); g_painter->setColor(Color::white); if(!m_follow && m_followingCreature) { Position pos = m_followingCreature->getPosition(); Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset; p.x = p.x * horizontalStretchFactor; p.y = p.y * verticalStretchFactor; p += rect.topLeft(); vRect.setX(p.x); vRect.setY(p.y - 4); hRect.setX(p.x - 4); hRect.setY(p.y); hRect.setWidth(10); hRect.setHeight(2); vRect.setWidth(2); vRect.setHeight(10); } else { vRect.moveCenter(rect.center()); hRect.moveCenter(rect.center()); } g_painter->drawFilledRect(vRect); g_painter->drawFilledRect(hRect); } }
void MapView::draw(const Rect& rect) { // update visible tiles cache when needed if(m_mustUpdateVisibleTilesCache || m_updateTilesPos > 0) updateVisibleTilesCache(m_mustUpdateVisibleTilesCache ? 0 : m_updateTilesPos); float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS; Position cameraPosition = getCameraPosition(); int drawFlags = 0; if(m_viewMode == NEAR_VIEW) drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems | Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawMissiles | Otc::DrawAnimations; else drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems; if(m_mustDrawVisibleTilesCache || (drawFlags & Otc::DrawAnimations)) { m_framebuffer->bind(); if(m_mustCleanFramebuffer) { Rect clearRect = Rect(0, 0, m_drawDimension * m_tileSize); g_painter->setColor(Color::black); g_painter->drawFilledRect(clearRect); if(m_drawLights) { m_lightView->reset(); m_lightView->resize(m_framebuffer->getSize()); Light ambientLight; if(cameraPosition.z <= Otc::SEA_FLOOR) { ambientLight = g_map.getLight(); } else { ambientLight.color = 215; ambientLight.intensity = 0; } ambientLight.intensity = std::max<int>(m_minimumAmbientLight*255, ambientLight.intensity); m_lightView->setGlobalLight(ambientLight); } } g_painter->setColor(Color::white); auto it = m_cachedVisibleTiles.begin(); auto end = m_cachedVisibleTiles.end(); for(int z=m_cachedLastVisibleFloor;z>=m_cachedFirstVisibleFloor;--z) { while(it != end) { const TilePtr& tile = *it; Position tilePos = tile->getPosition(); if(tilePos.z != z) break; else ++it; tile->draw(transformPositionTo2D(tilePos, cameraPosition), scaleFactor, drawFlags, m_lightView.get()); } if(drawFlags & Otc::DrawMissiles) { for(const MissilePtr& missile : g_map.getFloorMissiles(z)) { missile->draw(transformPositionTo2D(missile->getPosition(), cameraPosition), scaleFactor, drawFlags & Otc::DrawAnimations, m_lightView.get()); } } } m_framebuffer->release(); // generating mipmaps each frame can be slow in older cards //m_framebuffer->getTexture()->buildHardwareMipmaps(); m_mustDrawVisibleTilesCache = false; } float fadeOpacity = 1.0f; if(!m_shaderSwitchDone && m_fadeOutTime > 0) { fadeOpacity = 1.0f - (m_fadeTimer.timeElapsed() / m_fadeOutTime); if(fadeOpacity < 0.0f) { m_shader = m_nextShader; m_nextShader = nullptr; m_shaderSwitchDone = true; m_fadeTimer.restart(); } } if(m_shaderSwitchDone && m_shader && m_fadeInTime > 0) fadeOpacity = std::min(m_fadeTimer.timeElapsed() / m_fadeInTime, 1.0f); Rect srcRect = calcFramebufferSource(rect.size()); Point drawOffset = srcRect.topLeft(); if(m_shader && g_painter->hasShaders() && g_graphics.shouldUseShaders() && m_viewMode == NEAR_VIEW) { Rect framebufferRect = Rect(0,0, m_drawDimension * m_tileSize); Point center = srcRect.center(); Point globalCoord = Point(cameraPosition.x - m_drawDimension.width()/2, -(cameraPosition.y - m_drawDimension.height()/2)) * m_tileSize; m_shader->bind(); m_shader->setUniformValue(ShaderManager::MAP_CENTER_COORD, center.x / (float)framebufferRect.width(), 1.0f - center.y / (float)framebufferRect.height()); m_shader->setUniformValue(ShaderManager::MAP_GLOBAL_COORD, globalCoord.x / (float)framebufferRect.height(), globalCoord.y / (float)framebufferRect.height()); m_shader->setUniformValue(ShaderManager::MAP_ZOOM, scaleFactor); g_painter->setShaderProgram(m_shader); } g_painter->setColor(Color::white); g_painter->setOpacity(fadeOpacity); glDisable(GL_BLEND); #if 0 // debug source area g_painter->saveAndResetState(); m_framebuffer->bind(); g_painter->setColor(Color::green); g_painter->drawBoundingRect(srcRect, 2); m_framebuffer->release(); g_painter->restoreSavedState(); m_framebuffer->draw(rect); #else m_framebuffer->draw(rect, srcRect); #endif g_painter->resetShaderProgram(); g_painter->resetOpacity(); glEnable(GL_BLEND); // this could happen if the player position is not known yet if(!cameraPosition.isValid()) return; float horizontalStretchFactor = rect.width() / (float)srcRect.width(); float verticalStretchFactor = rect.height() / (float)srcRect.height(); // avoid drawing texts on map in far zoom outs if(m_viewMode == NEAR_VIEW) { for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) { if(!creature->canBeSeen()) continue; PointF jumpOffset = creature->getJumpOffset() * scaleFactor; Point creatureOffset = Point(16 - creature->getDisplacementX(), - creature->getDisplacementY() - 2); Position pos = creature->getPosition(); Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset; p += (creature->getDrawOffset() + creatureOffset) * scaleFactor - Point(stdext::round(jumpOffset.x), stdext::round(jumpOffset.y)); p.x = p.x * horizontalStretchFactor; p.y = p.y * verticalStretchFactor; p += rect.topLeft(); int flags = 0; if(m_drawNames){ flags = Otc::DrawNames; } if(m_drawHealthBars) { flags |= Otc::DrawBars; } creature->drawInformation(p, g_map.isCovered(pos, m_cachedFirstVisibleFloor), rect, flags); } } // lights are drawn after names and before texts if(m_drawLights) m_lightView->draw(rect, srcRect); if(m_viewMode == NEAR_VIEW && m_drawTexts) { for(const StaticTextPtr& staticText : g_map.getStaticTexts()) { Position pos = staticText->getPosition(); // ony draw static texts from current camera floor, unless yells //if(pos.z != cameraPosition.z && !staticText->isYell()) // continue; if(pos.z != cameraPosition.z && staticText->getMessageMode() == Otc::MessageNone) continue; Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset; p.x = p.x * horizontalStretchFactor; p.y = p.y * verticalStretchFactor; p += rect.topLeft(); staticText->drawText(p, rect); } for(const AnimatedTextPtr& animatedText : g_map.getAnimatedTexts()) { Position pos = animatedText->getPosition(); /* // only draw animated texts from visible floors if(pos.z < m_cachedFirstVisibleFloor || pos.z > m_cachedLastVisibleFloor) continue; // dont draw animated texts from covered tiles if(pos.z != cameraPosition.z && g_map.isCovered(pos, m_cachedFirstVisibleFloor)) continue; */ if(pos.z != cameraPosition.z) continue; Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset; p.x = p.x * horizontalStretchFactor; p.y = p.y * verticalStretchFactor; p += rect.topLeft(); animatedText->drawText(p, rect); } } }