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