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