]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/hud.cpp
Add a key to toggle map block bounds (#11172)
[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::toggleBlockBounds()
866 {
867         m_block_bounds_mode = static_cast<BlockBoundsMode>(m_block_bounds_mode + 1);
868
869         if (m_block_bounds_mode >= BLOCK_BOUNDS_MAX) {
870                 m_block_bounds_mode = BLOCK_BOUNDS_OFF;
871         }
872 }
873
874 void Hud::drawBlockBounds()
875 {
876         if (m_block_bounds_mode == BLOCK_BOUNDS_OFF) {
877                 return; 
878         }
879
880         video::SMaterial old_material = driver->getMaterial2D();
881         driver->setMaterial(m_selection_material);
882
883         v3s16 pos = player->getStandingNodePos();
884
885         v3s16 blockPos(
886                 floorf((float) pos.X / MAP_BLOCKSIZE),
887                 floorf((float) pos.Y / MAP_BLOCKSIZE),
888                 floorf((float) pos.Z / MAP_BLOCKSIZE)
889         );
890
891         v3f offset = intToFloat(client->getCamera()->getOffset(), BS);
892
893         s8 radius = m_block_bounds_mode == BLOCK_BOUNDS_ALL ? 2 : 0;
894
895         v3f halfNode = v3f(BS, BS, BS) / 2.0f;
896
897         for (s8 x = -radius; x <= radius; x++)
898         for (s8 y = -radius; y <= radius; y++)
899         for (s8 z = -radius; z <= radius; z++) {
900                 v3s16 blockOffset(x, y, z);
901
902                 aabb3f box(
903                         intToFloat((blockPos + blockOffset) * MAP_BLOCKSIZE, BS) - offset - halfNode,
904                         intToFloat(((blockPos + blockOffset) * MAP_BLOCKSIZE) + (MAP_BLOCKSIZE - 1), BS) - offset + halfNode
905                 );
906
907                 driver->draw3DBox(box, video::SColor(255, 255, 0, 0));
908         }
909
910         driver->setMaterial(old_material);
911 }
912
913 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
914 {
915         m_camera_offset = camera_offset;
916         if (m_mode != HIGHLIGHT_HALO)
917                 return;
918
919         if (m_selection_mesh) {
920                 m_selection_mesh->drop();
921                 m_selection_mesh = NULL;
922         }
923
924         if (m_selection_boxes.empty()) {
925                 // No pointed object
926                 return;
927         }
928
929         // New pointed object, create new mesh.
930
931         // Texture UV coordinates for selection boxes
932         static f32 texture_uv[24] = {
933                 0,0,1,1,
934                 0,0,1,1,
935                 0,0,1,1,
936                 0,0,1,1,
937                 0,0,1,1,
938                 0,0,1,1
939         };
940
941         // Use single halo box instead of multiple overlapping boxes.
942         // Temporary solution - problem can be solved with multiple
943         // rendering targets, or some method to remove inner surfaces.
944         // Thats because of halo transparency.
945
946         aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
947         m_halo_boxes.clear();
948
949         for (const auto &selection_box : m_selection_boxes) {
950                 halo_box.addInternalBox(selection_box);
951         }
952
953         m_halo_boxes.push_back(halo_box);
954         m_selection_mesh = convertNodeboxesToMesh(
955                 m_halo_boxes, texture_uv, 0.5);
956 }
957
958 void Hud::resizeHotbar() {
959         const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
960
961         if (m_screensize != window_size) {
962                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
963                         RenderingEngine::getDisplayDensity() + 0.5);
964                 m_hotbar_imagesize *= m_hud_scaling;
965                 m_padding = m_hotbar_imagesize / 12;
966                 m_screensize = window_size;
967                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
968         }
969 }
970
971 struct MeshTimeInfo {
972         u64 time;
973         scene::IMesh *mesh = nullptr;
974 };
975
976 void drawItemStack(
977                 video::IVideoDriver *driver,
978                 gui::IGUIFont *font,
979                 const ItemStack &item,
980                 const core::rect<s32> &rect,
981                 const core::rect<s32> *clip,
982                 Client *client,
983                 ItemRotationKind rotation_kind,
984                 const v3s16 &angle,
985                 const v3s16 &rotation_speed)
986 {
987         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
988
989         if (item.empty()) {
990                 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
991                         rotation_time_infos[rotation_kind].mesh = NULL;
992                 }
993                 return;
994         }
995
996         const static thread_local bool enable_animations =
997                 g_settings->getBool("inventory_items_animations");
998
999         const ItemDefinition &def = item.getDefinition(client->idef());
1000
1001         // Render as mesh if animated or no inventory image
1002         if ((enable_animations && rotation_kind < IT_ROT_NONE) || def.inventory_image.empty()) {
1003                 ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
1004                 if (!imesh || !imesh->mesh)
1005                         return;
1006                 scene::IMesh *mesh = imesh->mesh;
1007                 driver->clearBuffers(video::ECBF_DEPTH);
1008                 s32 delta = 0;
1009                 if (rotation_kind < IT_ROT_NONE) {
1010                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
1011                         if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
1012                                 ti.mesh = mesh;
1013                                 ti.time = porting::getTimeMs();
1014                         } else {
1015                                 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
1016                         }
1017                 }
1018                 core::rect<s32> oldViewPort = driver->getViewPort();
1019                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
1020                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
1021                 core::rect<s32> viewrect = rect;
1022                 if (clip)
1023                         viewrect.clipAgainst(*clip);
1024
1025                 core::matrix4 ProjMatrix;
1026                 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
1027
1028                 core::matrix4 ViewMatrix;
1029                 ViewMatrix.buildProjectionMatrixOrthoLH(
1030                         2.0f * viewrect.getWidth() / rect.getWidth(),
1031                         2.0f * viewrect.getHeight() / rect.getHeight(),
1032                         -1.0f,
1033                         100.0f);
1034                 ViewMatrix.setTranslation(core::vector3df(
1035                         1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
1036                                         viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
1037                                         viewrect.getWidth(),
1038                         1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
1039                                         rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
1040                                         viewrect.getHeight(),
1041                         0.0f));
1042
1043                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
1044                 driver->setTransform(video::ETS_VIEW, ViewMatrix);
1045
1046                 core::matrix4 matrix;
1047                 matrix.makeIdentity();
1048
1049                 if (enable_animations) {
1050                         float timer_f = (float) delta / 5000.f;
1051                         matrix.setRotationDegrees(v3f(
1052                                 angle.X + rotation_speed.X * 3.60f * timer_f,
1053                                 angle.Y + rotation_speed.Y * 3.60f * timer_f,
1054                                 angle.Z + rotation_speed.Z * 3.60f * timer_f)
1055                         );
1056                 }
1057
1058                 driver->setTransform(video::ETS_WORLD, matrix);
1059                 driver->setViewPort(viewrect);
1060
1061                 video::SColor basecolor =
1062                         client->idef()->getItemstackColor(item, client);
1063
1064                 u32 mc = mesh->getMeshBufferCount();
1065                 for (u32 j = 0; j < mc; ++j) {
1066                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1067                         // we can modify vertices relatively fast,
1068                         // because these meshes are not buffered.
1069                         assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
1070                         video::SColor c = basecolor;
1071
1072                         if (imesh->buffer_colors.size() > j) {
1073                                 ItemPartColor *p = &imesh->buffer_colors[j];
1074                                 if (p->override_base)
1075                                         c = p->color;
1076                         }
1077
1078                         if (imesh->needs_shading)
1079                                 colorizeMeshBuffer(buf, &c);
1080                         else
1081                                 setMeshBufferColor(buf, c);
1082
1083                         video::SMaterial &material = buf->getMaterial();
1084                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1085                         material.Lighting = false;
1086                         driver->setMaterial(material);
1087                         driver->drawMeshBuffer(buf);
1088                 }
1089
1090                 driver->setTransform(video::ETS_VIEW, oldViewMat);
1091                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
1092                 driver->setViewPort(oldViewPort);
1093         } else { // Otherwise just draw as 2D
1094                 video::ITexture *texture = client->idef()->getInventoryTexture(def.name, client);
1095                 if (!texture)
1096                         return;
1097                 video::SColor color =
1098                         client->idef()->getItemstackColor(item, client);
1099                 const video::SColor colors[] = { color, color, color, color };
1100
1101                 draw2DImageFilterScaled(driver, texture, rect,
1102                         core::rect<s32>({0, 0}, core::dimension2di(texture->getOriginalSize())),
1103                         clip, colors, true);
1104         }
1105
1106         // draw the inventory_overlay
1107         if (def.type == ITEM_NODE && def.inventory_image.empty() &&
1108                         !def.inventory_overlay.empty()) {
1109                 ITextureSource *tsrc = client->getTextureSource();
1110                 video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
1111                 core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
1112                 core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
1113                 draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
1114         }
1115
1116         if (def.type == ITEM_TOOL && item.wear != 0) {
1117                 // Draw a progressbar
1118                 float barheight = static_cast<float>(rect.getHeight()) / 16;
1119                 float barpad_x = static_cast<float>(rect.getWidth()) / 16;
1120                 float barpad_y = static_cast<float>(rect.getHeight()) / 16;
1121
1122                 core::rect<s32> progressrect(
1123                         rect.UpperLeftCorner.X + barpad_x,
1124                         rect.LowerRightCorner.Y - barpad_y - barheight,
1125                         rect.LowerRightCorner.X - barpad_x,
1126                         rect.LowerRightCorner.Y - barpad_y);
1127
1128                 // Shrink progressrect by amount of tool damage
1129                 float wear = item.wear / 65535.0f;
1130                 int progressmid =
1131                         wear * progressrect.UpperLeftCorner.X +
1132                         (1 - wear) * progressrect.LowerRightCorner.X;
1133
1134                 // Compute progressbar color
1135                 //   wear = 0.0: green
1136                 //   wear = 0.5: yellow
1137                 //   wear = 1.0: red
1138                 video::SColor color(255, 255, 255, 255);
1139                 int wear_i = MYMIN(std::floor(wear * 600), 511);
1140                 wear_i = MYMIN(wear_i + 10, 511);
1141
1142                 if (wear_i <= 255)
1143                         color.set(255, wear_i, 255, 0);
1144                 else
1145                         color.set(255, 255, 511 - wear_i, 0);
1146
1147                 core::rect<s32> progressrect2 = progressrect;
1148                 progressrect2.LowerRightCorner.X = progressmid;
1149                 driver->draw2DRectangle(color, progressrect2, clip);
1150
1151                 color = video::SColor(255, 0, 0, 0);
1152                 progressrect2 = progressrect;
1153                 progressrect2.UpperLeftCorner.X = progressmid;
1154                 driver->draw2DRectangle(color, progressrect2, clip);
1155         }
1156
1157         if (font != NULL && item.count >= 2) {
1158                 // Get the item count as a string
1159                 std::string text = itos(item.count);
1160                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
1161                 v2s32 sdim(dim.X, dim.Y);
1162
1163                 core::rect<s32> rect2(
1164                         /*rect.UpperLeftCorner,
1165                         core::dimension2d<u32>(rect.getWidth(), 15)*/
1166                         rect.LowerRightCorner - sdim,
1167                         sdim
1168                 );
1169
1170                 video::SColor bgcolor(128, 0, 0, 0);
1171                 driver->draw2DRectangle(bgcolor, rect2, clip);
1172
1173                 video::SColor color(255, 255, 255, 255);
1174                 font->draw(text.c_str(), rect2, color, false, false, clip);
1175         }
1176 }
1177
1178 void drawItemStack(
1179                 video::IVideoDriver *driver,
1180                 gui::IGUIFont *font,
1181                 const ItemStack &item,
1182                 const core::rect<s32> &rect,
1183                 const core::rect<s32> *clip,
1184                 Client *client,
1185                 ItemRotationKind rotation_kind)
1186 {
1187         drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
1188                 v3s16(0, 0, 0), v3s16(0, 100, 0));
1189 }