57 m_lockedFirstVisibleFloor = -1;
58 m_cachedFirstVisibleFloor = 7;
59 m_cachedLastVisibleFloor = 7;
60 m_minimumAmbientLight = 0;
76 initViewPortDirection();
89 if(m_mustUpdateVisibleTilesCache)
90 updateVisibleTilesCache();
96 m_frameCache.tile->bind();
98 if(m_mustCleanFramebuffer) {
101 m_mustCleanFramebuffer =
false;
105 m_lightView->
reset();
106 m_lightView->
resize(m_frameCache.tile->getSize());
110 ambientLight.
color = 215;
114 ambientLight.
intensity = std::max<int>(m_minimumAmbientLight * 255, ambientLight.
intensity);
120 const auto& lightView = (m_lightView && m_lightView->
isDark()) ? m_lightView.
get() :
nullptr;
122 for(int_fast8_t
z = m_floorMax;
z >= m_floorMin; --
z) {
123 #if DRAW_SEPARATELY == 1
124 drawSeparately(
z, viewPort, lightView);
126 for(
const auto& tile : m_cachedVisibleTiles[
z]) {
127 if(!redrawThing && !tile->hasLight())
continue;
129 if(!canRenderTile(tile, viewPort, lightView))
continue;
131 const Position& tilePos = tile->getPosition();
133 tile->draw(transformPositionTo2D(tilePos, cameraPosition), m_scaleFactor, m_redrawFlag,
g_map.
isCovered(tilePos, m_floorMin) ?
nullptr : lightView);
137 missile->draw(transformPositionTo2D(missile->getPosition(), cameraPosition), m_scaleFactor, m_redrawFlag, lightView);
141 m_frameCache.tile->release();
149 float fadeOpacity = 1.0f;
150 if(!m_shaderSwitchDone && m_fadeOutTime > 0) {
151 fadeOpacity = 1.0f - (m_fadeTimer.
timeElapsed() / m_fadeOutTime);
152 if(fadeOpacity < 0.0f) {
153 m_shader = m_nextShader;
154 m_nextShader =
nullptr;
155 m_shaderSwitchDone =
true;
160 if(m_shaderSwitchDone && m_shader && m_fadeInTime > 0)
161 fadeOpacity = std::min<float>(m_fadeTimer.
timeElapsed() / m_fadeInTime, 1.0f);
163 const Rect srcRect = calcFramebufferSource(rect.
size());
168 const Point globalCoord =
Point(cameraPosition.
x - m_drawDimension.
width() / 2, -(cameraPosition.
y - m_drawDimension.
height() / 2)) * m_tileSize;
179 m_frameCache.tile->draw(rect, srcRect);
188 const float horizontalStretchFactor = rect.
width() /
static_cast<float>(srcRect.
width());
189 const float verticalStretchFactor = rect.
height() /
static_cast<float>(srcRect.
height());
192 #if DRAW_CREATURE_INFORMATION_AFTER_LIGHT == 0
193 drawCreatureInformation(rect, drawOffset, horizontalStretchFactor, verticalStretchFactor);
198 m_lightView->
draw(rect, srcRect);
200 #if DRAW_CREATURE_INFORMATION_AFTER_LIGHT == 1
201 drawCreatureInformation(rect, drawOffset, horizontalStretchFactor, verticalStretchFactor);
204 drawText(rect, drawOffset, horizontalStretchFactor, verticalStretchFactor);
209 void MapView::drawCreatureInformation(
const Rect& rect,
Point drawOffset,
const float horizontalStretchFactor,
const float verticalStretchFactor)
222 m_frameCache.creatureInformation->bind();
224 if(drawStaticCreatureInf) {
229 for(
const auto& creature : m_visibleCreatures) {
230 if(!creature->canBeSeen())
234 if(!drawStaticCreatureInf && !creature->updateDynamicInformation()) {
238 creature->updateDynamicInformation(
false);
240 const PointF jumpOffset = creature->getJumpOffset() * m_scaleFactor;
241 Point creatureOffset =
Point(16 - creature->getDisplacementX(), -creature->getDisplacementY() - 2);
242 Position pos = creature->getPosition();
243 Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
245 p.
x = p.
x * horizontalStretchFactor;
246 p.
y = p.
y * verticalStretchFactor;
249 creature->drawInformation(p,
g_map.
isCovered(pos, m_floorMin), rect, flags);
252 m_frameCache.creatureInformation->release();
255 m_frameCache.creatureInformation->draw();
258 void MapView::drawText(
const Rect& rect,
Point drawOffset,
const float horizontalStretchFactor,
const float verticalStretchFactor)
260 if(!m_drawTexts)
return;
266 m_frameCache.staticText->bind();
272 const Position pos = staticText->getPosition();
277 Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
278 p.
x = p.
x * horizontalStretchFactor;
279 p.
y = p.
y * verticalStretchFactor;
281 staticText->drawText(p, rect);
283 m_frameCache.staticText->release();
286 m_frameCache.staticText->draw();
290 const Position pos = animatedText->getPosition();
292 if(pos.
z != cameraPosition.
z)
295 Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
296 p.
x *= horizontalStretchFactor;
297 p.
y *= verticalStretchFactor;
300 animatedText->drawText(p, rect);
304 void MapView::updateVisibleTilesCache()
306 m_mustUpdateVisibleTilesCache =
false;
313 const int cachedFirstVisibleFloor = calcFirstVisibleFloor();
314 int cachedLastVisibleFloor = calcLastVisibleFloor();
316 assert(cachedFirstVisibleFloor >= 0 && cachedLastVisibleFloor >= 0 &&
319 if(cachedLastVisibleFloor < cachedFirstVisibleFloor)
320 cachedLastVisibleFloor = cachedFirstVisibleFloor;
322 m_mustCleanFramebuffer =
true;
331 if(m_lastCameraPosition.
z != cameraPosition.
z)
334 m_lastCameraPosition = cameraPosition;
335 m_cachedFirstVisibleFloor = cachedFirstVisibleFloor;
336 m_cachedLastVisibleFloor = cachedLastVisibleFloor;
340 m_cachedVisibleTiles[m_floorMin].clear();
341 }
while(++m_floorMin <= m_floorMax);
343 uint_fast16_t processedTiles = 0;
344 m_floorMin = cameraPosition.
z;
345 m_floorMax = cameraPosition.
z;
351 const int numDiagonals = m_drawDimension.
width() + m_drawDimension.
height() - 1;
352 for(
int iz = m_cachedLastVisibleFloor; iz >= m_cachedFirstVisibleFloor && !stop; --iz) {
353 auto& floor = m_cachedVisibleTiles[iz];
356 for(
int diagonal = 0; diagonal < numDiagonals && !stop; ++diagonal) {
358 const int advance = std::max<int>(diagonal - m_drawDimension.
height(), 0);
359 for(
int iy = diagonal - advance, ix = advance; iy >= 0 && ix < m_drawDimension.
width() && !stop; --iy, ++ix) {
368 Position tilePos = cameraPosition.
translated(ix - m_virtualCenterOffset.
x, iy - m_virtualCenterOffset.
y);
373 if(!tile->isDrawable())
380 floor.push_back(tile);
384 else if(iz > m_floorMax)
394 void MapView::updateGeometry(
const Size& visibleDimension,
const Size& optimizedSize)
399 int possiblesTileSizes[] = { 1,2,4,8,16,32 };
400 for(
int candidateTileSize : possiblesTileSizes) {
401 bufferSize = (visibleDimension +
Size(3, 3)) * candidateTileSize;
405 tileSize = candidateTileSize;
406 if(optimizedSize.
width() < bufferSize.
width() - 3 * candidateTileSize && optimizedSize.
height() < bufferSize.
height() - 3 * candidateTileSize)
411 g_logger.traceError(
"reached max zoom out");
415 Size drawDimension = visibleDimension +
Size(3, 3);
416 Point virtualCenterOffset = (drawDimension / 2 -
Size(1, 1)).toPoint();
417 const Point visibleCenterOffset = virtualCenterOffset;
441 m_viewMode = viewMode;
442 m_visibleDimension = visibleDimension;
443 m_drawDimension = drawDimension;
444 m_tileSize = tileSize;
445 m_virtualCenterOffset = virtualCenterOffset;
446 m_visibleCenterOffset = visibleCenterOffset;
447 m_optimizedSize = optimizedSize;
449 m_rectDimension =
Rect(0, 0, m_drawDimension * m_tileSize);
453 m_frameCache.tile->resize(bufferSize);
455 const Size aboveMapSize = bufferSize * 4;
456 m_frameCache.staticText->resize(aboveMapSize);
457 m_frameCache.creatureInformation->resize(aboveMapSize);
460 requestVisibleTilesCacheUpdate();
465 m_mustCleanFramebuffer =
true;
469 requestVisibleTilesCacheUpdate();
471 if(thing && thing->isCreature() && !thing->isLocalPlayer() && m_lastCameraPosition.
z ==
getCameraPosition().
z) {
474 m_visibleCreatures.push_back(creature);
476 const auto it = std::find(m_visibleCreatures.begin(), m_visibleCreatures.end(), creature);
477 if(it != m_visibleCreatures.end())
478 m_visibleCreatures.erase(it);
485 requestVisibleTilesCacheUpdate();
490 m_lockedFirstVisibleFloor = firstVisibleFloor;
491 requestVisibleTilesCacheUpdate();
496 m_lockedFirstVisibleFloor = -1;
497 requestVisibleTilesCacheUpdate();
502 if(visibleDimension == m_visibleDimension)
505 if(visibleDimension.
width() % 2 != 1 || visibleDimension.
height() % 2 != 1) {
506 g_logger.traceError(
"visible dimension must be odd");
510 if(visibleDimension <
Size(3, 3)) {
511 g_logger.traceError(
"reach max zoom in");
515 updateGeometry(visibleDimension, m_optimizedSize);
520 m_viewMode = viewMode;
521 requestVisibleTilesCacheUpdate();
526 m_autoViewMode = enable;
528 updateGeometry(m_visibleDimension, m_optimizedSize);
533 updateGeometry(m_visibleDimension, visibleSize);
539 m_followingCreature = creature;
540 requestVisibleTilesCacheUpdate();
546 m_customCameraPosition = pos;
547 requestVisibleTilesCacheUpdate();
558 const Rect srcRect = calcFramebufferSource(mapSize);
559 const float sh = srcRect.
width() /
static_cast<float>(mapSize.
width());
560 const float sv = srcRect.
height() /
static_cast<float>(mapSize.
height());
562 const Point framebufferPos =
Point(point.
x * sh, point.
y * sv);
563 const Point centerOffset = (framebufferPos + srcRect.
topLeft()) / m_tileSize;
566 if(tilePos2D.
x + cameraPosition.
x < 0 && tilePos2D.
y + cameraPosition.
y < 0)
582 int32_t tmp = m_moveOffset.
x / 32;
583 bool requestTilesUpdate =
false;
585 m_customCameraPosition.
x += tmp;
586 m_moveOffset.
x %= 32;
587 requestTilesUpdate =
true;
590 tmp = m_moveOffset.
y / 32;
592 m_customCameraPosition.
y += tmp;
593 m_moveOffset.
y %= 32;
594 requestTilesUpdate =
true;
597 if(requestTilesUpdate)
598 requestVisibleTilesCacheUpdate();
601 Rect MapView::calcFramebufferSource(
const Size& destSize)
603 Point drawOffset = ((m_drawDimension - m_visibleDimension -
Size(1, 1)).toPoint() / 2) * m_tileSize;
605 drawOffset += m_followingCreature->
getWalkOffset() * m_scaleFactor;
606 else if(!m_moveOffset.
isNull())
607 drawOffset += m_moveOffset * m_scaleFactor;
609 Size srcSize = destSize;
610 const Size srcVisible = m_visibleDimension * m_tileSize;
612 drawOffset.
x += (srcVisible.
width() - srcSize.
width()) / 2;
613 drawOffset.
y += (srcVisible.
height() - srcSize.
height()) / 2;
615 return Rect(drawOffset, srcSize);
618 int MapView::calcFirstVisibleFloor()
622 if(m_lockedFirstVisibleFloor != -1) {
623 z = m_lockedFirstVisibleFloor;
631 z = cameraPosition.
z;
641 for(
int ix = -1; ix <= 1 && firstFloor < cameraPosition.
z; ++ix) {
642 for(
int iy = -1; iy <= 1 && firstFloor < cameraPosition.
z; ++iy) {
646 if((ix == 0 && iy == 0) || ((std::abs(ix) != std::abs(iy)) &&
g_map.
isLookPossible(pos))) {
651 while(coveredPos.
coveredUp() && upperPos.
up() && upperPos.
z >= firstFloor) {
655 firstFloor = upperPos.
z + 1;
662 firstFloor = coveredPos.
z + 1;
676 z = stdext::clamp<int>(
z, 0,
static_cast<int>(
Otc::MAX_Z));
680 int MapView::calcLastVisibleFloor()
683 return calcFirstVisibleFloor();
697 if(m_lockedFirstVisibleFloor != -1)
698 z = std::max<int>(m_lockedFirstVisibleFloor,
z);
701 z = stdext::clamp<int>(
z, 0,
static_cast<int>(
Otc::MAX_Z));
710 return m_customCameraPosition;
715 if((m_shader == shader && m_shaderSwitchDone) || (m_nextShader == shader && !m_shaderSwitchDone))
718 if(fadeout > 0.0f && m_shader) {
719 m_nextShader = shader;
720 m_shaderSwitchDone =
false;
723 m_nextShader =
nullptr;
724 m_shaderSwitchDone =
true;
727 m_fadeInTime = fadein;
728 m_fadeOutTime = fadeout;
733 if(enable == m_drawLights)
return;
738 m_mustCleanFramebuffer =
true;
739 m_drawLights = enable;
742 void MapView::initViewPortDirection()
746 ViewPort& vp = m_viewPortDirection[dir];
747 vp.top = awareRange.
top;
748 vp.right = awareRange.
right;
781 bool MapView::canRenderTile(
const TilePtr& tile,
const ViewPort& viewPort,
LightView* lightView)
786 const int dz = tilePos.
z - cameraPosition.
z;
789 if(lightView && lightView->
isDark() && tile->
hasLight())
return true;
799 if((checkPos.
x - cameraPosition.
x > viewPort.right) || (checkPos.
y - cameraPosition.
y > viewPort.bottom))
808 if(!isLocalPlayer && pos.
isValid() && !isInRange(pos))
return;
811 m_redrawFlag |= reDrawFlags;
816 bool MapView::isInRange(
const Position& pos)
820 if(camera.
z != m_lastCameraPosition.
z)
return false;
826 #if DRAW_SEPARATELY == 1
827 void MapView::drawSeparately(
const int floor,
const ViewPort& viewPort,
LightView* lightView)
830 const auto& tiles = m_cachedVisibleTiles[floor];
833 for(
const auto& tile : tiles) {
835 if(!redrawThing && !tile->
hasLight())
continue;
837 if(!canRenderTile(tile, viewPort, lightView))
continue;
839 tile->
drawGround(transformPositionTo2D(tilePos, cameraPosition), m_scaleFactor, m_redrawFlag,
g_map.
isCovered(tilePos, m_floorMin) ?
nullptr : lightView);
842 for(
const auto& tile : tiles) {
844 if(!redrawThing && !tile->
hasLight())
continue;
846 if(!canRenderTile(tile, viewPort, lightView))
continue;
850 const Point pos2d = transformPositionTo2D(tilePos, cameraPosition);
851 const auto& drawLight =
g_map.
isCovered(tilePos, m_floorMin) ? nullptr : lightView;
853 tile->
drawBottom(pos2d, m_scaleFactor, m_redrawFlag, drawLight);
854 tile->
drawTop(pos2d, m_scaleFactor, m_redrawFlag, drawLight);