Fix last clang-tidy reported problems for performance-type-promotion-in-math-fn
[oweals/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         //////////////////////////// compatibility code to be removed //////////////
480         // this is ugly as hell but there's no other way to keep compatibility to
481         // old servers
482         if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
483                 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
484                         floor(1 * (float) m_screensize.Y + 0.5)),
485                         HUD_CORNER_UPPER, 0, "heart.png",
486                         player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
487         }
488
489         if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
490                         (player->getBreath() < 11)) {
491                 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
492                         floor(1 * (float) m_screensize.Y + 0.5)),
493                         HUD_CORNER_UPPER, 0, "bubble.png",
494                         player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
495         }
496         ////////////////////////////////////////////////////////////////////////////
497 }
498
499
500 void Hud::drawCrosshair()
501 {
502         if (use_crosshair_image) {
503                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
504                 v2u32 size  = crosshair->getOriginalSize();
505                 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
506                                 m_displaycenter.Y - (size.Y / 2));
507                 driver->draw2DImage(crosshair, lsize,
508                                 core::rect<s32>(0, 0, size.X, size.Y),
509                                 0, crosshair_argb, true);
510         } else {
511                 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
512                                 m_displaycenter + v2s32(10, 0), crosshair_argb);
513                 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
514                                 m_displaycenter + v2s32(0, 10), crosshair_argb);
515         }
516 }
517
518 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
519 {
520         m_camera_offset = camera_offset;
521         m_selection_pos = pos;
522         m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
523 }
524
525 void Hud::drawSelectionMesh()
526 {
527         if (m_mode == HIGHLIGHT_BOX) {
528                 // Draw 3D selection boxes
529                 video::SMaterial oldmaterial = driver->getMaterial2D();
530                 driver->setMaterial(m_selection_material);
531                 for (std::vector<aabb3f>::const_iterator
532                                 i = m_selection_boxes.begin();
533                                 i != m_selection_boxes.end(); ++i) {
534                         aabb3f box = aabb3f(
535                                 i->MinEdge + m_selection_pos_with_offset,
536                                 i->MaxEdge + m_selection_pos_with_offset);
537
538                         u32 r = (selectionbox_argb.getRed() *
539                                         m_selection_mesh_color.getRed() / 255);
540                         u32 g = (selectionbox_argb.getGreen() *
541                                         m_selection_mesh_color.getGreen() / 255);
542                         u32 b = (selectionbox_argb.getBlue() *
543                                         m_selection_mesh_color.getBlue() / 255);
544                         driver->draw3DBox(box, video::SColor(255, r, g, b));
545                 }
546                 driver->setMaterial(oldmaterial);
547         } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
548                 // Draw selection mesh
549                 video::SMaterial oldmaterial = driver->getMaterial2D();
550                 driver->setMaterial(m_selection_material);
551                 setMeshColor(m_selection_mesh, m_selection_mesh_color);
552                 video::SColor face_color(0,
553                         MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
554                         MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
555                         MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
556                 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
557                         face_color);
558                 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
559                 translateMesh(mesh, m_selection_pos_with_offset);
560                 u32 mc = m_selection_mesh->getMeshBufferCount();
561                 for (u32 i = 0; i < mc; i++) {
562                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
563                         driver->drawMeshBuffer(buf);
564                 }
565                 mesh->drop();
566                 driver->setMaterial(oldmaterial);
567         }
568 }
569
570 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
571 {
572         m_camera_offset = camera_offset;
573         if (m_mode != HIGHLIGHT_HALO)
574                 return;
575
576         if (m_selection_mesh) {
577                 m_selection_mesh->drop();
578                 m_selection_mesh = NULL;
579         }
580
581         if (m_selection_boxes.empty()) {
582                 // No pointed object
583                 return;
584         }
585
586         // New pointed object, create new mesh.
587
588         // Texture UV coordinates for selection boxes
589         static f32 texture_uv[24] = {
590                 0,0,1,1,
591                 0,0,1,1,
592                 0,0,1,1,
593                 0,0,1,1,
594                 0,0,1,1,
595                 0,0,1,1
596         };
597
598         // Use single halo box instead of multiple overlapping boxes.
599         // Temporary solution - problem can be solved with multiple
600         // rendering targets, or some method to remove inner surfaces.
601         // Thats because of halo transparency.
602
603         aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
604         m_halo_boxes.clear();
605
606         for (const auto &selection_box : m_selection_boxes) {
607                 halo_box.addInternalBox(selection_box);
608         }
609
610         m_halo_boxes.push_back(halo_box);
611         m_selection_mesh = convertNodeboxesToMesh(
612                 m_halo_boxes, texture_uv, 0.5);
613 }
614
615 void Hud::resizeHotbar() {
616         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
617
618         if (m_screensize != window_size) {
619                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
620                         RenderingEngine::getDisplayDensity() + 0.5);
621                 m_hotbar_imagesize *= m_hud_scaling;
622                 m_padding = m_hotbar_imagesize / 12;
623                 m_screensize = window_size;
624                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
625         }
626 }
627
628 struct MeshTimeInfo {
629         u64 time;
630         scene::IMesh *mesh;
631 };
632
633 void drawItemStack(video::IVideoDriver *driver,
634                 gui::IGUIFont *font,
635                 const ItemStack &item,
636                 const core::rect<s32> &rect,
637                 const core::rect<s32> *clip,
638                 Client *client,
639                 ItemRotationKind rotation_kind)
640 {
641         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
642         static thread_local bool enable_animations =
643                 g_settings->getBool("inventory_items_animations");
644
645         if (item.empty()) {
646                 if (rotation_kind < IT_ROT_NONE) {
647                         rotation_time_infos[rotation_kind].mesh = NULL;
648                 }
649                 return;
650         }
651
652         const ItemDefinition &def = item.getDefinition(client->idef());
653         ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
654
655         if (imesh && imesh->mesh) {
656                 scene::IMesh *mesh = imesh->mesh;
657                 driver->clearZBuffer();
658                 s32 delta = 0;
659                 if (rotation_kind < IT_ROT_NONE) {
660                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
661                         if (mesh != ti.mesh) {
662                                 ti.mesh = mesh;
663                                 ti.time = porting::getTimeMs();
664                         } else {
665                                 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
666                         }
667                 }
668                 core::rect<s32> oldViewPort = driver->getViewPort();
669                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
670                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
671                 core::matrix4 ProjMatrix;
672                 ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
673                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
674                 driver->setTransform(video::ETS_VIEW, ProjMatrix);
675                 core::matrix4 matrix;
676                 matrix.makeIdentity();
677
678                 if (enable_animations) {
679                         float timer_f = (float) delta / 5000.0;
680                         matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
681                 }
682
683                 driver->setTransform(video::ETS_WORLD, matrix);
684                 driver->setViewPort(rect);
685
686                 video::SColor basecolor =
687                         client->idef()->getItemstackColor(item, client);
688
689                 u32 mc = mesh->getMeshBufferCount();
690                 for (u32 j = 0; j < mc; ++j) {
691                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
692                         // we can modify vertices relatively fast,
693                         // because these meshes are not buffered.
694                         assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
695                         video::SColor c = basecolor;
696                         if (imesh->buffer_colors.size() > j) {
697                                 ItemPartColor *p = &imesh->buffer_colors[j];
698                                 if (p->override_base)
699                                         c = p->color;
700                         }
701                         if (imesh->needs_shading)
702                                 colorizeMeshBuffer(buf, &c);
703                         else
704                                 setMeshBufferColor(buf, c);
705                         video::SMaterial &material = buf->getMaterial();
706                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
707                         material.Lighting = false;
708                         driver->setMaterial(material);
709                         driver->drawMeshBuffer(buf);
710                 }
711
712                 driver->setTransform(video::ETS_VIEW, oldViewMat);
713                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
714                 driver->setViewPort(oldViewPort);
715         }
716
717         if(def.type == ITEM_TOOL && item.wear != 0)
718         {
719                 // Draw a progressbar
720                 float barheight = rect.getHeight()/16;
721                 float barpad_x = rect.getWidth()/16;
722                 float barpad_y = rect.getHeight()/16;
723                 core::rect<s32> progressrect(
724                         rect.UpperLeftCorner.X + barpad_x,
725                         rect.LowerRightCorner.Y - barpad_y - barheight,
726                         rect.LowerRightCorner.X - barpad_x,
727                         rect.LowerRightCorner.Y - barpad_y);
728
729                 // Shrink progressrect by amount of tool damage
730                 float wear = item.wear / 65535.0;
731                 int progressmid =
732                         wear * progressrect.UpperLeftCorner.X +
733                         (1-wear) * progressrect.LowerRightCorner.X;
734
735                 // Compute progressbar color
736                 //   wear = 0.0: green
737                 //   wear = 0.5: yellow
738                 //   wear = 1.0: red
739                 video::SColor color(255,255,255,255);
740                 int wear_i = MYMIN(std::floor(wear * 600), 511);
741                 wear_i = MYMIN(wear_i + 10, 511);
742                 if (wear_i <= 255)
743                         color.set(255, wear_i, 255, 0);
744                 else
745                         color.set(255, 255, 511 - wear_i, 0);
746
747                 core::rect<s32> progressrect2 = progressrect;
748                 progressrect2.LowerRightCorner.X = progressmid;
749                 driver->draw2DRectangle(color, progressrect2, clip);
750
751                 color = video::SColor(255,0,0,0);
752                 progressrect2 = progressrect;
753                 progressrect2.UpperLeftCorner.X = progressmid;
754                 driver->draw2DRectangle(color, progressrect2, clip);
755         }
756
757         if(font != NULL && item.count >= 2)
758         {
759                 // Get the item count as a string
760                 std::string text = itos(item.count);
761                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
762                 v2s32 sdim(dim.X,dim.Y);
763
764                 core::rect<s32> rect2(
765                         /*rect.UpperLeftCorner,
766                         core::dimension2d<u32>(rect.getWidth(), 15)*/
767                         rect.LowerRightCorner - sdim,
768                         sdim
769                 );
770
771                 video::SColor bgcolor(128,0,0,0);
772                 driver->draw2DRectangle(bgcolor, rect2, clip);
773
774                 video::SColor color(255,255,255,255);
775                 font->draw(text.c_str(), rect2, color, false, false, clip);
776         }
777 }