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