Add clientside translations.
[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 "shader.h"
29 #include "client/tile.h"
30 #include "localplayer.h"
31 #include "camera.h"
32 #include "porting.h"
33 #include "fontengine.h"
34 #include "guiscalingfilter.h"
35 #include "mesh.h"
36 #include "wieldmesh.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 (auto &hbar_color : hbar_colors)
59                 hbar_color = 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.empty())
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.empty())
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_translate(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_translate(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_translate(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.empty()) {
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 (const auto &selection_box : m_selection_boxes) {
601                 halo_box.addInternalBox(selection_box);
602         }
603
604         m_halo_boxes.push_back(halo_box);
605         m_selection_mesh = convertNodeboxesToMesh(
606                 m_halo_boxes, texture_uv, 0.5);
607 }
608
609 void Hud::resizeHotbar() {
610         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
611
612         if (m_screensize != window_size) {
613                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
614                         RenderingEngine::getDisplayDensity() + 0.5);
615                 m_hotbar_imagesize *= m_hud_scaling;
616                 m_padding = m_hotbar_imagesize / 12;
617                 m_screensize = window_size;
618                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
619         }
620 }
621
622 struct MeshTimeInfo {
623         u64 time;
624         scene::IMesh *mesh;
625 };
626
627 void drawItemStack(video::IVideoDriver *driver,
628                 gui::IGUIFont *font,
629                 const ItemStack &item,
630                 const core::rect<s32> &rect,
631                 const core::rect<s32> *clip,
632                 Client *client,
633                 ItemRotationKind rotation_kind)
634 {
635         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
636         static thread_local bool enable_animations =
637                 g_settings->getBool("inventory_items_animations");
638
639         if (item.empty()) {
640                 if (rotation_kind < IT_ROT_NONE) {
641                         rotation_time_infos[rotation_kind].mesh = NULL;
642                 }
643                 return;
644         }
645
646         const ItemDefinition &def = item.getDefinition(client->idef());
647         ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
648
649         if (imesh && imesh->mesh) {
650                 scene::IMesh *mesh = imesh->mesh;
651                 driver->clearZBuffer();
652                 s32 delta = 0;
653                 if (rotation_kind < IT_ROT_NONE) {
654                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
655                         if (mesh != ti.mesh) {
656                                 ti.mesh = mesh;
657                                 ti.time = porting::getTimeMs();
658                         } else {
659                                 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
660                         }
661                 }
662                 core::rect<s32> oldViewPort = driver->getViewPort();
663                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
664                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
665                 core::matrix4 ProjMatrix;
666                 ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
667                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
668                 driver->setTransform(video::ETS_VIEW, ProjMatrix);
669                 core::matrix4 matrix;
670                 matrix.makeIdentity();
671
672                 if (enable_animations) {
673                         float timer_f = (float) delta / 5000.0;
674                         matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
675                 }
676
677                 driver->setTransform(video::ETS_WORLD, matrix);
678                 driver->setViewPort(rect);
679
680                 video::SColor basecolor =
681                         client->idef()->getItemstackColor(item, client);
682
683                 u32 mc = mesh->getMeshBufferCount();
684                 for (u32 j = 0; j < mc; ++j) {
685                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
686                         // we can modify vertices relatively fast,
687                         // because these meshes are not buffered.
688                         assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
689                         video::SColor c = basecolor;
690                         if (imesh->buffer_colors.size() > j) {
691                                 ItemPartColor *p = &imesh->buffer_colors[j];
692                                 if (p->override_base)
693                                         c = p->color;
694                         }
695                         if (imesh->needs_shading)
696                                 colorizeMeshBuffer(buf, &c);
697                         else
698                                 setMeshBufferColor(buf, c);
699                         video::SMaterial &material = buf->getMaterial();
700                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
701                         material.Lighting = false;
702                         driver->setMaterial(material);
703                         driver->drawMeshBuffer(buf);
704                 }
705
706                 driver->setTransform(video::ETS_VIEW, oldViewMat);
707                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
708                 driver->setViewPort(oldViewPort);
709         }
710
711         if(def.type == ITEM_TOOL && item.wear != 0)
712         {
713                 // Draw a progressbar
714                 float barheight = rect.getHeight()/16;
715                 float barpad_x = rect.getWidth()/16;
716                 float barpad_y = rect.getHeight()/16;
717                 core::rect<s32> progressrect(
718                         rect.UpperLeftCorner.X + barpad_x,
719                         rect.LowerRightCorner.Y - barpad_y - barheight,
720                         rect.LowerRightCorner.X - barpad_x,
721                         rect.LowerRightCorner.Y - barpad_y);
722
723                 // Shrink progressrect by amount of tool damage
724                 float wear = item.wear / 65535.0;
725                 int progressmid =
726                         wear * progressrect.UpperLeftCorner.X +
727                         (1-wear) * progressrect.LowerRightCorner.X;
728
729                 // Compute progressbar color
730                 //   wear = 0.0: green
731                 //   wear = 0.5: yellow
732                 //   wear = 1.0: red
733                 video::SColor color(255,255,255,255);
734                 int wear_i = MYMIN(floor(wear * 600), 511);
735                 wear_i = MYMIN(wear_i + 10, 511);
736                 if(wear_i <= 255)
737                         color.set(255, wear_i, 255, 0);
738                 else
739                         color.set(255, 255, 511-wear_i, 0);
740
741                 core::rect<s32> progressrect2 = progressrect;
742                 progressrect2.LowerRightCorner.X = progressmid;
743                 driver->draw2DRectangle(color, progressrect2, clip);
744
745                 color = video::SColor(255,0,0,0);
746                 progressrect2 = progressrect;
747                 progressrect2.UpperLeftCorner.X = progressmid;
748                 driver->draw2DRectangle(color, progressrect2, clip);
749         }
750
751         if(font != NULL && item.count >= 2)
752         {
753                 // Get the item count as a string
754                 std::string text = itos(item.count);
755                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
756                 v2s32 sdim(dim.X,dim.Y);
757
758                 core::rect<s32> rect2(
759                         /*rect.UpperLeftCorner,
760                         core::dimension2d<u32>(rect.getWidth(), 15)*/
761                         rect.LowerRightCorner - sdim,
762                         sdim
763                 );
764
765                 video::SColor bgcolor(128,0,0,0);
766                 driver->draw2DRectangle(bgcolor, rect2, clip);
767
768                 video::SColor color(255,255,255,255);
769                 font->draw(text.c_str(), rect2, color, false, false, clip);
770         }
771 }