]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/hud.cpp
Cleanup shader generation code (#10663)
[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<size_t> ids;
340
341         for (size_t i = 0; i != player->maxHudId(); i++) {
342                 HudElement *e = player->getHud(i);
343                 if (!e)
344                         continue;
345
346                 auto it = ids.begin();
347                 while (it != ids.end() && player->getHud(*it)->z_index <= e->z_index)
348                         ++it;
349
350                 ids.insert(it, i);
351         }
352
353         for (size_t i : ids) {
354                 HudElement *e = player->getHud(i);
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                                         " of hud element ID " << i << " 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::dimension2di imgsize(texture->getOriginalSize());
575
576         core::rect<s32> oldViewPort = driver->getViewPort();
577         core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
578         core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
579
580         core::matrix4 Matrix;
581         Matrix.makeIdentity();
582         Matrix.setRotationDegrees(v3f(0.f, 0.f, angle));
583
584         driver->setViewPort(rect);
585         driver->setTransform(video::ETS_PROJECTION, core::matrix4());
586         driver->setTransform(video::ETS_VIEW, core::matrix4());
587         driver->setTransform(video::ETS_WORLD, Matrix);
588
589         video::SMaterial &material = m_rotation_mesh_buffer.getMaterial();
590         material.TextureLayer[0].Texture = texture;
591         driver->setMaterial(material);
592         driver->drawMeshBuffer(&m_rotation_mesh_buffer);
593
594         driver->setTransform(video::ETS_WORLD, core::matrix4());
595         driver->setTransform(video::ETS_VIEW, oldViewMat);
596         driver->setTransform(video::ETS_PROJECTION, oldProjMat);
597
598         // restore the view area
599         driver->setViewPort(oldViewPort);
600 }
601
602 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
603                 const std::string &texture, const std::string &bgtexture,
604                 s32 count, s32 maxcount, v2s32 offset, v2s32 size)
605 {
606         const video::SColor color(255, 255, 255, 255);
607         const video::SColor colors[] = {color, color, color, color};
608
609         video::ITexture *stat_texture = tsrc->getTexture(texture);
610         if (!stat_texture)
611                 return;
612
613         video::ITexture *stat_texture_bg = nullptr;
614         if (!bgtexture.empty()) {
615                 stat_texture_bg = tsrc->getTexture(bgtexture);
616         }
617
618         core::dimension2di srcd(stat_texture->getOriginalSize());
619         core::dimension2di dstd;
620         if (size == v2s32()) {
621                 dstd = srcd;
622                 dstd.Height *= m_scale_factor;
623                 dstd.Width  *= m_scale_factor;
624                 offset.X *= m_scale_factor;
625                 offset.Y *= m_scale_factor;
626         } else {
627                 dstd.Height = size.Y * m_scale_factor;
628                 dstd.Width  = size.X * m_scale_factor;
629                 offset.X *= m_scale_factor;
630                 offset.Y *= m_scale_factor;
631         }
632
633         v2s32 p = pos;
634         if (corner & HUD_CORNER_LOWER)
635                 p -= dstd.Height;
636
637         p += offset;
638
639         v2s32 steppos;
640         switch (drawdir) {
641                 case HUD_DIR_RIGHT_LEFT:
642                         steppos = v2s32(-1, 0);
643                         break;
644                 case HUD_DIR_TOP_BOTTOM:
645                         steppos = v2s32(0, 1);
646                         break;
647                 case HUD_DIR_BOTTOM_TOP:
648                         steppos = v2s32(0, -1);
649                         break;
650                 default:
651                         // From left to right
652                         steppos = v2s32(1, 0);
653                         break;
654         }
655
656         auto calculate_clipping_rect = [] (core::dimension2di src,
657                         v2s32 steppos) -> core::rect<s32> {
658
659                 // Create basic rectangle
660                 core::rect<s32> rect(0, 0,
661                         src.Width  - std::abs(steppos.X) * src.Width / 2,
662                         src.Height - std::abs(steppos.Y) * src.Height / 2
663                 );
664                 // Move rectangle left or down
665                 if (steppos.X == -1)
666                         rect += v2s32(src.Width / 2, 0);
667                 if (steppos.Y == -1)
668                         rect += v2s32(0, src.Height / 2);
669                 return rect;
670         };
671         // Rectangles for 1/2 the actual value to display
672         core::rect<s32> srchalfrect, dsthalfrect;
673         // Rectangles for 1/2 the "off state" texture
674         core::rect<s32> srchalfrect2, dsthalfrect2;
675
676         if (count % 2 == 1) {
677                 // Need to draw halves: Calculate rectangles
678                 srchalfrect  = calculate_clipping_rect(srcd, steppos);
679                 dsthalfrect  = calculate_clipping_rect(dstd, steppos);
680                 srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1);
681                 dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1);
682         }
683
684         steppos.X *= dstd.Width;
685         steppos.Y *= dstd.Height;
686
687         // Draw full textures
688         for (s32 i = 0; i < count / 2; i++) {
689                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
690                 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
691
692                 dstrect += p;
693                 draw2DImageFilterScaled(driver, stat_texture,
694                         dstrect, srcrect, NULL, colors, true);
695                 p += steppos;
696         }
697
698         if (count % 2 == 1) {
699                 // Draw half a texture
700                 draw2DImageFilterScaled(driver, stat_texture,
701                         dsthalfrect + p, srchalfrect, NULL, colors, true);
702
703                 if (stat_texture_bg && maxcount > count) {
704                         draw2DImageFilterScaled(driver, stat_texture_bg,
705                                         dsthalfrect2 + p, srchalfrect2,
706                                         NULL, colors, true);
707                         p += steppos;
708                 }
709         }
710
711         if (stat_texture_bg && maxcount > count / 2) {
712                 // Draw "off state" textures
713                 s32 start_offset;
714                 if (count % 2 == 1)
715                         start_offset = count / 2 + 1;
716                 else
717                         start_offset = count / 2;
718                 for (s32 i = start_offset; i < maxcount / 2; i++) {
719                         core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
720                         core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
721
722                         dstrect += p;
723                         draw2DImageFilterScaled(driver, stat_texture_bg,
724                                         dstrect, srcrect,
725                                         NULL, colors, true);
726                         p += steppos;
727                 }
728
729                 if (maxcount % 2 == 1) {
730                         draw2DImageFilterScaled(driver, stat_texture_bg,
731                                         dsthalfrect + p, srchalfrect,
732                                         NULL, colors, true);
733                 }
734         }
735 }
736
737
738 void Hud::drawHotbar(u16 playeritem) {
739
740         v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
741
742         InventoryList *mainlist = inventory->getList("main");
743         if (mainlist == NULL) {
744                 //silently ignore this we may not be initialized completely
745                 return;
746         }
747
748         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
749         s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
750         v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
751
752         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
753         if ((float) width / (float) window_size.X <=
754                         g_settings->getFloat("hud_hotbar_max_width")) {
755                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
756                         drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
757                 }
758         } else {
759                 pos.X += width/4;
760
761                 v2s32 secondpos = pos;
762                 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
763
764                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
765                         drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
766                                 mainlist, playeritem + 1, 0);
767                         drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
768                                 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
769                 }
770         }
771 }
772
773
774 void Hud::drawCrosshair()
775 {
776         if (pointing_at_object) {
777                 if (use_object_crosshair_image) {
778                         video::ITexture *object_crosshair = tsrc->getTexture("object_crosshair.png");
779                         v2u32 size  = object_crosshair->getOriginalSize();
780                         v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
781                                         m_displaycenter.Y - (size.Y / 2));
782                         driver->draw2DImage(object_crosshair, lsize,
783                                         core::rect<s32>(0, 0, size.X, size.Y),
784                                         nullptr, crosshair_argb, true);
785                 } else {
786                         driver->draw2DLine(
787                                         m_displaycenter - v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
788                                         OBJECT_CROSSHAIR_LINE_SIZE),
789                                         m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
790                                         OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
791                         driver->draw2DLine(
792                                         m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
793                                         -OBJECT_CROSSHAIR_LINE_SIZE),
794                                         m_displaycenter + v2s32(-OBJECT_CROSSHAIR_LINE_SIZE,
795                                         OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
796                 }
797
798                 return;
799         }
800
801         if (use_crosshair_image) {
802                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
803                 v2u32 size  = crosshair->getOriginalSize();
804                 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
805                                 m_displaycenter.Y - (size.Y / 2));
806                 driver->draw2DImage(crosshair, lsize,
807                                 core::rect<s32>(0, 0, size.X, size.Y),
808                                 nullptr, crosshair_argb, true);
809         } else {
810                 driver->draw2DLine(m_displaycenter - v2s32(CROSSHAIR_LINE_SIZE, 0),
811                                 m_displaycenter + v2s32(CROSSHAIR_LINE_SIZE, 0), crosshair_argb);
812                 driver->draw2DLine(m_displaycenter - v2s32(0, CROSSHAIR_LINE_SIZE),
813                                 m_displaycenter + v2s32(0, CROSSHAIR_LINE_SIZE), crosshair_argb);
814         }
815 }
816
817 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
818 {
819         m_camera_offset = camera_offset;
820         m_selection_pos = pos;
821         m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
822 }
823
824 void Hud::drawSelectionMesh()
825 {
826         if (m_mode == HIGHLIGHT_BOX) {
827                 // Draw 3D selection boxes
828                 video::SMaterial oldmaterial = driver->getMaterial2D();
829                 driver->setMaterial(m_selection_material);
830                 for (auto & selection_box : m_selection_boxes) {
831                         aabb3f box = aabb3f(
832                                 selection_box.MinEdge + m_selection_pos_with_offset,
833                                 selection_box.MaxEdge + m_selection_pos_with_offset);
834
835                         u32 r = (selectionbox_argb.getRed() *
836                                         m_selection_mesh_color.getRed() / 255);
837                         u32 g = (selectionbox_argb.getGreen() *
838                                         m_selection_mesh_color.getGreen() / 255);
839                         u32 b = (selectionbox_argb.getBlue() *
840                                         m_selection_mesh_color.getBlue() / 255);
841                         driver->draw3DBox(box, video::SColor(255, r, g, b));
842                 }
843                 driver->setMaterial(oldmaterial);
844         } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
845                 // Draw selection mesh
846                 video::SMaterial oldmaterial = driver->getMaterial2D();
847                 driver->setMaterial(m_selection_material);
848                 setMeshColor(m_selection_mesh, m_selection_mesh_color);
849                 video::SColor face_color(0,
850                         MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
851                         MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
852                         MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
853                 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
854                         face_color);
855                 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
856                 translateMesh(mesh, m_selection_pos_with_offset);
857                 u32 mc = m_selection_mesh->getMeshBufferCount();
858                 for (u32 i = 0; i < mc; i++) {
859                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
860                         driver->drawMeshBuffer(buf);
861                 }
862                 mesh->drop();
863                 driver->setMaterial(oldmaterial);
864         }
865 }
866
867 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
868 {
869         m_camera_offset = camera_offset;
870         if (m_mode != HIGHLIGHT_HALO)
871                 return;
872
873         if (m_selection_mesh) {
874                 m_selection_mesh->drop();
875                 m_selection_mesh = NULL;
876         }
877
878         if (m_selection_boxes.empty()) {
879                 // No pointed object
880                 return;
881         }
882
883         // New pointed object, create new mesh.
884
885         // Texture UV coordinates for selection boxes
886         static f32 texture_uv[24] = {
887                 0,0,1,1,
888                 0,0,1,1,
889                 0,0,1,1,
890                 0,0,1,1,
891                 0,0,1,1,
892                 0,0,1,1
893         };
894
895         // Use single halo box instead of multiple overlapping boxes.
896         // Temporary solution - problem can be solved with multiple
897         // rendering targets, or some method to remove inner surfaces.
898         // Thats because of halo transparency.
899
900         aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
901         m_halo_boxes.clear();
902
903         for (const auto &selection_box : m_selection_boxes) {
904                 halo_box.addInternalBox(selection_box);
905         }
906
907         m_halo_boxes.push_back(halo_box);
908         m_selection_mesh = convertNodeboxesToMesh(
909                 m_halo_boxes, texture_uv, 0.5);
910 }
911
912 void Hud::resizeHotbar() {
913         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
914
915         if (m_screensize != window_size) {
916                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
917                         RenderingEngine::getDisplayDensity() + 0.5);
918                 m_hotbar_imagesize *= m_hud_scaling;
919                 m_padding = m_hotbar_imagesize / 12;
920                 m_screensize = window_size;
921                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
922         }
923 }
924
925 struct MeshTimeInfo {
926         u64 time;
927         scene::IMesh *mesh = nullptr;
928 };
929
930 void drawItemStack(
931                 video::IVideoDriver *driver,
932                 gui::IGUIFont *font,
933                 const ItemStack &item,
934                 const core::rect<s32> &rect,
935                 const core::rect<s32> *clip,
936                 Client *client,
937                 ItemRotationKind rotation_kind,
938                 const v3s16 &angle,
939                 const v3s16 &rotation_speed)
940 {
941         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
942
943         if (item.empty()) {
944                 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
945                         rotation_time_infos[rotation_kind].mesh = NULL;
946                 }
947                 return;
948         }
949
950         const ItemDefinition &def = item.getDefinition(client->idef());
951         ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
952
953         if (imesh && imesh->mesh) {
954                 scene::IMesh *mesh = imesh->mesh;
955                 driver->clearZBuffer();
956                 s32 delta = 0;
957                 if (rotation_kind < IT_ROT_NONE) {
958                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
959                         if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
960                                 ti.mesh = mesh;
961                                 ti.time = porting::getTimeMs();
962                         } else {
963                                 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
964                         }
965                 }
966                 core::rect<s32> oldViewPort = driver->getViewPort();
967                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
968                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
969                 core::rect<s32> viewrect = rect;
970                 if (clip)
971                         viewrect.clipAgainst(*clip);
972
973                 core::matrix4 ProjMatrix;
974                 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
975
976                 core::matrix4 ViewMatrix;
977                 ViewMatrix.buildProjectionMatrixOrthoLH(
978                         2.0f * viewrect.getWidth() / rect.getWidth(),
979                         2.0f * viewrect.getHeight() / rect.getHeight(),
980                         -1.0f,
981                         100.0f);
982                 ViewMatrix.setTranslation(core::vector3df(
983                         1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
984                                         viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
985                                         viewrect.getWidth(),
986                         1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
987                                         rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
988                                         viewrect.getHeight(),
989                         0.0f));
990
991                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
992                 driver->setTransform(video::ETS_VIEW, ViewMatrix);
993
994                 core::matrix4 matrix;
995                 matrix.makeIdentity();
996
997                 static thread_local bool enable_animations =
998                         g_settings->getBool("inventory_items_animations");
999
1000                 if (enable_animations) {
1001                         float timer_f = (float) delta / 5000.f;
1002                         matrix.setRotationDegrees(v3f(
1003                                 angle.X + rotation_speed.X * 3.60f * timer_f,
1004                                 angle.Y + rotation_speed.Y * 3.60f * timer_f,
1005                                 angle.Z + rotation_speed.Z * 3.60f * timer_f)
1006                         );
1007                 }
1008
1009                 driver->setTransform(video::ETS_WORLD, matrix);
1010                 driver->setViewPort(viewrect);
1011
1012                 video::SColor basecolor =
1013                         client->idef()->getItemstackColor(item, client);
1014
1015                 u32 mc = mesh->getMeshBufferCount();
1016                 for (u32 j = 0; j < mc; ++j) {
1017                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1018                         // we can modify vertices relatively fast,
1019                         // because these meshes are not buffered.
1020                         assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
1021                         video::SColor c = basecolor;
1022
1023                         if (imesh->buffer_colors.size() > j) {
1024                                 ItemPartColor *p = &imesh->buffer_colors[j];
1025                                 if (p->override_base)
1026                                         c = p->color;
1027                         }
1028
1029                         if (imesh->needs_shading)
1030                                 colorizeMeshBuffer(buf, &c);
1031                         else
1032                                 setMeshBufferColor(buf, c);
1033
1034                         video::SMaterial &material = buf->getMaterial();
1035                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1036                         material.Lighting = false;
1037                         driver->setMaterial(material);
1038                         driver->drawMeshBuffer(buf);
1039                 }
1040
1041                 driver->setTransform(video::ETS_VIEW, oldViewMat);
1042                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
1043                 driver->setViewPort(oldViewPort);
1044
1045                 // draw the inventory_overlay
1046                 if (def.type == ITEM_NODE && def.inventory_image.empty() &&
1047                                 !def.inventory_overlay.empty()) {
1048                         ITextureSource *tsrc = client->getTextureSource();
1049                         video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
1050                         core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
1051                         core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
1052                         draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
1053                 }
1054         }
1055
1056         if (def.type == ITEM_TOOL && item.wear != 0) {
1057                 // Draw a progressbar
1058                 float barheight = rect.getHeight() / 16;
1059                 float barpad_x = rect.getWidth() / 16;
1060                 float barpad_y = rect.getHeight() / 16;
1061
1062                 core::rect<s32> progressrect(
1063                         rect.UpperLeftCorner.X + barpad_x,
1064                         rect.LowerRightCorner.Y - barpad_y - barheight,
1065                         rect.LowerRightCorner.X - barpad_x,
1066                         rect.LowerRightCorner.Y - barpad_y);
1067
1068                 // Shrink progressrect by amount of tool damage
1069                 float wear = item.wear / 65535.0f;
1070                 int progressmid =
1071                         wear * progressrect.UpperLeftCorner.X +
1072                         (1 - wear) * progressrect.LowerRightCorner.X;
1073
1074                 // Compute progressbar color
1075                 //   wear = 0.0: green
1076                 //   wear = 0.5: yellow
1077                 //   wear = 1.0: red
1078                 video::SColor color(255, 255, 255, 255);
1079                 int wear_i = MYMIN(std::floor(wear * 600), 511);
1080                 wear_i = MYMIN(wear_i + 10, 511);
1081
1082                 if (wear_i <= 255)
1083                         color.set(255, wear_i, 255, 0);
1084                 else
1085                         color.set(255, 255, 511 - wear_i, 0);
1086
1087                 core::rect<s32> progressrect2 = progressrect;
1088                 progressrect2.LowerRightCorner.X = progressmid;
1089                 driver->draw2DRectangle(color, progressrect2, clip);
1090
1091                 color = video::SColor(255, 0, 0, 0);
1092                 progressrect2 = progressrect;
1093                 progressrect2.UpperLeftCorner.X = progressmid;
1094                 driver->draw2DRectangle(color, progressrect2, clip);
1095         }
1096
1097         if (font != NULL && item.count >= 2) {
1098                 // Get the item count as a string
1099                 std::string text = itos(item.count);
1100                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
1101                 v2s32 sdim(dim.X, dim.Y);
1102
1103                 core::rect<s32> rect2(
1104                         /*rect.UpperLeftCorner,
1105                         core::dimension2d<u32>(rect.getWidth(), 15)*/
1106                         rect.LowerRightCorner - sdim,
1107                         sdim
1108                 );
1109
1110                 video::SColor bgcolor(128, 0, 0, 0);
1111                 driver->draw2DRectangle(bgcolor, rect2, clip);
1112
1113                 video::SColor color(255, 255, 255, 255);
1114                 font->draw(text.c_str(), rect2, color, false, false, clip);
1115         }
1116 }
1117
1118 void drawItemStack(
1119                 video::IVideoDriver *driver,
1120                 gui::IGUIFont *font,
1121                 const ItemStack &item,
1122                 const core::rect<s32> &rect,
1123                 const core::rect<s32> *clip,
1124                 Client *client,
1125                 ItemRotationKind rotation_kind)
1126 {
1127         drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
1128                 v3s16(0, 0, 0), v3s16(0, 100, 0));
1129 }