Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
4 :
5 : This program is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Lesser General Public License as published by
7 : the Free Software Foundation; either version 2.1 of the License, or
8 : (at your option) any later version.
9 :
10 : This program is distributed in the hope that it will be useful,
11 : but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU Lesser General Public License for more details.
14 :
15 : You should have received a copy of the GNU Lesser General Public License along
16 : with this program; if not, write to the Free Software Foundation, Inc.,
17 : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 : */
19 :
20 : #include "minimap.h"
21 : #include "logoutputbuffer.h"
22 : #include "jthread/jmutexautolock.h"
23 : #include "jthread/jsemaphore.h"
24 : #include "clientmap.h"
25 : #include "settings.h"
26 : #include "nodedef.h"
27 : #include "porting.h"
28 : #include "util/numeric.h"
29 : #include "util/string.h"
30 : #include <math.h>
31 :
32 2124 : QueuedMinimapUpdate::QueuedMinimapUpdate():
33 : pos(-1337,-1337,-1337),
34 2124 : data(NULL)
35 : {
36 2124 : }
37 :
38 0 : QueuedMinimapUpdate::~QueuedMinimapUpdate()
39 : {
40 0 : delete data;
41 0 : }
42 :
43 1 : MinimapUpdateQueue::MinimapUpdateQueue()
44 : {
45 1 : }
46 :
47 0 : MinimapUpdateQueue::~MinimapUpdateQueue()
48 : {
49 0 : JMutexAutoLock lock(m_mutex);
50 :
51 0 : for (std::list<QueuedMinimapUpdate*>::iterator
52 0 : i = m_queue.begin();
53 0 : i != m_queue.end(); ++i) {
54 0 : QueuedMinimapUpdate *q = *i;
55 0 : delete q;
56 : }
57 0 : }
58 :
59 2124 : bool MinimapUpdateQueue::addBlock(v3s16 pos, MinimapMapblock *data)
60 : {
61 4248 : DSTACK(__FUNCTION_NAME);
62 :
63 4248 : JMutexAutoLock lock(m_mutex);
64 :
65 : /*
66 : Find if block is already in queue.
67 : If it is, update the data and quit.
68 : */
69 7614 : for (std::list<QueuedMinimapUpdate*>::iterator
70 2124 : i = m_queue.begin();
71 6492 : i != m_queue.end(); ++i) {
72 1122 : QueuedMinimapUpdate *q = *i;
73 1122 : if (q->pos == pos) {
74 0 : delete q->data;
75 0 : q->data = data;
76 0 : return false;
77 : }
78 : }
79 :
80 : /*
81 : Add the block
82 : */
83 2124 : QueuedMinimapUpdate *q = new QueuedMinimapUpdate;
84 2124 : q->pos = pos;
85 2124 : q->data = data;
86 2124 : m_queue.push_back(q);
87 2124 : return true;
88 : }
89 :
90 2124 : QueuedMinimapUpdate * MinimapUpdateQueue::pop()
91 : {
92 4248 : JMutexAutoLock lock(m_mutex);
93 :
94 4248 : for (std::list<QueuedMinimapUpdate*>::iterator
95 2124 : i = m_queue.begin();
96 4248 : i != m_queue.end(); i++) {
97 2124 : QueuedMinimapUpdate *q = *i;
98 2124 : m_queue.erase(i);
99 2124 : return q;
100 : }
101 0 : return NULL;
102 : }
103 :
104 : /*
105 : Minimap update thread
106 : */
107 :
108 2124 : void MinimapUpdateThread::enqueue_Block(v3s16 pos, MinimapMapblock *data)
109 : {
110 2124 : m_queue.addBlock(pos, data);
111 2124 : deferUpdate();
112 2124 : }
113 :
114 3615 : void MinimapUpdateThread::doUpdate()
115 : {
116 5739 : while (m_queue.size()) {
117 2124 : QueuedMinimapUpdate *q = m_queue.pop();
118 2124 : std::map<v3s16, MinimapMapblock *>::iterator it;
119 2124 : it = m_blocks_cache.find(q->pos);
120 2124 : if (q->data) {
121 2124 : m_blocks_cache[q->pos] = q->data;
122 0 : } else if (it != m_blocks_cache.end()) {
123 0 : delete it->second;
124 0 : m_blocks_cache.erase(it);
125 : }
126 : }
127 1491 : if (data->map_invalidated) {
128 1491 : if (data->mode != MINIMAP_MODE_OFF) {
129 0 : getMap(data->pos, data->map_size, data->scan_height, data->radar);
130 0 : data->map_invalidated = false;
131 : }
132 : }
133 1491 : }
134 :
135 0 : MinimapUpdateThread::~MinimapUpdateThread()
136 : {
137 0 : for (std::map<v3s16, MinimapMapblock *>::iterator
138 0 : it = m_blocks_cache.begin();
139 0 : it != m_blocks_cache.end(); it++) {
140 0 : delete it->second;
141 : }
142 0 : }
143 :
144 0 : MinimapPixel *MinimapUpdateThread::getMinimapPixel(v3s16 pos, s16 height, s16 &pixel_height)
145 : {
146 0 : pixel_height = height - MAP_BLOCKSIZE;
147 0 : v3s16 blockpos_max, blockpos_min, relpos;
148 0 : getNodeBlockPosWithOffset(v3s16(pos.X, pos.Y - height / 2, pos.Z), blockpos_min, relpos);
149 0 : getNodeBlockPosWithOffset(v3s16(pos.X, pos.Y + height / 2, pos.Z), blockpos_max, relpos);
150 0 : std::map<v3s16, MinimapMapblock *>::iterator it;
151 0 : for (s16 i = blockpos_max.Y; i > blockpos_min.Y - 1; i--) {
152 0 : it = m_blocks_cache.find(v3s16(blockpos_max.X, i, blockpos_max.Z));
153 0 : if (it != m_blocks_cache.end()) {
154 0 : MinimapPixel *pixel = &it->second->data[relpos.X + relpos.Z * MAP_BLOCKSIZE];
155 0 : if (pixel->id != CONTENT_AIR) {
156 0 : pixel_height += pixel->height;
157 0 : return pixel;
158 : }
159 : }
160 0 : pixel_height -= MAP_BLOCKSIZE;
161 : }
162 0 : return NULL;
163 : }
164 :
165 0 : s16 MinimapUpdateThread::getAirCount(v3s16 pos, s16 height)
166 : {
167 0 : s16 air_count = 0;
168 0 : v3s16 blockpos_max, blockpos_min, relpos;
169 0 : getNodeBlockPosWithOffset(v3s16(pos.X, pos.Y - height / 2, pos.Z), blockpos_min, relpos);
170 0 : getNodeBlockPosWithOffset(v3s16(pos.X, pos.Y + height / 2, pos.Z), blockpos_max, relpos);
171 0 : std::map<v3s16, MinimapMapblock *>::iterator it;
172 0 : for (s16 i = blockpos_max.Y; i > blockpos_min.Y - 1; i--) {
173 0 : it = m_blocks_cache.find(v3s16(blockpos_max.X, i, blockpos_max.Z));
174 0 : if (it != m_blocks_cache.end()) {
175 0 : MinimapPixel *pixel = &it->second->data[relpos.X + relpos.Z * MAP_BLOCKSIZE];
176 0 : air_count += pixel->air_count;
177 : }
178 : }
179 0 : return air_count;
180 : }
181 :
182 0 : void MinimapUpdateThread::getMap(v3s16 pos, s16 size, s16 height, bool radar)
183 : {
184 0 : v3s16 p = v3s16 (pos.X - size / 2, pos.Y, pos.Z - size / 2);
185 :
186 0 : for (s16 x = 0; x < size; x++) {
187 0 : for (s16 z = 0; z < size; z++){
188 0 : u16 id = CONTENT_AIR;
189 0 : MinimapPixel* minimap_pixel = &data->minimap_scan[x + z * size];
190 0 : if (!radar) {
191 0 : s16 pixel_height = 0;
192 : MinimapPixel* cached_pixel =
193 0 : getMinimapPixel(v3s16(p.X + x, p.Y, p.Z + z), height, pixel_height);
194 0 : if (cached_pixel) {
195 0 : id = cached_pixel->id;
196 0 : minimap_pixel->height = pixel_height;
197 : }
198 : } else {
199 0 : minimap_pixel->air_count = getAirCount (v3s16(p.X + x, p.Y, p.Z + z), height);
200 : }
201 0 : minimap_pixel->id = id;
202 : }
203 : }
204 0 : }
205 :
206 1 : Mapper::Mapper(IrrlichtDevice *device, Client *client)
207 : {
208 1 : this->device = device;
209 1 : this->client = client;
210 1 : this->driver = device->getVideoDriver();
211 1 : this->tsrc = client->getTextureSource();
212 1 : this->player = client->getEnv().getLocalPlayer();
213 1 : this->shdrsrc = client->getShaderSource();
214 :
215 1 : m_enable_shaders = g_settings->getBool("enable_shaders");
216 1 : m_enable_shaders = g_settings->getBool("enable_shaders");
217 1 : if (g_settings->getBool("minimap_double_scan_height")) {
218 1 : m_surface_mode_scan_height = 256;
219 : } else {
220 0 : m_surface_mode_scan_height = 128;
221 : }
222 1 : data = new MinimapData;
223 1 : data->mode = MINIMAP_MODE_OFF;
224 1 : data->radar = false;
225 1 : data->map_invalidated = true;
226 1 : data->heightmap_image = NULL;
227 1 : data->minimap_image = NULL;
228 1 : data->texture = NULL;
229 1 : data->minimap_shape_round = g_settings->getBool("minimap_shape_round");
230 2 : std::string fname1 = "minimap_mask_round.png";
231 2 : std::string fname2 = "minimap_overlay_round.png";
232 3 : data->minimap_mask_round = driver->createImage (tsrc->getTexture(fname1),
233 2 : core::position2d<s32>(0,0), core::dimension2d<u32>(512,512));
234 1 : data->minimap_overlay_round = tsrc->getTexture(fname2);
235 1 : fname1 = "minimap_mask_square.png";
236 1 : fname2 = "minimap_overlay_square.png";
237 3 : data->minimap_mask_square = driver->createImage (tsrc->getTexture(fname1),
238 2 : core::position2d<s32>(0,0), core::dimension2d<u32>(512,512));
239 1 : data->minimap_overlay_square = tsrc->getTexture(fname2);
240 1 : data->player_marker = tsrc->getTexture("player_marker.png");
241 1 : m_meshbuffer = getMinimapMeshBuffer();
242 1 : m_minimap_update_thread = new MinimapUpdateThread(device, client);
243 1 : m_minimap_update_thread->data = data;
244 1 : m_minimap_update_thread->Start();
245 1 : }
246 :
247 0 : Mapper::~Mapper()
248 : {
249 0 : m_minimap_update_thread->Stop();
250 0 : m_minimap_update_thread->Wait();
251 0 : m_meshbuffer->drop();
252 0 : data->minimap_mask_round->drop();
253 0 : data->minimap_mask_square->drop();
254 0 : driver->removeTexture(data->texture);
255 0 : driver->removeTexture(data->heightmap_texture);
256 0 : driver->removeTexture(data->minimap_overlay_round);
257 0 : driver->removeTexture(data->minimap_overlay_square);
258 0 : delete data;
259 0 : delete m_minimap_update_thread;
260 0 : }
261 :
262 2124 : void Mapper::addBlock (v3s16 pos, MinimapMapblock *data)
263 : {
264 2124 : m_minimap_update_thread->enqueue_Block(pos, data);
265 2124 : }
266 :
267 0 : MinimapMode Mapper::getMinimapMode()
268 : {
269 0 : return data->mode;
270 : }
271 :
272 0 : void Mapper::toggleMinimapShape()
273 : {
274 0 : data->minimap_shape_round = !data->minimap_shape_round;
275 0 : g_settings->setBool(("minimap_shape_round"), data->minimap_shape_round);
276 0 : m_minimap_update_thread->deferUpdate();
277 0 : }
278 :
279 1 : void Mapper::setMinimapMode(MinimapMode mode)
280 : {
281 : static const u16 modeDefs[7 * 3] = {
282 : 0, 0, 0,
283 1 : 0, m_surface_mode_scan_height, 256,
284 1 : 0, m_surface_mode_scan_height, 128,
285 1 : 0, m_surface_mode_scan_height, 64,
286 : 1, 32, 128,
287 : 1, 32, 64,
288 4 : 1, 32, 32};
289 :
290 2 : JMutexAutoLock lock(m_mutex);
291 1 : data->radar = (bool)modeDefs[(int)mode * 3];
292 1 : data->scan_height = modeDefs[(int)mode * 3 + 1];
293 1 : data->map_size = modeDefs[(int)mode * 3 + 2];
294 1 : data->mode = mode;
295 1 : m_minimap_update_thread->deferUpdate();
296 1 : }
297 :
298 1166 : void Mapper::setPos(v3s16 pos)
299 : {
300 2332 : JMutexAutoLock lock(m_mutex);
301 1166 : if (pos != data->old_pos) {
302 155 : data->old_pos = data->pos;
303 155 : data->pos = pos;
304 155 : m_minimap_update_thread->deferUpdate();
305 : }
306 1166 : }
307 :
308 1166 : video::ITexture *Mapper::getMinimapTexture()
309 : {
310 : // update minimap textures when new scan is ready
311 1166 : if (!data->map_invalidated) {
312 : // create minimap and heightmap image
313 0 : core::dimension2d<u32> dim(data->map_size,data->map_size);
314 0 : video::IImage *map_image = driver->createImage(video::ECF_A8R8G8B8, dim);
315 0 : video::IImage *heightmap_image = driver->createImage(video::ECF_A8R8G8B8, dim);
316 0 : video::IImage *minimap_image = driver->createImage(video::ECF_A8R8G8B8,
317 0 : core::dimension2d<u32>(512, 512));
318 :
319 0 : video::SColor c;
320 0 : if (!data->radar) {
321 : // surface mode
322 0 : for (s16 x = 0; x < data->map_size; x++) {
323 0 : for (s16 z = 0; z < data->map_size; z++) {
324 0 : MinimapPixel* minimap_pixel = &data->minimap_scan[x + z * data->map_size];
325 0 : const ContentFeatures &f = client->getNodeDefManager()->get(minimap_pixel->id);
326 0 : c = f.minimap_color;
327 0 : c.setAlpha(240);
328 0 : map_image->setPixel(x, data->map_size - z -1, c);
329 0 : u32 h = minimap_pixel->height;
330 0 : heightmap_image->setPixel(x,data->map_size -z -1,
331 0 : video::SColor(255, h, h, h));
332 : }
333 : }
334 : } else {
335 : // radar mode
336 0 : c = video::SColor (240, 0 , 0, 0);
337 0 : for (s16 x = 0; x < data->map_size; x++) {
338 0 : for (s16 z = 0; z < data->map_size; z++) {
339 0 : MinimapPixel* minimap_pixel = &data->minimap_scan[x + z * data->map_size];
340 0 : if (minimap_pixel->air_count > 0) {
341 0 : c.setGreen(core::clamp(core::round32(32 + minimap_pixel->air_count * 8), 0, 255));
342 : } else {
343 0 : c.setGreen(0);
344 : }
345 0 : map_image->setPixel(x, data->map_size - z -1, c);
346 : }
347 : }
348 : }
349 :
350 0 : map_image->copyToScaling(minimap_image);
351 0 : map_image->drop();
352 :
353 : video::IImage *minimap_mask;
354 0 : if (data->minimap_shape_round) {
355 0 : minimap_mask = data->minimap_mask_round;
356 : } else {
357 0 : minimap_mask = data->minimap_mask_square;
358 : }
359 0 : for (s16 x = 0; x < 512; x++) {
360 0 : for (s16 y = 0; y < 512; y++) {
361 0 : video::SColor mask_col = minimap_mask->getPixel(x, y);
362 0 : if (!mask_col.getAlpha()) {
363 0 : minimap_image->setPixel(x, y, video::SColor(0,0,0,0));
364 : }
365 : }
366 : }
367 :
368 0 : if (data->texture) {
369 0 : driver->removeTexture(data->texture);
370 : }
371 0 : if (data->heightmap_texture) {
372 0 : driver->removeTexture(data->heightmap_texture);
373 : }
374 0 : data->texture = driver->addTexture("minimap__", minimap_image);
375 0 : data->heightmap_texture = driver->addTexture("minimap_heightmap__", heightmap_image);
376 0 : minimap_image->drop();
377 0 : heightmap_image->drop();
378 :
379 0 : data->map_invalidated = true;
380 : }
381 1166 : return data->texture;
382 : }
383 :
384 1426078 : v3f Mapper::getYawVec()
385 : {
386 1426078 : if (data->minimap_shape_round) {
387 1426078 : return v3f(cos(player->getYaw()* core::DEGTORAD),
388 2852156 : sin(player->getYaw()* core::DEGTORAD), 1.0);
389 : } else {
390 0 : return v3f(1.0, 0.0, 1.0);
391 : }
392 : }
393 :
394 1 : scene::SMeshBuffer *Mapper::getMinimapMeshBuffer()
395 : {
396 1 : scene::SMeshBuffer *buf = new scene::SMeshBuffer();
397 1 : buf->Vertices.set_used(4);
398 1 : buf->Indices .set_used(6);
399 1 : video::SColor c(255, 255, 255, 255);
400 :
401 1 : buf->Vertices[0] = video::S3DVertex(-1, -1, 0, 0, 0, 1, c, 0, 1);
402 1 : buf->Vertices[1] = video::S3DVertex(-1, 1, 0, 0, 0, 1, c, 0, 0);
403 1 : buf->Vertices[2] = video::S3DVertex( 1, 1, 0, 0, 0, 1, c, 1, 0);
404 1 : buf->Vertices[3] = video::S3DVertex( 1, -1, 0, 0, 0, 1, c, 1, 1);
405 :
406 1 : buf->Indices[0] = 0;
407 1 : buf->Indices[1] = 1;
408 1 : buf->Indices[2] = 2;
409 1 : buf->Indices[3] = 2;
410 1 : buf->Indices[4] = 3;
411 1 : buf->Indices[5] = 0;
412 :
413 1 : return buf;
414 : }
415 :
416 1166 : void Mapper::drawMinimap()
417 : {
418 1166 : v2u32 screensize = porting::getWindowSize();
419 1166 : u32 size = 0.25 * screensize.Y;
420 1166 : video::ITexture* minimap_texture = getMinimapTexture();
421 1166 : core::matrix4 matrix;
422 :
423 1166 : core::rect<s32> oldViewPort = driver->getViewPort();
424 4664 : driver->setViewPort(core::rect<s32>(screensize.X - size - 10, 10,
425 3498 : screensize.X - 10, size + 10));
426 1166 : core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
427 1166 : driver->setTransform(video::ETS_PROJECTION, core::matrix4());
428 1166 : core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
429 1166 : driver->setTransform(video::ETS_VIEW, core::matrix4());
430 1166 : matrix.makeIdentity();
431 :
432 1166 : if (minimap_texture) {
433 0 : video::SMaterial& material = m_meshbuffer->getMaterial();
434 0 : material.setFlag(video::EMF_TRILINEAR_FILTER, true);
435 0 : material.Lighting = false;
436 0 : material.TextureLayer[0].Texture = minimap_texture;
437 0 : material.TextureLayer[1].Texture = data->heightmap_texture;
438 0 : if (m_enable_shaders && !data->radar) {
439 0 : u16 sid = shdrsrc->getShader("minimap_shader", 1, 1);
440 0 : material.MaterialType = shdrsrc->getShaderInfo(sid).material;
441 : } else {
442 0 : material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
443 : }
444 :
445 0 : if (data->minimap_shape_round)
446 0 : matrix.setRotationDegrees(core::vector3df(0, 0, 360 - player->getYaw()));
447 0 : driver->setTransform(video::ETS_WORLD, matrix);
448 0 : driver->setMaterial(material);
449 0 : driver->drawMeshBuffer(m_meshbuffer);
450 : video::ITexture *minimap_overlay;
451 0 : if (data->minimap_shape_round) {
452 0 : minimap_overlay = data->minimap_overlay_round;
453 : } else {
454 0 : minimap_overlay = data->minimap_overlay_square;
455 : }
456 0 : material.TextureLayer[0].Texture = minimap_overlay;
457 0 : material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
458 0 : driver->setMaterial(material);
459 0 : driver->drawMeshBuffer(m_meshbuffer);
460 :
461 0 : if (!data->minimap_shape_round) {
462 0 : matrix.setRotationDegrees(core::vector3df(0, 0, player->getYaw()));
463 0 : driver->setTransform(video::ETS_WORLD, matrix);
464 0 : material.TextureLayer[0].Texture = data->player_marker;
465 0 : driver->setMaterial(material);
466 0 : driver->drawMeshBuffer(m_meshbuffer);
467 : }
468 : }
469 :
470 1166 : driver->setTransform(video::ETS_VIEW, oldViewMat);
471 1166 : driver->setTransform(video::ETS_PROJECTION, oldProjMat);
472 1166 : driver->setViewPort(oldViewPort);
473 1169 : }
|