Otclient 1.0  14/8/2020
mapview.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2010-2020 OTClient <https://github.com/edubart/otclient>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  * THE SOFTWARE.
21  */
22 
23 #include "mapview.h"
24 
25 #include "animatedtext.h"
26 #include "creature.h"
27 #include "game.h"
28 #include "lightview.h"
29 #include "map.h"
30 #include "missile.h"
31 #include "shadermanager.h"
32 #include "statictext.h"
33 #include "tile.h"
34 
41 
42 enum {
43  // 3840x2160 => 1080p optimized
44  // 2560x1440 => 720p optimized
45  // 1728x972 => 480p optimized
46 
47  NEAR_VIEW_AREA = 32 * 32,
48  MID_VIEW_AREA = 64 * 64,
49  FAR_VIEW_AREA = 128 * 128,
51 };
52 
54 {
55  m_viewMode = NEAR_VIEW;
56  m_redrawFlag = Otc::RedrawAll;
57  m_lockedFirstVisibleFloor = -1;
58  m_cachedFirstVisibleFloor = 7;
59  m_cachedLastVisibleFloor = 7;
60  m_minimumAmbientLight = 0;
61  m_fadeOutTime = 0;
62  m_fadeInTime = 0;
63  m_floorMax = 0;
64  m_floorMin = 0;
65 
67 
68  m_frameCache.tile = g_framebuffers.createFrameBuffer();
69  m_frameCache.staticText = g_framebuffers.createFrameBuffer();
70  m_frameCache.creatureInformation = g_framebuffers.createFrameBuffer();
71 
72  setVisibleDimension(Size(15, 11));
73 
74  m_shader = g_shaders.getDefaultMapShader();
75 
76  initViewPortDirection();
77 }
78 
80 {
81 #ifndef NDEBUG
82  assert(!g_app.isTerminated());
83 #endif
84 }
85 
86 void MapView::draw(const Rect& rect)
87 {
88  // update visible tiles cache when needed
89  if(m_mustUpdateVisibleTilesCache)
90  updateVisibleTilesCache();
91 
92  const Position cameraPosition = getCameraPosition();
93 
94  const auto redrawThing = m_redrawFlag & Otc::ReDrawThing;
95  if(redrawThing || m_redrawFlag & Otc::ReDrawLight) {
96  m_frameCache.tile->bind();
97 
98  if(m_mustCleanFramebuffer) {
100  g_painter->drawFilledRect(m_rectDimension);
101  m_mustCleanFramebuffer = false;
102  }
103 
104  if(m_redrawFlag & Otc::ReDrawLight && m_drawLights) {
105  m_lightView->reset();
106  m_lightView->resize(m_frameCache.tile->getSize());
107 
108  Light ambientLight;
109  if(cameraPosition.z > Otc::SEA_FLOOR) {
110  ambientLight.color = 215;
111  ambientLight.intensity = 0;
112  } else ambientLight = g_map.getLight();
113 
114  ambientLight.intensity = std::max<int>(m_minimumAmbientLight * 255, ambientLight.intensity);
115  m_lightView->setGlobalLight(ambientLight);
116  }
117 
119 
120  const auto& lightView = (m_lightView && m_lightView->isDark()) ? m_lightView.get() : nullptr;
121  const auto& viewPort = isFollowingCreature() && m_followingCreature->isWalking() ? m_viewPortDirection[m_followingCreature->getDirection()] : m_viewPortDirection[Otc::InvalidDirection];
122  for(int_fast8_t z = m_floorMax; z >= m_floorMin; --z) {
123 #if DRAW_SEPARATELY == 1
124  drawSeparately(z, viewPort, lightView);
125 #else
126  for(const auto& tile : m_cachedVisibleTiles[z]) {
127  if(!redrawThing && !tile->hasLight()) continue;
128 
129  if(!canRenderTile(tile, viewPort, lightView)) continue;
130 
131  const Position& tilePos = tile->getPosition();
132 
133  tile->draw(transformPositionTo2D(tilePos, cameraPosition), m_scaleFactor, m_redrawFlag, g_map.isCovered(tilePos, m_floorMin) ? nullptr : lightView);
134  }
135 #endif
136  for(const MissilePtr& missile : g_map.getFloorMissiles(z)) {
137  missile->draw(transformPositionTo2D(missile->getPosition(), cameraPosition), m_scaleFactor, m_redrawFlag, lightView);
138  }
139  }
140 
141  m_frameCache.tile->release();
142 
143  m_minTimeRender.restart();
144  }
145 
146  // generating mipmaps each frame can be slow in older cards
147  //m_framebuffer->getTexture()->buildHardwareMipmaps();
148 
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;
156  m_fadeTimer.restart();
157  }
158  }
159 
160  if(m_shaderSwitchDone && m_shader && m_fadeInTime > 0)
161  fadeOpacity = std::min<float>(m_fadeTimer.timeElapsed() / m_fadeInTime, 1.0f);
162 
163  const Rect srcRect = calcFramebufferSource(rect.size());
164  const Point drawOffset = srcRect.topLeft();
165 
166  if(m_shader && g_painter->hasShaders() && g_graphics.shouldUseShaders() && m_viewMode == NEAR_VIEW) {
167  const Point center = srcRect.center();
168  const Point globalCoord = Point(cameraPosition.x - m_drawDimension.width() / 2, -(cameraPosition.y - m_drawDimension.height() / 2)) * m_tileSize;
169  m_shader->bind();
170  m_shader->setUniformValue(ShaderManager::MAP_CENTER_COORD, center.x / static_cast<float>(m_rectDimension.width()), 1.0f - center.y / static_cast<float>(m_rectDimension.height()));
171  m_shader->setUniformValue(ShaderManager::MAP_GLOBAL_COORD, globalCoord.x / static_cast<float>(m_rectDimension.height()), globalCoord.y / static_cast<float>(m_rectDimension.height()));
172  m_shader->setUniformValue(ShaderManager::MAP_ZOOM, m_scaleFactor);
173  g_painter->setShaderProgram(m_shader);
174  }
175 
177  g_painter->setOpacity(fadeOpacity);
178  glDisable(GL_BLEND);
179  m_frameCache.tile->draw(rect, srcRect);
182  glEnable(GL_BLEND);
183 
184  // this could happen if the player position is not known yet
185  if(!cameraPosition.isValid())
186  return;
187 
188  const float horizontalStretchFactor = rect.width() / static_cast<float>(srcRect.width());
189  const float verticalStretchFactor = rect.height() / static_cast<float>(srcRect.height());
190 
191  // avoid drawing texts on map in far zoom outs
192 #if DRAW_CREATURE_INFORMATION_AFTER_LIGHT == 0
193  drawCreatureInformation(rect, drawOffset, horizontalStretchFactor, verticalStretchFactor);
194 #endif
195 
196  // lights are drawn after names and before texts
197  if(m_drawLights)
198  m_lightView->draw(rect, srcRect);
199 
200 #if DRAW_CREATURE_INFORMATION_AFTER_LIGHT == 1
201  drawCreatureInformation(rect, drawOffset, horizontalStretchFactor, verticalStretchFactor);
202 #endif
203 
204  drawText(rect, drawOffset, horizontalStretchFactor, verticalStretchFactor);
205 
206  m_redrawFlag = 0;
207 }
208 
209 void MapView::drawCreatureInformation(const Rect& rect, Point drawOffset, const float horizontalStretchFactor, const float verticalStretchFactor)
210 {
211  const bool drawStaticCreatureInf = m_redrawFlag & Otc::ReDrawStaticCreatureInformation;
212 
213  if(m_redrawFlag & Otc::ReDrawDynamicInformation || drawStaticCreatureInf) {
214  int flags = 0;
215  if(m_drawNames) { flags = Otc::DrawNames; }
216  if(m_drawHealthBars) { flags |= Otc::DrawBars; }
217  if(m_drawManaBar) { flags |= Otc::DrawManaBar; }
218 
219  if(flags) {
220  const Position cameraPosition = getCameraPosition();
221 
222  m_frameCache.creatureInformation->bind();
223 
224  if(drawStaticCreatureInf) {
225  g_painter->setAlphaWriting(true);
227  }
228 
229  for(const auto& creature : m_visibleCreatures) {
230  if(!creature->canBeSeen())
231  continue;
232 
233  // This avoids redesigning the health of the monsters that were not asked for the drawing.
234  if(!drawStaticCreatureInf && !creature->updateDynamicInformation()) {
235  continue;
236  }
237 
238  creature->updateDynamicInformation(false);
239 
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;
244  p += (creature->getDrawOffset() + creatureOffset) * m_scaleFactor - Point(stdext::round(jumpOffset.x), stdext::round(jumpOffset.y));
245  p.x = p.x * horizontalStretchFactor;
246  p.y = p.y * verticalStretchFactor;
247  p += rect.topLeft();
248 
249  creature->drawInformation(p, g_map.isCovered(pos, m_floorMin), rect, flags);
250  }
251 
252  m_frameCache.creatureInformation->release();
253  }
254  }
255  m_frameCache.creatureInformation->draw();
256 }
257 
258 void MapView::drawText(const Rect& rect, Point drawOffset, const float horizontalStretchFactor, const float verticalStretchFactor)
259 {
260  if(!m_drawTexts) return;
261 
262  const Position cameraPosition = getCameraPosition();
263 
264  if(!g_map.getStaticTexts().empty()) {
265  if(m_redrawFlag & Otc::ReDrawStaticText) {
266  m_frameCache.staticText->bind();
267 
268  g_painter->setAlphaWriting(true);
270 
271  for(const StaticTextPtr& staticText : g_map.getStaticTexts()) {
272  const Position pos = staticText->getPosition();
273 
274  if(pos.z != cameraPosition.z && staticText->getMessageMode() == Otc::MessageNone)
275  continue;
276 
277  Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
278  p.x = p.x * horizontalStretchFactor;
279  p.y = p.y * verticalStretchFactor;
280  p += rect.topLeft();
281  staticText->drawText(p, rect);
282  }
283  m_frameCache.staticText->release();
284  }
285 
286  m_frameCache.staticText->draw();
287  }
288 
289  for(const AnimatedTextPtr& animatedText : g_map.getAnimatedTexts()) {
290  const Position pos = animatedText->getPosition();
291 
292  if(pos.z != cameraPosition.z)
293  continue;
294 
295  Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
296  p.x *= horizontalStretchFactor;
297  p.y *= verticalStretchFactor;
298  p += rect.topLeft();
299 
300  animatedText->drawText(p, rect);
301  }
302 }
303 
304 void MapView::updateVisibleTilesCache()
305 {
306  m_mustUpdateVisibleTilesCache = false;
307 
308  // there is no tile to render on invalid positions
309  const Position cameraPosition = getCameraPosition();
310  if(!cameraPosition.isValid())
311  return;
312 
313  const int cachedFirstVisibleFloor = calcFirstVisibleFloor();
314  int cachedLastVisibleFloor = calcLastVisibleFloor();
315 
316  assert(cachedFirstVisibleFloor >= 0 && cachedLastVisibleFloor >= 0 &&
317  cachedFirstVisibleFloor <= Otc::MAX_Z && cachedLastVisibleFloor <= Otc::MAX_Z);
318 
319  if(cachedLastVisibleFloor < cachedFirstVisibleFloor)
320  cachedLastVisibleFloor = cachedFirstVisibleFloor;
321 
322  m_mustCleanFramebuffer = true;
323 
324  // TODO: Review
325  /*if(m_cachedFirstVisibleFloor == cachedFirstVisibleFloor &&
326  m_cachedLastVisibleFloor == cachedLastVisibleFloor &&
327  cameraPosition.z == m_lastCameraPosition.z &&
328  cameraPosition.distance(m_lastCameraPosition) < 2
329  ) return;*/
330 
331  if(m_lastCameraPosition.z != cameraPosition.z)
332  m_visibleCreatures = g_map.getSightSpectators(cameraPosition, false);
333 
334  m_lastCameraPosition = cameraPosition;
335  m_cachedFirstVisibleFloor = cachedFirstVisibleFloor;
336  m_cachedLastVisibleFloor = cachedLastVisibleFloor;
337 
338  // clear current visible tiles cache
339  do {
340  m_cachedVisibleTiles[m_floorMin].clear();
341  } while(++m_floorMin <= m_floorMax);
342 
343  uint_fast16_t processedTiles = 0;
344  m_floorMin = cameraPosition.z;
345  m_floorMax = cameraPosition.z;
346 
347  bool stop = false;
348 
349  // cache visible tiles in draw order
350  // draw from last floor (the lower) to first floor (the higher)
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];
354 
355  // loop through / diagonals beginning at top left and going to top right
356  for(int diagonal = 0; diagonal < numDiagonals && !stop; ++diagonal) {
357  // loop current diagonal tiles
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) {
360  // avoid rendering too much tiles at once
361  if(processedTiles > MAX_TILE_DRAWS && m_viewMode >= HUGE_VIEW) {
362  stop = true;
363  break;
364  }
365 
366  // position on current floor
367  //TODO: check position limits
368  Position tilePos = cameraPosition.translated(ix - m_virtualCenterOffset.x, iy - m_virtualCenterOffset.y);
369  // adjust tilePos to the wanted floor
370  tilePos.coveredUp(cameraPosition.z - iz);
371  if(const TilePtr& tile = g_map.getTile(tilePos)) {
372  // skip tiles that have nothing
373  if(!tile->isDrawable())
374  continue;
375 
376  // skip tiles that are completely behind another tile
377  if(g_map.isCompletelyCovered(tilePos, m_cachedFirstVisibleFloor))
378  continue;
379 
380  floor.push_back(tile);
381 
382  if(iz < m_floorMin)
383  m_floorMin = iz;
384  else if(iz > m_floorMax)
385  m_floorMax = iz;
386 
387  ++processedTiles;
388  }
389  }
390  }
391  }
392 }
393 
394 void MapView::updateGeometry(const Size& visibleDimension, const Size& optimizedSize)
395 {
396  int tileSize = 0;
397  Size bufferSize;
398 
399  int possiblesTileSizes[] = { 1,2,4,8,16,32 };
400  for(int candidateTileSize : possiblesTileSizes) {
401  bufferSize = (visibleDimension + Size(3, 3)) * candidateTileSize;
402  if(bufferSize.width() > g_graphics.getMaxTextureSize() || bufferSize.height() > g_graphics.getMaxTextureSize())
403  break;
404 
405  tileSize = candidateTileSize;
406  if(optimizedSize.width() < bufferSize.width() - 3 * candidateTileSize && optimizedSize.height() < bufferSize.height() - 3 * candidateTileSize)
407  break;
408  }
409 
410  if(tileSize == 0) {
411  g_logger.traceError("reached max zoom out");
412  return;
413  }
414 
415  Size drawDimension = visibleDimension + Size(3, 3);
416  Point virtualCenterOffset = (drawDimension / 2 - Size(1, 1)).toPoint();
417  const Point visibleCenterOffset = virtualCenterOffset;
418 
419  ViewMode viewMode = m_viewMode;
420  if(m_autoViewMode) {
421 
422  if(tileSize >= 32 && visibleDimension.area() <= NEAR_VIEW_AREA)
423  viewMode = NEAR_VIEW;
424  else if(tileSize >= 16 && visibleDimension.area() <= MID_VIEW_AREA)
425  viewMode = MID_VIEW;
426  else if(tileSize >= 8 && visibleDimension.area() <= FAR_VIEW_AREA)
427  viewMode = FAR_VIEW;
428  else
429  viewMode = HUGE_VIEW;
430 
431  m_multifloor = viewMode < FAR_VIEW;
432  }
433 
434  // draw actually more than what is needed to avoid massive recalculations on huge views
435  /* if(viewMode >= HUGE_VIEW) {
436  Size oldDimension = drawDimension;
437  drawDimension = (m_framebuffer->getSize() / tileSize);
438  virtualCenterOffset += (drawDimension - oldDimension).toPoint() / 2;
439  }*/
440 
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;
448 
449  m_rectDimension = Rect(0, 0, m_drawDimension * m_tileSize);
450 
451  m_scaleFactor = m_tileSize / static_cast<float>(Otc::TILE_PIXELS);
452 
453  m_frameCache.tile->resize(bufferSize);
454 
455  const Size aboveMapSize = bufferSize * 4;
456  m_frameCache.staticText->resize(aboveMapSize);
457  m_frameCache.creatureInformation->resize(aboveMapSize);
458 
459  resetLastCamera();
460  requestVisibleTilesCacheUpdate();
461 }
462 
463 void MapView::onTileUpdate(const Position& /*pos*/, const ThingPtr& thing, const Otc::Operation operation)
464 {
465  m_mustCleanFramebuffer = true;
466 
467  // Need Optimization (update only the specific Tile)
468  if(Otc::OPERATION_CLEAN == operation || m_followingCreature->isWalking())
469  requestVisibleTilesCacheUpdate();
470 
471  if(thing && thing->isCreature() && !thing->isLocalPlayer() && m_lastCameraPosition.z == getCameraPosition().z) {
472  const CreaturePtr& creature = thing->static_self_cast<Creature>();
473  if(Otc::OPERATION_ADD == operation && isInRange(thing->getPosition())) {
474  m_visibleCreatures.push_back(creature);
475  } else if(Otc::OPERATION_REMOVE == operation) {
476  const auto it = std::find(m_visibleCreatures.begin(), m_visibleCreatures.end(), creature);
477  if(it != m_visibleCreatures.end())
478  m_visibleCreatures.erase(it);
479  }
480  }
481 }
482 
484 {
485  requestVisibleTilesCacheUpdate();
486 }
487 
488 void MapView::lockFirstVisibleFloor(int firstVisibleFloor)
489 {
490  m_lockedFirstVisibleFloor = firstVisibleFloor;
491  requestVisibleTilesCacheUpdate();
492 }
493 
495 {
496  m_lockedFirstVisibleFloor = -1;
497  requestVisibleTilesCacheUpdate();
498 }
499 
500 void MapView::setVisibleDimension(const Size& visibleDimension)
501 {
502  if(visibleDimension == m_visibleDimension)
503  return;
504 
505  if(visibleDimension.width() % 2 != 1 || visibleDimension.height() % 2 != 1) {
506  g_logger.traceError("visible dimension must be odd");
507  return;
508  }
509 
510  if(visibleDimension < Size(3, 3)) {
511  g_logger.traceError("reach max zoom in");
512  return;
513  }
514 
515  updateGeometry(visibleDimension, m_optimizedSize);
516 }
517 
519 {
520  m_viewMode = viewMode;
521  requestVisibleTilesCacheUpdate();
522 }
523 
524 void MapView::setAutoViewMode(bool enable)
525 {
526  m_autoViewMode = enable;
527  if(enable)
528  updateGeometry(m_visibleDimension, m_optimizedSize);
529 }
530 
531 void MapView::optimizeForSize(const Size& visibleSize)
532 {
533  updateGeometry(m_visibleDimension, visibleSize);
534 }
535 
537 {
538  m_follow = true;
539  m_followingCreature = creature;
540  requestVisibleTilesCacheUpdate();
541 }
542 
544 {
545  m_follow = false;
546  m_customCameraPosition = pos;
547  requestVisibleTilesCacheUpdate();
548 }
549 
550 Position MapView::getPosition(const Point& point, const Size& mapSize)
551 {
552  const Position cameraPosition = getCameraPosition();
553 
554  // if we have no camera, its impossible to get the tile
555  if(!cameraPosition.isValid())
556  return Position();
557 
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());
561 
562  const Point framebufferPos = Point(point.x * sh, point.y * sv);
563  const Point centerOffset = (framebufferPos + srcRect.topLeft()) / m_tileSize;
564 
565  const Point tilePos2D = getVisibleCenterOffset() - m_drawDimension.toPoint() + centerOffset + Point(2, 2);
566  if(tilePos2D.x + cameraPosition.x < 0 && tilePos2D.y + cameraPosition.y < 0)
567  return Position();
568 
569  Position position = Position(tilePos2D.x, tilePos2D.y, 0) + cameraPosition;
570 
571  if(!position.isValid())
572  return Position();
573 
574  return position;
575 }
576 
577 void MapView::move(int x, int y)
578 {
579  m_moveOffset.x += x;
580  m_moveOffset.y += y;
581 
582  int32_t tmp = m_moveOffset.x / 32;
583  bool requestTilesUpdate = false;
584  if(tmp != 0) {
585  m_customCameraPosition.x += tmp;
586  m_moveOffset.x %= 32;
587  requestTilesUpdate = true;
588  }
589 
590  tmp = m_moveOffset.y / 32;
591  if(tmp != 0) {
592  m_customCameraPosition.y += tmp;
593  m_moveOffset.y %= 32;
594  requestTilesUpdate = true;
595  }
596 
597  if(requestTilesUpdate)
598  requestVisibleTilesCacheUpdate();
599 }
600 
601 Rect MapView::calcFramebufferSource(const Size& destSize)
602 {
603  Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1, 1)).toPoint() / 2) * m_tileSize;
604  if(isFollowingCreature())
605  drawOffset += m_followingCreature->getWalkOffset() * m_scaleFactor;
606  else if(!m_moveOffset.isNull())
607  drawOffset += m_moveOffset * m_scaleFactor;
608 
609  Size srcSize = destSize;
610  const Size srcVisible = m_visibleDimension * m_tileSize;
611  srcSize.scale(srcVisible, Fw::KeepAspectRatio);
612  drawOffset.x += (srcVisible.width() - srcSize.width()) / 2;
613  drawOffset.y += (srcVisible.height() - srcSize.height()) / 2;
614 
615  return Rect(drawOffset, srcSize);
616 }
617 
618 int MapView::calcFirstVisibleFloor()
619 {
620  int z = 7;
621  // return forced first visible floor
622  if(m_lockedFirstVisibleFloor != -1) {
623  z = m_lockedFirstVisibleFloor;
624  } else {
625  const Position cameraPosition = getCameraPosition();
626 
627  // this could happens if the player is not known yet
628  if(cameraPosition.isValid()) {
629  // avoid rendering multifloors in far views
630  if(!m_multifloor) {
631  z = cameraPosition.z;
632  } else {
633  // if nothing is limiting the view, the first visible floor is 0
634  int firstFloor = 0;
635 
636  // limits to underground floors while under sea level
637  if(cameraPosition.z > Otc::SEA_FLOOR)
638  firstFloor = std::max<int>(cameraPosition.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, static_cast<int>(Otc::UNDERGROUND_FLOOR));
639 
640  // loop in 3x3 tiles around the camera
641  for(int ix = -1; ix <= 1 && firstFloor < cameraPosition.z; ++ix) {
642  for(int iy = -1; iy <= 1 && firstFloor < cameraPosition.z; ++iy) {
643  const Position pos = cameraPosition.translated(ix, iy);
644 
645  // process tiles that we can look through, e.g. windows, doors
646  if((ix == 0 && iy == 0) || ((std::abs(ix) != std::abs(iy)) && g_map.isLookPossible(pos))) {
647  Position upperPos = pos;
648  Position coveredPos = pos;
649 
650  const auto isLookPossible = g_map.isLookPossible(pos);
651  while(coveredPos.coveredUp() && upperPos.up() && upperPos.z >= firstFloor) {
652  // check tiles physically above
653  TilePtr tile = g_map.getTile(upperPos);
654  if(tile && tile->limitsFloorsView(!isLookPossible)) {
655  firstFloor = upperPos.z + 1;
656  break;
657  }
658 
659  // check tiles geometrically above
660  tile = g_map.getTile(coveredPos);
661  if(tile && tile->limitsFloorsView(isLookPossible)) {
662  firstFloor = coveredPos.z + 1;
663  break;
664  }
665  }
666  }
667  }
668  }
669 
670  z = firstFloor;
671  }
672  }
673  }
674 
675  // just ensure the that the floor is in the valid range
676  z = stdext::clamp<int>(z, 0, static_cast<int>(Otc::MAX_Z));
677  return z;
678 }
679 
680 int MapView::calcLastVisibleFloor()
681 {
682  if(!m_multifloor)
683  return calcFirstVisibleFloor();
684 
685  int z = 7;
686 
687  const Position cameraPosition = getCameraPosition();
688  // this could happens if the player is not known yet
689  if(cameraPosition.isValid()) {
690  // view only underground floors when below sea level
691  if(cameraPosition.z > Otc::SEA_FLOOR)
692  z = cameraPosition.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE;
693  else
694  z = Otc::SEA_FLOOR;
695  }
696 
697  if(m_lockedFirstVisibleFloor != -1)
698  z = std::max<int>(m_lockedFirstVisibleFloor, z);
699 
700  // just ensure the that the floor is in the valid range
701  z = stdext::clamp<int>(z, 0, static_cast<int>(Otc::MAX_Z));
702  return z;
703 }
704 
706 {
707  if(isFollowingCreature())
708  return m_followingCreature->getPosition();
709 
710  return m_customCameraPosition;
711 }
712 
713 void MapView::setShader(const PainterShaderProgramPtr& shader, float fadein, float fadeout)
714 {
715  if((m_shader == shader && m_shaderSwitchDone) || (m_nextShader == shader && !m_shaderSwitchDone))
716  return;
717 
718  if(fadeout > 0.0f && m_shader) {
719  m_nextShader = shader;
720  m_shaderSwitchDone = false;
721  } else {
722  m_shader = shader;
723  m_nextShader = nullptr;
724  m_shaderSwitchDone = true;
725  }
726  m_fadeTimer.restart();
727  m_fadeInTime = fadein;
728  m_fadeOutTime = fadeout;
729 }
730 
731 void MapView::setDrawLights(bool enable)
732 {
733  if(enable == m_drawLights) return;
734 
735  m_lightView = enable ? LightViewPtr(new LightView) : nullptr;
736 
738  m_mustCleanFramebuffer = true;
739  m_drawLights = enable;
740 }
741 
742 void MapView::initViewPortDirection()
743 {
744  const AwareRange& awareRange = g_map.getAwareRange();
745  for(int dir = Otc::North; dir <= Otc::InvalidDirection; ++dir) {
746  ViewPort& vp = m_viewPortDirection[dir];
747  vp.top = awareRange.top;
748  vp.right = awareRange.right;
749  vp.bottom = vp.top;
750  vp.left = vp.right;
751 
752  switch(dir) {
753  case Otc::North:
754  case Otc::South:
755  vp.top += 1;
756  vp.bottom += 1;
757  break;
758 
759  case Otc::West:
760  case Otc::East:
761  vp.right += 1;
762  vp.left += 1;
763  break;
764 
765  case Otc::NorthEast:
766  case Otc::SouthEast:
767  case Otc::NorthWest:
768  case Otc::SouthWest:
769  vp.left += 1;
770  vp.bottom += 1;
771  vp.top += 1;
772  vp.right += 1;
773  break;
774 
775  default:
776  break;
777  }
778  }
779 }
780 
781 bool MapView::canRenderTile(const TilePtr& tile, const ViewPort& viewPort, LightView* lightView)
782 {
783  const Position cameraPosition = getCameraPosition();
784  const Position& tilePos = tile->getPosition();
785 
786  const int dz = tilePos.z - cameraPosition.z;
787  const Position checkPos = tilePos.translated(dz, dz);
788 
789  if(lightView && lightView->isDark() && tile->hasLight()) return true;
790 
791  // Check for non-visible tiles on the screen and ignore them
792  {
793  if((cameraPosition.x - checkPos.x >= viewPort.left) || (checkPos.x - cameraPosition.x == viewPort.right && tile->isSingleDimension() && !tile->hasDisplacement()))
794  return false;
795 
796  if((cameraPosition.y - checkPos.y >= viewPort.top) || (checkPos.y - cameraPosition.y == viewPort.bottom && tile->isSingleDimension() && !tile->hasDisplacement()))
797  return false;
798 
799  if((checkPos.x - cameraPosition.x > viewPort.right) || (checkPos.y - cameraPosition.y > viewPort.bottom))
800  return false;
801  }
802 
803  return true;
804 }
805 
806 void MapView::requestDrawing(const Position& pos, const Otc::RequestDrawFlags reDrawFlags, const bool force, const bool isLocalPlayer)
807 {
808  if(!isLocalPlayer && pos.isValid() && !isInRange(pos)) return;
809 
810  if(((force && (!isLocalPlayer || m_viewMode == NEAR_VIEW)) || m_minTimeRender.ticksElapsed() > 15))
811  m_redrawFlag |= reDrawFlags;
812 
813  if(reDrawFlags & Otc::ReDrawLight && m_lightView) m_lightView->requestDrawing(force);
814 }
815 
816 bool MapView::isInRange(const Position& pos)
817 {
818  const Position camera = getCameraPosition();
819 
820  if(camera.z != m_lastCameraPosition.z) return false;
821 
822  const AwareRange& awareRange = g_map.getAwareRange();
823  return camera.isInRange(pos, awareRange.left, awareRange.right, awareRange.top, awareRange.bottom);
824 }
825 
826 #if DRAW_SEPARATELY == 1
827 void MapView::drawSeparately(const int floor, const ViewPort& viewPort, LightView* lightView)
828 {
829  const Position cameraPosition = getCameraPosition();
830  const auto& tiles = m_cachedVisibleTiles[floor];
831  const auto redrawThing = m_redrawFlag & Otc::ReDrawThing;
832 
833  for(const auto& tile : tiles) {
834  if(!tile->hasGroundToDraw()) continue;
835  if(!redrawThing && !tile->hasLight()) continue;
836 
837  if(!canRenderTile(tile, viewPort, lightView)) continue;
838  const Position& tilePos = tile->getPosition();
839  tile->drawGround(transformPositionTo2D(tilePos, cameraPosition), m_scaleFactor, m_redrawFlag, g_map.isCovered(tilePos, m_floorMin) ? nullptr : lightView);
840  }
841 
842  for(const auto& tile : tiles) {
843  if(!tile->hasBottomToDraw() && !tile->hasTopToDraw()) continue;
844  if(!redrawThing && !tile->hasLight()) continue;
845 
846  if(!canRenderTile(tile, viewPort, lightView)) continue;
847 
848  const Position& tilePos = tile->getPosition();
849 
850  const Point pos2d = transformPositionTo2D(tilePos, cameraPosition);
851  const auto& drawLight = g_map.isCovered(tilePos, m_floorMin) ? nullptr : lightView;
852 
853  tile->drawBottom(pos2d, m_scaleFactor, m_redrawFlag, drawLight);
854  tile->drawTop(pos2d, m_scaleFactor, m_redrawFlag, drawLight);
855  }
856 }
857 #endif
858 /* vim: set ts=4 sw=4 et: */
Painter::setAlphaWriting
virtual void setAlphaWriting(bool enable)=0
Otc::InvalidDirection
@ InvalidDirection
Definition: const.h:192
Position::isInRange
bool isInRange(const Position &pos, int xRange, int yRange) const
Definition: position.h:214
Painter::setColor
virtual void setColor(const Color &color)
Definition: painter.h:77
MAX_TILE_DRAWS
@ MAX_TILE_DRAWS
Definition: mapview.cpp:50
MapView::NEAR_VIEW
@ NEAR_VIEW
Definition: mapview.h:43
LightView::setGlobalLight
void setGlobalLight(const Light &light)
Definition: lightview.cpp:75
ShaderManager::MAP_CENTER_COORD
@ MAP_CENTER_COORD
Definition: shadermanager.h:35
Otc::TILE_PIXELS
@ TILE_PIXELS
Definition: const.h:33
lightview.h
eventdispatcher.h
Otc::ReDrawStaticCreatureInformation
@ ReDrawStaticCreatureInformation
Definition: const.h:59
graphics.h
g_map
Map g_map
Definition: map.cpp:36
Otc::ReDrawStaticText
@ ReDrawStaticText
Definition: const.h:58
LightView::resize
void resize(const Size &size)
Definition: lightview.cpp:129
MapView::lockFirstVisibleFloor
void lockFirstVisibleFloor(int firstVisibleFloor)
Definition: mapview.cpp:488
FAR_VIEW_AREA
@ FAR_VIEW_AREA
Definition: mapview.cpp:49
TPoint::y
T y
Definition: point.h:83
Otc::DrawNames
@ DrawNames
Definition: const.h:83
Timer::ticksElapsed
ticks_t ticksElapsed()
Definition: timer.cpp:33
z
gc sort z
Definition: CMakeLists.txt:176
Otc::North
@ North
Definition: const.h:184
Tile::drawGround
void drawGround(const Point &dest, float scaleFactor, int reDrawFlags, LightView *lightView=nullptr)
Definition: tile.cpp:47
TRect< int >
Position::x
int x
Definition: position.h:265
TSize::scale
void scale(const TSize< T > &s, Fw::AspectRatioMode mode)
Definition: size.h:76
Painter::clear
virtual void clear(const Color &color)=0
Tile::hasLight
bool hasLight()
Definition: tile.cpp:629
Position::isValid
bool isValid() const
Definition: position.h:196
Otc::DrawBars
@ DrawBars
Definition: const.h:82
Otc::DrawManaBar
@ DrawManaBar
Definition: const.h:85
Creature::isWalking
bool isWalking()
Definition: creature.h:125
Creature
Definition: creature.h:37
Fw::KeepAspectRatio
@ KeepAspectRatio
Definition: const.h:188
Tile::hasBottomToDraw
bool hasBottomToDraw() const
Definition: tile.h:139
Tile::limitsFloorsView
bool limitsFloorsView(bool isFreeView=false)
Definition: tile.cpp:612
AwareRange::bottom
int bottom
Definition: map.h:133
Tile::hasDisplacement
bool hasDisplacement()
Definition: tile.h:134
TSize::area
T area() const
Definition: size.h:101
resourcemanager.h
Map::getStaticTexts
std::vector< StaticTextPtr > getStaticTexts()
Definition: map.h:248
Tile::getPosition
const Position & getPosition()
Definition: tile.h:89
Map::isCompletelyCovered
bool isCompletelyCovered(const Position &pos, int firstFloor=0)
Definition: map.cpp:695
Map::getTile
const TilePtr & getTile(const Position &pos)
Definition: map.cpp:358
LightView::isDark
bool isDark() const
Definition: lightview.h:51
Otc::ReDrawThing
@ ReDrawThing
Definition: const.h:56
MapView::getPosition
Position getPosition(const Point &point, const Size &mapSize)
Definition: mapview.cpp:550
Graphics::getMaxTextureSize
int getMaxTextureSize()
Definition: graphics.h:55
Otc::SEA_FLOOR
@ SEA_FLOOR
Definition: const.h:36
MapView::FAR_VIEW
@ FAR_VIEW
Definition: mapview.h:45
Point
TPoint< int > Point
Definition: point.h:86
Position::y
int y
Definition: position.h:266
MapView::isFollowingCreature
bool isFollowingCreature()
Definition: mapview.h:92
Otc::ReDrawLight
@ ReDrawLight
Definition: const.h:57
MapView::setDrawLights
void setDrawLights(bool enable)
Definition: mapview.cpp:731
Tile::drawTop
void drawTop(const Point &dest, float scaleFactor, int reDrawFlags, LightView *lightView)
Definition: tile.cpp:109
stdext::round
double round(double r)
Definition: math.cpp:64
Otc::RequestDrawFlags
RequestDrawFlags
Definition: const.h:55
ShaderProgram::setUniformValue
void setUniformValue(int location, const Color &color)
Definition: shaderprogram.h:68
TSize::width
int width() const
Definition: size.h:43
Otc::OPERATION_CLEAN
@ OPERATION_CLEAN
Definition: const.h:29
ShaderManager::getDefaultMapShader
const PainterShaderProgramPtr & getDefaultMapShader()
Definition: shadermanager.h:51
Painter::setShaderProgram
virtual void setShaderProgram(PainterShaderProgram *shaderProgram)
Definition: painter.h:80
LightView
Definition: lightview.h:37
creature.h
Map::getSightSpectators
std::vector< CreaturePtr > getSightSpectators(const Position &centerPos, bool multiFloor)
Definition: map.cpp:630
Otc::ReDrawDynamicInformation
@ ReDrawDynamicInformation
Definition: const.h:60
framebuffermanager.h
Map::getAnimatedTexts
std::vector< AnimatedTextPtr > getAnimatedTexts()
Definition: map.h:247
Otc::NorthEast
@ NorthEast
Definition: const.h:188
Position::z
short z
Definition: position.h:267
Otc::RedrawAll
@ RedrawAll
Definition: const.h:66
Painter::hasShaders
virtual bool hasShaders()=0
MapView::setShader
void setShader(const PainterShaderProgramPtr &shader, float fadein, float fadeout)
Definition: mapview.cpp:713
TRect::topLeft
TPoint< T > topLeft() const
Definition: rect.h:60
Light::intensity
uint8 intensity
Definition: thingtype.h:122
Otc::UNDERGROUND_FLOOR
@ UNDERGROUND_FLOOR
Definition: const.h:38
mapview.h
g_logger
Logger g_logger
Definition: logger.cpp:35
Tile::hasTopToDraw
bool hasTopToDraw() const
Definition: tile.h:140
Otc::OPERATION_REMOVE
@ OPERATION_REMOVE
Definition: const.h:29
Position::translated
Position translated(int dx, int dy, short dz=0) const
Definition: position.h:201
Otc::West
@ West
Definition: const.h:187
MapView::setVisibleDimension
void setVisibleDimension(const Size &visibleDimension)
Definition: mapview.cpp:500
Otc::MessageNone
@ MessageNone
Definition: const.h:309
MapView::setAutoViewMode
void setAutoViewMode(bool enable)
Definition: mapview.cpp:524
MapView::unlockFirstVisibleFloor
void unlockFirstVisibleFloor()
Definition: mapview.cpp:494
Tile::hasGroundToDraw
bool hasGroundToDraw() const
Definition: tile.h:138
Position
Definition: position.h:33
MapView::MID_VIEW
@ MID_VIEW
Definition: mapview.h:44
g_shaders
ShaderManager g_shaders
Definition: shadermanager.cpp:29
Color::white
static const Color white
Definition: color.h:101
Timer::timeElapsed
float timeElapsed()
Definition: timer.h:38
Tile::isSingleDimension
bool isSingleDimension()
Definition: tile.cpp:559
Painter::resetOpacity
void resetOpacity()
Definition: painter.h:106
missile.h
MapView::resetLastCamera
void resetLastCamera()
Definition: mapview.h:128
image.h
MID_VIEW_AREA
@ MID_VIEW_AREA
Definition: mapview.cpp:48
Otc::OPERATION_ADD
@ OPERATION_ADD
Definition: const.h:29
Map::getLight
Light getLight()
Definition: map.h:241
ShaderProgram::bind
bool bind()
Definition: shaderprogram.cpp:106
Size
TSize< int > Size
Definition: size.h:107
map.h
Timer::restart
void restart()
Definition: timer.cpp:27
TPoint::x
T x
Definition: point.h:83
g_graphics
Graphics g_graphics
Definition: graphics.cpp:44
AwareRange::vertical
int vertical()
Definition: map.h:137
MapView::getCameraPosition
Position getCameraPosition()
Definition: mapview.cpp:705
MapView::requestDrawing
void requestDrawing(const Position &pos, const Otc::RequestDrawFlags reDrawFlags, const bool force=false, const bool isLocalPlayer=false)
Definition: mapview.cpp:806
AwareRange::horizontal
int horizontal()
Definition: map.h:136
Creature::getWalkOffset
Point getWalkOffset()
Definition: creature.h:99
Creature::getDirection
Otc::Direction getDirection()
Definition: creature.h:85
Otc::MAX_Z
@ MAX_Z
Definition: const.h:37
Otc::SouthEast
@ SouthEast
Definition: const.h:189
Painter::resetShaderProgram
void resetShaderProgram()
Definition: painter.h:109
g_app
ConsoleApplication g_app
Definition: consoleapplication.cpp:32
NEAR_VIEW_AREA
@ NEAR_VIEW_AREA
Definition: mapview.cpp:47
TPoint::isNull
bool isNull() const
Definition: point.h:41
Color::black
static const Color black
Definition: color.h:102
MapView::setCameraPosition
void setCameraPosition(const Position &pos)
Definition: mapview.cpp:543
ShaderManager::MAP_GLOBAL_COORD
@ MAP_GLOBAL_COORD
Definition: shadermanager.h:36
MapView::onTileUpdate
void onTileUpdate(const Position &pos, const ThingPtr &thing, const Otc::Operation operation)
Definition: mapview.cpp:463
Rect
TRect< int > Rect
Definition: rect.h:319
FrameBufferManager::createFrameBuffer
FrameBufferPtr createFrameBuffer()
Definition: framebuffermanager.cpp:39
Otc::South
@ South
Definition: const.h:186
Graphics::shouldUseShaders
bool shouldUseShaders()
Definition: graphics.h:78
AwareRange
Definition: map.h:129
Color::alpha
static const Color alpha
Definition: color.h:100
TSize::height
int height() const
Definition: size.h:44
LightViewPtr
stdext::shared_object_ptr< LightView > LightViewPtr
Definition: declarations.h:58
LightView::requestDrawing
void requestDrawing(const bool force)
Definition: lightview.h:49
Map::getFloorMissiles
const std::vector< MissilePtr > & getFloorMissiles(int z)
Definition: map.h:245
AwareRange::left
int left
Definition: map.h:134
Otc::NorthWest
@ NorthWest
Definition: const.h:191
LightView::draw
void draw(const Rect &dest, const Rect &src)
Definition: lightview.cpp:134
stdext::shared_object_ptr
Definition: shared_object.h:39
Otc::East
@ East
Definition: const.h:185
Map::isLookPossible
bool isLookPossible(const Position &pos)
Definition: map.cpp:676
Application::isTerminated
bool isTerminated()
Definition: application.h:50
g_painter
Painter * g_painter
Definition: painter.cpp:28
Light::color
uint8 color
Definition: thingtype.h:123
LightView::reset
void reset()
Definition: lightview.cpp:70
TRect::height
T height() const
Definition: rect.h:70
ShaderManager::MAP_ZOOM
@ MAP_ZOOM
Definition: shadermanager.h:37
Light
Definition: thingtype.h:120
MapView::MapView
MapView()
Definition: mapview.cpp:53
TSize::toPoint
TPoint< T > toPoint() const
Definition: size.h:37
game.h
MapView::getVisibleCenterOffset
Point getVisibleCenterOffset()
Definition: mapview.h:74
MapView::draw
void draw(const Rect &rect)
Definition: mapview.cpp:86
MapView::ViewMode
ViewMode
Definition: mapview.h:42
MapView::followCreature
void followCreature(const CreaturePtr &creature)
Definition: mapview.cpp:536
MapView::optimizeForSize
void optimizeForSize(const Size &visibleSize)
Definition: mapview.cpp:531
Otc::Operation
Operation
Definition: const.h:28
statictext.h
TPoint< int >
TRect::size
TSize< T > size() const
Definition: rect.h:71
Thing::getPosition
Position getPosition()
Definition: thing.h:46
Otc::AWARE_UNDEGROUND_FLOOR_RANGE
@ AWARE_UNDEGROUND_FLOOR_RANGE
Definition: const.h:39
g_framebuffers
FrameBufferManager g_framebuffers
Definition: framebuffermanager.cpp:26
Painter::drawFilledRect
virtual void drawFilledRect(const Rect &dest)=0
MapView::move
void move(int x, int y)
Definition: mapview.cpp:577
Position::up
bool up(int n=1)
Definition: position.h:222
tile.h
animatedtext.h
TRect::width
T width() const
Definition: rect.h:69
MapView::~MapView
~MapView()
Definition: mapview.cpp:79
TRect::center
TPoint< T > center() const
Definition: rect.h:68
MapView::setViewMode
void setViewMode(ViewMode viewMode)
Definition: mapview.cpp:518
TSize< int >
AwareRange::top
int top
Definition: map.h:131
AwareRange::right
int right
Definition: map.h:132
Tile::drawBottom
void drawBottom(const Point &dest, float scaleFactor, int reDrawFlags, LightView *lightView=nullptr)
Definition: tile.cpp:59
Otc::SouthWest
@ SouthWest
Definition: const.h:190
stdext::shared_object_ptr::get
T * get() const
Definition: shared_object.h:82
Painter::setOpacity
virtual void setOpacity(float opacity)
Definition: painter.h:91
shadermanager.h
application.h
MapView::onMapCenterChange
void onMapCenterChange(const Position &pos)
Definition: mapview.cpp:483
Map::isCovered
bool isCovered(const Position &pos, int firstFloor=0)
Definition: map.cpp:682
MapView::HUGE_VIEW
@ HUGE_VIEW
Definition: mapview.h:46
Map::getAwareRange
AwareRange getAwareRange()
Definition: map.h:239
Position::coveredUp
bool coveredUp(int n=1)
Definition: position.h:243