]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/hud.cpp
Remove unused ITextSceneNode header (#11476)
[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(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         u32 text_height = g_fontengine->getTextHeight();
335         irr::gui::IGUIFont* 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                                 irr::gui::IGUIFont *textfont = font;
360                                 unsigned int font_size = g_fontengine->getDefaultFontSize();
361
362                                 if (e->size.X > 0)
363                                         font_size *= e->size.X;
364
365                                 if (font_size != g_fontengine->getDefaultFontSize())
366                                         textfont = g_fontengine->getFont(font_size);
367
368                                 video::SColor color(255, (e->number >> 16) & 0xFF,
369                                                                                  (e->number >> 8)  & 0xFF,
370                                                                                  (e->number >> 0)  & 0xFF);
371                                 std::wstring text = unescape_translate(utf8_to_wide(e->text));
372                                 core::dimension2d<u32> textsize = textfont->getDimension(text.c_str());
373 #ifdef __ANDROID__
374                                 // The text size on Android is not proportional with the actual scaling
375                                 irr::gui::IGUIFont *font_scaled = font_size <= 3 ?
376                                         textfont : g_fontengine->getFont(font_size - 3);
377                                 if (e->offset.X < -20)
378                                         textsize = font_scaled->getDimension(text.c_str());
379 #endif
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 #ifdef __ANDROID__
387                                 if (e->offset.X < -20)
388                                         font_scaled->draw(text.c_str(), size + pos + offset + offs, color);
389                                 else
390 #endif
391                                 {
392                                         textfont->draw(text.c_str(), size + pos + offset + offs, color);
393                                 }
394                                 break; }
395                         case HUD_ELEM_STATBAR: {
396                                 v2s32 offs(e->offset.X, e->offset.Y);
397                                 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2,
398                                         e->number, e->item, offs, e->size);
399                                 break; }
400                         case HUD_ELEM_INVENTORY: {
401                                 InventoryList *inv = inventory->getList(e->text);
402                                 drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
403                                         inv, e->item, e->dir);
404                                 break; }
405                         case HUD_ELEM_WAYPOINT: {
406                                 if (!calculateScreenPos(camera_offset, e, &pos))
407                                         break;
408                                 v3f p_pos = player->getPosition() / BS;
409                                 pos += v2s32(e->offset.X, e->offset.Y);
410                                 video::SColor color(255, (e->number >> 16) & 0xFF,
411                                                                                  (e->number >> 8)  & 0xFF,
412                                                                                  (e->number >> 0)  & 0xFF);
413                                 std::wstring text = unescape_translate(utf8_to_wide(e->name));
414                                 const std::string &unit = e->text;
415                                 // waypoints reuse the item field to store precision, item = precision + 1
416                                 u32 item = e->item;
417                                 float precision = (item == 0) ? 10.0f : (item - 1.f);
418                                 bool draw_precision = precision > 0;
419
420                                 core::rect<s32> bounds(0, 0, font->getDimension(text.c_str()).Width, (draw_precision ? 2:1) * text_height);
421                                 pos.Y += (e->align.Y - 1.0) * bounds.getHeight() / 2;
422                                 bounds += pos;
423                                 font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0) * bounds.getWidth() / 2, 0), color);
424                                 if (draw_precision) {
425                                         std::ostringstream os;
426                                         float distance = std::floor(precision * p_pos.getDistanceFrom(e->world_pos)) / precision;
427                                         os << distance << unit;
428                                         text = unescape_translate(utf8_to_wide(os.str()));
429                                         bounds.LowerRightCorner.X = bounds.UpperLeftCorner.X + font->getDimension(text.c_str()).Width;
430                                         font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0f) * bounds.getWidth() / 2, text_height), color);
431                                 }
432                                 break; }
433                         case HUD_ELEM_IMAGE_WAYPOINT: {
434                                 if (!calculateScreenPos(camera_offset, e, &pos))
435                                         break;
436                         }
437                         case HUD_ELEM_IMAGE: {
438                                 video::ITexture *texture = tsrc->getTexture(e->text);
439                                 if (!texture)
440                                         continue;
441
442                                 const video::SColor color(255, 255, 255, 255);
443                                 const video::SColor colors[] = {color, color, color, color};
444                                 core::dimension2di imgsize(texture->getOriginalSize());
445                                 v2s32 dstsize(imgsize.Width * e->scale.X * m_scale_factor,
446                                               imgsize.Height * e->scale.Y * m_scale_factor);
447                                 if (e->scale.X < 0)
448                                         dstsize.X = m_screensize.X * (e->scale.X * -0.01);
449                                 if (e->scale.Y < 0)
450                                         dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
451                                 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
452                                              (e->align.Y - 1.0) * dstsize.Y / 2);
453                                 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
454                                 rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
455                                                              e->offset.Y * m_scale_factor);
456                                 draw2DImageFilterScaled(driver, texture, rect,
457                                         core::rect<s32>(core::position2d<s32>(0,0), imgsize),
458                                         NULL, colors, true);
459                                 break; }
460                         case HUD_ELEM_COMPASS: {
461                                 video::ITexture *texture = tsrc->getTexture(e->text);
462                                 if (!texture)
463                                         continue;
464
465                                 // Positionning :
466                                 v2s32 dstsize(e->size.X, e->size.Y);
467                                 if (e->size.X < 0)
468                                         dstsize.X = m_screensize.X * (e->size.X * -0.01);
469                                 if (e->size.Y < 0)
470                                         dstsize.Y = m_screensize.Y * (e->size.Y * -0.01);
471
472                                 if (dstsize.X <= 0 || dstsize.Y <= 0)
473                                         return; // Avoid zero divides
474
475                                 // Angle according to camera view
476                                 v3f fore(0.f, 0.f, 1.f);
477                                 scene::ICameraSceneNode *cam = client->getSceneManager()->getActiveCamera();
478                                 cam->getAbsoluteTransformation().rotateVect(fore);
479                                 int angle = - fore.getHorizontalAngle().Y;
480
481                                 // Limit angle and ajust with given offset
482                                 angle = (angle + (int)e->number) % 360;
483
484                                 core::rect<s32> dstrect(0, 0, dstsize.X, dstsize.Y);
485                                 dstrect += pos + v2s32(
486                                                                 (e->align.X - 1.0) * dstsize.X / 2,
487                                                                 (e->align.Y - 1.0) * dstsize.Y / 2) +
488                                                 v2s32(e->offset.X * m_hud_scaling, e->offset.Y * m_hud_scaling);
489
490                                 switch (e->dir) {
491                                 case HUD_COMPASS_ROTATE:
492                                         drawCompassRotate(e, texture, dstrect, angle);
493                                         break;
494                                 case HUD_COMPASS_ROTATE_REVERSE:
495                                         drawCompassRotate(e, texture, dstrect, -angle);
496                                         break;
497                                 case HUD_COMPASS_TRANSLATE:
498                                         drawCompassTranslate(e, texture, dstrect, angle);
499                                         break;
500                                 case HUD_COMPASS_TRANSLATE_REVERSE:
501                                         drawCompassTranslate(e, texture, dstrect, -angle);
502                                         break;
503                                 default:
504                                         break;
505                                 }
506                                 break; }
507                         case HUD_ELEM_MINIMAP: {
508                                 if (e->size.X <= 0 || e->size.Y <= 0)
509                                         break;
510                                 if (!client->getMinimap())
511                                         break;
512                                 // Draw a minimap of size "size"
513                                 v2s32 dstsize(e->size.X * m_scale_factor,
514                                               e->size.Y * m_scale_factor);
515                                 // (no percent size as minimap would likely be anamorphosed)
516                                 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
517                                              (e->align.Y - 1.0) * dstsize.Y / 2);
518                                 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
519                                 rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
520                                                              e->offset.Y * m_scale_factor);
521                                 client->getMinimap()->drawMinimap(rect);
522                                 break; }
523                         default:
524                                 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type
525                                         << " due to unrecognized type" << std::endl;
526                 }
527         }
528 }
529
530 void Hud::drawCompassTranslate(HudElement *e, video::ITexture *texture,
531                 const core::rect<s32> &rect, int angle)
532 {
533         const video::SColor color(255, 255, 255, 255);
534         const video::SColor colors[] = {color, color, color, color};
535
536         // Compute source image scaling
537         core::dimension2di imgsize(texture->getOriginalSize());
538         core::rect<s32> srcrect(0, 0, imgsize.Width, imgsize.Height);
539
540         v2s32 dstsize(rect.getHeight() * e->scale.X * imgsize.Width / imgsize.Height,
541                         rect.getHeight() * e->scale.Y);
542
543         // Avoid infinite loop
544         if (dstsize.X <= 0 || dstsize.Y <= 0)
545                 return;
546
547         core::rect<s32> tgtrect(0, 0, dstsize.X, dstsize.Y);
548         tgtrect +=  v2s32(
549                                 (rect.getWidth() - dstsize.X) / 2,
550                                 (rect.getHeight() - dstsize.Y) / 2) +
551                         rect.UpperLeftCorner;
552
553         int offset = angle * dstsize.X / 360;
554
555         tgtrect += v2s32(offset, 0);
556
557         // Repeat image as much as needed
558         while (tgtrect.UpperLeftCorner.X > rect.UpperLeftCorner.X)
559                 tgtrect -= v2s32(dstsize.X, 0);
560
561         draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true);
562         tgtrect += v2s32(dstsize.X, 0);
563
564         while (tgtrect.UpperLeftCorner.X < rect.LowerRightCorner.X) {
565                 draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true);
566                 tgtrect += v2s32(dstsize.X, 0);
567         }
568 }
569
570 void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture,
571                 const core::rect<s32> &rect, int angle)
572 {
573         core::rect<s32> oldViewPort = driver->getViewPort();
574         core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
575         core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
576
577         core::matrix4 Matrix;
578         Matrix.makeIdentity();
579         Matrix.setRotationDegrees(v3f(0.f, 0.f, angle));
580
581         driver->setViewPort(rect);
582         driver->setTransform(video::ETS_PROJECTION, core::matrix4());
583         driver->setTransform(video::ETS_VIEW, core::matrix4());
584         driver->setTransform(video::ETS_WORLD, Matrix);
585
586         video::SMaterial &material = m_rotation_mesh_buffer.getMaterial();
587         material.TextureLayer[0].Texture = texture;
588         driver->setMaterial(material);
589         driver->drawMeshBuffer(&m_rotation_mesh_buffer);
590
591         driver->setTransform(video::ETS_WORLD, core::matrix4());
592         driver->setTransform(video::ETS_VIEW, oldViewMat);
593         driver->setTransform(video::ETS_PROJECTION, oldProjMat);
594
595         // restore the view area
596         driver->setViewPort(oldViewPort);
597 }
598
599 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
600                 const std::string &texture, const std::string &bgtexture,
601                 s32 count, s32 maxcount, v2s32 offset, v2s32 size)
602 {
603         const video::SColor color(255, 255, 255, 255);
604         const video::SColor colors[] = {color, color, color, color};
605
606         video::ITexture *stat_texture = tsrc->getTexture(texture);
607         if (!stat_texture)
608                 return;
609
610         video::ITexture *stat_texture_bg = nullptr;
611         if (!bgtexture.empty()) {
612                 stat_texture_bg = tsrc->getTexture(bgtexture);
613         }
614
615         core::dimension2di srcd(stat_texture->getOriginalSize());
616         core::dimension2di dstd;
617         if (size == v2s32()) {
618                 dstd = srcd;
619                 dstd.Height *= m_scale_factor;
620                 dstd.Width  *= m_scale_factor;
621                 offset.X *= m_scale_factor;
622                 offset.Y *= m_scale_factor;
623         } else {
624                 dstd.Height = size.Y * m_scale_factor;
625                 dstd.Width  = size.X * m_scale_factor;
626                 offset.X *= m_scale_factor;
627                 offset.Y *= m_scale_factor;
628         }
629
630         v2s32 p = pos;
631         if (corner & HUD_CORNER_LOWER)
632                 p -= dstd.Height;
633
634         p += offset;
635
636         v2s32 steppos;
637         switch (drawdir) {
638                 case HUD_DIR_RIGHT_LEFT:
639                         steppos = v2s32(-1, 0);
640                         break;
641                 case HUD_DIR_TOP_BOTTOM:
642                         steppos = v2s32(0, 1);
643                         break;
644                 case HUD_DIR_BOTTOM_TOP:
645                         steppos = v2s32(0, -1);
646                         break;
647                 default:
648                         // From left to right
649                         steppos = v2s32(1, 0);
650                         break;
651         }
652
653         auto calculate_clipping_rect = [] (core::dimension2di src,
654                         v2s32 steppos) -> core::rect<s32> {
655
656                 // Create basic rectangle
657                 core::rect<s32> rect(0, 0,
658                         src.Width  - std::abs(steppos.X) * src.Width / 2,
659                         src.Height - std::abs(steppos.Y) * src.Height / 2
660                 );
661                 // Move rectangle left or down
662                 if (steppos.X == -1)
663                         rect += v2s32(src.Width / 2, 0);
664                 if (steppos.Y == -1)
665                         rect += v2s32(0, src.Height / 2);
666                 return rect;
667         };
668         // Rectangles for 1/2 the actual value to display
669         core::rect<s32> srchalfrect, dsthalfrect;
670         // Rectangles for 1/2 the "off state" texture
671         core::rect<s32> srchalfrect2, dsthalfrect2;
672
673         if (count % 2 == 1) {
674                 // Need to draw halves: Calculate rectangles
675                 srchalfrect  = calculate_clipping_rect(srcd, steppos);
676                 dsthalfrect  = calculate_clipping_rect(dstd, steppos);
677                 srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1);
678                 dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1);
679         }
680
681         steppos.X *= dstd.Width;
682         steppos.Y *= dstd.Height;
683
684         // Draw full textures
685         for (s32 i = 0; i < count / 2; i++) {
686                 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
687                 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
688
689                 dstrect += p;
690                 draw2DImageFilterScaled(driver, stat_texture,
691                         dstrect, srcrect, NULL, colors, true);
692                 p += steppos;
693         }
694
695         if (count % 2 == 1) {
696                 // Draw half a texture
697                 draw2DImageFilterScaled(driver, stat_texture,
698                         dsthalfrect + p, srchalfrect, NULL, colors, true);
699
700                 if (stat_texture_bg && maxcount > count) {
701                         draw2DImageFilterScaled(driver, stat_texture_bg,
702                                         dsthalfrect2 + p, srchalfrect2,
703                                         NULL, colors, true);
704                         p += steppos;
705                 }
706         }
707
708         if (stat_texture_bg && maxcount > count / 2) {
709                 // Draw "off state" textures
710                 s32 start_offset;
711                 if (count % 2 == 1)
712                         start_offset = count / 2 + 1;
713                 else
714                         start_offset = count / 2;
715                 for (s32 i = start_offset; i < maxcount / 2; i++) {
716                         core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
717                         core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
718
719                         dstrect += p;
720                         draw2DImageFilterScaled(driver, stat_texture_bg,
721                                         dstrect, srcrect,
722                                         NULL, colors, true);
723                         p += steppos;
724                 }
725
726                 if (maxcount % 2 == 1) {
727                         draw2DImageFilterScaled(driver, stat_texture_bg,
728                                         dsthalfrect + p, srchalfrect,
729                                         NULL, colors, true);
730                 }
731         }
732 }
733
734
735 void Hud::drawHotbar(u16 playeritem) {
736
737         v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
738
739         InventoryList *mainlist = inventory->getList("main");
740         if (mainlist == NULL) {
741                 //silently ignore this we may not be initialized completely
742                 return;
743         }
744
745         s32 hotbar_itemcount = player->hud_hotbar_itemcount;
746         s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
747         v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
748
749         const v2u32 &window_size = RenderingEngine::getWindowSize();
750         if ((float) width / (float) window_size.X <=
751                         g_settings->getFloat("hud_hotbar_max_width")) {
752                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
753                         drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
754                 }
755         } else {
756                 pos.X += width/4;
757
758                 v2s32 secondpos = pos;
759                 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
760
761                 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
762                         drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
763                                 mainlist, playeritem + 1, 0);
764                         drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
765                                 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
766                 }
767         }
768 }
769
770
771 void Hud::drawCrosshair()
772 {
773         if (pointing_at_object) {
774                 if (use_object_crosshair_image) {
775                         video::ITexture *object_crosshair = tsrc->getTexture("object_crosshair.png");
776                         v2u32 size  = object_crosshair->getOriginalSize();
777                         v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
778                                         m_displaycenter.Y - (size.Y / 2));
779                         driver->draw2DImage(object_crosshair, lsize,
780                                         core::rect<s32>(0, 0, size.X, size.Y),
781                                         nullptr, crosshair_argb, true);
782                 } else {
783                         driver->draw2DLine(
784                                         m_displaycenter - v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
785                                         OBJECT_CROSSHAIR_LINE_SIZE),
786                                         m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
787                                         OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
788                         driver->draw2DLine(
789                                         m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
790                                         -OBJECT_CROSSHAIR_LINE_SIZE),
791                                         m_displaycenter + v2s32(-OBJECT_CROSSHAIR_LINE_SIZE,
792                                         OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
793                 }
794
795                 return;
796         }
797
798         if (use_crosshair_image) {
799                 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
800                 v2u32 size  = crosshair->getOriginalSize();
801                 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
802                                 m_displaycenter.Y - (size.Y / 2));
803                 driver->draw2DImage(crosshair, lsize,
804                                 core::rect<s32>(0, 0, size.X, size.Y),
805                                 nullptr, crosshair_argb, true);
806         } else {
807                 driver->draw2DLine(m_displaycenter - v2s32(CROSSHAIR_LINE_SIZE, 0),
808                                 m_displaycenter + v2s32(CROSSHAIR_LINE_SIZE, 0), crosshair_argb);
809                 driver->draw2DLine(m_displaycenter - v2s32(0, CROSSHAIR_LINE_SIZE),
810                                 m_displaycenter + v2s32(0, CROSSHAIR_LINE_SIZE), crosshair_argb);
811         }
812 }
813
814 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
815 {
816         m_camera_offset = camera_offset;
817         m_selection_pos = pos;
818         m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
819 }
820
821 void Hud::drawSelectionMesh()
822 {
823         if (m_mode == HIGHLIGHT_BOX) {
824                 // Draw 3D selection boxes
825                 video::SMaterial oldmaterial = driver->getMaterial2D();
826                 driver->setMaterial(m_selection_material);
827                 for (auto & selection_box : m_selection_boxes) {
828                         aabb3f box = aabb3f(
829                                 selection_box.MinEdge + m_selection_pos_with_offset,
830                                 selection_box.MaxEdge + m_selection_pos_with_offset);
831
832                         u32 r = (selectionbox_argb.getRed() *
833                                         m_selection_mesh_color.getRed() / 255);
834                         u32 g = (selectionbox_argb.getGreen() *
835                                         m_selection_mesh_color.getGreen() / 255);
836                         u32 b = (selectionbox_argb.getBlue() *
837                                         m_selection_mesh_color.getBlue() / 255);
838                         driver->draw3DBox(box, video::SColor(255, r, g, b));
839                 }
840                 driver->setMaterial(oldmaterial);
841         } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
842                 // Draw selection mesh
843                 video::SMaterial oldmaterial = driver->getMaterial2D();
844                 driver->setMaterial(m_selection_material);
845                 setMeshColor(m_selection_mesh, m_selection_mesh_color);
846                 video::SColor face_color(0,
847                         MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
848                         MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
849                         MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
850                 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
851                         face_color);
852                 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
853                 translateMesh(mesh, m_selection_pos_with_offset);
854                 u32 mc = m_selection_mesh->getMeshBufferCount();
855                 for (u32 i = 0; i < mc; i++) {
856                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
857                         driver->drawMeshBuffer(buf);
858                 }
859                 mesh->drop();
860                 driver->setMaterial(oldmaterial);
861         }
862 }
863
864 void Hud::toggleBlockBounds()
865 {
866         m_block_bounds_mode = static_cast<BlockBoundsMode>(m_block_bounds_mode + 1);
867
868         if (m_block_bounds_mode >= BLOCK_BOUNDS_MAX) {
869                 m_block_bounds_mode = BLOCK_BOUNDS_OFF;
870         }
871 }
872
873 void Hud::disableBlockBounds()
874 {
875         m_block_bounds_mode = BLOCK_BOUNDS_OFF;
876 }
877
878 void Hud::drawBlockBounds()
879 {
880         if (m_block_bounds_mode == BLOCK_BOUNDS_OFF) {
881                 return;
882         }
883
884         video::SMaterial old_material = driver->getMaterial2D();
885         driver->setMaterial(m_selection_material);
886
887         v3s16 pos = player->getStandingNodePos();
888
889         v3s16 blockPos(
890                 floorf((float) pos.X / MAP_BLOCKSIZE),
891                 floorf((float) pos.Y / MAP_BLOCKSIZE),
892                 floorf((float) pos.Z / MAP_BLOCKSIZE)
893         );
894
895         v3f offset = intToFloat(client->getCamera()->getOffset(), BS);
896
897         s8 radius = m_block_bounds_mode == BLOCK_BOUNDS_ALL ? 2 : 0;
898
899         v3f halfNode = v3f(BS, BS, BS) / 2.0f;
900
901         for (s8 x = -radius; x <= radius; x++)
902         for (s8 y = -radius; y <= radius; y++)
903         for (s8 z = -radius; z <= radius; z++) {
904                 v3s16 blockOffset(x, y, z);
905
906                 aabb3f box(
907                         intToFloat((blockPos + blockOffset) * MAP_BLOCKSIZE, BS) - offset - halfNode,
908                         intToFloat(((blockPos + blockOffset) * MAP_BLOCKSIZE) + (MAP_BLOCKSIZE - 1), BS) - offset + halfNode
909                 );
910
911                 driver->draw3DBox(box, video::SColor(255, 255, 0, 0));
912         }
913
914         driver->setMaterial(old_material);
915 }
916
917 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
918 {
919         m_camera_offset = camera_offset;
920         if (m_mode != HIGHLIGHT_HALO)
921                 return;
922
923         if (m_selection_mesh) {
924                 m_selection_mesh->drop();
925                 m_selection_mesh = NULL;
926         }
927
928         if (m_selection_boxes.empty()) {
929                 // No pointed object
930                 return;
931         }
932
933         // New pointed object, create new mesh.
934
935         // Texture UV coordinates for selection boxes
936         static f32 texture_uv[24] = {
937                 0,0,1,1,
938                 0,0,1,1,
939                 0,0,1,1,
940                 0,0,1,1,
941                 0,0,1,1,
942                 0,0,1,1
943         };
944
945         // Use single halo box instead of multiple overlapping boxes.
946         // Temporary solution - problem can be solved with multiple
947         // rendering targets, or some method to remove inner surfaces.
948         // Thats because of halo transparency.
949
950         aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
951         m_halo_boxes.clear();
952
953         for (const auto &selection_box : m_selection_boxes) {
954                 halo_box.addInternalBox(selection_box);
955         }
956
957         m_halo_boxes.push_back(halo_box);
958         m_selection_mesh = convertNodeboxesToMesh(
959                 m_halo_boxes, texture_uv, 0.5);
960 }
961
962 void Hud::resizeHotbar() {
963         const v2u32 &window_size = RenderingEngine::getWindowSize();
964
965         if (m_screensize != window_size) {
966                 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
967                         RenderingEngine::getDisplayDensity() + 0.5);
968                 m_hotbar_imagesize *= m_hud_scaling;
969                 m_padding = m_hotbar_imagesize / 12;
970                 m_screensize = window_size;
971                 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
972         }
973 }
974
975 struct MeshTimeInfo {
976         u64 time;
977         scene::IMesh *mesh = nullptr;
978 };
979
980 void drawItemStack(
981                 video::IVideoDriver *driver,
982                 gui::IGUIFont *font,
983                 const ItemStack &item,
984                 const core::rect<s32> &rect,
985                 const core::rect<s32> *clip,
986                 Client *client,
987                 ItemRotationKind rotation_kind,
988                 const v3s16 &angle,
989                 const v3s16 &rotation_speed)
990 {
991         static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
992
993         if (item.empty()) {
994                 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
995                         rotation_time_infos[rotation_kind].mesh = NULL;
996                 }
997                 return;
998         }
999
1000         const static thread_local bool enable_animations =
1001                 g_settings->getBool("inventory_items_animations");
1002
1003         const ItemDefinition &def = item.getDefinition(client->idef());
1004
1005         bool draw_overlay = false;
1006
1007         // Render as mesh if animated or no inventory image
1008         if ((enable_animations && rotation_kind < IT_ROT_NONE) || def.inventory_image.empty()) {
1009                 ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
1010                 if (!imesh || !imesh->mesh)
1011                         return;
1012                 scene::IMesh *mesh = imesh->mesh;
1013                 driver->clearBuffers(video::ECBF_DEPTH);
1014                 s32 delta = 0;
1015                 if (rotation_kind < IT_ROT_NONE) {
1016                         MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
1017                         if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
1018                                 ti.mesh = mesh;
1019                                 ti.time = porting::getTimeMs();
1020                         } else {
1021                                 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
1022                         }
1023                 }
1024                 core::rect<s32> oldViewPort = driver->getViewPort();
1025                 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
1026                 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
1027                 core::rect<s32> viewrect = rect;
1028                 if (clip)
1029                         viewrect.clipAgainst(*clip);
1030
1031                 core::matrix4 ProjMatrix;
1032                 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
1033
1034                 core::matrix4 ViewMatrix;
1035                 ViewMatrix.buildProjectionMatrixOrthoLH(
1036                         2.0f * viewrect.getWidth() / rect.getWidth(),
1037                         2.0f * viewrect.getHeight() / rect.getHeight(),
1038                         -1.0f,
1039                         100.0f);
1040                 ViewMatrix.setTranslation(core::vector3df(
1041                         1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
1042                                         viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
1043                                         viewrect.getWidth(),
1044                         1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
1045                                         rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
1046                                         viewrect.getHeight(),
1047                         0.0f));
1048
1049                 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
1050                 driver->setTransform(video::ETS_VIEW, ViewMatrix);
1051
1052                 core::matrix4 matrix;
1053                 matrix.makeIdentity();
1054
1055                 if (enable_animations) {
1056                         float timer_f = (float) delta / 5000.f;
1057                         matrix.setRotationDegrees(v3f(
1058                                 angle.X + rotation_speed.X * 3.60f * timer_f,
1059                                 angle.Y + rotation_speed.Y * 3.60f * timer_f,
1060                                 angle.Z + rotation_speed.Z * 3.60f * timer_f)
1061                         );
1062                 }
1063
1064                 driver->setTransform(video::ETS_WORLD, matrix);
1065                 driver->setViewPort(viewrect);
1066
1067                 video::SColor basecolor =
1068                         client->idef()->getItemstackColor(item, client);
1069
1070                 u32 mc = mesh->getMeshBufferCount();
1071                 for (u32 j = 0; j < mc; ++j) {
1072                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1073                         // we can modify vertices relatively fast,
1074                         // because these meshes are not buffered.
1075                         assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
1076                         video::SColor c = basecolor;
1077
1078                         if (imesh->buffer_colors.size() > j) {
1079                                 ItemPartColor *p = &imesh->buffer_colors[j];
1080                                 if (p->override_base)
1081                                         c = p->color;
1082                         }
1083
1084                         if (imesh->needs_shading)
1085                                 colorizeMeshBuffer(buf, &c);
1086                         else
1087                                 setMeshBufferColor(buf, c);
1088
1089                         video::SMaterial &material = buf->getMaterial();
1090                         material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1091                         material.Lighting = false;
1092                         driver->setMaterial(material);
1093                         driver->drawMeshBuffer(buf);
1094                 }
1095
1096                 driver->setTransform(video::ETS_VIEW, oldViewMat);
1097                 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
1098                 driver->setViewPort(oldViewPort);
1099
1100                 draw_overlay = def.type == ITEM_NODE && def.inventory_image.empty();
1101         } else { // Otherwise just draw as 2D
1102                 video::ITexture *texture = client->idef()->getInventoryTexture(def.name, client);
1103                 if (!texture)
1104                         return;
1105                 video::SColor color =
1106                         client->idef()->getItemstackColor(item, client);
1107                 const video::SColor colors[] = { color, color, color, color };
1108
1109                 draw2DImageFilterScaled(driver, texture, rect,
1110                         core::rect<s32>({0, 0}, core::dimension2di(texture->getOriginalSize())),
1111                         clip, colors, true);
1112
1113                 draw_overlay = true;
1114         }
1115
1116         // draw the inventory_overlay
1117         if (!def.inventory_overlay.empty() && draw_overlay) {
1118                 ITextureSource *tsrc = client->getTextureSource();
1119                 video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
1120                 core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
1121                 core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
1122                 draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
1123         }
1124
1125         if (def.type == ITEM_TOOL && item.wear != 0) {
1126                 // Draw a progressbar
1127                 float barheight = static_cast<float>(rect.getHeight()) / 16;
1128                 float barpad_x = static_cast<float>(rect.getWidth()) / 16;
1129                 float barpad_y = static_cast<float>(rect.getHeight()) / 16;
1130
1131                 core::rect<s32> progressrect(
1132                         rect.UpperLeftCorner.X + barpad_x,
1133                         rect.LowerRightCorner.Y - barpad_y - barheight,
1134                         rect.LowerRightCorner.X - barpad_x,
1135                         rect.LowerRightCorner.Y - barpad_y);
1136
1137                 // Shrink progressrect by amount of tool damage
1138                 float wear = item.wear / 65535.0f;
1139                 int progressmid =
1140                         wear * progressrect.UpperLeftCorner.X +
1141                         (1 - wear) * progressrect.LowerRightCorner.X;
1142
1143                 // Compute progressbar color
1144                 //   wear = 0.0: green
1145                 //   wear = 0.5: yellow
1146                 //   wear = 1.0: red
1147                 video::SColor color(255, 255, 255, 255);
1148                 int wear_i = MYMIN(std::floor(wear * 600), 511);
1149                 wear_i = MYMIN(wear_i + 10, 511);
1150
1151                 if (wear_i <= 255)
1152                         color.set(255, wear_i, 255, 0);
1153                 else
1154                         color.set(255, 255, 511 - wear_i, 0);
1155
1156                 core::rect<s32> progressrect2 = progressrect;
1157                 progressrect2.LowerRightCorner.X = progressmid;
1158                 driver->draw2DRectangle(color, progressrect2, clip);
1159
1160                 color = video::SColor(255, 0, 0, 0);
1161                 progressrect2 = progressrect;
1162                 progressrect2.UpperLeftCorner.X = progressmid;
1163                 driver->draw2DRectangle(color, progressrect2, clip);
1164         }
1165
1166         if (font != NULL && item.count >= 2) {
1167                 // Get the item count as a string
1168                 std::string text = itos(item.count);
1169                 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
1170                 v2s32 sdim(dim.X, dim.Y);
1171
1172                 core::rect<s32> rect2(
1173                         /*rect.UpperLeftCorner,
1174                         core::dimension2d<u32>(rect.getWidth(), 15)*/
1175                         rect.LowerRightCorner - sdim,
1176                         sdim
1177                 );
1178
1179                 video::SColor bgcolor(128, 0, 0, 0);
1180                 driver->draw2DRectangle(bgcolor, rect2, clip);
1181
1182                 video::SColor color(255, 255, 255, 255);
1183                 font->draw(text.c_str(), rect2, color, false, false, clip);
1184         }
1185 }
1186
1187 void drawItemStack(
1188                 video::IVideoDriver *driver,
1189                 gui::IGUIFont *font,
1190                 const ItemStack &item,
1191                 const core::rect<s32> &rect,
1192                 const core::rect<s32> *clip,
1193                 Client *client,
1194                 ItemRotationKind rotation_kind)
1195 {
1196         drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
1197                 v3s16(0, 0, 0), v3s16(0, 100, 0));
1198 }