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