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