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