]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/hud.cpp
Reserve vectors before pushing and other code quality changes (#11161)
[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 #include "client/minimap.h"
40
41 #ifdef HAVE_TOUCHSCREENGUI
42 #include "gui/touchscreengui.h"
43 #endif
44
45 #define OBJECT_CROSSHAIR_LINE_SIZE 8
46 #define CROSSHAIR_LINE_SIZE 10
47
48 Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
49                 Inventory *inventory)
50 {
51         driver            = RenderingEngine::get_video_driver();
52         this->guienv      = guienv;
53         this->client      = client;
54         this->player      = player;
55         this->inventory   = inventory;
56
57         m_hud_scaling      = g_settings->getFloat("hud_scaling");
58         m_scale_factor     = m_hud_scaling * RenderingEngine::getDisplayDensity();
59         m_hotbar_imagesize = std::floor(HOTBAR_IMAGE_SIZE *
60                 RenderingEngine::getDisplayDensity() + 0.5f);
61         m_hotbar_imagesize *= m_hud_scaling;
62         m_padding = m_hotbar_imagesize / 12;
63
64         for (auto &hbar_color : hbar_colors)
65                 hbar_color = video::SColor(255, 255, 255, 255);
66
67         tsrc = client->getTextureSource();
68
69         v3f crosshair_color = g_settings->getV3F("crosshair_color");
70         u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
71         u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
72         u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
73         u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
74         crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
75
76         v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
77         u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
78         u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
79         u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
80         selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
81
82         use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
83         use_object_crosshair_image = tsrc->isKnownSourceImage("object_crosshair.png");
84
85         m_selection_boxes.clear();
86         m_halo_boxes.clear();
87
88         std::string mode_setting = g_settings->get("node_highlighting");
89
90         if (mode_setting == "halo") {
91                 m_mode = HIGHLIGHT_HALO;
92         } else if (mode_setting == "none") {
93                 m_mode = HIGHLIGHT_NONE;
94         } else {
95                 m_mode = HIGHLIGHT_BOX;
96         }
97
98         m_selection_material.Lighting = false;
99
100         if (g_settings->getBool("enable_shaders")) {
101                 IShaderSource *shdrsrc = client->getShaderSource();
102                 u16 shader_id = shdrsrc->getShader(
103                         m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", TILE_MATERIAL_ALPHA);
104                 m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
105         } else {
106                 m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
107         }
108
109         if (m_mode == HIGHLIGHT_BOX) {
110                 m_selection_material.Thickness =
111                         rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
112         } else if (m_mode == HIGHLIGHT_HALO) {
113                 m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
114                 m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
115         } else {
116                 m_selection_material.MaterialType = video::EMT_SOLID;
117         }
118
119         // Prepare mesh for compass drawing
120         m_rotation_mesh_buffer.Vertices.set_used(4);
121         m_rotation_mesh_buffer.Indices.set_used(6);
122
123         video::SColor white(255, 255, 255, 255);
124         v3f normal(0.f, 0.f, 1.f);
125
126         m_rotation_mesh_buffer.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f));
127         m_rotation_mesh_buffer.Vertices[1] = video::S3DVertex(v3f(-1.f,  1.f, 0.f), normal, white, v2f(0.f, 0.f));
128         m_rotation_mesh_buffer.Vertices[2] = video::S3DVertex(v3f( 1.f,  1.f, 0.f), normal, white, v2f(1.f, 0.f));
129         m_rotation_mesh_buffer.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f));
130
131         m_rotation_mesh_buffer.Indices[0] = 0;
132         m_rotation_mesh_buffer.Indices[1] = 1;
133         m_rotation_mesh_buffer.Indices[2] = 2;
134         m_rotation_mesh_buffer.Indices[3] = 2;
135         m_rotation_mesh_buffer.Indices[4] = 3;
136         m_rotation_mesh_buffer.Indices[5] = 0;
137
138         m_rotation_mesh_buffer.getMaterial().Lighting = false;
139         m_rotation_mesh_buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
140 }
141
142 Hud::~Hud()
143 {
144         if (m_selection_mesh)
145                 m_selection_mesh->drop();
146 }
147
148 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
149                 bool selected)
150 {
151         if (selected) {
152                 /* draw hihlighting around selected item */
153                 if (use_hotbar_selected_image) {
154                         core::rect<s32> imgrect2 = rect;
155                         imgrect2.UpperLeftCorner.X  -= (m_padding*2);
156                         imgrect2.UpperLeftCorner.Y  -= (m_padding*2);
157                         imgrect2.LowerRightCorner.X += (m_padding*2);
158                         imgrect2.LowerRightCorner.Y += (m_padding*2);
159                                 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
160                                 core::dimension2di imgsize(texture->getOriginalSize());
161                         draw2DImageFilterScaled(driver, texture, imgrect2,
162                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
163                                         NULL, hbar_colors, true);
164                 } else {
165                         video::SColor c_outside(255,255,0,0);
166                         //video::SColor c_outside(255,0,0,0);
167                         //video::SColor c_inside(255,192,192,192);
168                         s32 x1 = rect.UpperLeftCorner.X;
169                         s32 y1 = rect.UpperLeftCorner.Y;
170                         s32 x2 = rect.LowerRightCorner.X;
171                         s32 y2 = rect.LowerRightCorner.Y;
172                         // Black base borders
173                         driver->draw2DRectangle(c_outside,
174                                 core::rect<s32>(
175                                 v2s32(x1 - m_padding, y1 - m_padding),
176                                 v2s32(x2 + m_padding, y1)
177                                 ), NULL);
178                         driver->draw2DRectangle(c_outside,
179                                 core::rect<s32>(
180                                 v2s32(x1 - m_padding, y2),
181                                 v2s32(x2 + m_padding, y2 + m_padding)
182                                 ), NULL);
183                         driver->draw2DRectangle(c_outside,
184                                 core::rect<s32>(
185                                 v2s32(x1 - m_padding, y1),
186                                         v2s32(x1, y2)
187                                 ), NULL);
188                         driver->draw2DRectangle(c_outside,
189                                 core::rect<s32>(
190                                         v2s32(x2, y1),
191                                 v2s32(x2 + m_padding, y2)
192                                 ), NULL);
193                         /*// Light inside borders
194                         driver->draw2DRectangle(c_inside,
195                                 core::rect<s32>(
196                                         v2s32(x1 - padding/2, y1 - padding/2),
197                                         v2s32(x2 + padding/2, y1)
198                                 ), NULL);
199                         driver->draw2DRectangle(c_inside,
200                                 core::rect<s32>(
201                                         v2s32(x1 - padding/2, y2),
202                                         v2s32(x2 + padding/2, y2 + padding/2)
203                                 ), NULL);
204                         driver->draw2DRectangle(c_inside,
205                                 core::rect<s32>(
206                                         v2s32(x1 - padding/2, y1),
207                                         v2s32(x1, y2)
208                                 ), NULL);
209                         driver->draw2DRectangle(c_inside,
210                                 core::rect<s32>(
211                                         v2s32(x2, y1),
212                                         v2s32(x2 + padding/2, y2)
213                                 ), NULL);
214                         */
215                 }
216         }
217
218         video::SColor bgcolor2(128, 0, 0, 0);
219         if (!use_hotbar_image)
220                 driver->draw2DRectangle(bgcolor2, rect, NULL);
221         drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
222                 client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
223 }
224
225 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
226 void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
227                 s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
228 {
229 #ifdef HAVE_TOUCHSCREENGUI
230         if (g_touchscreengui && inv_offset == 0)
231                 g_touchscreengui->resetHud();
232 #endif
233
234         s32 height  = m_hotbar_imagesize + m_padding * 2;
235         s32 width   = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
236
237         if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
238                 s32 tmp = height;
239                 height = width;
240                 width = tmp;
241         }
242
243         // Position of upper left corner of bar
244         v2s32 pos = screen_offset * m_scale_factor;
245         pos += upperleftpos;
246
247         // Store hotbar_image in member variable, used by drawItem()
248         if (hotbar_image != player->hotbar_image) {
249                 hotbar_image = player->hotbar_image;
250                 use_hotbar_image = !hotbar_image.empty();
251         }
252
253         // Store hotbar_selected_image in member variable, used by drawItem()
254         if (hotbar_selected_image != player->hotbar_selected_image) {
255                 hotbar_selected_image = player->hotbar_selected_image;
256                 use_hotbar_selected_image = !hotbar_selected_image.empty();
257         }
258
259         // draw customized item background
260         if (use_hotbar_image) {
261                 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
262                         width+m_padding/2, height+m_padding/2);
263                 core::rect<s32> rect2 = imgrect2 + pos;
264                 video::ITexture *texture = tsrc->getTexture(hotbar_image);
265                 core::dimension2di imgsize(texture->getOriginalSize());
266                 draw2DImageFilterScaled(driver, texture, rect2,
267                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
268                         NULL, hbar_colors, true);
269         }
270
271         // Draw items
272         core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
273         for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
274                 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
275
276                 v2s32 steppos;
277                 switch (direction) {
278                 case HUD_DIR_RIGHT_LEFT:
279                         steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
280                         break;
281                 case HUD_DIR_TOP_BOTTOM:
282                         steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
283                         break;
284                 case HUD_DIR_BOTTOM_TOP:
285                         steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
286                         break;
287                 default:
288                         steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
289                         break;
290                 }
291
292                 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
293
294 #ifdef HAVE_TOUCHSCREENGUI
295                 if (g_touchscreengui)
296                         g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
297 #endif
298         }
299 }
300
301 bool Hud::hasElementOfType(HudElementType type)
302 {
303         for (size_t i = 0; i != player->maxHudId(); i++) {
304                 HudElement *e = player->getHud(i);
305                 if (!e)
306                         continue;
307                 if (e->type == type)
308                         return true;
309         }
310         return false;
311 }
312
313 // Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false.
314 bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos)
315 {
316         v3f w_pos = e->world_pos * BS;
317         scene::ICameraSceneNode* camera =
318                 RenderingEngine::get_scene_manager()->getActiveCamera();
319         w_pos -= intToFloat(camera_offset, BS);
320         core::matrix4 trans = camera->getProjectionMatrix();
321         trans *= camera->getViewMatrix();
322         f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
323         trans.multiplyWith1x4Matrix(transformed_pos);
324         if (transformed_pos[3] < 0)
325                 return false;
326         f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
327                 core::reciprocal(transformed_pos[3]);
328         pos->X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
329         pos->Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
330         return true;
331 }
332
333 void Hud::drawLuaElements(const v3s16 &camera_offset)
334 {
335         u32 text_height = g_fontengine->getTextHeight();
336         irr::gui::IGUIFont* font = g_fontengine->getFont();
337
338         // Reorder elements by z_index
339         std::vector<HudElement*> elems;
340         elems.reserve(player->maxHudId());
341
342         for (size_t i = 0; i != player->maxHudId(); i++) {
343                 HudElement *e = player->getHud(i);
344                 if (!e)
345                         continue;
346
347                 auto it = elems.begin();
348                 while (it != elems.end() && (*it)->z_index <= e->z_index)
349                         ++it;
350
351                 elems.insert(it, e);
352         }
353
354         for (HudElement *e : elems) {
355
356                 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
357                                 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
358                 switch (e->type) {
359                         case HUD_ELEM_TEXT: {
360                                 irr::gui::IGUIFont *textfont = font;
361                                 unsigned int font_size = g_fontengine->getDefaultFontSize();
362
363                                 if (e->size.X > 0)
364                                         font_size *= e->size.X;
365
366                                 if (font_size != g_fontengine->getDefaultFontSize())
367                                         textfont = g_fontengine->getFont(font_size);
368
369                                 video::SColor color(255, (e->number >> 16) & 0xFF,
370                                                                                  (e->number >> 8)  & 0xFF,
371                                                                                  (e->number >> 0)  & 0xFF);
372                                 std::wstring text = unescape_translate(utf8_to_wide(e->text));
373                                 core::dimension2d<u32> textsize = textfont->getDimension(text.c_str());
374 #ifdef __ANDROID__
375                                 // The text size on Android is not proportional with the actual scaling
376                                 irr::gui::IGUIFont *font_scaled = font_size <= 3 ?
377                                         textfont : g_fontengine->getFont(font_size - 3);
378                                 if (e->offset.X < -20)
379                                         textsize = font_scaled->getDimension(text.c_str());
380 #endif
381                                 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
382                                              (e->align.Y - 1.0) * (textsize.Height / 2));
383                                 core::rect<s32> size(0, 0, e->scale.X * m_scale_factor,
384                                                      text_height * e->scale.Y * m_scale_factor);
385                                 v2s32 offs(e->offset.X * m_scale_factor,
386                                            e->offset.Y * m_scale_factor);
387 #ifdef __ANDROID__
388                                 if (e->offset.X < -20)
389                                         font_scaled->draw(text.c_str(), size + pos + offset + offs, color);
390                                 else
391 #endif
392                                 {
393                                         textfont->draw(text.c_str(), size + pos + offset + offs, color);
394                                 }
395                                 break; }
396                         case HUD_ELEM_STATBAR: {
397                                 v2s32 offs(e->offset.X, e->offset.Y);
398                                 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2,
399                                         e->number, e->item, offs, e->size);
400                                 break; }
401                         case HUD_ELEM_INVENTORY: {
402                                 InventoryList *inv = inventory->getList(e->text);
403                                 drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
404                                         inv, e->item, e->dir);
405                                 break; }
406                         case HUD_ELEM_WAYPOINT: {
407                                 if (!calculateScreenPos(camera_offset, e, &pos))
408                                         break;
409                                 v3f p_pos = player->getPosition() / BS;
410                                 pos += v2s32(e->offset.X, e->offset.Y);
411                                 video::SColor color(255, (e->number >> 16) & 0xFF,
412                                                                                  (e->number >> 8)  & 0xFF,
413                                                                                  (e->number >> 0)  & 0xFF);
414                                 std::wstring text = unescape_translate(utf8_to_wide(e->name));
415                                 const std::string &unit = e->text;
416                                 // waypoints reuse the item field to store precision, item = precision + 1
417                                 u32 item = e->item;
418                                 float precision = (item == 0) ? 10.0f : (item - 1.f);
419                                 bool draw_precision = precision > 0;
420
421                                 core::rect<s32> bounds(0, 0, font->getDimension(text.c_str()).Width, (draw_precision ? 2:1) * text_height);
422                                 pos.Y += (e->align.Y - 1.0) * bounds.getHeight() / 2;
423                                 bounds += pos;
424                                 font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0) * bounds.getWidth() / 2, 0), color);
425                                 if (draw_precision) {
426                                         std::ostringstream os;
427                                         float distance = std::floor(precision * p_pos.getDistanceFrom(e->world_pos)) / precision;
428                                         os << distance << unit;
429                                         text = unescape_translate(utf8_to_wide(os.str()));
430                                         bounds.LowerRightCorner.X = bounds.UpperLeftCorner.X + font->getDimension(text.c_str()).Width;
431                                         font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0f) * bounds.getWidth() / 2, text_height), color);
432                                 }
433                                 break; }
434                         case HUD_ELEM_IMAGE_WAYPOINT: {
435                                 if (!calculateScreenPos(camera_offset, e, &pos))
436                                         break;
437                         }
438                         case HUD_ELEM_IMAGE: {
439                                 video::ITexture *texture = tsrc->getTexture(e->text);
440                                 if (!texture)
441                                         continue;
442
443                                 const video::SColor color(255, 255, 255, 255);
444                                 const video::SColor colors[] = {color, color, color, color};
445                                 core::dimension2di imgsize(texture->getOriginalSize());
446                                 v2s32 dstsize(imgsize.Width * e->scale.X * m_scale_factor,
447                                               imgsize.Height * e->scale.Y * m_scale_factor);
448                                 if (e->scale.X < 0)
449                                         dstsize.X = m_screensize.X * (e->scale.X * -0.01);
450                                 if (e->scale.Y < 0)
451                                         dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
452                                 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
453                                              (e->align.Y - 1.0) * dstsize.Y / 2);
454                                 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
455                                 rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
456                                                              e->offset.Y * m_scale_factor);
457                                 draw2DImageFilterScaled(driver, texture, rect,
458                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
459                                         NULL, colors, true);
460                                 break; }
461                         case HUD_ELEM_COMPASS: {
462                                 video::ITexture *texture = tsrc->getTexture(e->text);
463                                 if (!texture)
464                                         continue;
465
466                                 // Positionning :
467                                 v2s32 dstsize(e->size.X, e->size.Y);
468                                 if (e->size.X < 0)
469                                         dstsize.X = m_screensize.X * (e->size.X * -0.01);
470                                 if (e->size.Y < 0)
471                                         dstsize.Y = m_screensize.Y * (e->size.Y * -0.01);
472
473                                 if (dstsize.X <= 0 || dstsize.Y <= 0)
474                                         return; // Avoid zero divides
475
476                                 // Angle according to camera view
477                                 v3f fore(0.f, 0.f, 1.f);
478                                 scene::ICameraSceneNode *cam = RenderingEngine::get_scene_manager()->getActiveCamera();
479                                 cam->getAbsoluteTransformation().rotateVect(fore);
480                                 int angle = - fore.getHorizontalAngle().Y;
481
482                                 // Limit angle and ajust with given offset
483                                 angle = (angle + (int)e->number) % 360;
484
485                                 core::rect<s32> dstrect(0, 0, dstsize.X, dstsize.Y);
486                                 dstrect += pos + v2s32(
487                                                                 (e->align.X - 1.0) * dstsize.X / 2,
488                                                                 (e->align.Y - 1.0) * dstsize.Y / 2) +
489                                                 v2s32(e->offset.X * m_hud_scaling, e->offset.Y * m_hud_scaling);
490
491                                 switch (e->dir) {
492                                 case HUD_COMPASS_ROTATE:
493                                         drawCompassRotate(e, texture, dstrect, angle);
494                                         break;
495                                 case HUD_COMPASS_ROTATE_REVERSE:
496                                         drawCompassRotate(e, texture, dstrect, -angle);
497                                         break;
498                                 case HUD_COMPASS_TRANSLATE:
499                                         drawCompassTranslate(e, texture, dstrect, angle);
500                                         break;
501                                 case HUD_COMPASS_TRANSLATE_REVERSE:
502                                         drawCompassTranslate(e, texture, dstrect, -angle);
503                                         break;
504                                 default:
505                                         break;
506                                 }
507                                 break; }
508                         case HUD_ELEM_MINIMAP: {
509                                 if (e->size.X <= 0 || e->size.Y <= 0)
510                                         break;
511                                 if (!client->getMinimap())
512                                         break;
513                                 // Draw a minimap of size "size"
514                                 v2s32 dstsize(e->size.X * m_scale_factor,
515                                               e->size.Y * m_scale_factor);
516                                 // (no percent size as minimap would likely be anamorphosed)
517                                 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
518                                              (e->align.Y - 1.0) * dstsize.Y / 2);
519                                 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
520                                 rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
521                                                              e->offset.Y * m_scale_factor);
522                                 client->getMinimap()->drawMinimap(rect);
523                                 break; }
524                         default:
525                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type
526                                         << " due to unrecognized type" << std::endl;
527                 }
528         }
529 }
530
531 void Hud::drawCompassTranslate(HudElement *e, video::ITexture *texture,
532                 const core::rect<s32> &rect, int angle)
533 {
534         const video::SColor color(255, 255, 255, 255);
535         const video::SColor colors[] = {color, color, color, color};
536
537         // Compute source image scaling
538         core::dimension2di imgsize(texture->getOriginalSize());
539         core::rect<s32> srcrect(0, 0, imgsize.Width, imgsize.Height);
540
541         v2s32 dstsize(rect.getHeight() * e->scale.X * imgsize.Width / imgsize.Height,
542                         rect.getHeight() * e->scale.Y);
543
544         // Avoid infinite loop
545         if (dstsize.X <= 0 || dstsize.Y <= 0)
546                 return;
547
548         core::rect<s32> tgtrect(0, 0, dstsize.X, dstsize.Y);
549         tgtrect +=  v2s32(
550                                 (rect.getWidth() - dstsize.X) / 2,
551                                 (rect.getHeight() - dstsize.Y) / 2) +
552                         rect.UpperLeftCorner;
553
554         int offset = angle * dstsize.X / 360;
555
556         tgtrect += v2s32(offset, 0);
557
558         // Repeat image as much as needed
559         while (tgtrect.UpperLeftCorner.X > rect.UpperLeftCorner.X)
560                 tgtrect -= v2s32(dstsize.X, 0);
561
562         draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true);
563         tgtrect += v2s32(dstsize.X, 0);
564
565         while (tgtrect.UpperLeftCorner.X < rect.LowerRightCorner.X) {
566                 draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true);
567                 tgtrect += v2s32(dstsize.X, 0);
568         }
569 }
570
571 void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture,
572                 const core::rect<s32> &rect, int angle)
573 {
574         core::rect<s32> oldViewPort = driver->getViewPort();
575         core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
576         core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
577
578         core::matrix4 Matrix;
579         Matrix.makeIdentity();
580         Matrix.setRotationDegrees(v3f(0.f, 0.f, angle));
581
582         driver->setViewPort(rect);
583         driver->setTransform(video::ETS_PROJECTION, core::matrix4());
584         driver->setTransform(video::ETS_VIEW, core::matrix4());
585         driver->setTransform(video::ETS_WORLD, Matrix);
586
587         video::SMaterial &material = m_rotation_mesh_buffer.getMaterial();
588         material.TextureLayer[0].Texture = texture;
589         driver->setMaterial(material);
590         driver->drawMeshBuffer(&m_rotation_mesh_buffer);
591
592         driver->setTransform(video::ETS_WORLD, core::matrix4());
593         driver->setTransform(video::ETS_VIEW, oldViewMat);
594         driver->setTransform(video::ETS_PROJECTION, oldProjMat);
595
596         // restore the view area
597         driver->setViewPort(oldViewPort);
598 }
599
600 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
601                 const std::string &texture, const std::string &bgtexture,
602                 s32 count, s32 maxcount, v2s32 offset, v2s32 size)
603 {
604         const video::SColor color(255, 255, 255, 255);
605         const video::SColor colors[] = {color, color, color, color};
606
607         video::ITexture *stat_texture = tsrc->getTexture(texture);
608         if (!stat_texture)
609                 return;
610
611         video::ITexture *stat_texture_bg = nullptr;
612         if (!bgtexture.empty()) {
613                 stat_texture_bg = tsrc->getTexture(bgtexture);
614         }
615
616         core::dimension2di srcd(stat_texture->getOriginalSize());
617         core::dimension2di dstd;
618         if (size == v2s32()) {
619                 dstd = srcd;
620                 dstd.Height *= m_scale_factor;
621                 dstd.Width  *= m_scale_factor;
622                 offset.X *= m_scale_factor;
623                 offset.Y *= m_scale_factor;
624         } else {
625                 dstd.Height = size.Y * m_scale_factor;
626                 dstd.Width  = size.X * m_scale_factor;
627                 offset.X *= m_scale_factor;
628                 offset.Y *= m_scale_factor;
629         }
630
631         v2s32 p = pos;
632         if (corner & HUD_CORNER_LOWER)
633                 p -= dstd.Height;
634
635         p += offset;
636
637         v2s32 steppos;
638         switch (drawdir) {
639                 case HUD_DIR_RIGHT_LEFT:
640                         steppos = v2s32(-1, 0);
641                         break;
642                 case HUD_DIR_TOP_BOTTOM:
643                         steppos = v2s32(0, 1);
644                         break;
645                 case HUD_DIR_BOTTOM_TOP:
646                         steppos = v2s32(0, -1);
647                         break;
648                 default:
649                         // From left to right
650                         steppos = v2s32(1, 0);
651                         break;
652         }
653
654         auto calculate_clipping_rect = [] (core::dimension2di src,
655                         v2s32 steppos) -> core::rect<s32> {
656
657                 // Create basic rectangle
658                 core::rect<s32> rect(0, 0,
659                         src.Width  - std::abs(steppos.X) * src.Width / 2,
660                         src.Height - std::abs(steppos.Y) * src.Height / 2
661                 );
662                 // Move rectangle left or down
663                 if (steppos.X == -1)
664                         rect += v2s32(src.Width / 2, 0);
665                 if (steppos.Y == -1)
666                         rect += v2s32(0, src.Height / 2);
667                 return rect;
668         };
669         // Rectangles for 1/2 the actual value to display
670         core::rect<s32> srchalfrect, dsthalfrect;
671         // Rectangles for 1/2 the "off state" texture
672         core::rect<s32> srchalfrect2, dsthalfrect2;
673
674         if (count % 2 == 1) {
675                 // Need to draw halves: Calculate rectangles
676                 srchalfrect  = calculate_clipping_rect(srcd, steppos);
677                 dsthalfrect  = calculate_clipping_rect(dstd, steppos);
678                 srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1);
679                 dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1);
680         }
681
682         steppos.X *= dstd.Width;
683         steppos.Y *= dstd.Height;
684
685         // Draw full textures
686         for (s32 i = 0; i < count / 2; i++) {
687                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
688                 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
689
690                 dstrect += p;
691                 draw2DImageFilterScaled(driver, stat_texture,
692                         dstrect, srcrect, NULL, colors, true);
693                 p += steppos;
694         }
695
696         if (count % 2 == 1) {
697                 // Draw half a texture
698                 draw2DImageFilterScaled(driver, stat_texture,
699                         dsthalfrect + p, srchalfrect, NULL, colors, true);
700
701                 if (stat_texture_bg && maxcount > count) {
702                         draw2DImageFilterScaled(driver, stat_texture_bg,
703                                         dsthalfrect2 + p, srchalfrect2,
704                                         NULL, colors, true);
705                         p += steppos;
706                 }
707         }
708
709         if (stat_texture_bg && maxcount > count / 2) {
710                 // Draw "off state" textures
711                 s32 start_offset;
712                 if (count % 2 == 1)
713                         start_offset = count / 2 + 1;
714                 else
715                         start_offset = count / 2;
716                 for (s32 i = start_offset; i < maxcount / 2; i++) {
717                         core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
718                         core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
719
720                         dstrect += p;
721                         draw2DImageFilterScaled(driver, stat_texture_bg,
722                                         dstrect, srcrect,
723                                         NULL, colors, true);
724                         p += steppos;
725                 }
726
727                 if (maxcount % 2 == 1) {
728                         draw2DImageFilterScaled(driver, stat_texture_bg,
729                                         dsthalfrect + p, srchalfrect,
730                                         NULL, colors, true);
731                 }
732         }
733 }
734
735
736 void Hud::drawHotbar(u16 playeritem) {
737
738         v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
739
740         InventoryList *mainlist = inventory->getList("main");
741         if (mainlist == NULL) {
742                 //silently ignore this we may not be initialized completely
743                 return;
744         }
745
746         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
747         s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
748         v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
749
750         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
751         if ((float) width / (float) window_size.X <=
752                         g_settings->getFloat("hud_hotbar_max_width")) {
753                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
754                         drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
755                 }
756         } else {
757                 pos.X += width/4;
758
759                 v2s32 secondpos = pos;
760                 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
761
762                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
763                         drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
764                                 mainlist, playeritem + 1, 0);
765                         drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
766                                 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
767                 }
768         }
769 }
770
771
772 void Hud::drawCrosshair()
773 {
774         if (pointing_at_object) {
775                 if (use_object_crosshair_image) {
776                         video::ITexture *object_crosshair = tsrc->getTexture("object_crosshair.png");
777                         v2u32 size  = object_crosshair->getOriginalSize();
778                         v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
779                                         m_displaycenter.Y - (size.Y / 2));
780                         driver->draw2DImage(object_crosshair, lsize,
781                                         core::rect<s32>(0, 0, size.X, size.Y),
782                                         nullptr, crosshair_argb, true);
783                 } else {
784                         driver->draw2DLine(
785                                         m_displaycenter - v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
786                                         OBJECT_CROSSHAIR_LINE_SIZE),
787                                         m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
788                                         OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
789                         driver->draw2DLine(
790                                         m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
791                                         -OBJECT_CROSSHAIR_LINE_SIZE),
792                                         m_displaycenter + v2s32(-OBJECT_CROSSHAIR_LINE_SIZE,
793                                         OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
794                 }
795
796                 return;
797         }
798
799         if (use_crosshair_image) {
800                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
801                 v2u32 size  = crosshair->getOriginalSize();
802                 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
803                                 m_displaycenter.Y - (size.Y / 2));
804                 driver->draw2DImage(crosshair, lsize,
805                                 core::rect<s32>(0, 0, size.X, size.Y),
806                                 nullptr, crosshair_argb, true);
807         } else {
808                 driver->draw2DLine(m_displaycenter - v2s32(CROSSHAIR_LINE_SIZE, 0),
809                                 m_displaycenter + v2s32(CROSSHAIR_LINE_SIZE, 0), crosshair_argb);
810                 driver->draw2DLine(m_displaycenter - v2s32(0, CROSSHAIR_LINE_SIZE),
811                                 m_displaycenter + v2s32(0, CROSSHAIR_LINE_SIZE), crosshair_argb);
812         }
813 }
814
815 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
816 {
817         m_camera_offset = camera_offset;
818         m_selection_pos = pos;
819         m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
820 }
821
822 void Hud::drawSelectionMesh()
823 {
824         if (m_mode == HIGHLIGHT_BOX) {
825                 // Draw 3D selection boxes
826                 video::SMaterial oldmaterial = driver->getMaterial2D();
827                 driver->setMaterial(m_selection_material);
828                 for (auto & selection_box : m_selection_boxes) {
829                         aabb3f box = aabb3f(
830                                 selection_box.MinEdge + m_selection_pos_with_offset,
831                                 selection_box.MaxEdge + m_selection_pos_with_offset);
832
833                         u32 r = (selectionbox_argb.getRed() *
834                                         m_selection_mesh_color.getRed() / 255);
835                         u32 g = (selectionbox_argb.getGreen() *
836                                         m_selection_mesh_color.getGreen() / 255);
837                         u32 b = (selectionbox_argb.getBlue() *
838                                         m_selection_mesh_color.getBlue() / 255);
839                         driver->draw3DBox(box, video::SColor(255, r, g, b));
840                 }
841                 driver->setMaterial(oldmaterial);
842         } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
843                 // Draw selection mesh
844                 video::SMaterial oldmaterial = driver->getMaterial2D();
845                 driver->setMaterial(m_selection_material);
846                 setMeshColor(m_selection_mesh, m_selection_mesh_color);
847                 video::SColor face_color(0,
848                         MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
849                         MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
850                         MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
851                 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
852                         face_color);
853                 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
854                 translateMesh(mesh, m_selection_pos_with_offset);
855                 u32 mc = m_selection_mesh->getMeshBufferCount();
856                 for (u32 i = 0; i < mc; i++) {
857                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
858                         driver->drawMeshBuffer(buf);
859                 }
860                 mesh->drop();
861                 driver->setMaterial(oldmaterial);
862         }
863 }
864
865 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
866 {
867         m_camera_offset = camera_offset;
868         if (m_mode != HIGHLIGHT_HALO)
869                 return;
870
871         if (m_selection_mesh) {
872                 m_selection_mesh->drop();
873                 m_selection_mesh = NULL;
874         }
875
876         if (m_selection_boxes.empty()) {
877                 // No pointed object
878                 return;
879         }
880
881         // New pointed object, create new mesh.
882
883         // Texture UV coordinates for selection boxes
884         static f32 texture_uv[24] = {
885                 0,0,1,1,
886                 0,0,1,1,
887                 0,0,1,1,
888                 0,0,1,1,
889                 0,0,1,1,
890                 0,0,1,1
891         };
892
893         // Use single halo box instead of multiple overlapping boxes.
894         // Temporary solution - problem can be solved with multiple
895         // rendering targets, or some method to remove inner surfaces.
896         // Thats because of halo transparency.
897
898         aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
899         m_halo_boxes.clear();
900
901         for (const auto &selection_box : m_selection_boxes) {
902                 halo_box.addInternalBox(selection_box);
903         }
904
905         m_halo_boxes.push_back(halo_box);
906         m_selection_mesh = convertNodeboxesToMesh(
907                 m_halo_boxes, texture_uv, 0.5);
908 }
909
910 void Hud::resizeHotbar() {
911         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
912
913         if (m_screensize != window_size) {
914                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
915                         RenderingEngine::getDisplayDensity() + 0.5);
916                 m_hotbar_imagesize *= m_hud_scaling;
917                 m_padding = m_hotbar_imagesize / 12;
918                 m_screensize = window_size;
919                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
920         }
921 }
922
923 struct MeshTimeInfo {
924         u64 time;
925         scene::IMesh *mesh = nullptr;
926 };
927
928 void drawItemStack(
929                 video::IVideoDriver *driver,
930                 gui::IGUIFont *font,
931                 const ItemStack &item,
932                 const core::rect<s32> &rect,
933                 const core::rect<s32> *clip,
934                 Client *client,
935                 ItemRotationKind rotation_kind,
936                 const v3s16 &angle,
937                 const v3s16 &rotation_speed)
938 {
939         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
940
941         if (item.empty()) {
942                 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
943                         rotation_time_infos[rotation_kind].mesh = NULL;
944                 }
945                 return;
946         }
947
948         const static thread_local bool enable_animations =
949                 g_settings->getBool("inventory_items_animations");
950
951         const ItemDefinition &def = item.getDefinition(client->idef());
952
953         // Render as mesh if animated or no inventory image
954         if ((enable_animations && rotation_kind < IT_ROT_NONE) || def.inventory_image.empty()) {
955                 ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
956                 if (!imesh || !imesh->mesh)
957                         return;
958                 scene::IMesh *mesh = imesh->mesh;
959                 driver->clearBuffers(video::ECBF_DEPTH);
960                 s32 delta = 0;
961                 if (rotation_kind < IT_ROT_NONE) {
962                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
963                         if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
964                                 ti.mesh = mesh;
965                                 ti.time = porting::getTimeMs();
966                         } else {
967                                 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
968                         }
969                 }
970                 core::rect<s32> oldViewPort = driver->getViewPort();
971                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
972                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
973                 core::rect<s32> viewrect = rect;
974                 if (clip)
975                         viewrect.clipAgainst(*clip);
976
977                 core::matrix4 ProjMatrix;
978                 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
979
980                 core::matrix4 ViewMatrix;
981                 ViewMatrix.buildProjectionMatrixOrthoLH(
982                         2.0f * viewrect.getWidth() / rect.getWidth(),
983                         2.0f * viewrect.getHeight() / rect.getHeight(),
984                         -1.0f,
985                         100.0f);
986                 ViewMatrix.setTranslation(core::vector3df(
987                         1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
988                                         viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
989                                         viewrect.getWidth(),
990                         1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
991                                         rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
992                                         viewrect.getHeight(),
993                         0.0f));
994
995                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
996                 driver->setTransform(video::ETS_VIEW, ViewMatrix);
997
998                 core::matrix4 matrix;
999                 matrix.makeIdentity();
1000
1001                 if (enable_animations) {
1002                         float timer_f = (float) delta / 5000.f;
1003                         matrix.setRotationDegrees(v3f(
1004                                 angle.X + rotation_speed.X * 3.60f * timer_f,
1005                                 angle.Y + rotation_speed.Y * 3.60f * timer_f,
1006                                 angle.Z + rotation_speed.Z * 3.60f * timer_f)
1007                         );
1008                 }
1009
1010                 driver->setTransform(video::ETS_WORLD, matrix);
1011                 driver->setViewPort(viewrect);
1012
1013                 video::SColor basecolor =
1014                         client->idef()->getItemstackColor(item, client);
1015
1016                 u32 mc = mesh->getMeshBufferCount();
1017                 for (u32 j = 0; j < mc; ++j) {
1018                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1019                         // we can modify vertices relatively fast,
1020                         // because these meshes are not buffered.
1021                         assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
1022                         video::SColor c = basecolor;
1023
1024                         if (imesh->buffer_colors.size() > j) {
1025                                 ItemPartColor *p = &imesh->buffer_colors[j];
1026                                 if (p->override_base)
1027                                         c = p->color;
1028                         }
1029
1030                         if (imesh->needs_shading)
1031                                 colorizeMeshBuffer(buf, &c);
1032                         else
1033                                 setMeshBufferColor(buf, c);
1034
1035                         video::SMaterial &material = buf->getMaterial();
1036                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1037                         material.Lighting = false;
1038                         driver->setMaterial(material);
1039                         driver->drawMeshBuffer(buf);
1040                 }
1041
1042                 driver->setTransform(video::ETS_VIEW, oldViewMat);
1043                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
1044                 driver->setViewPort(oldViewPort);
1045         } else { // Otherwise just draw as 2D
1046                 video::ITexture *texture = client->idef()->getInventoryTexture(def.name, client);
1047                 if (!texture)
1048                         return;
1049                 video::SColor color =
1050                         client->idef()->getItemstackColor(item, client);
1051                 const video::SColor colors[] = { color, color, color, color };
1052
1053                 draw2DImageFilterScaled(driver, texture, rect,
1054                         core::rect<s32>({0, 0}, core::dimension2di(texture->getOriginalSize())),
1055                         clip, colors, true);
1056         }
1057
1058         // draw the inventory_overlay
1059         if (def.type == ITEM_NODE && def.inventory_image.empty() &&
1060                         !def.inventory_overlay.empty()) {
1061                 ITextureSource *tsrc = client->getTextureSource();
1062                 video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
1063                 core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
1064                 core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
1065                 draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
1066         }
1067
1068         if (def.type == ITEM_TOOL && item.wear != 0) {
1069                 // Draw a progressbar
1070                 float barheight = static_cast<float>(rect.getHeight()) / 16;
1071                 float barpad_x = static_cast<float>(rect.getWidth()) / 16;
1072                 float barpad_y = static_cast<float>(rect.getHeight()) / 16;
1073
1074                 core::rect<s32> progressrect(
1075                         rect.UpperLeftCorner.X + barpad_x,
1076                         rect.LowerRightCorner.Y - barpad_y - barheight,
1077                         rect.LowerRightCorner.X - barpad_x,
1078                         rect.LowerRightCorner.Y - barpad_y);
1079
1080                 // Shrink progressrect by amount of tool damage
1081                 float wear = item.wear / 65535.0f;
1082                 int progressmid =
1083                         wear * progressrect.UpperLeftCorner.X +
1084                         (1 - wear) * progressrect.LowerRightCorner.X;
1085
1086                 // Compute progressbar color
1087                 //   wear = 0.0: green
1088                 //   wear = 0.5: yellow
1089                 //   wear = 1.0: red
1090                 video::SColor color(255, 255, 255, 255);
1091                 int wear_i = MYMIN(std::floor(wear * 600), 511);
1092                 wear_i = MYMIN(wear_i + 10, 511);
1093
1094                 if (wear_i <= 255)
1095                         color.set(255, wear_i, 255, 0);
1096                 else
1097                         color.set(255, 255, 511 - wear_i, 0);
1098
1099                 core::rect<s32> progressrect2 = progressrect;
1100                 progressrect2.LowerRightCorner.X = progressmid;
1101                 driver->draw2DRectangle(color, progressrect2, clip);
1102
1103                 color = video::SColor(255, 0, 0, 0);
1104                 progressrect2 = progressrect;
1105                 progressrect2.UpperLeftCorner.X = progressmid;
1106                 driver->draw2DRectangle(color, progressrect2, clip);
1107         }
1108
1109         if (font != NULL && item.count >= 2) {
1110                 // Get the item count as a string
1111                 std::string text = itos(item.count);
1112                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
1113                 v2s32 sdim(dim.X, dim.Y);
1114
1115                 core::rect<s32> rect2(
1116                         /*rect.UpperLeftCorner,
1117                         core::dimension2d<u32>(rect.getWidth(), 15)*/
1118                         rect.LowerRightCorner - sdim,
1119                         sdim
1120                 );
1121
1122                 video::SColor bgcolor(128, 0, 0, 0);
1123                 driver->draw2DRectangle(bgcolor, rect2, clip);
1124
1125                 video::SColor color(255, 255, 255, 255);
1126                 font->draw(text.c_str(), rect2, color, false, false, clip);
1127         }
1128 }
1129
1130 void drawItemStack(
1131                 video::IVideoDriver *driver,
1132                 gui::IGUIFont *font,
1133                 const ItemStack &item,
1134                 const core::rect<s32> &rect,
1135                 const core::rect<s32> *clip,
1136                 Client *client,
1137                 ItemRotationKind rotation_kind)
1138 {
1139         drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
1140                 v3s16(0, 0, 0), v3s16(0, 100, 0));
1141 }