]> git.lizzy.rs Git - minetest.git/blob - src/client/hud.cpp
Consistent HP and damage types (#8167)
[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                 if (!hotbar_image.empty())
225                         use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
226                 else
227                         use_hotbar_image = false;
228         }
229
230         // Store hotbar_selected_image in member variable, used by drawItem()
231         if (hotbar_selected_image != player->hotbar_selected_image) {
232                 hotbar_selected_image = player->hotbar_selected_image;
233                 if (!hotbar_selected_image.empty())
234                         use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
235                 else
236                         use_hotbar_selected_image = false;
237         }
238
239         // draw customized item background
240         if (use_hotbar_image) {
241                 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
242                         width+m_padding/2, height+m_padding/2);
243                 core::rect<s32> rect2 = imgrect2 + pos;
244                 video::ITexture *texture = tsrc->getTexture(hotbar_image);
245                 core::dimension2di imgsize(texture->getOriginalSize());
246                 draw2DImageFilterScaled(driver, texture, rect2,
247                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
248                         NULL, hbar_colors, true);
249         }
250
251         // Draw items
252         core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
253         for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
254                 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
255
256                 v2s32 steppos;
257                 switch (direction) {
258                 case HUD_DIR_RIGHT_LEFT:
259                         steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
260                         break;
261                 case HUD_DIR_TOP_BOTTOM:
262                         steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
263                         break;
264                 case HUD_DIR_BOTTOM_TOP:
265                         steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
266                         break;
267                 default:
268                         steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
269                         break;
270                 }
271
272                 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
273
274 #ifdef HAVE_TOUCHSCREENGUI
275                 if (g_touchscreengui)
276                         g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
277 #endif
278         }
279 }
280
281
282 void Hud::drawLuaElements(const v3s16 &camera_offset)
283 {
284         u32 text_height = g_fontengine->getTextHeight();
285         irr::gui::IGUIFont* font = g_fontengine->getFont();
286         for (size_t i = 0; i != player->maxHudId(); i++) {
287                 HudElement *e = player->getHud(i);
288                 if (!e)
289                         continue;
290
291                 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
292                                 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
293                 switch (e->type) {
294                         case HUD_ELEM_IMAGE: {
295                                 video::ITexture *texture = tsrc->getTexture(e->text);
296                                 if (!texture)
297                                         continue;
298
299                                 const video::SColor color(255, 255, 255, 255);
300                                 const video::SColor colors[] = {color, color, color, color};
301                                 core::dimension2di imgsize(texture->getOriginalSize());
302                                 v2s32 dstsize(imgsize.Width * e->scale.X,
303                                               imgsize.Height * e->scale.Y);
304                                 if (e->scale.X < 0)
305                                         dstsize.X = m_screensize.X * (e->scale.X * -0.01);
306                                 if (e->scale.Y < 0)
307                                         dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
308                                 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
309                                              (e->align.Y - 1.0) * dstsize.Y / 2);
310                                 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
311                                 rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
312                                 draw2DImageFilterScaled(driver, texture, rect,
313                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
314                                         NULL, colors, true);
315                                 break; }
316                         case HUD_ELEM_TEXT: {
317                                 video::SColor color(255, (e->number >> 16) & 0xFF,
318                                                                                  (e->number >> 8)  & 0xFF,
319                                                                                  (e->number >> 0)  & 0xFF);
320                                 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
321                                 std::wstring text = unescape_translate(utf8_to_wide(e->text));
322                                 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
323                                 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
324                                              (e->align.Y - 1.0) * (textsize.Height / 2));
325                                 v2s32 offs(e->offset.X, e->offset.Y);
326                                 font->draw(text.c_str(), size + pos + offset + offs, color);
327                                 break; }
328                         case HUD_ELEM_STATBAR: {
329                                 v2s32 offs(e->offset.X, e->offset.Y);
330                                 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
331                                 break; }
332                         case HUD_ELEM_INVENTORY: {
333                                 InventoryList *inv = inventory->getList(e->text);
334                                 drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
335                                         inv, e->item, e->dir);
336                                 break; }
337                         case HUD_ELEM_WAYPOINT: {
338                                 v3f p_pos = player->getPosition() / BS;
339                                 v3f w_pos = e->world_pos * BS;
340                                 float distance = std::floor(10 * p_pos.getDistanceFrom(e->world_pos)) /
341                                         10.0f;
342                                 scene::ICameraSceneNode* camera =
343                                         RenderingEngine::get_scene_manager()->getActiveCamera();
344                                 w_pos -= intToFloat(camera_offset, BS);
345                                 core::matrix4 trans = camera->getProjectionMatrix();
346                                 trans *= camera->getViewMatrix();
347                                 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
348                                 trans.multiplyWith1x4Matrix(transformed_pos);
349                                 if (transformed_pos[3] < 0)
350                                         break;
351                                 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
352                                         core::reciprocal(transformed_pos[3]);
353                                 pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
354                                 pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
355                                 video::SColor color(255, (e->number >> 16) & 0xFF,
356                                                                                  (e->number >> 8)  & 0xFF,
357                                                                                  (e->number >> 0)  & 0xFF);
358                                 core::rect<s32> size(0, 0, 200, 2 * text_height);
359                                 std::wstring text = unescape_translate(utf8_to_wide(e->name));
360                                 font->draw(text.c_str(), size + pos, color);
361                                 std::ostringstream os;
362                                 os << distance << e->text;
363                                 text = unescape_translate(utf8_to_wide(os.str()));
364                                 pos.Y += text_height;
365                                 font->draw(text.c_str(), size + pos, color);
366                                 break; }
367                         default:
368                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
369                                         " of hud element ID " << i << " due to unrecognized type" << std::endl;
370                 }
371         }
372 }
373
374
375 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
376                 s32 count, v2s32 offset, v2s32 size)
377 {
378         const video::SColor color(255, 255, 255, 255);
379         const video::SColor colors[] = {color, color, color, color};
380
381         video::ITexture *stat_texture = tsrc->getTexture(texture);
382         if (!stat_texture)
383                 return;
384
385         core::dimension2di srcd(stat_texture->getOriginalSize());
386         core::dimension2di dstd;
387         if (size == v2s32()) {
388                 dstd = srcd;
389         } else {
390                 float size_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
391                 dstd.Height = size.Y * size_factor;
392                 dstd.Width  = size.X * size_factor;
393                 offset.X *= size_factor;
394                 offset.Y *= size_factor;
395         }
396
397         v2s32 p = pos;
398         if (corner & HUD_CORNER_LOWER)
399                 p -= dstd.Height;
400
401         p += offset;
402
403         v2s32 steppos;
404         core::rect<s32> srchalfrect, dsthalfrect;
405         switch (drawdir) {
406                 case HUD_DIR_RIGHT_LEFT:
407                         steppos = v2s32(-1, 0);
408                         srchalfrect = core::rect<s32>(srcd.Width / 2, 0, srcd.Width, srcd.Height);
409                         dsthalfrect = core::rect<s32>(dstd.Width / 2, 0, dstd.Width, dstd.Height);
410                         break;
411                 case HUD_DIR_TOP_BOTTOM:
412                         steppos = v2s32(0, 1);
413                         srchalfrect = core::rect<s32>(0, 0, srcd.Width, srcd.Height / 2);
414                         dsthalfrect = core::rect<s32>(0, 0, dstd.Width, dstd.Height / 2);
415                         break;
416                 case HUD_DIR_BOTTOM_TOP:
417                         steppos = v2s32(0, -1);
418                         srchalfrect = core::rect<s32>(0, srcd.Height / 2, srcd.Width, srcd.Height);
419                         dsthalfrect = core::rect<s32>(0, dstd.Height / 2, dstd.Width, dstd.Height);
420                         break;
421                 default:
422                         steppos = v2s32(1, 0);
423                         srchalfrect = core::rect<s32>(0, 0, srcd.Width / 2, srcd.Height);
424                         dsthalfrect = core::rect<s32>(0, 0, dstd.Width / 2, dstd.Height);
425         }
426         steppos.X *= dstd.Width;
427         steppos.Y *= dstd.Height;
428
429         for (s32 i = 0; i < count / 2; i++) {
430                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
431                 core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
432
433                 dstrect += p;
434                 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
435                 p += steppos;
436         }
437
438         if (count % 2 == 1) {
439                 dsthalfrect += p;
440                 draw2DImageFilterScaled(driver, stat_texture, dsthalfrect, srchalfrect, NULL, colors, true);
441         }
442 }
443
444
445 void Hud::drawHotbar(u16 playeritem) {
446
447         v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
448
449         InventoryList *mainlist = inventory->getList("main");
450         if (mainlist == NULL) {
451                 //silently ignore this we may not be initialized completely
452                 return;
453         }
454
455         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
456         s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
457         v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
458
459         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
460         if ( (float) width / (float) window_size.X <=
461                         g_settings->getFloat("hud_hotbar_max_width")) {
462                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
463                         drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
464                 }
465         } else {
466                 pos.X += width/4;
467
468                 v2s32 secondpos = pos;
469                 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
470
471                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
472                         drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
473                                 mainlist, playeritem + 1, 0);
474                         drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
475                                 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
476                 }
477         }
478 }
479
480
481 void Hud::drawCrosshair()
482 {
483         if (use_crosshair_image) {
484                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
485                 v2u32 size  = crosshair->getOriginalSize();
486                 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
487                                 m_displaycenter.Y - (size.Y / 2));
488                 driver->draw2DImage(crosshair, lsize,
489                                 core::rect<s32>(0, 0, size.X, size.Y),
490                                 0, crosshair_argb, true);
491         } else {
492                 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
493                                 m_displaycenter + v2s32(10, 0), crosshair_argb);
494                 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
495                                 m_displaycenter + v2s32(0, 10), crosshair_argb);
496         }
497 }
498
499 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
500 {
501         m_camera_offset = camera_offset;
502         m_selection_pos = pos;
503         m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
504 }
505
506 void Hud::drawSelectionMesh()
507 {
508         if (m_mode == HIGHLIGHT_BOX) {
509                 // Draw 3D selection boxes
510                 video::SMaterial oldmaterial = driver->getMaterial2D();
511                 driver->setMaterial(m_selection_material);
512                 for (std::vector<aabb3f>::const_iterator
513                                 i = m_selection_boxes.begin();
514                                 i != m_selection_boxes.end(); ++i) {
515                         aabb3f box = aabb3f(
516                                 i->MinEdge + m_selection_pos_with_offset,
517                                 i->MaxEdge + m_selection_pos_with_offset);
518
519                         u32 r = (selectionbox_argb.getRed() *
520                                         m_selection_mesh_color.getRed() / 255);
521                         u32 g = (selectionbox_argb.getGreen() *
522                                         m_selection_mesh_color.getGreen() / 255);
523                         u32 b = (selectionbox_argb.getBlue() *
524                                         m_selection_mesh_color.getBlue() / 255);
525                         driver->draw3DBox(box, video::SColor(255, r, g, b));
526                 }
527                 driver->setMaterial(oldmaterial);
528         } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
529                 // Draw selection mesh
530                 video::SMaterial oldmaterial = driver->getMaterial2D();
531                 driver->setMaterial(m_selection_material);
532                 setMeshColor(m_selection_mesh, m_selection_mesh_color);
533                 video::SColor face_color(0,
534                         MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
535                         MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
536                         MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
537                 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
538                         face_color);
539                 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
540                 translateMesh(mesh, m_selection_pos_with_offset);
541                 u32 mc = m_selection_mesh->getMeshBufferCount();
542                 for (u32 i = 0; i < mc; i++) {
543                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
544                         driver->drawMeshBuffer(buf);
545                 }
546                 mesh->drop();
547                 driver->setMaterial(oldmaterial);
548         }
549 }
550
551 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
552 {
553         m_camera_offset = camera_offset;
554         if (m_mode != HIGHLIGHT_HALO)
555                 return;
556
557         if (m_selection_mesh) {
558                 m_selection_mesh->drop();
559                 m_selection_mesh = NULL;
560         }
561
562         if (m_selection_boxes.empty()) {
563                 // No pointed object
564                 return;
565         }
566
567         // New pointed object, create new mesh.
568
569         // Texture UV coordinates for selection boxes
570         static f32 texture_uv[24] = {
571                 0,0,1,1,
572                 0,0,1,1,
573                 0,0,1,1,
574                 0,0,1,1,
575                 0,0,1,1,
576                 0,0,1,1
577         };
578
579         // Use single halo box instead of multiple overlapping boxes.
580         // Temporary solution - problem can be solved with multiple
581         // rendering targets, or some method to remove inner surfaces.
582         // Thats because of halo transparency.
583
584         aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
585         m_halo_boxes.clear();
586
587         for (const auto &selection_box : m_selection_boxes) {
588                 halo_box.addInternalBox(selection_box);
589         }
590
591         m_halo_boxes.push_back(halo_box);
592         m_selection_mesh = convertNodeboxesToMesh(
593                 m_halo_boxes, texture_uv, 0.5);
594 }
595
596 void Hud::resizeHotbar() {
597         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
598
599         if (m_screensize != window_size) {
600                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
601                         RenderingEngine::getDisplayDensity() + 0.5);
602                 m_hotbar_imagesize *= m_hud_scaling;
603                 m_padding = m_hotbar_imagesize / 12;
604                 m_screensize = window_size;
605                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
606         }
607 }
608
609 struct MeshTimeInfo {
610         u64 time;
611         scene::IMesh *mesh;
612 };
613
614 void drawItemStack(video::IVideoDriver *driver,
615                 gui::IGUIFont *font,
616                 const ItemStack &item,
617                 const core::rect<s32> &rect,
618                 const core::rect<s32> *clip,
619                 Client *client,
620                 ItemRotationKind rotation_kind)
621 {
622         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
623         static thread_local bool enable_animations =
624                 g_settings->getBool("inventory_items_animations");
625
626         if (item.empty()) {
627                 if (rotation_kind < IT_ROT_NONE) {
628                         rotation_time_infos[rotation_kind].mesh = NULL;
629                 }
630                 return;
631         }
632
633         const ItemDefinition &def = item.getDefinition(client->idef());
634         ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
635
636         if (imesh && imesh->mesh) {
637                 scene::IMesh *mesh = imesh->mesh;
638                 driver->clearZBuffer();
639                 s32 delta = 0;
640                 if (rotation_kind < IT_ROT_NONE) {
641                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
642                         if (mesh != ti.mesh) {
643                                 ti.mesh = mesh;
644                                 ti.time = porting::getTimeMs();
645                         } else {
646                                 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
647                         }
648                 }
649                 core::rect<s32> oldViewPort = driver->getViewPort();
650                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
651                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
652                 core::matrix4 ProjMatrix;
653                 ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
654                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
655                 driver->setTransform(video::ETS_VIEW, ProjMatrix);
656                 core::matrix4 matrix;
657                 matrix.makeIdentity();
658
659                 if (enable_animations) {
660                         float timer_f = (float) delta / 5000.0;
661                         matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
662                 }
663
664                 driver->setTransform(video::ETS_WORLD, matrix);
665                 driver->setViewPort(rect);
666
667                 video::SColor basecolor =
668                         client->idef()->getItemstackColor(item, client);
669
670                 u32 mc = mesh->getMeshBufferCount();
671                 for (u32 j = 0; j < mc; ++j) {
672                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
673                         // we can modify vertices relatively fast,
674                         // because these meshes are not buffered.
675                         assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
676                         video::SColor c = basecolor;
677                         if (imesh->buffer_colors.size() > j) {
678                                 ItemPartColor *p = &imesh->buffer_colors[j];
679                                 if (p->override_base)
680                                         c = p->color;
681                         }
682                         if (imesh->needs_shading)
683                                 colorizeMeshBuffer(buf, &c);
684                         else
685                                 setMeshBufferColor(buf, c);
686                         video::SMaterial &material = buf->getMaterial();
687                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
688                         material.Lighting = false;
689                         driver->setMaterial(material);
690                         driver->drawMeshBuffer(buf);
691                 }
692
693                 driver->setTransform(video::ETS_VIEW, oldViewMat);
694                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
695                 driver->setViewPort(oldViewPort);
696         }
697
698         if(def.type == ITEM_TOOL && item.wear != 0)
699         {
700                 // Draw a progressbar
701                 float barheight = rect.getHeight()/16;
702                 float barpad_x = rect.getWidth()/16;
703                 float barpad_y = rect.getHeight()/16;
704                 core::rect<s32> progressrect(
705                         rect.UpperLeftCorner.X + barpad_x,
706                         rect.LowerRightCorner.Y - barpad_y - barheight,
707                         rect.LowerRightCorner.X - barpad_x,
708                         rect.LowerRightCorner.Y - barpad_y);
709
710                 // Shrink progressrect by amount of tool damage
711                 float wear = item.wear / 65535.0;
712                 int progressmid =
713                         wear * progressrect.UpperLeftCorner.X +
714                         (1-wear) * progressrect.LowerRightCorner.X;
715
716                 // Compute progressbar color
717                 //   wear = 0.0: green
718                 //   wear = 0.5: yellow
719                 //   wear = 1.0: red
720                 video::SColor color(255,255,255,255);
721                 int wear_i = MYMIN(std::floor(wear * 600), 511);
722                 wear_i = MYMIN(wear_i + 10, 511);
723                 if (wear_i <= 255)
724                         color.set(255, wear_i, 255, 0);
725                 else
726                         color.set(255, 255, 511 - wear_i, 0);
727
728                 core::rect<s32> progressrect2 = progressrect;
729                 progressrect2.LowerRightCorner.X = progressmid;
730                 driver->draw2DRectangle(color, progressrect2, clip);
731
732                 color = video::SColor(255,0,0,0);
733                 progressrect2 = progressrect;
734                 progressrect2.UpperLeftCorner.X = progressmid;
735                 driver->draw2DRectangle(color, progressrect2, clip);
736         }
737
738         if(font != NULL && item.count >= 2)
739         {
740                 // Get the item count as a string
741                 std::string text = itos(item.count);
742                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
743                 v2s32 sdim(dim.X,dim.Y);
744
745                 core::rect<s32> rect2(
746                         /*rect.UpperLeftCorner,
747                         core::dimension2d<u32>(rect.getWidth(), 15)*/
748                         rect.LowerRightCorner - sdim,
749                         sdim
750                 );
751
752                 video::SColor bgcolor(128,0,0,0);
753                 driver->draw2DRectangle(bgcolor, rect2, clip);
754
755                 video::SColor color(255,255,255,255);
756                 font->draw(text.c_str(), rect2, color, false, false, clip);
757         }
758 }