]> git.lizzy.rs Git - minetest.git/blob - src/client/hud.cpp
Fix pathfinder bugs: returning nil frequently, broken A*, jump through solid nodes...
[minetest.git] / src / client / hud.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2010-2013 blue42u, Jonathon Anderson <anderjon@umail.iu.edu>
5 Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "client/hud.h"
23 #include <cmath>
24 #include "settings.h"
25 #include "util/numeric.h"
26 #include "log.h"
27 #include "client.h"
28 #include "inventory.h"
29 #include "shader.h"
30 #include "client/tile.h"
31 #include "localplayer.h"
32 #include "camera.h"
33 #include "porting.h"
34 #include "fontengine.h"
35 #include "guiscalingfilter.h"
36 #include "mesh.h"
37 #include "wieldmesh.h"
38 #include "client/renderingengine.h"
39
40 #ifdef HAVE_TOUCHSCREENGUI
41 #include "gui/touchscreengui.h"
42 #endif
43
44 Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
45                 Inventory *inventory)
46 {
47         driver            = RenderingEngine::get_video_driver();
48         this->guienv      = guienv;
49         this->client      = client;
50         this->player      = player;
51         this->inventory   = inventory;
52
53         m_hud_scaling      = g_settings->getFloat("hud_scaling");
54         m_hotbar_imagesize = std::floor(HOTBAR_IMAGE_SIZE *
55                 RenderingEngine::getDisplayDensity() + 0.5f);
56         m_hotbar_imagesize *= m_hud_scaling;
57         m_padding = m_hotbar_imagesize / 12;
58
59         for (auto &hbar_color : hbar_colors)
60                 hbar_color = video::SColor(255, 255, 255, 255);
61
62         tsrc = client->getTextureSource();
63
64         v3f crosshair_color = g_settings->getV3F("crosshair_color");
65         u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
66         u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
67         u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
68         u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
69         crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
70
71         v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
72         u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
73         u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
74         u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
75         selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
76
77         use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
78
79         m_selection_boxes.clear();
80         m_halo_boxes.clear();
81
82         std::string mode_setting = g_settings->get("node_highlighting");
83
84         if (mode_setting == "halo") {
85                 m_mode = HIGHLIGHT_HALO;
86         } else if (mode_setting == "none") {
87                 m_mode = HIGHLIGHT_NONE;
88         } else {
89                 m_mode = HIGHLIGHT_BOX;
90         }
91
92         m_selection_material.Lighting = false;
93
94         if (g_settings->getBool("enable_shaders")) {
95                 IShaderSource *shdrsrc = client->getShaderSource();
96                 u16 shader_id = shdrsrc->getShader(
97                         m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", 1, 1);
98                 m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
99         } else {
100                 m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
101         }
102
103         if (m_mode == HIGHLIGHT_BOX) {
104                 m_selection_material.Thickness =
105                         rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
106         } else if (m_mode == HIGHLIGHT_HALO) {
107                 m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
108                 m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
109         } else {
110                 m_selection_material.MaterialType = video::EMT_SOLID;
111         }
112 }
113
114 Hud::~Hud()
115 {
116         if (m_selection_mesh)
117                 m_selection_mesh->drop();
118 }
119
120 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
121                 bool selected)
122 {
123         if (selected) {
124                 /* draw hihlighting around selected item */
125                 if (use_hotbar_selected_image) {
126                         core::rect<s32> imgrect2 = rect;
127                         imgrect2.UpperLeftCorner.X  -= (m_padding*2);
128                         imgrect2.UpperLeftCorner.Y  -= (m_padding*2);
129                         imgrect2.LowerRightCorner.X += (m_padding*2);
130                         imgrect2.LowerRightCorner.Y += (m_padding*2);
131                                 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
132                                 core::dimension2di imgsize(texture->getOriginalSize());
133                         draw2DImageFilterScaled(driver, texture, imgrect2,
134                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
135                                         NULL, hbar_colors, true);
136                 } else {
137                         video::SColor c_outside(255,255,0,0);
138                         //video::SColor c_outside(255,0,0,0);
139                         //video::SColor c_inside(255,192,192,192);
140                         s32 x1 = rect.UpperLeftCorner.X;
141                         s32 y1 = rect.UpperLeftCorner.Y;
142                         s32 x2 = rect.LowerRightCorner.X;
143                         s32 y2 = rect.LowerRightCorner.Y;
144                         // Black base borders
145                         driver->draw2DRectangle(c_outside,
146                                 core::rect<s32>(
147                                 v2s32(x1 - m_padding, y1 - m_padding),
148                                 v2s32(x2 + m_padding, y1)
149                                 ), NULL);
150                         driver->draw2DRectangle(c_outside,
151                                 core::rect<s32>(
152                                 v2s32(x1 - m_padding, y2),
153                                 v2s32(x2 + m_padding, y2 + m_padding)
154                                 ), NULL);
155                         driver->draw2DRectangle(c_outside,
156                                 core::rect<s32>(
157                                 v2s32(x1 - m_padding, y1),
158                                         v2s32(x1, y2)
159                                 ), NULL);
160                         driver->draw2DRectangle(c_outside,
161                                 core::rect<s32>(
162                                         v2s32(x2, y1),
163                                 v2s32(x2 + m_padding, y2)
164                                 ), NULL);
165                         /*// Light inside borders
166                         driver->draw2DRectangle(c_inside,
167                                 core::rect<s32>(
168                                         v2s32(x1 - padding/2, y1 - padding/2),
169                                         v2s32(x2 + padding/2, y1)
170                                 ), NULL);
171                         driver->draw2DRectangle(c_inside,
172                                 core::rect<s32>(
173                                         v2s32(x1 - padding/2, y2),
174                                         v2s32(x2 + padding/2, y2 + padding/2)
175                                 ), NULL);
176                         driver->draw2DRectangle(c_inside,
177                                 core::rect<s32>(
178                                         v2s32(x1 - padding/2, y1),
179                                         v2s32(x1, y2)
180                                 ), NULL);
181                         driver->draw2DRectangle(c_inside,
182                                 core::rect<s32>(
183                                         v2s32(x2, y1),
184                                         v2s32(x2 + padding/2, y2)
185                                 ), NULL);
186                         */
187                 }
188         }
189
190         video::SColor bgcolor2(128, 0, 0, 0);
191         if (!use_hotbar_image)
192                 driver->draw2DRectangle(bgcolor2, rect, NULL);
193         drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
194                 client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
195 }
196
197 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
198 void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
199                 s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
200 {
201 #ifdef HAVE_TOUCHSCREENGUI
202         if (g_touchscreengui && inv_offset == 0)
203                 g_touchscreengui->resetHud();
204 #endif
205
206         s32 height  = m_hotbar_imagesize + m_padding * 2;
207         s32 width   = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
208
209         if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
210                 s32 tmp = height;
211                 height = width;
212                 width = tmp;
213         }
214
215         // Position of upper left corner of bar
216         v2s32 pos = screen_offset;
217         pos.X *= m_hud_scaling * RenderingEngine::getDisplayDensity();
218         pos.Y *= m_hud_scaling * RenderingEngine::getDisplayDensity();
219         pos += upperleftpos;
220
221         // Store hotbar_image in member variable, used by drawItem()
222         if (hotbar_image != player->hotbar_image) {
223                 hotbar_image = player->hotbar_image;
224                 use_hotbar_image = !hotbar_image.empty();
225         }
226
227         // Store hotbar_selected_image in member variable, used by drawItem()
228         if (hotbar_selected_image != player->hotbar_selected_image) {
229                 hotbar_selected_image = player->hotbar_selected_image;
230                 use_hotbar_selected_image = !hotbar_selected_image.empty();
231         }
232
233         // draw customized item background
234         if (use_hotbar_image) {
235                 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
236                         width+m_padding/2, height+m_padding/2);
237                 core::rect<s32> rect2 = imgrect2 + pos;
238                 video::ITexture *texture = tsrc->getTexture(hotbar_image);
239                 core::dimension2di imgsize(texture->getOriginalSize());
240                 draw2DImageFilterScaled(driver, texture, rect2,
241                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
242                         NULL, hbar_colors, true);
243         }
244
245         // Draw items
246         core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
247         for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
248                 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
249
250                 v2s32 steppos;
251                 switch (direction) {
252                 case HUD_DIR_RIGHT_LEFT:
253                         steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
254                         break;
255                 case HUD_DIR_TOP_BOTTOM:
256                         steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
257                         break;
258                 case HUD_DIR_BOTTOM_TOP:
259                         steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
260                         break;
261                 default:
262                         steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
263                         break;
264                 }
265
266                 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
267
268 #ifdef HAVE_TOUCHSCREENGUI
269                 if (g_touchscreengui)
270                         g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
271 #endif
272         }
273 }
274
275
276 void Hud::drawLuaElements(const v3s16 &camera_offset)
277 {
278         u32 text_height = g_fontengine->getTextHeight();
279         irr::gui::IGUIFont* font = g_fontengine->getFont();
280
281         // Reorder elements by z_index
282         std::vector<size_t> ids;
283
284         for (size_t i = 0; i != player->maxHudId(); i++) {
285                 HudElement *e = player->getHud(i);
286                 if (!e)
287                         continue;
288
289                 auto it = ids.begin();
290                 while (it != ids.end() && player->getHud(*it)->z_index <= e->z_index)
291                         ++it;
292
293                 ids.insert(it, i);
294         }
295
296         for (size_t i : ids) {
297                 HudElement *e = player->getHud(i);
298
299                 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
300                                 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
301                 switch (e->type) {
302                         case HUD_ELEM_IMAGE: {
303                                 video::ITexture *texture = tsrc->getTexture(e->text);
304                                 if (!texture)
305                                         continue;
306
307                                 const video::SColor color(255, 255, 255, 255);
308                                 const video::SColor colors[] = {color, color, color, color};
309                                 core::dimension2di imgsize(texture->getOriginalSize());
310                                 v2s32 dstsize(imgsize.Width * e->scale.X,
311                                               imgsize.Height * e->scale.Y);
312                                 if (e->scale.X < 0)
313                                         dstsize.X = m_screensize.X * (e->scale.X * -0.01);
314                                 if (e->scale.Y < 0)
315                                         dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
316                                 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
317                                              (e->align.Y - 1.0) * dstsize.Y / 2);
318                                 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
319                                 rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
320                                 draw2DImageFilterScaled(driver, texture, rect,
321                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
322                                         NULL, colors, true);
323                                 break; }
324                         case HUD_ELEM_TEXT: {
325                                 video::SColor color(255, (e->number >> 16) & 0xFF,
326                                                                                  (e->number >> 8)  & 0xFF,
327                                                                                  (e->number >> 0)  & 0xFF);
328                                 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
329                                 std::wstring text = unescape_translate(utf8_to_wide(e->text));
330                                 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
331                                 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
332                                              (e->align.Y - 1.0) * (textsize.Height / 2));
333                                 v2s32 offs(e->offset.X, e->offset.Y);
334                                 font->draw(text.c_str(), size + pos + offset + offs, color);
335                                 break; }
336                         case HUD_ELEM_STATBAR: {
337                                 v2s32 offs(e->offset.X, e->offset.Y);
338                                 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
339                                 break; }
340                         case HUD_ELEM_INVENTORY: {
341                                 InventoryList *inv = inventory->getList(e->text);
342                                 drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
343                                         inv, e->item, e->dir);
344                                 break; }
345                         case HUD_ELEM_WAYPOINT: {
346                                 v3f p_pos = player->getPosition() / BS;
347                                 v3f w_pos = e->world_pos * BS;
348                                 float distance = std::floor(10 * p_pos.getDistanceFrom(e->world_pos)) /
349                                         10.0f;
350                                 scene::ICameraSceneNode* camera =
351                                         RenderingEngine::get_scene_manager()->getActiveCamera();
352                                 w_pos -= intToFloat(camera_offset, BS);
353                                 core::matrix4 trans = camera->getProjectionMatrix();
354                                 trans *= camera->getViewMatrix();
355                                 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
356                                 trans.multiplyWith1x4Matrix(transformed_pos);
357                                 if (transformed_pos[3] < 0)
358                                         break;
359                                 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
360                                         core::reciprocal(transformed_pos[3]);
361                                 pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
362                                 pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
363                                 video::SColor color(255, (e->number >> 16) & 0xFF,
364                                                                                  (e->number >> 8)  & 0xFF,
365                                                                                  (e->number >> 0)  & 0xFF);
366                                 core::rect<s32> size(0, 0, 200, 2 * text_height);
367                                 std::wstring text = unescape_translate(utf8_to_wide(e->name));
368                                 font->draw(text.c_str(), size + pos, color);
369                                 std::ostringstream os;
370                                 os << distance << e->text;
371                                 text = unescape_translate(utf8_to_wide(os.str()));
372                                 pos.Y += text_height;
373                                 font->draw(text.c_str(), size + pos, color);
374                                 break; }
375                         default:
376                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
377                                         " of hud element ID " << i << " due to unrecognized type" << std::endl;
378                 }
379         }
380 }
381
382
383 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, const std::string &texture,
384                 s32 count, v2s32 offset, v2s32 size)
385 {
386         const video::SColor color(255, 255, 255, 255);
387         const video::SColor colors[] = {color, color, color, color};
388
389         video::ITexture *stat_texture = tsrc->getTexture(texture);
390         if (!stat_texture)
391                 return;
392
393         core::dimension2di srcd(stat_texture->getOriginalSize());
394         core::dimension2di dstd;
395         if (size == v2s32()) {
396                 dstd = srcd;
397         } else {
398                 float size_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
399                 dstd.Height = size.Y * size_factor;
400                 dstd.Width  = size.X * size_factor;
401                 offset.X *= size_factor;
402                 offset.Y *= size_factor;
403         }
404
405         v2s32 p = pos;
406         if (corner & HUD_CORNER_LOWER)
407                 p -= dstd.Height;
408
409         p += offset;
410
411         v2s32 steppos;
412         core::rect<s32> srchalfrect, dsthalfrect;
413         switch (drawdir) {
414                 case HUD_DIR_RIGHT_LEFT:
415                         steppos = v2s32(-1, 0);
416                         srchalfrect = core::rect<s32>(srcd.Width / 2, 0, srcd.Width, srcd.Height);
417                         dsthalfrect = core::rect<s32>(dstd.Width / 2, 0, dstd.Width, dstd.Height);
418                         break;
419                 case HUD_DIR_TOP_BOTTOM:
420                         steppos = v2s32(0, 1);
421                         srchalfrect = core::rect<s32>(0, 0, srcd.Width, srcd.Height / 2);
422                         dsthalfrect = core::rect<s32>(0, 0, dstd.Width, dstd.Height / 2);
423                         break;
424                 case HUD_DIR_BOTTOM_TOP:
425                         steppos = v2s32(0, -1);
426                         srchalfrect = core::rect<s32>(0, srcd.Height / 2, srcd.Width, srcd.Height);
427                         dsthalfrect = core::rect<s32>(0, dstd.Height / 2, dstd.Width, dstd.Height);
428                         break;
429                 default:
430                         steppos = v2s32(1, 0);
431                         srchalfrect = core::rect<s32>(0, 0, srcd.Width / 2, srcd.Height);
432                         dsthalfrect = core::rect<s32>(0, 0, dstd.Width / 2, dstd.Height);
433         }
434         steppos.X *= dstd.Width;
435         steppos.Y *= dstd.Height;
436
437         for (s32 i = 0; i < count / 2; i++) {
438                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
439                 core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
440
441                 dstrect += p;
442                 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
443                 p += steppos;
444         }
445
446         if (count % 2 == 1) {
447                 dsthalfrect += p;
448                 draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
449         }
450 }
451
452
453 void Hud::drawHotbar(u16 playeritem) {
454
455         v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
456
457         InventoryList *mainlist = inventory->getList("main");
458         if (mainlist == NULL) {
459                 //silently ignore this we may not be initialized completely
460                 return;
461         }
462
463         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
464         s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
465         v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
466
467         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
468         if ( (float) width / (float) window_size.X <=
469                         g_settings->getFloat("hud_hotbar_max_width")) {
470                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
471                         drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
472                 }
473         } else {
474                 pos.X += width/4;
475
476                 v2s32 secondpos = pos;
477                 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
478
479                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
480                         drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
481                                 mainlist, playeritem + 1, 0);
482                         drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
483                                 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
484                 }
485         }
486 }
487
488
489 void Hud::drawCrosshair()
490 {
491         if (use_crosshair_image) {
492                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
493                 v2u32 size  = crosshair->getOriginalSize();
494                 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
495                                 m_displaycenter.Y - (size.Y / 2));
496                 driver->draw2DImage(crosshair, lsize,
497                                 core::rect<s32>(0, 0, size.X, size.Y),
498                                 0, crosshair_argb, true);
499         } else {
500                 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
501                                 m_displaycenter + v2s32(10, 0), crosshair_argb);
502                 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
503                                 m_displaycenter + v2s32(0, 10), crosshair_argb);
504         }
505 }
506
507 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
508 {
509         m_camera_offset = camera_offset;
510         m_selection_pos = pos;
511         m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
512 }
513
514 void Hud::drawSelectionMesh()
515 {
516         if (m_mode == HIGHLIGHT_BOX) {
517                 // Draw 3D selection boxes
518                 video::SMaterial oldmaterial = driver->getMaterial2D();
519                 driver->setMaterial(m_selection_material);
520                 for (std::vector<aabb3f>::const_iterator
521                                 i = m_selection_boxes.begin();
522                                 i != m_selection_boxes.end(); ++i) {
523                         aabb3f box = aabb3f(
524                                 i->MinEdge + m_selection_pos_with_offset,
525                                 i->MaxEdge + m_selection_pos_with_offset);
526
527                         u32 r = (selectionbox_argb.getRed() *
528                                         m_selection_mesh_color.getRed() / 255);
529                         u32 g = (selectionbox_argb.getGreen() *
530                                         m_selection_mesh_color.getGreen() / 255);
531                         u32 b = (selectionbox_argb.getBlue() *
532                                         m_selection_mesh_color.getBlue() / 255);
533                         driver->draw3DBox(box, video::SColor(255, r, g, b));
534                 }
535                 driver->setMaterial(oldmaterial);
536         } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
537                 // Draw selection mesh
538                 video::SMaterial oldmaterial = driver->getMaterial2D();
539                 driver->setMaterial(m_selection_material);
540                 setMeshColor(m_selection_mesh, m_selection_mesh_color);
541                 video::SColor face_color(0,
542                         MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
543                         MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
544                         MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
545                 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
546                         face_color);
547                 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
548                 translateMesh(mesh, m_selection_pos_with_offset);
549                 u32 mc = m_selection_mesh->getMeshBufferCount();
550                 for (u32 i = 0; i < mc; i++) {
551                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
552                         driver->drawMeshBuffer(buf);
553                 }
554                 mesh->drop();
555                 driver->setMaterial(oldmaterial);
556         }
557 }
558
559 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
560 {
561         m_camera_offset = camera_offset;
562         if (m_mode != HIGHLIGHT_HALO)
563                 return;
564
565         if (m_selection_mesh) {
566                 m_selection_mesh->drop();
567                 m_selection_mesh = NULL;
568         }
569
570         if (m_selection_boxes.empty()) {
571                 // No pointed object
572                 return;
573         }
574
575         // New pointed object, create new mesh.
576
577         // Texture UV coordinates for selection boxes
578         static f32 texture_uv[24] = {
579                 0,0,1,1,
580                 0,0,1,1,
581                 0,0,1,1,
582                 0,0,1,1,
583                 0,0,1,1,
584                 0,0,1,1
585         };
586
587         // Use single halo box instead of multiple overlapping boxes.
588         // Temporary solution - problem can be solved with multiple
589         // rendering targets, or some method to remove inner surfaces.
590         // Thats because of halo transparency.
591
592         aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
593         m_halo_boxes.clear();
594
595         for (const auto &selection_box : m_selection_boxes) {
596                 halo_box.addInternalBox(selection_box);
597         }
598
599         m_halo_boxes.push_back(halo_box);
600         m_selection_mesh = convertNodeboxesToMesh(
601                 m_halo_boxes, texture_uv, 0.5);
602 }
603
604 void Hud::resizeHotbar() {
605         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
606
607         if (m_screensize != window_size) {
608                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
609                         RenderingEngine::getDisplayDensity() + 0.5);
610                 m_hotbar_imagesize *= m_hud_scaling;
611                 m_padding = m_hotbar_imagesize / 12;
612                 m_screensize = window_size;
613                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
614         }
615 }
616
617 struct MeshTimeInfo {
618         u64 time;
619         scene::IMesh *mesh = nullptr;
620 };
621
622 void drawItemStack(
623                 video::IVideoDriver *driver,
624                 gui::IGUIFont *font,
625                 const ItemStack &item,
626                 const core::rect<s32> &rect,
627                 const core::rect<s32> *clip,
628                 Client *client,
629                 ItemRotationKind rotation_kind,
630                 const v3s16 &angle,
631                 const v3s16 &rotation_speed)
632 {
633         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
634
635         if (item.empty()) {
636                 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
637                         rotation_time_infos[rotation_kind].mesh = NULL;
638                 }
639                 return;
640         }
641
642         const ItemDefinition &def = item.getDefinition(client->idef());
643         ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
644
645         if (imesh && imesh->mesh) {
646                 scene::IMesh *mesh = imesh->mesh;
647                 driver->clearZBuffer();
648                 s32 delta = 0;
649                 if (rotation_kind < IT_ROT_NONE) {
650                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
651                         if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
652                                 ti.mesh = mesh;
653                                 ti.time = porting::getTimeMs();
654                         } else {
655                                 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
656                         }
657                 }
658                 core::rect<s32> oldViewPort = driver->getViewPort();
659                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
660                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
661                 core::rect<s32> viewrect = rect;
662                 if (clip)
663                         viewrect.clipAgainst(*clip);
664
665                 core::matrix4 ProjMatrix;
666                 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
667
668                 core::matrix4 ViewMatrix;
669                 ViewMatrix.buildProjectionMatrixOrthoLH(
670                         2.0f * viewrect.getWidth() / rect.getWidth(),
671                         2.0f * viewrect.getHeight() / rect.getHeight(),
672                         -1.0f,
673                         100.0f);
674                 ViewMatrix.setTranslation(core::vector3df(
675                         1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
676                                         viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
677                                         viewrect.getWidth(),
678                         1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
679                                         rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
680                                         viewrect.getHeight(),
681                         0.0f));
682
683                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
684                 driver->setTransform(video::ETS_VIEW, ViewMatrix);
685
686                 core::matrix4 matrix;
687                 matrix.makeIdentity();
688
689                 static thread_local bool enable_animations =
690                         g_settings->getBool("inventory_items_animations");
691
692                 if (enable_animations) {
693                         float timer_f = (float) delta / 5000.f;
694                         matrix.setRotationDegrees(v3f(
695                                 angle.X + rotation_speed.X * 3.60f * timer_f,
696                                 angle.Y + rotation_speed.Y * 3.60f * timer_f,
697                                 angle.Z + rotation_speed.Z * 3.60f * timer_f)
698                         );
699                 }
700
701                 driver->setTransform(video::ETS_WORLD, matrix);
702                 driver->setViewPort(viewrect);
703
704                 video::SColor basecolor =
705                         client->idef()->getItemstackColor(item, client);
706
707                 u32 mc = mesh->getMeshBufferCount();
708                 for (u32 j = 0; j < mc; ++j) {
709                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
710                         // we can modify vertices relatively fast,
711                         // because these meshes are not buffered.
712                         assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
713                         video::SColor c = basecolor;
714
715                         if (imesh->buffer_colors.size() > j) {
716                                 ItemPartColor *p = &imesh->buffer_colors[j];
717                                 if (p->override_base)
718                                         c = p->color;
719                         }
720
721                         if (imesh->needs_shading)
722                                 colorizeMeshBuffer(buf, &c);
723                         else
724                                 setMeshBufferColor(buf, c);
725
726                         video::SMaterial &material = buf->getMaterial();
727                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
728                         material.Lighting = false;
729                         driver->setMaterial(material);
730                         driver->drawMeshBuffer(buf);
731                 }
732
733                 driver->setTransform(video::ETS_VIEW, oldViewMat);
734                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
735                 driver->setViewPort(oldViewPort);
736
737                 // draw the inventory_overlay
738                 if (def.type == ITEM_NODE && def.inventory_image.empty() &&
739                                 !def.inventory_overlay.empty()) {
740                         ITextureSource *tsrc = client->getTextureSource();
741                         video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
742                         core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
743                         core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
744                         draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
745                 }
746         }
747
748         if (def.type == ITEM_TOOL && item.wear != 0) {
749                 // Draw a progressbar
750                 float barheight = rect.getHeight() / 16;
751                 float barpad_x = rect.getWidth() / 16;
752                 float barpad_y = rect.getHeight() / 16;
753
754                 core::rect<s32> progressrect(
755                         rect.UpperLeftCorner.X + barpad_x,
756                         rect.LowerRightCorner.Y - barpad_y - barheight,
757                         rect.LowerRightCorner.X - barpad_x,
758                         rect.LowerRightCorner.Y - barpad_y);
759
760                 // Shrink progressrect by amount of tool damage
761                 float wear = item.wear / 65535.0f;
762                 int progressmid =
763                         wear * progressrect.UpperLeftCorner.X +
764                         (1 - wear) * progressrect.LowerRightCorner.X;
765
766                 // Compute progressbar color
767                 //   wear = 0.0: green
768                 //   wear = 0.5: yellow
769                 //   wear = 1.0: red
770                 video::SColor color(255, 255, 255, 255);
771                 int wear_i = MYMIN(std::floor(wear * 600), 511);
772                 wear_i = MYMIN(wear_i + 10, 511);
773
774                 if (wear_i <= 255)
775                         color.set(255, wear_i, 255, 0);
776                 else
777                         color.set(255, 255, 511 - wear_i, 0);
778
779                 core::rect<s32> progressrect2 = progressrect;
780                 progressrect2.LowerRightCorner.X = progressmid;
781                 driver->draw2DRectangle(color, progressrect2, clip);
782
783                 color = video::SColor(255, 0, 0, 0);
784                 progressrect2 = progressrect;
785                 progressrect2.UpperLeftCorner.X = progressmid;
786                 driver->draw2DRectangle(color, progressrect2, clip);
787         }
788
789         if (font != NULL && item.count >= 2) {
790                 // Get the item count as a string
791                 std::string text = itos(item.count);
792                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
793                 v2s32 sdim(dim.X, dim.Y);
794
795                 core::rect<s32> rect2(
796                         /*rect.UpperLeftCorner,
797                         core::dimension2d<u32>(rect.getWidth(), 15)*/
798                         rect.LowerRightCorner - sdim,
799                         sdim
800                 );
801
802                 video::SColor bgcolor(128, 0, 0, 0);
803                 driver->draw2DRectangle(bgcolor, rect2, clip);
804
805                 video::SColor color(255, 255, 255, 255);
806                 font->draw(text.c_str(), rect2, color, false, false, clip);
807         }
808 }
809
810 void drawItemStack(
811                 video::IVideoDriver *driver,
812                 gui::IGUIFont *font,
813                 const ItemStack &item,
814                 const core::rect<s32> &rect,
815                 const core::rect<s32> *clip,
816                 Client *client,
817                 ItemRotationKind rotation_kind)
818 {
819         drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
820                 v3s16(0, 0, 0), v3s16(0, 100, 0));
821 }