No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mapview.cpp 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. /*
  2. * Copyright (c) 2010-2017 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. #include "mapview.h"
  23. #include "creature.h"
  24. #include "map.h"
  25. #include "tile.h"
  26. #include "statictext.h"
  27. #include "animatedtext.h"
  28. #include "missile.h"
  29. #include "shadermanager.h"
  30. #include "lightview.h"
  31. #include <framework/graphics/graphics.h>
  32. #include <framework/graphics/image.h>
  33. #include <framework/graphics/framebuffermanager.h>
  34. #include <framework/core/eventdispatcher.h>
  35. #include <framework/core/application.h>
  36. #include <framework/core/resourcemanager.h>
  37. enum {
  38. // 3840x2160 => 1080p optimized
  39. // 2560x1440 => 720p optimized
  40. // 1728x972 => 480p optimized
  41. NEAR_VIEW_AREA = 32*32,
  42. MID_VIEW_AREA = 64*64,
  43. FAR_VIEW_AREA = 128*128,
  44. MAX_TILE_DRAWS = NEAR_VIEW_AREA*7
  45. };
  46. MapView::MapView()
  47. {
  48. m_viewMode = NEAR_VIEW;
  49. m_lockedFirstVisibleFloor = -1;
  50. m_cachedFirstVisibleFloor = 7;
  51. m_cachedLastVisibleFloor = 7;
  52. m_updateTilesPos = 0;
  53. m_fadeOutTime = 0;
  54. m_fadeInTime = 0;
  55. m_minimumAmbientLight = 0;
  56. m_optimizedSize = Size(g_map.getAwareRange().horizontal(), g_map.getAwareRange().vertical()) * Otc::TILE_PIXELS;
  57. m_framebuffer = g_framebuffers.createFrameBuffer();
  58. setVisibleDimension(Size(15, 11));
  59. m_shader = g_shaders.getDefaultMapShader();
  60. }
  61. MapView::~MapView()
  62. {
  63. #ifndef NDEBUG
  64. assert(!g_app.isTerminated());
  65. #endif
  66. }
  67. void MapView::draw(const Rect& rect)
  68. {
  69. // update visible tiles cache when needed
  70. if(m_mustUpdateVisibleTilesCache || m_updateTilesPos > 0)
  71. updateVisibleTilesCache(m_mustUpdateVisibleTilesCache ? 0 : m_updateTilesPos);
  72. float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS;
  73. Position cameraPosition = getCameraPosition();
  74. int drawFlags = 0;
  75. // First branch:
  76. // This is unlikely to be false because a lot of us
  77. // don't wanna hear their GPU fan while playing a
  78. // 2D game.
  79. //
  80. // Second & Third branch:
  81. // This is likely to be true since not many people have
  82. // low-end graphics cards.
  83. if(unlikely(g_map.isForcingAnimations()) || (likely(g_map.isShowingAnimations()) && m_viewMode == NEAR_VIEW))
  84. drawFlags = Otc::DrawAnimations;
  85. if(m_viewMode == NEAR_VIEW)
  86. drawFlags |= Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls |
  87. Otc::DrawItems | Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawMissiles;
  88. else
  89. drawFlags |= Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems;
  90. if(m_mustDrawVisibleTilesCache || (drawFlags & Otc::DrawAnimations)) {
  91. m_framebuffer->bind();
  92. if(m_mustCleanFramebuffer) {
  93. Rect clearRect = Rect(0, 0, m_drawDimension * m_tileSize);
  94. g_painter->setColor(Color::black);
  95. g_painter->drawFilledRect(clearRect);
  96. if(m_drawLights) {
  97. m_lightView->reset();
  98. m_lightView->resize(m_framebuffer->getSize());
  99. Light ambientLight;
  100. if(cameraPosition.z <= Otc::SEA_FLOOR) {
  101. ambientLight = g_map.getLight();
  102. } else {
  103. ambientLight.color = 215;
  104. ambientLight.intensity = 0;
  105. }
  106. ambientLight.intensity = std::max<int>(m_minimumAmbientLight*255, ambientLight.intensity);
  107. m_lightView->setGlobalLight(ambientLight);
  108. }
  109. }
  110. g_painter->setColor(Color::white);
  111. auto it = m_cachedVisibleTiles.begin();
  112. auto end = m_cachedVisibleTiles.end();
  113. for(int z=m_cachedLastVisibleFloor;z>=m_cachedFirstVisibleFloor;--z) {
  114. while(it != end) {
  115. const TilePtr& tile = *it;
  116. Position tilePos = tile->getPosition();
  117. if(tilePos.z != z)
  118. break;
  119. else
  120. ++it;
  121. if (g_map.isCovered(tilePos, m_cachedFirstVisibleFloor))
  122. tile->draw(transformPositionTo2D(tilePos, cameraPosition), scaleFactor, drawFlags);
  123. else
  124. tile->draw(transformPositionTo2D(tilePos, cameraPosition), scaleFactor, drawFlags, m_lightView.get());
  125. }
  126. if(drawFlags & Otc::DrawMissiles) {
  127. for(const MissilePtr& missile : g_map.getFloorMissiles(z)) {
  128. missile->draw(transformPositionTo2D(missile->getPosition(), cameraPosition), scaleFactor, drawFlags & Otc::DrawAnimations, m_lightView.get());
  129. }
  130. }
  131. }
  132. m_framebuffer->release();
  133. // generating mipmaps each frame can be slow in older cards
  134. //m_framebuffer->getTexture()->buildHardwareMipmaps();
  135. m_mustDrawVisibleTilesCache = false;
  136. }
  137. float fadeOpacity = 1.0f;
  138. if(!m_shaderSwitchDone && m_fadeOutTime > 0) {
  139. fadeOpacity = 1.0f - (m_fadeTimer.timeElapsed() / m_fadeOutTime);
  140. if(fadeOpacity < 0.0f) {
  141. m_shader = m_nextShader;
  142. m_nextShader = nullptr;
  143. m_shaderSwitchDone = true;
  144. m_fadeTimer.restart();
  145. }
  146. }
  147. if(m_shaderSwitchDone && m_shader && m_fadeInTime > 0)
  148. fadeOpacity = std::min<float>(m_fadeTimer.timeElapsed() / m_fadeInTime, 1.0f);
  149. Rect srcRect = calcFramebufferSource(rect.size());
  150. Point drawOffset = srcRect.topLeft();
  151. if(m_shader && g_painter->hasShaders() && g_graphics.shouldUseShaders() && m_viewMode == NEAR_VIEW) {
  152. Rect framebufferRect = Rect(0,0, m_drawDimension * m_tileSize);
  153. Point center = srcRect.center();
  154. Point globalCoord = Point(cameraPosition.x - m_drawDimension.width()/2, -(cameraPosition.y - m_drawDimension.height()/2)) * m_tileSize;
  155. m_shader->bind();
  156. m_shader->setUniformValue(ShaderManager::MAP_CENTER_COORD, center.x / (float)framebufferRect.width(), 1.0f - center.y / (float)framebufferRect.height());
  157. m_shader->setUniformValue(ShaderManager::MAP_GLOBAL_COORD, globalCoord.x / (float)framebufferRect.height(), globalCoord.y / (float)framebufferRect.height());
  158. m_shader->setUniformValue(ShaderManager::MAP_ZOOM, scaleFactor);
  159. g_painter->setShaderProgram(m_shader);
  160. }
  161. g_painter->setColor(Color::white);
  162. g_painter->setOpacity(fadeOpacity);
  163. glDisable(GL_BLEND);
  164. #if 0
  165. // debug source area
  166. g_painter->saveAndResetState();
  167. m_framebuffer->bind();
  168. g_painter->setColor(Color::green);
  169. g_painter->drawBoundingRect(srcRect, 2);
  170. m_framebuffer->release();
  171. g_painter->restoreSavedState();
  172. m_framebuffer->draw(rect);
  173. #else
  174. m_framebuffer->draw(rect, srcRect);
  175. #endif
  176. g_painter->resetShaderProgram();
  177. g_painter->resetOpacity();
  178. glEnable(GL_BLEND);
  179. // this could happen if the player position is not known yet
  180. if(!cameraPosition.isValid())
  181. return;
  182. float horizontalStretchFactor = rect.width() / (float)srcRect.width();
  183. float verticalStretchFactor = rect.height() / (float)srcRect.height();
  184. // avoid drawing texts on map in far zoom outs
  185. if(m_viewMode == NEAR_VIEW) {
  186. for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) {
  187. if(!creature->canBeSeen())
  188. continue;
  189. PointF jumpOffset = creature->getJumpOffset() * scaleFactor;
  190. Point creatureOffset = Point(16 - creature->getDisplacementX(), - creature->getDisplacementY() - 2);
  191. Position pos = creature->getPosition();
  192. Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
  193. p += (creature->getDrawOffset() + creatureOffset) * scaleFactor - Point(stdext::round(jumpOffset.x), stdext::round(jumpOffset.y));
  194. p.x = p.x * horizontalStretchFactor;
  195. p.y = p.y * verticalStretchFactor;
  196. p += rect.topLeft();
  197. int flags = 0;
  198. if(m_drawNames){ flags = Otc::DrawNames; }
  199. if(m_drawHealthBars) { flags |= Otc::DrawBars; }
  200. if(m_drawManaBar) { flags |= Otc::DrawManaBar; }
  201. creature->drawInformation(p, g_map.isCovered(pos, m_cachedFirstVisibleFloor), rect, flags);
  202. }
  203. }
  204. // lights are drawn after names and before texts
  205. if(m_drawLights)
  206. m_lightView->draw(rect, srcRect);
  207. if(m_viewMode == NEAR_VIEW && m_drawTexts) {
  208. for(const StaticTextPtr& staticText : g_map.getStaticTexts()) {
  209. Position pos = staticText->getPosition();
  210. // ony draw static texts from current camera floor, unless yells
  211. //if(pos.z != cameraPosition.z && !staticText->isYell())
  212. // continue;
  213. if(pos.z != cameraPosition.z && staticText->getMessageMode() == Otc::MessageNone)
  214. continue;
  215. Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
  216. p.x = p.x * horizontalStretchFactor;
  217. p.y = p.y * verticalStretchFactor;
  218. p += rect.topLeft();
  219. staticText->drawText(p, rect);
  220. }
  221. for(const AnimatedTextPtr& animatedText : g_map.getAnimatedTexts()) {
  222. Position pos = animatedText->getPosition();
  223. /*
  224. // only draw animated texts from visible floors
  225. if(pos.z < m_cachedFirstVisibleFloor || pos.z > m_cachedLastVisibleFloor)
  226. continue;
  227. // dont draw animated texts from covered tiles
  228. if(pos.z != cameraPosition.z && g_map.isCovered(pos, m_cachedFirstVisibleFloor))
  229. continue;
  230. */
  231. if(pos.z != cameraPosition.z)
  232. continue;
  233. Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
  234. p.x = p.x * horizontalStretchFactor;
  235. p.y = p.y * verticalStretchFactor;
  236. p += rect.topLeft();
  237. animatedText->drawText(p, rect);
  238. }
  239. }
  240. }
  241. void MapView::updateVisibleTilesCache(int start)
  242. {
  243. if(start == 0) {
  244. m_cachedFirstVisibleFloor = calcFirstVisibleFloor();
  245. m_cachedLastVisibleFloor = calcLastVisibleFloor();
  246. assert(m_cachedFirstVisibleFloor >= 0 && m_cachedLastVisibleFloor >= 0 &&
  247. m_cachedFirstVisibleFloor <= Otc::MAX_Z && m_cachedLastVisibleFloor <= Otc::MAX_Z);
  248. if(m_cachedLastVisibleFloor < m_cachedFirstVisibleFloor)
  249. m_cachedLastVisibleFloor = m_cachedFirstVisibleFloor;
  250. m_cachedFloorVisibleCreatures.clear();
  251. m_cachedVisibleTiles.clear();
  252. m_mustCleanFramebuffer = true;
  253. m_mustDrawVisibleTilesCache = true;
  254. m_mustUpdateVisibleTilesCache = false;
  255. m_updateTilesPos = 0;
  256. } else
  257. m_mustCleanFramebuffer = false;
  258. // there is no tile to render on invalid positions
  259. Position cameraPosition = getCameraPosition();
  260. if(!cameraPosition.isValid())
  261. return;
  262. bool stop = false;
  263. // clear current visible tiles cache
  264. m_cachedVisibleTiles.clear();
  265. m_mustDrawVisibleTilesCache = true;
  266. m_updateTilesPos = 0;
  267. // cache visible tiles in draw order
  268. // draw from last floor (the lower) to first floor (the higher)
  269. for(int iz = m_cachedLastVisibleFloor; iz >= m_cachedFirstVisibleFloor && !stop; --iz) {
  270. if(m_viewMode <= FAR_VIEW) {
  271. const int numDiagonals = m_drawDimension.width() + m_drawDimension.height() - 1;
  272. // loop through / diagonals beginning at top left and going to top right
  273. for(int diagonal = 0; diagonal < numDiagonals && !stop; ++diagonal) {
  274. // loop current diagonal tiles
  275. int advance = std::max<int>(diagonal - m_drawDimension.height(), 0);
  276. for(int iy = diagonal - advance, ix = advance; iy >= 0 && ix < m_drawDimension.width() && !stop; --iy, ++ix) {
  277. // only start really looking tiles in the desired start
  278. if(m_updateTilesPos < start) {
  279. m_updateTilesPos++;
  280. continue;
  281. }
  282. // avoid rendering too much tiles at once
  283. if((int)m_cachedVisibleTiles.size() > MAX_TILE_DRAWS && m_viewMode >= HUGE_VIEW) {
  284. stop = true;
  285. break;
  286. }
  287. // position on current floor
  288. //TODO: check position limits
  289. Position tilePos = cameraPosition.translated(ix - m_virtualCenterOffset.x, iy - m_virtualCenterOffset.y);
  290. // adjust tilePos to the wanted floor
  291. tilePos.coveredUp(cameraPosition.z - iz);
  292. if(const TilePtr& tile = g_map.getTile(tilePos)) {
  293. // skip tiles that have nothing
  294. if(!tile->isDrawable())
  295. continue;
  296. // skip tiles that are completely behind another tile
  297. if(g_map.isCompletelyCovered(tilePos, m_cachedFirstVisibleFloor))
  298. continue;
  299. m_cachedVisibleTiles.push_back(tile);
  300. }
  301. m_updateTilesPos++;
  302. }
  303. }
  304. } else {
  305. // cache tiles in spiral mode
  306. static std::vector<Point> m_spiral;
  307. if(start == 0) {
  308. m_spiral.resize(m_drawDimension.area());
  309. int width = m_drawDimension.width();
  310. int height = m_drawDimension.height();
  311. int tpx = width/2 - 2;
  312. int tpy = height/2 - 2;
  313. int count = 0;
  314. Rect area(0, 0, m_drawDimension);
  315. m_spiral[count++] = Point(tpx+1,tpy+1);
  316. for(int step = 1; tpx >= 0 || tpy >= 0; ++step, --tpx, --tpy) {
  317. int qs = 2*step;
  318. Rect lines[4] = {
  319. Rect(tpx, tpy, qs, 1),
  320. Rect(tpx + qs, tpy, 1, qs),
  321. Rect(tpx + 1, tpy + qs, qs, 1),
  322. Rect(tpx, tpy + 1, 1, qs),
  323. };
  324. for(int i=0;i<4;++i) {
  325. int sx = std::max<int>(lines[i].left(), area.left());
  326. int ex = std::min<int>(lines[i].right(), area.right());
  327. int sy = std::max<int>(lines[i].top(), area.top());
  328. int ey = std::min<int>(lines[i].bottom(), area.bottom());
  329. for(int qx=sx;qx<=ex;++qx)
  330. for(int qy=sy;qy<=ey;++qy)
  331. m_spiral[count++] = Point(qx, qy);
  332. }
  333. }
  334. }
  335. for(m_updateTilesPos = start; m_updateTilesPos < (int)m_spiral.size(); ++m_updateTilesPos) {
  336. // avoid rendering too much tiles at once
  337. if((int)m_cachedVisibleTiles.size() > MAX_TILE_DRAWS) {
  338. stop = true;
  339. break;
  340. }
  341. const Point& p = m_spiral[m_updateTilesPos];
  342. Position tilePos = cameraPosition.translated(p.x - m_virtualCenterOffset.x, p.y - m_virtualCenterOffset.y);
  343. tilePos.coveredUp(cameraPosition.z - iz);
  344. if(const TilePtr& tile = g_map.getTile(tilePos)) {
  345. if(tile->isDrawable())
  346. m_cachedVisibleTiles.push_back(tile);
  347. }
  348. }
  349. }
  350. }
  351. if(!stop) {
  352. m_updateTilesPos = 0;
  353. m_spiral.clear();
  354. }
  355. if(start == 0 && m_viewMode <= NEAR_VIEW)
  356. m_cachedFloorVisibleCreatures = g_map.getSightSpectators(cameraPosition, false);
  357. }
  358. void MapView::updateGeometry(const Size& visibleDimension, const Size& optimizedSize)
  359. {
  360. int tileSize = 0;
  361. Size bufferSize;
  362. int possiblesTileSizes[] = {1,2,4,8,16,32};
  363. for(int candidateTileSize : possiblesTileSizes) {
  364. bufferSize = (visibleDimension + Size(3,3)) * candidateTileSize;
  365. if(bufferSize.width() > g_graphics.getMaxTextureSize() || bufferSize.height() > g_graphics.getMaxTextureSize())
  366. break;
  367. tileSize = candidateTileSize;
  368. if(optimizedSize.width() < bufferSize.width() - 3*candidateTileSize && optimizedSize.height() < bufferSize.height() - 3*candidateTileSize)
  369. break;
  370. }
  371. if(tileSize == 0) {
  372. g_logger.traceError("reached max zoom out");
  373. return;
  374. }
  375. Size drawDimension = visibleDimension + Size(3,3);
  376. Point virtualCenterOffset = (drawDimension/2 - Size(1,1)).toPoint();
  377. Point visibleCenterOffset = virtualCenterOffset;
  378. ViewMode viewMode = m_viewMode;
  379. if(m_autoViewMode) {
  380. if(tileSize >= 32 && visibleDimension.area() <= NEAR_VIEW_AREA)
  381. viewMode = NEAR_VIEW;
  382. else if(tileSize >= 16 && visibleDimension.area() <= MID_VIEW_AREA)
  383. viewMode = MID_VIEW;
  384. else if(tileSize >= 8 && visibleDimension.area() <= FAR_VIEW_AREA)
  385. viewMode = FAR_VIEW;
  386. else
  387. viewMode = HUGE_VIEW;
  388. if(viewMode >= FAR_VIEW)
  389. m_multifloor = false;
  390. else
  391. m_multifloor = true;
  392. }
  393. // draw actually more than what is needed to avoid massive recalculations on huge views
  394. /*
  395. if(viewMode >= HUGE_VIEW) {
  396. Size oldDimension = drawDimension;
  397. drawDimension = (m_framebuffer->getSize() / tileSize);
  398. virtualCenterOffset += (drawDimension - oldDimension).toPoint() / 2;
  399. }
  400. */
  401. m_viewMode = viewMode;
  402. m_visibleDimension = visibleDimension;
  403. m_drawDimension = drawDimension;
  404. m_tileSize = tileSize;
  405. m_virtualCenterOffset = virtualCenterOffset;
  406. m_visibleCenterOffset = visibleCenterOffset;
  407. m_optimizedSize = optimizedSize;
  408. m_framebuffer->resize(bufferSize);
  409. requestVisibleTilesCacheUpdate();
  410. }
  411. void MapView::onTileUpdate(const Position& pos)
  412. {
  413. requestVisibleTilesCacheUpdate();
  414. }
  415. void MapView::onMapCenterChange(const Position& pos)
  416. {
  417. requestVisibleTilesCacheUpdate();
  418. }
  419. void MapView::lockFirstVisibleFloor(int firstVisibleFloor)
  420. {
  421. m_lockedFirstVisibleFloor = firstVisibleFloor;
  422. requestVisibleTilesCacheUpdate();
  423. }
  424. void MapView::unlockFirstVisibleFloor()
  425. {
  426. m_lockedFirstVisibleFloor = -1;
  427. requestVisibleTilesCacheUpdate();
  428. }
  429. void MapView::setVisibleDimension(const Size& visibleDimension)
  430. {
  431. if(visibleDimension == m_visibleDimension)
  432. return;
  433. if(visibleDimension.width() % 2 != 1 || visibleDimension.height() % 2 != 1) {
  434. g_logger.traceError("visible dimension must be odd");
  435. return;
  436. }
  437. if(visibleDimension < Size(3,3)) {
  438. g_logger.traceError("reach max zoom in");
  439. return;
  440. }
  441. updateGeometry(visibleDimension, m_optimizedSize);
  442. }
  443. void MapView::setViewMode(MapView::ViewMode viewMode)
  444. {
  445. m_viewMode = viewMode;
  446. requestVisibleTilesCacheUpdate();
  447. }
  448. void MapView::setAutoViewMode(bool enable)
  449. {
  450. m_autoViewMode = enable;
  451. if(enable)
  452. updateGeometry(m_visibleDimension, m_optimizedSize);
  453. }
  454. void MapView::optimizeForSize(const Size& visibleSize)
  455. {
  456. updateGeometry(m_visibleDimension, visibleSize);
  457. }
  458. void MapView::followCreature(const CreaturePtr& creature)
  459. {
  460. m_follow = true;
  461. m_followingCreature = creature;
  462. requestVisibleTilesCacheUpdate();
  463. }
  464. void MapView::setCameraPosition(const Position& pos)
  465. {
  466. m_follow = false;
  467. m_customCameraPosition = pos;
  468. requestVisibleTilesCacheUpdate();
  469. }
  470. Position MapView::getPosition(const Point& point, const Size& mapSize)
  471. {
  472. Position cameraPosition = getCameraPosition();
  473. // if we have no camera, its impossible to get the tile
  474. if(!cameraPosition.isValid())
  475. return Position();
  476. Rect srcRect = calcFramebufferSource(mapSize);
  477. float sh = srcRect.width() / (float)mapSize.width();
  478. float sv = srcRect.height() / (float)mapSize.height();
  479. Point framebufferPos = Point(point.x * sh, point.y * sv);
  480. Point centerOffset = (framebufferPos + srcRect.topLeft()) / m_tileSize;
  481. Point tilePos2D = getVisibleCenterOffset() - m_drawDimension.toPoint() + centerOffset + Point(2,2);
  482. if(tilePos2D.x + cameraPosition.x < 0 && tilePos2D.y + cameraPosition.y < 0)
  483. return Position();
  484. Position position = Position(tilePos2D.x, tilePos2D.y, 0) + cameraPosition;
  485. if(!position.isValid())
  486. return Position();
  487. return position;
  488. }
  489. void MapView::move(int x, int y)
  490. {
  491. m_moveOffset.x += x;
  492. m_moveOffset.y += y;
  493. int32_t tmp = m_moveOffset.x / 32;
  494. bool requestTilesUpdate = false;
  495. if(tmp != 0) {
  496. m_customCameraPosition.x += tmp;
  497. m_moveOffset.x %= 32;
  498. requestTilesUpdate = true;
  499. }
  500. tmp = m_moveOffset.y / 32;
  501. if(tmp != 0) {
  502. m_customCameraPosition.y += tmp;
  503. m_moveOffset.y %= 32;
  504. requestTilesUpdate = true;
  505. }
  506. if(requestTilesUpdate)
  507. requestVisibleTilesCacheUpdate();
  508. }
  509. Rect MapView::calcFramebufferSource(const Size& destSize)
  510. {
  511. float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS;
  512. Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1,1)).toPoint()/2) * m_tileSize;
  513. if(isFollowingCreature())
  514. drawOffset += m_followingCreature->getWalkOffset() * scaleFactor;
  515. else if(!m_moveOffset.isNull())
  516. drawOffset += m_moveOffset * scaleFactor;
  517. Size srcSize = destSize;
  518. Size srcVisible = m_visibleDimension * m_tileSize;
  519. srcSize.scale(srcVisible, Fw::KeepAspectRatio);
  520. drawOffset.x += (srcVisible.width() - srcSize.width()) / 2;
  521. drawOffset.y += (srcVisible.height() - srcSize.height()) / 2;
  522. return Rect(drawOffset, srcSize);
  523. }
  524. int MapView::calcFirstVisibleFloor()
  525. {
  526. int z = 7;
  527. // return forced first visible floor
  528. if(m_lockedFirstVisibleFloor != -1) {
  529. z = m_lockedFirstVisibleFloor;
  530. } else {
  531. Position cameraPosition = getCameraPosition();
  532. // this could happens if the player is not known yet
  533. if(cameraPosition.isValid()) {
  534. // avoid rendering multifloors in far views
  535. if(!m_multifloor) {
  536. z = cameraPosition.z;
  537. } else {
  538. // if nothing is limiting the view, the first visible floor is 0
  539. int firstFloor = 0;
  540. // limits to underground floors while under sea level
  541. if(cameraPosition.z > Otc::SEA_FLOOR)
  542. firstFloor = std::max<int>(cameraPosition.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::UNDERGROUND_FLOOR);
  543. // loop in 3x3 tiles around the camera
  544. for(int ix = -1; ix <= 1 && firstFloor < cameraPosition.z; ++ix) {
  545. for(int iy = -1; iy <= 1 && firstFloor < cameraPosition.z; ++iy) {
  546. Position pos = cameraPosition.translated(ix, iy);
  547. // process tiles that we can look through, e.g. windows, doors
  548. if((ix == 0 && iy == 0) || ((std::abs(ix) != std::abs(iy)) && g_map.isLookPossible(pos))) {
  549. Position upperPos = pos;
  550. Position coveredPos = pos;
  551. while(coveredPos.coveredUp() && upperPos.up() && upperPos.z >= firstFloor) {
  552. // check tiles physically above
  553. TilePtr tile = g_map.getTile(upperPos);
  554. if(tile && tile->limitsFloorsView(!g_map.isLookPossible(pos))) {
  555. firstFloor = upperPos.z + 1;
  556. break;
  557. }
  558. // check tiles geometrically above
  559. tile = g_map.getTile(coveredPos);
  560. if(tile && tile->limitsFloorsView(g_map.isLookPossible(pos))) {
  561. firstFloor = coveredPos.z + 1;
  562. break;
  563. }
  564. }
  565. }
  566. }
  567. }
  568. z = firstFloor;
  569. }
  570. }
  571. }
  572. // just ensure the that the floor is in the valid range
  573. z = stdext::clamp<int>(z, 0, (int)Otc::MAX_Z);
  574. return z;
  575. }
  576. int MapView::calcLastVisibleFloor()
  577. {
  578. if(!m_multifloor)
  579. return calcFirstVisibleFloor();
  580. int z = 7;
  581. Position cameraPosition = getCameraPosition();
  582. // this could happens if the player is not known yet
  583. if(cameraPosition.isValid()) {
  584. // view only underground floors when below sea level
  585. if(cameraPosition.z > Otc::SEA_FLOOR)
  586. z = cameraPosition.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE;
  587. else
  588. z = Otc::SEA_FLOOR;
  589. }
  590. if(m_lockedFirstVisibleFloor != -1)
  591. z = std::max<int>(m_lockedFirstVisibleFloor, z);
  592. // just ensure the that the floor is in the valid range
  593. z = stdext::clamp<int>(z, 0, (int)Otc::MAX_Z);
  594. return z;
  595. }
  596. Position MapView::getCameraPosition()
  597. {
  598. if(isFollowingCreature())
  599. return m_followingCreature->getPosition();
  600. return m_customCameraPosition;
  601. }
  602. void MapView::setShader(const PainterShaderProgramPtr& shader, float fadein, float fadeout)
  603. {
  604. if((m_shader == shader && m_shaderSwitchDone) || (m_nextShader == shader && !m_shaderSwitchDone))
  605. return;
  606. if(fadeout > 0.0f && m_shader) {
  607. m_nextShader = shader;
  608. m_shaderSwitchDone = false;
  609. } else {
  610. m_shader = shader;
  611. m_nextShader = nullptr;
  612. m_shaderSwitchDone = true;
  613. }
  614. m_fadeTimer.restart();
  615. m_fadeInTime = fadein;
  616. m_fadeOutTime = fadeout;
  617. }
  618. void MapView::setDrawLights(bool enable)
  619. {
  620. if(enable == m_drawLights)
  621. return;
  622. if(enable)
  623. m_lightView = LightViewPtr(new LightView);
  624. else
  625. m_lightView = nullptr;
  626. m_drawLights = enable;
  627. }
  628. /* vim: set ts=4 sw=4 et: */