minimap.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "minimap.h"
  17. #include "client.h"
  18. #include "clientmap.h"
  19. #include "settings.h"
  20. #include "shader.h"
  21. #include "mapblock.h"
  22. #include "client/renderingengine.h"
  23. ////
  24. //// MinimapUpdateThread
  25. ////
  26. MinimapUpdateThread::~MinimapUpdateThread()
  27. {
  28. for (auto &it : m_blocks_cache) {
  29. delete it.second;
  30. }
  31. for (auto &q : m_update_queue) {
  32. delete q.data;
  33. }
  34. }
  35. bool MinimapUpdateThread::pushBlockUpdate(v3s16 pos, MinimapMapblock *data)
  36. {
  37. MutexAutoLock lock(m_queue_mutex);
  38. // Find if block is already in queue.
  39. // If it is, update the data and quit.
  40. for (QueuedMinimapUpdate &q : m_update_queue) {
  41. if (q.pos == pos) {
  42. delete q.data;
  43. q.data = data;
  44. return false;
  45. }
  46. }
  47. // Add the block
  48. QueuedMinimapUpdate q;
  49. q.pos = pos;
  50. q.data = data;
  51. m_update_queue.push_back(q);
  52. return true;
  53. }
  54. bool MinimapUpdateThread::popBlockUpdate(QueuedMinimapUpdate *update)
  55. {
  56. MutexAutoLock lock(m_queue_mutex);
  57. if (m_update_queue.empty())
  58. return false;
  59. *update = m_update_queue.front();
  60. m_update_queue.pop_front();
  61. return true;
  62. }
  63. void MinimapUpdateThread::enqueueBlock(v3s16 pos, MinimapMapblock *data)
  64. {
  65. pushBlockUpdate(pos, data);
  66. deferUpdate();
  67. }
  68. void MinimapUpdateThread::doUpdate()
  69. {
  70. QueuedMinimapUpdate update;
  71. while (popBlockUpdate(&update)) {
  72. if (update.data) {
  73. // Swap two values in the map using single lookup
  74. std::pair<std::map<v3s16, MinimapMapblock*>::iterator, bool>
  75. result = m_blocks_cache.insert(std::make_pair(update.pos, update.data));
  76. if (!result.second) {
  77. delete result.first->second;
  78. result.first->second = update.data;
  79. }
  80. } else {
  81. std::map<v3s16, MinimapMapblock *>::iterator it;
  82. it = m_blocks_cache.find(update.pos);
  83. if (it != m_blocks_cache.end()) {
  84. delete it->second;
  85. m_blocks_cache.erase(it);
  86. }
  87. }
  88. }
  89. if (data->map_invalidated && data->mode != MINIMAP_MODE_OFF) {
  90. getMap(data->pos, data->map_size, data->scan_height);
  91. data->map_invalidated = false;
  92. }
  93. }
  94. void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height)
  95. {
  96. v3s16 pos_min(pos.X - size / 2, pos.Y - height / 2, pos.Z - size / 2);
  97. v3s16 pos_max(pos_min.X + size - 1, pos.Y + height / 2, pos_min.Z + size - 1);
  98. v3s16 blockpos_min = getNodeBlockPos(pos_min);
  99. v3s16 blockpos_max = getNodeBlockPos(pos_max);
  100. // clear the map
  101. for (int z = 0; z < size; z++)
  102. for (int x = 0; x < size; x++) {
  103. MinimapPixel &mmpixel = data->minimap_scan[x + z * size];
  104. mmpixel.air_count = 0;
  105. mmpixel.height = 0;
  106. mmpixel.n = MapNode(CONTENT_AIR);
  107. }
  108. // draw the map
  109. v3s16 blockpos;
  110. for (blockpos.Z = blockpos_min.Z; blockpos.Z <= blockpos_max.Z; ++blockpos.Z)
  111. for (blockpos.Y = blockpos_min.Y; blockpos.Y <= blockpos_max.Y; ++blockpos.Y)
  112. for (blockpos.X = blockpos_min.X; blockpos.X <= blockpos_max.X; ++blockpos.X) {
  113. std::map<v3s16, MinimapMapblock *>::const_iterator pblock =
  114. m_blocks_cache.find(blockpos);
  115. if (pblock == m_blocks_cache.end())
  116. continue;
  117. const MinimapMapblock &block = *pblock->second;
  118. v3s16 block_node_min(blockpos * MAP_BLOCKSIZE);
  119. v3s16 block_node_max(block_node_min + MAP_BLOCKSIZE - 1);
  120. // clip
  121. v3s16 range_min = componentwise_max(block_node_min, pos_min);
  122. v3s16 range_max = componentwise_min(block_node_max, pos_max);
  123. v3s16 pos;
  124. pos.Y = range_min.Y;
  125. for (pos.Z = range_min.Z; pos.Z <= range_max.Z; ++pos.Z)
  126. for (pos.X = range_min.X; pos.X <= range_max.X; ++pos.X) {
  127. v3s16 inblock_pos = pos - block_node_min;
  128. const MinimapPixel &in_pixel =
  129. block.data[inblock_pos.Z * MAP_BLOCKSIZE + inblock_pos.X];
  130. v3s16 inmap_pos = pos - pos_min;
  131. MinimapPixel &out_pixel =
  132. data->minimap_scan[inmap_pos.X + inmap_pos.Z * size];
  133. out_pixel.air_count += in_pixel.air_count;
  134. if (in_pixel.n.param0 != CONTENT_AIR) {
  135. out_pixel.n = in_pixel.n;
  136. out_pixel.height = inmap_pos.Y + in_pixel.height;
  137. }
  138. }
  139. }
  140. }
  141. ////
  142. //// Mapper
  143. ////
  144. Minimap::Minimap(Client *client)
  145. {
  146. this->client = client;
  147. this->driver = RenderingEngine::get_video_driver();
  148. this->m_tsrc = client->getTextureSource();
  149. this->m_shdrsrc = client->getShaderSource();
  150. this->m_ndef = client->getNodeDefManager();
  151. m_angle = 0.f;
  152. // Initialize static settings
  153. m_enable_shaders = g_settings->getBool("enable_shaders");
  154. m_surface_mode_scan_height =
  155. g_settings->getBool("minimap_double_scan_height") ? 256 : 128;
  156. // Initialize minimap data
  157. data = new MinimapData;
  158. data->mode = MINIMAP_MODE_OFF;
  159. data->is_radar = false;
  160. data->map_invalidated = true;
  161. data->texture = NULL;
  162. data->heightmap_texture = NULL;
  163. data->minimap_shape_round = g_settings->getBool("minimap_shape_round");
  164. // Get round minimap textures
  165. data->minimap_mask_round = driver->createImage(
  166. m_tsrc->getTexture("minimap_mask_round.png"),
  167. core::position2d<s32>(0, 0),
  168. core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
  169. data->minimap_overlay_round = m_tsrc->getTexture("minimap_overlay_round.png");
  170. // Get square minimap textures
  171. data->minimap_mask_square = driver->createImage(
  172. m_tsrc->getTexture("minimap_mask_square.png"),
  173. core::position2d<s32>(0, 0),
  174. core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
  175. data->minimap_overlay_square = m_tsrc->getTexture("minimap_overlay_square.png");
  176. // Create player marker texture
  177. data->player_marker = m_tsrc->getTexture("player_marker.png");
  178. // Create object marker texture
  179. data->object_marker_red = m_tsrc->getTexture("object_marker_red.png");
  180. // Create mesh buffer for minimap
  181. m_meshbuffer = getMinimapMeshBuffer();
  182. // Initialize and start thread
  183. m_minimap_update_thread = new MinimapUpdateThread();
  184. m_minimap_update_thread->data = data;
  185. m_minimap_update_thread->start();
  186. }
  187. Minimap::~Minimap()
  188. {
  189. m_minimap_update_thread->stop();
  190. m_minimap_update_thread->wait();
  191. m_meshbuffer->drop();
  192. data->minimap_mask_round->drop();
  193. data->minimap_mask_square->drop();
  194. driver->removeTexture(data->texture);
  195. driver->removeTexture(data->heightmap_texture);
  196. driver->removeTexture(data->minimap_overlay_round);
  197. driver->removeTexture(data->minimap_overlay_square);
  198. driver->removeTexture(data->object_marker_red);
  199. delete data;
  200. delete m_minimap_update_thread;
  201. }
  202. void Minimap::addBlock(v3s16 pos, MinimapMapblock *data)
  203. {
  204. m_minimap_update_thread->enqueueBlock(pos, data);
  205. }
  206. void Minimap::toggleMinimapShape()
  207. {
  208. MutexAutoLock lock(m_mutex);
  209. data->minimap_shape_round = !data->minimap_shape_round;
  210. g_settings->setBool("minimap_shape_round", data->minimap_shape_round);
  211. m_minimap_update_thread->deferUpdate();
  212. }
  213. void Minimap::setMinimapShape(MinimapShape shape)
  214. {
  215. MutexAutoLock lock(m_mutex);
  216. if (shape == MINIMAP_SHAPE_SQUARE)
  217. data->minimap_shape_round = false;
  218. else if (shape == MINIMAP_SHAPE_ROUND)
  219. data->minimap_shape_round = true;
  220. g_settings->setBool("minimap_shape_round", data->minimap_shape_round);
  221. m_minimap_update_thread->deferUpdate();
  222. }
  223. MinimapShape Minimap::getMinimapShape()
  224. {
  225. if (data->minimap_shape_round) {
  226. return MINIMAP_SHAPE_ROUND;
  227. }
  228. return MINIMAP_SHAPE_SQUARE;
  229. }
  230. void Minimap::setMinimapMode(MinimapMode mode)
  231. {
  232. static const MinimapModeDef modedefs[MINIMAP_MODE_COUNT] = {
  233. {false, 0, 0},
  234. {false, m_surface_mode_scan_height, 256},
  235. {false, m_surface_mode_scan_height, 128},
  236. {false, m_surface_mode_scan_height, 64},
  237. {true, 32, 128},
  238. {true, 32, 64},
  239. {true, 32, 32}
  240. };
  241. if (mode >= MINIMAP_MODE_COUNT)
  242. return;
  243. MutexAutoLock lock(m_mutex);
  244. data->is_radar = modedefs[mode].is_radar;
  245. data->scan_height = modedefs[mode].scan_height;
  246. data->map_size = modedefs[mode].map_size;
  247. data->mode = mode;
  248. m_minimap_update_thread->deferUpdate();
  249. }
  250. void Minimap::setPos(v3s16 pos)
  251. {
  252. bool do_update = false;
  253. {
  254. MutexAutoLock lock(m_mutex);
  255. if (pos != data->old_pos) {
  256. data->old_pos = data->pos;
  257. data->pos = pos;
  258. do_update = true;
  259. }
  260. }
  261. if (do_update)
  262. m_minimap_update_thread->deferUpdate();
  263. }
  264. void Minimap::setAngle(f32 angle)
  265. {
  266. m_angle = angle;
  267. }
  268. void Minimap::blitMinimapPixelsToImageRadar(video::IImage *map_image)
  269. {
  270. video::SColor c(240, 0, 0, 0);
  271. for (s16 x = 0; x < data->map_size; x++)
  272. for (s16 z = 0; z < data->map_size; z++) {
  273. MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size];
  274. if (mmpixel->air_count > 0)
  275. c.setGreen(core::clamp(core::round32(32 + mmpixel->air_count * 8), 0, 255));
  276. else
  277. c.setGreen(0);
  278. map_image->setPixel(x, data->map_size - z - 1, c);
  279. }
  280. }
  281. void Minimap::blitMinimapPixelsToImageSurface(
  282. video::IImage *map_image, video::IImage *heightmap_image)
  283. {
  284. // This variable creation/destruction has a 1% cost on rendering minimap
  285. video::SColor tilecolor;
  286. for (s16 x = 0; x < data->map_size; x++)
  287. for (s16 z = 0; z < data->map_size; z++) {
  288. MinimapPixel *mmpixel = &data->minimap_scan[x + z * data->map_size];
  289. const ContentFeatures &f = m_ndef->get(mmpixel->n);
  290. const TileDef *tile = &f.tiledef[0];
  291. // Color of the 0th tile (mostly this is the topmost)
  292. if(tile->has_color)
  293. tilecolor = tile->color;
  294. else
  295. mmpixel->n.getColor(f, &tilecolor);
  296. tilecolor.setRed(tilecolor.getRed() * f.minimap_color.getRed() / 255);
  297. tilecolor.setGreen(tilecolor.getGreen() * f.minimap_color.getGreen() / 255);
  298. tilecolor.setBlue(tilecolor.getBlue() * f.minimap_color.getBlue() / 255);
  299. tilecolor.setAlpha(240);
  300. map_image->setPixel(x, data->map_size - z - 1, tilecolor);
  301. u32 h = mmpixel->height;
  302. heightmap_image->setPixel(x,data->map_size - z - 1,
  303. video::SColor(255, h, h, h));
  304. }
  305. }
  306. video::ITexture *Minimap::getMinimapTexture()
  307. {
  308. // update minimap textures when new scan is ready
  309. if (data->map_invalidated)
  310. return data->texture;
  311. // create minimap and heightmap images in memory
  312. core::dimension2d<u32> dim(data->map_size, data->map_size);
  313. video::IImage *map_image = driver->createImage(video::ECF_A8R8G8B8, dim);
  314. video::IImage *heightmap_image = driver->createImage(video::ECF_A8R8G8B8, dim);
  315. video::IImage *minimap_image = driver->createImage(video::ECF_A8R8G8B8,
  316. core::dimension2d<u32>(MINIMAP_MAX_SX, MINIMAP_MAX_SY));
  317. // Blit MinimapPixels to images
  318. if (data->is_radar)
  319. blitMinimapPixelsToImageRadar(map_image);
  320. else
  321. blitMinimapPixelsToImageSurface(map_image, heightmap_image);
  322. map_image->copyToScaling(minimap_image);
  323. map_image->drop();
  324. video::IImage *minimap_mask = data->minimap_shape_round ?
  325. data->minimap_mask_round : data->minimap_mask_square;
  326. if (minimap_mask) {
  327. for (s16 y = 0; y < MINIMAP_MAX_SY; y++)
  328. for (s16 x = 0; x < MINIMAP_MAX_SX; x++) {
  329. const video::SColor &mask_col = minimap_mask->getPixel(x, y);
  330. if (!mask_col.getAlpha())
  331. minimap_image->setPixel(x, y, video::SColor(0,0,0,0));
  332. }
  333. }
  334. if (data->texture)
  335. driver->removeTexture(data->texture);
  336. if (data->heightmap_texture)
  337. driver->removeTexture(data->heightmap_texture);
  338. data->texture = driver->addTexture("minimap__", minimap_image);
  339. data->heightmap_texture =
  340. driver->addTexture("minimap_heightmap__", heightmap_image);
  341. minimap_image->drop();
  342. heightmap_image->drop();
  343. data->map_invalidated = true;
  344. return data->texture;
  345. }
  346. v3f Minimap::getYawVec()
  347. {
  348. if (data->minimap_shape_round) {
  349. return v3f(
  350. cos(m_angle * core::DEGTORAD),
  351. sin(m_angle * core::DEGTORAD),
  352. 1.0);
  353. }
  354. return v3f(1.0, 0.0, 1.0);
  355. }
  356. scene::SMeshBuffer *Minimap::getMinimapMeshBuffer()
  357. {
  358. scene::SMeshBuffer *buf = new scene::SMeshBuffer();
  359. buf->Vertices.set_used(4);
  360. buf->Indices.set_used(6);
  361. static const video::SColor c(255, 255, 255, 255);
  362. buf->Vertices[0] = video::S3DVertex(-1, -1, 0, 0, 0, 1, c, 0, 1);
  363. buf->Vertices[1] = video::S3DVertex(-1, 1, 0, 0, 0, 1, c, 0, 0);
  364. buf->Vertices[2] = video::S3DVertex( 1, 1, 0, 0, 0, 1, c, 1, 0);
  365. buf->Vertices[3] = video::S3DVertex( 1, -1, 0, 0, 0, 1, c, 1, 1);
  366. buf->Indices[0] = 0;
  367. buf->Indices[1] = 1;
  368. buf->Indices[2] = 2;
  369. buf->Indices[3] = 2;
  370. buf->Indices[4] = 3;
  371. buf->Indices[5] = 0;
  372. return buf;
  373. }
  374. void Minimap::drawMinimap()
  375. {
  376. video::ITexture *minimap_texture = getMinimapTexture();
  377. if (!minimap_texture)
  378. return;
  379. updateActiveMarkers();
  380. v2u32 screensize = RenderingEngine::get_instance()->getWindowSize();
  381. const u32 size = 0.25 * screensize.Y;
  382. core::rect<s32> oldViewPort = driver->getViewPort();
  383. core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
  384. core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
  385. driver->setViewPort(core::rect<s32>(
  386. screensize.X - size - 10, 10,
  387. screensize.X - 10, size + 10));
  388. driver->setTransform(video::ETS_PROJECTION, core::matrix4());
  389. driver->setTransform(video::ETS_VIEW, core::matrix4());
  390. core::matrix4 matrix;
  391. matrix.makeIdentity();
  392. video::SMaterial &material = m_meshbuffer->getMaterial();
  393. material.setFlag(video::EMF_TRILINEAR_FILTER, true);
  394. material.Lighting = false;
  395. material.TextureLayer[0].Texture = minimap_texture;
  396. material.TextureLayer[1].Texture = data->heightmap_texture;
  397. if (m_enable_shaders && !data->is_radar) {
  398. u16 sid = m_shdrsrc->getShader("minimap_shader", 1, 1);
  399. material.MaterialType = m_shdrsrc->getShaderInfo(sid).material;
  400. } else {
  401. material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  402. }
  403. if (data->minimap_shape_round)
  404. matrix.setRotationDegrees(core::vector3df(0, 0, 360 - m_angle));
  405. // Draw minimap
  406. driver->setTransform(video::ETS_WORLD, matrix);
  407. driver->setMaterial(material);
  408. driver->drawMeshBuffer(m_meshbuffer);
  409. // Draw overlay
  410. video::ITexture *minimap_overlay = data->minimap_shape_round ?
  411. data->minimap_overlay_round : data->minimap_overlay_square;
  412. material.TextureLayer[0].Texture = minimap_overlay;
  413. material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
  414. driver->setMaterial(material);
  415. driver->drawMeshBuffer(m_meshbuffer);
  416. // If round minimap, draw player marker
  417. if (!data->minimap_shape_round) {
  418. matrix.setRotationDegrees(core::vector3df(0, 0, m_angle));
  419. material.TextureLayer[0].Texture = data->player_marker;
  420. driver->setTransform(video::ETS_WORLD, matrix);
  421. driver->setMaterial(material);
  422. driver->drawMeshBuffer(m_meshbuffer);
  423. }
  424. // Reset transformations
  425. driver->setTransform(video::ETS_VIEW, oldViewMat);
  426. driver->setTransform(video::ETS_PROJECTION, oldProjMat);
  427. driver->setViewPort(oldViewPort);
  428. // Draw player markers
  429. v2s32 s_pos(screensize.X - size - 10, 10);
  430. core::dimension2di imgsize(data->object_marker_red->getOriginalSize());
  431. core::rect<s32> img_rect(0, 0, imgsize.Width, imgsize.Height);
  432. static const video::SColor col(255, 255, 255, 255);
  433. static const video::SColor c[4] = {col, col, col, col};
  434. f32 sin_angle = sin(m_angle * core::DEGTORAD);
  435. f32 cos_angle = cos(m_angle * core::DEGTORAD);
  436. s32 marker_size2 = 0.025 * (float)size;
  437. for (std::list<v2f>::const_iterator
  438. i = m_active_markers.begin();
  439. i != m_active_markers.end(); ++i) {
  440. v2f posf = *i;
  441. if (data->minimap_shape_round) {
  442. f32 t1 = posf.X * cos_angle - posf.Y * sin_angle;
  443. f32 t2 = posf.X * sin_angle + posf.Y * cos_angle;
  444. posf.X = t1;
  445. posf.Y = t2;
  446. }
  447. posf.X = (posf.X + 0.5) * (float)size;
  448. posf.Y = (posf.Y + 0.5) * (float)size;
  449. core::rect<s32> dest_rect(
  450. s_pos.X + posf.X - marker_size2,
  451. s_pos.Y + posf.Y - marker_size2,
  452. s_pos.X + posf.X + marker_size2,
  453. s_pos.Y + posf.Y + marker_size2);
  454. driver->draw2DImage(data->object_marker_red, dest_rect,
  455. img_rect, &dest_rect, &c[0], true);
  456. }
  457. }
  458. void Minimap::updateActiveMarkers()
  459. {
  460. video::IImage *minimap_mask = data->minimap_shape_round ?
  461. data->minimap_mask_round : data->minimap_mask_square;
  462. const std::list<Nametag *> &nametags = client->getCamera()->getNametags();
  463. m_active_markers.clear();
  464. for (Nametag *nametag : nametags) {
  465. v3s16 pos = floatToInt(nametag->parent_node->getPosition() +
  466. intToFloat(client->getCamera()->getOffset(), BS), BS);
  467. pos -= data->pos - v3s16(data->map_size / 2,
  468. data->scan_height / 2,
  469. data->map_size / 2);
  470. if (pos.X < 0 || pos.X > data->map_size ||
  471. pos.Y < 0 || pos.Y > data->scan_height ||
  472. pos.Z < 0 || pos.Z > data->map_size) {
  473. continue;
  474. }
  475. pos.X = ((float)pos.X / data->map_size) * MINIMAP_MAX_SX;
  476. pos.Z = ((float)pos.Z / data->map_size) * MINIMAP_MAX_SY;
  477. const video::SColor &mask_col = minimap_mask->getPixel(pos.X, pos.Z);
  478. if (!mask_col.getAlpha()) {
  479. continue;
  480. }
  481. m_active_markers.emplace_back(((float)pos.X / (float)MINIMAP_MAX_SX) - 0.5,
  482. (1.0 - (float)pos.Z / (float)MINIMAP_MAX_SY) - 0.5);
  483. }
  484. }
  485. ////
  486. //// MinimapMapblock
  487. ////
  488. void MinimapMapblock::getMinimapNodes(VoxelManipulator *vmanip, const v3s16 &pos)
  489. {
  490. for (s16 x = 0; x < MAP_BLOCKSIZE; x++)
  491. for (s16 z = 0; z < MAP_BLOCKSIZE; z++) {
  492. s16 air_count = 0;
  493. bool surface_found = false;
  494. MinimapPixel *mmpixel = &data[z * MAP_BLOCKSIZE + x];
  495. for (s16 y = MAP_BLOCKSIZE -1; y >= 0; y--) {
  496. v3s16 p(x, y, z);
  497. MapNode n = vmanip->getNodeNoEx(pos + p);
  498. if (!surface_found && n.getContent() != CONTENT_AIR) {
  499. mmpixel->height = y;
  500. mmpixel->n = n;
  501. surface_found = true;
  502. } else if (n.getContent() == CONTENT_AIR) {
  503. air_count++;
  504. }
  505. }
  506. if (!surface_found)
  507. mmpixel->n = MapNode(CONTENT_AIR);
  508. mmpixel->air_count = air_count;
  509. }
  510. }