0a77a1a28092bbc997fc4040125efad6a635a2c1
[oweals/minetest.git] / src / hud.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2010-2013 blue42u, Jonathon Anderson <anderjon@umail.iu.edu>
5 Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #include "hud.h"
23 #include "settings.h"
24 #include "util/numeric.h"
25 #include "log.h"
26 #include "gamedef.h"
27 #include "itemdef.h"
28 #include "inventory.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 <IGUIStaticText.h>
37
38 #ifdef HAVE_TOUCHSCREENGUI
39 #include "touchscreengui.h"
40 #endif
41
42 Hud::Hud(video::IVideoDriver *driver, scene::ISceneManager* smgr,
43                 gui::IGUIEnvironment* guienv, IGameDef *gamedef, LocalPlayer *player,
44                 Inventory *inventory) {
45         this->driver      = driver;
46         this->smgr        = smgr;
47         this->guienv      = guienv;
48         this->gamedef     = gamedef;
49         this->player      = player;
50         this->inventory   = inventory;
51
52         m_screensize       = v2u32(0, 0);
53         m_displaycenter    = v2s32(0, 0);
54         m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
55         m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
56         m_padding = m_hotbar_imagesize / 12;
57
58         const video::SColor hbar_color(255, 255, 255, 255);
59         for (unsigned int i=0; i < 4; i++ ){
60                 hbar_colors[i] = hbar_color;
61         }
62
63         tsrc = gamedef->getTextureSource();
64
65         v3f crosshair_color = g_settings->getV3F("crosshair_color");
66         u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
67         u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
68         u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
69         u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
70         crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
71
72         v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
73         u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
74         u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
75         u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
76         selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
77
78         use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
79
80         hotbar_image = "";
81         use_hotbar_image = false;
82         hotbar_selected_image = "";
83         use_hotbar_selected_image = false;
84
85         m_selection_mesh = NULL;
86         m_selection_boxes.clear();
87         m_selection_pos = v3f(0.0, 0.0, 0.0);
88         std::string mode = g_settings->get("node_highlighting");
89         m_selection_material.Lighting = false;
90
91         if (g_settings->getBool("enable_shaders")) {
92                 IShaderSource *shdrsrc = gamedef->getShaderSource();
93                 u16 shader_id = shdrsrc->getShader(
94                         mode == "halo" ? "selection_shader" : "default_shader", 1, 1);
95                 m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
96         } else {
97                 m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
98         }
99
100         if (mode == "box") {
101                 m_use_selection_mesh = false;
102                 m_selection_material.Thickness =
103                         rangelim(g_settings->getS16("selectionbox_width"), 1, 5);       
104         } else if (mode == "halo") {
105                 m_use_selection_mesh = true;
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                         gamedef, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
194         }
195
196 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
197 void Hud::drawItems(v2s32 upperleftpos, s32 itemcount, s32 offset,
198                 InventoryList *mainlist, u16 selectitem, u16 direction)
199 {
200 #ifdef HAVE_TOUCHSCREENGUI
201         if ( (g_touchscreengui) && (offset == 0))
202                 g_touchscreengui->resetHud();
203 #endif
204
205         s32 height  = m_hotbar_imagesize + m_padding * 2;
206         s32 width   = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
207
208         if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
209                 width  = m_hotbar_imagesize + m_padding * 2;
210                 height = (itemcount - offset) * (m_hotbar_imagesize + m_padding * 2);
211         }
212
213         // Position of upper left corner of bar
214         v2s32 pos = upperleftpos;
215
216         if (hotbar_image != player->hotbar_image) {
217                 hotbar_image = player->hotbar_image;
218                 if (hotbar_image != "")
219                         use_hotbar_image = tsrc->isKnownSourceImage(hotbar_image);
220                 else
221                         use_hotbar_image = false;
222         }
223
224         if (hotbar_selected_image != player->hotbar_selected_image) {
225                 hotbar_selected_image = player->hotbar_selected_image;
226                 if (hotbar_selected_image != "")
227                         use_hotbar_selected_image = tsrc->isKnownSourceImage(hotbar_selected_image);
228                 else
229                         use_hotbar_selected_image = false;
230         }
231
232         /* draw customized item background */
233         if (use_hotbar_image) {
234                 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
235                                 width+m_padding/2, height+m_padding/2);
236                 core::rect<s32> rect2 = imgrect2 + pos;
237                 video::ITexture *texture = tsrc->getTexture(hotbar_image);
238                 core::dimension2di imgsize(texture->getOriginalSize());
239                 draw2DImageFilterScaled(driver, texture, rect2,
240                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
241                         NULL, hbar_colors, true);
242         }
243
244         for (s32 i = offset; i < itemcount && (size_t)i < mainlist->getSize(); i++)
245         {
246                 v2s32 steppos;
247                 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
248
249                 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
250
251                 switch (direction) {
252                         case HUD_DIR_RIGHT_LEFT:
253                                 steppos = v2s32(-(m_padding + (i - offset) * fullimglen), m_padding);
254                                 break;
255                         case HUD_DIR_TOP_BOTTOM:
256                                 steppos = v2s32(m_padding, m_padding + (i - offset) * fullimglen);
257                                 break;
258                         case HUD_DIR_BOTTOM_TOP:
259                                 steppos = v2s32(m_padding, -(m_padding + (i - offset) * fullimglen));
260                                 break;
261                         default:
262                                 steppos = v2s32(m_padding + (i - 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
276 void Hud::drawLuaElements(const v3s16 &camera_offset) {
277         u32 text_height = g_fontengine->getTextHeight();
278         irr::gui::IGUIFont* font = g_fontengine->getFont();
279         for (size_t i = 0; i != player->maxHudId(); i++) {
280                 HudElement *e = player->getHud(i);
281                 if (!e)
282                         continue;
283
284                 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
285                                 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
286                 switch (e->type) {
287                         case HUD_ELEM_IMAGE: {
288                                 video::ITexture *texture = tsrc->getTexture(e->text);
289                                 if (!texture)
290                                         continue;
291
292                                 const video::SColor color(255, 255, 255, 255);
293                                 const video::SColor colors[] = {color, color, color, color};
294                                 core::dimension2di imgsize(texture->getOriginalSize());
295                                 v2s32 dstsize(imgsize.Width * e->scale.X,
296                                               imgsize.Height * e->scale.Y);
297                                 if (e->scale.X < 0)
298                                         dstsize.X = m_screensize.X * (e->scale.X * -0.01);
299                                 if (e->scale.Y < 0)
300                                         dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
301                                 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
302                                              (e->align.Y - 1.0) * dstsize.Y / 2);
303                                 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
304                                 rect += pos + offset + v2s32(e->offset.X, e->offset.Y);
305                                 draw2DImageFilterScaled(driver, texture, rect,
306                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
307                                         NULL, colors, true);
308                                 break; }
309                         case HUD_ELEM_TEXT: {
310                                 video::SColor color(255, (e->number >> 16) & 0xFF,
311                                                                                  (e->number >> 8)  & 0xFF,
312                                                                                  (e->number >> 0)  & 0xFF);
313                                 core::rect<s32> size(0, 0, e->scale.X, text_height * e->scale.Y);
314                                 std::wstring text = utf8_to_wide(e->text);
315                                 core::dimension2d<u32> textsize = font->getDimension(text.c_str());
316                                 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
317                                              (e->align.Y - 1.0) * (textsize.Height / 2));
318                                 v2s32 offs(e->offset.X, e->offset.Y);
319                                 font->draw(text.c_str(), size + pos + offset + offs, color);
320                                 break; }
321                         case HUD_ELEM_STATBAR: {
322                                 v2s32 offs(e->offset.X, e->offset.Y);
323                                 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->number, offs, e->size);
324                                 break; }
325                         case HUD_ELEM_INVENTORY: {
326                                 InventoryList *inv = inventory->getList(e->text);
327                                 drawItems(pos, e->number, 0, inv, e->item, e->dir);
328                                 break; }
329                         case HUD_ELEM_WAYPOINT: {
330                                 v3f p_pos = player->getPosition() / BS;
331                                 v3f w_pos = e->world_pos * BS;
332                                 float distance = floor(10 * p_pos.getDistanceFrom(e->world_pos)) / 10;
333                                 scene::ICameraSceneNode* camera = smgr->getActiveCamera();
334                                 w_pos -= intToFloat(camera_offset, BS);
335                                 core::matrix4 trans = camera->getProjectionMatrix();
336                                 trans *= camera->getViewMatrix();
337                                 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
338                                 trans.multiplyWith1x4Matrix(transformed_pos);
339                                 if (transformed_pos[3] < 0)
340                                         break;
341                                 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
342                                         core::reciprocal(transformed_pos[3]);
343                                 pos.X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
344                                 pos.Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
345                                 video::SColor color(255, (e->number >> 16) & 0xFF,
346                                                                                  (e->number >> 8)  & 0xFF,
347                                                                                  (e->number >> 0)  & 0xFF);
348                                 core::rect<s32> size(0, 0, 200, 2 * text_height);
349                                 std::wstring text = utf8_to_wide(e->name);
350                                 font->draw(text.c_str(), size + pos, color);
351                                 std::ostringstream os;
352                                 os << distance << e->text;
353                                 text = utf8_to_wide(os.str());
354                                 pos.Y += text_height;
355                                 font->draw(text.c_str(), size + pos, color);
356                                 break; }
357                         default:
358                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
359                                         " of hud element ID " << i << " due to unrecognized type" << std::endl;
360                 }
361         }
362 }
363
364
365 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, std::string texture,
366                 s32 count, v2s32 offset, v2s32 size)
367 {
368         const video::SColor color(255, 255, 255, 255);
369         const video::SColor colors[] = {color, color, color, color};
370
371         video::ITexture *stat_texture = tsrc->getTexture(texture);
372         if (!stat_texture)
373                 return;
374
375         core::dimension2di srcd(stat_texture->getOriginalSize());
376         core::dimension2di dstd;
377         if (size == v2s32()) {
378                 dstd = srcd;
379         } else {
380                 double size_factor = g_settings->getFloat("hud_scaling") *
381                                 porting::getDisplayDensity();
382                 dstd.Height = size.Y * size_factor;
383                 dstd.Width  = size.X * size_factor;
384                 offset.X *= size_factor;
385                 offset.Y *= size_factor;
386         }
387
388         v2s32 p = pos;
389         if (corner & HUD_CORNER_LOWER)
390                 p -= dstd.Height;
391
392         p += offset;
393
394         v2s32 steppos;
395         switch (drawdir) {
396                 case HUD_DIR_RIGHT_LEFT:
397                         steppos = v2s32(-1, 0);
398                         break;
399                 case HUD_DIR_TOP_BOTTOM:
400                         steppos = v2s32(0, 1);
401                         break;
402                 case HUD_DIR_BOTTOM_TOP:
403                         steppos = v2s32(0, -1);
404                         break;
405                 default:
406                         steppos = v2s32(1, 0);
407         }
408         steppos.X *= dstd.Width;
409         steppos.Y *= dstd.Height;
410
411         for (s32 i = 0; i < count / 2; i++)
412         {
413                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
414                 core::rect<s32> dstrect(0,0, dstd.Width, dstd.Height);
415
416                 dstrect += p;
417                 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
418                 p += steppos;
419         }
420
421         if (count % 2 == 1)
422         {
423                 core::rect<s32> srcrect(0, 0, srcd.Width / 2, srcd.Height);
424                 core::rect<s32> dstrect(0,0, dstd.Width / 2, dstd.Height);
425
426                 dstrect += p;
427                 draw2DImageFilterScaled(driver, stat_texture, dstrect, srcrect, NULL, colors, true);
428         }
429 }
430
431
432 void Hud::drawHotbar(u16 playeritem) {
433
434         v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
435
436         InventoryList *mainlist = inventory->getList("main");
437         if (mainlist == NULL) {
438                 //silently ignore this we may not be initialized completely
439                 return;
440         }
441
442         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
443         s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
444         v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
445
446         if ( (float) width / (float) porting::getWindowSize().X <=
447                         g_settings->getFloat("hud_hotbar_max_width")) {
448                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
449                         drawItems(pos, hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
450                 }
451         }
452         else {
453                 pos.X += width/4;
454
455                 v2s32 secondpos = pos;
456                 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
457
458                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
459                         drawItems(pos, hotbar_itemcount/2, 0, mainlist, playeritem + 1, 0);
460                         drawItems(secondpos, hotbar_itemcount, hotbar_itemcount/2, mainlist, playeritem + 1, 0);
461                 }
462         }
463
464         //////////////////////////// compatibility code to be removed //////////////
465         // this is ugly as hell but there's no other way to keep compatibility to
466         // old servers
467         if ((player->hud_flags & HUD_FLAG_HEALTHBAR_VISIBLE)) {
468                 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
469                         floor(1 * (float) m_screensize.Y + 0.5)),
470                         HUD_CORNER_UPPER, 0, "heart.png",
471                         player->hp, v2s32((-10*24)-25,-(48+24+10)), v2s32(24,24));
472         }
473
474         if ((player->hud_flags & HUD_FLAG_BREATHBAR_VISIBLE) &&
475                         (player->getBreath() < 11)) {
476                 drawStatbar(v2s32(floor(0.5 * (float)m_screensize.X + 0.5),
477                         floor(1 * (float) m_screensize.Y + 0.5)),
478                         HUD_CORNER_UPPER, 0, "bubble.png",
479                         player->getBreath(), v2s32(25,-(48+24+10)), v2s32(24,24));
480         }
481         ////////////////////////////////////////////////////////////////////////////
482 }
483
484
485 void Hud::drawCrosshair() {
486
487         if (use_crosshair_image) {
488                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
489                 v2u32 size  = crosshair->getOriginalSize();
490                 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
491                                 m_displaycenter.Y - (size.Y / 2));
492                 driver->draw2DImage(crosshair, lsize,
493                                 core::rect<s32>(0, 0, size.X, size.Y),
494                                 0, crosshair_argb, true);
495         } else {
496                 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
497                                 m_displaycenter + v2s32(10, 0), crosshair_argb);
498                 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
499                                 m_displaycenter + v2s32(0, 10), crosshair_argb);
500         }
501 }
502
503 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
504 {
505         m_camera_offset = camera_offset;
506         m_selection_pos = pos;
507         m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
508 }
509         
510 void Hud::drawSelectionMesh()
511 {       
512         if (!m_use_selection_mesh) {
513                 // Draw 3D selection boxes
514                 video::SMaterial oldmaterial = driver->getMaterial2D();
515                 driver->setMaterial(m_selection_material);
516                 for (std::vector<aabb3f>::const_iterator
517                                 i = m_selection_boxes.begin();
518                                 i != m_selection_boxes.end(); ++i) {
519                         aabb3f box = aabb3f(
520                                 i->MinEdge + m_selection_pos_with_offset,
521                                 i->MaxEdge + m_selection_pos_with_offset);
522                         
523                         u32 r = (selectionbox_argb.getRed() *
524                                         m_selection_mesh_color.getRed() / 255);         
525                         u32 g = (selectionbox_argb.getGreen() *
526                                         m_selection_mesh_color.getGreen() / 255);
527                         u32 b = (selectionbox_argb.getBlue() *
528                                         m_selection_mesh_color.getBlue() / 255);
529                         driver->draw3DBox(box, video::SColor(255, r, g, b));
530                 }
531                 driver->setMaterial(oldmaterial);
532         } else if (m_selection_mesh) {
533                 // Draw selection mesh
534                 video::SMaterial oldmaterial = driver->getMaterial2D();
535                 driver->setMaterial(m_selection_material);
536                 setMeshColor(m_selection_mesh, m_selection_mesh_color);
537                 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
538                 translateMesh(mesh, m_selection_pos_with_offset);
539                 u32 mc = m_selection_mesh->getMeshBufferCount();
540                 for (u32 i = 0; i < mc; i++) {
541                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
542                         driver->drawMeshBuffer(buf);
543                 }
544                 mesh->drop();
545                 driver->setMaterial(oldmaterial);
546         }
547 }
548
549 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
550 {
551         m_camera_offset = camera_offset;
552         if (!m_use_selection_mesh)
553                 return;
554
555         if (m_selection_mesh) {
556                 m_selection_mesh->drop();
557                 m_selection_mesh = NULL;
558         }
559
560         if (!m_selection_boxes.size()) {
561                 // No pointed object
562                 return;
563         }
564
565         // New pointed object, create new mesh.
566
567         // Texture UV coordinates for selection boxes
568         static f32 texture_uv[24] = {
569                 0,0,1,1,
570                 0,0,1,1,
571                 0,0,1,1,
572                 0,0,1,1,
573                 0,0,1,1,
574                 0,0,1,1
575         };
576
577         m_selection_mesh = convertNodeboxesToMesh(m_selection_boxes, texture_uv);
578
579         // scale final halo mesh
580         scaleMesh(m_selection_mesh, v3f(1.08, 1.08, 1.08));
581 }
582
583 void Hud::resizeHotbar() {
584         if (m_screensize != porting::getWindowSize()) {
585                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE * porting::getDisplayDensity() + 0.5);
586                 m_hotbar_imagesize *= g_settings->getFloat("hud_scaling");
587                 m_padding = m_hotbar_imagesize / 12;
588                 m_screensize = porting::getWindowSize();
589                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
590         }
591 }
592
593 struct MeshTimeInfo {
594         s32 time;
595         scene::IMesh *mesh;
596 };
597
598 void drawItemStack(video::IVideoDriver *driver,
599                 gui::IGUIFont *font,
600                 const ItemStack &item,
601                 const core::rect<s32> &rect,
602                 const core::rect<s32> *clip,
603                 IGameDef *gamedef,
604                 ItemRotationKind rotation_kind)
605 {
606         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
607         static bool enable_animations =
608                 g_settings->getBool("inventory_items_animations");
609
610         if (item.empty()) {
611                 if (rotation_kind < IT_ROT_NONE) {
612                         rotation_time_infos[rotation_kind].mesh = NULL;
613                 }
614                 return;
615         }
616
617         const ItemDefinition &def = item.getDefinition(gamedef->idef());
618         scene::IMesh* mesh = gamedef->idef()->getWieldMesh(def.name, gamedef);
619
620         if (mesh) {
621                 driver->clearZBuffer();
622                 s32 delta = 0;
623                 if (rotation_kind < IT_ROT_NONE) {
624                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
625                         if (mesh != ti.mesh) {
626                                 ti.mesh = mesh;
627                                 ti.time = getTimeMs();
628                         } else {
629                                 delta = porting::getDeltaMs(ti.time, getTimeMs()) % 100000;
630                         }
631                 }
632                 core::rect<s32> oldViewPort = driver->getViewPort();
633                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
634                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
635                 core::matrix4 ProjMatrix;
636                 ProjMatrix.buildProjectionMatrixOrthoLH(2, 2, -1, 100);
637                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
638                 driver->setTransform(video::ETS_VIEW, ProjMatrix);
639                 core::matrix4 matrix;
640                 matrix.makeIdentity();
641
642                 if (enable_animations) {
643                         float timer_f = (float)delta / 5000.0;
644                         matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0));
645                 }
646
647                 driver->setTransform(video::ETS_WORLD, matrix);
648                 driver->setViewPort(rect);
649
650                 u32 mc = mesh->getMeshBufferCount();
651                 for (u32 j = 0; j < mc; ++j) {
652                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
653                         video::SMaterial &material = buf->getMaterial();
654                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
655                         material.Lighting = false;
656                         driver->setMaterial(material);
657                         driver->drawMeshBuffer(buf);
658                 }
659
660                 driver->setTransform(video::ETS_VIEW, oldViewMat);
661                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
662                 driver->setViewPort(oldViewPort);
663         }
664
665         if(def.type == ITEM_TOOL && item.wear != 0)
666         {
667                 // Draw a progressbar
668                 float barheight = rect.getHeight()/16;
669                 float barpad_x = rect.getWidth()/16;
670                 float barpad_y = rect.getHeight()/16;
671                 core::rect<s32> progressrect(
672                         rect.UpperLeftCorner.X + barpad_x,
673                         rect.LowerRightCorner.Y - barpad_y - barheight,
674                         rect.LowerRightCorner.X - barpad_x,
675                         rect.LowerRightCorner.Y - barpad_y);
676
677                 // Shrink progressrect by amount of tool damage
678                 float wear = item.wear / 65535.0;
679                 int progressmid =
680                         wear * progressrect.UpperLeftCorner.X +
681                         (1-wear) * progressrect.LowerRightCorner.X;
682
683                 // Compute progressbar color
684                 //   wear = 0.0: green
685                 //   wear = 0.5: yellow
686                 //   wear = 1.0: red
687                 video::SColor color(255,255,255,255);
688                 int wear_i = MYMIN(floor(wear * 600), 511);
689                 wear_i = MYMIN(wear_i + 10, 511);
690                 if(wear_i <= 255)
691                         color.set(255, wear_i, 255, 0);
692                 else
693                         color.set(255, 255, 511-wear_i, 0);
694
695                 core::rect<s32> progressrect2 = progressrect;
696                 progressrect2.LowerRightCorner.X = progressmid;
697                 driver->draw2DRectangle(color, progressrect2, clip);
698
699                 color = video::SColor(255,0,0,0);
700                 progressrect2 = progressrect;
701                 progressrect2.UpperLeftCorner.X = progressmid;
702                 driver->draw2DRectangle(color, progressrect2, clip);
703         }
704
705         if(font != NULL && item.count >= 2)
706         {
707                 // Get the item count as a string
708                 std::string text = itos(item.count);
709                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
710                 v2s32 sdim(dim.X,dim.Y);
711
712                 core::rect<s32> rect2(
713                         /*rect.UpperLeftCorner,
714                         core::dimension2d<u32>(rect.getWidth(), 15)*/
715                         rect.LowerRightCorner - sdim,
716                         sdim
717                 );
718
719                 video::SColor bgcolor(128,0,0,0);
720                 driver->draw2DRectangle(bgcolor, rect2, clip);
721
722                 video::SColor color(255,255,255,255);
723                 font->draw(text.c_str(), rect2, color, false, false, clip);
724         }
725 }