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