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>
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.
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.
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.
22 #include "client/hud.h"
25 #include "util/numeric.h"
28 #include "inventory.h"
30 #include "client/tile.h"
31 #include "localplayer.h"
34 #include "fontengine.h"
35 #include "guiscalingfilter.h"
37 #include "wieldmesh.h"
38 #include "client/renderingengine.h"
40 #ifdef HAVE_TOUCHSCREENGUI
41 #include "gui/touchscreengui.h"
44 Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
47 driver = RenderingEngine::get_video_driver();
48 this->guienv = guienv;
49 this->client = client;
50 this->player = player;
51 this->inventory = inventory;
53 m_hud_scaling = g_settings->getFloat("hud_scaling");
54 m_scale_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
55 m_hotbar_imagesize = std::floor(HOTBAR_IMAGE_SIZE *
56 RenderingEngine::getDisplayDensity() + 0.5f);
57 m_hotbar_imagesize *= m_hud_scaling;
58 m_padding = m_hotbar_imagesize / 12;
60 for (auto &hbar_color : hbar_colors)
61 hbar_color = video::SColor(255, 255, 255, 255);
63 tsrc = client->getTextureSource();
65 v3f crosshair_color = g_settings->getV3F("crosshair_color");
66 u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
67 u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
68 u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
69 u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
70 crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
72 v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
73 u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
74 u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
75 u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
76 selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
78 use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
80 m_selection_boxes.clear();
83 std::string mode_setting = g_settings->get("node_highlighting");
85 if (mode_setting == "halo") {
86 m_mode = HIGHLIGHT_HALO;
87 } else if (mode_setting == "none") {
88 m_mode = HIGHLIGHT_NONE;
90 m_mode = HIGHLIGHT_BOX;
93 m_selection_material.Lighting = false;
95 if (g_settings->getBool("enable_shaders")) {
96 IShaderSource *shdrsrc = client->getShaderSource();
97 u16 shader_id = shdrsrc->getShader(
98 m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", 1, 1);
99 m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
101 m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
104 if (m_mode == HIGHLIGHT_BOX) {
105 m_selection_material.Thickness =
106 rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
107 } else if (m_mode == HIGHLIGHT_HALO) {
108 m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
109 m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
111 m_selection_material.MaterialType = video::EMT_SOLID;
117 if (m_selection_mesh)
118 m_selection_mesh->drop();
121 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
125 /* draw hihlighting around selected item */
126 if (use_hotbar_selected_image) {
127 core::rect<s32> imgrect2 = rect;
128 imgrect2.UpperLeftCorner.X -= (m_padding*2);
129 imgrect2.UpperLeftCorner.Y -= (m_padding*2);
130 imgrect2.LowerRightCorner.X += (m_padding*2);
131 imgrect2.LowerRightCorner.Y += (m_padding*2);
132 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
133 core::dimension2di imgsize(texture->getOriginalSize());
134 draw2DImageFilterScaled(driver, texture, imgrect2,
135 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
136 NULL, hbar_colors, true);
138 video::SColor c_outside(255,255,0,0);
139 //video::SColor c_outside(255,0,0,0);
140 //video::SColor c_inside(255,192,192,192);
141 s32 x1 = rect.UpperLeftCorner.X;
142 s32 y1 = rect.UpperLeftCorner.Y;
143 s32 x2 = rect.LowerRightCorner.X;
144 s32 y2 = rect.LowerRightCorner.Y;
145 // Black base borders
146 driver->draw2DRectangle(c_outside,
148 v2s32(x1 - m_padding, y1 - m_padding),
149 v2s32(x2 + m_padding, y1)
151 driver->draw2DRectangle(c_outside,
153 v2s32(x1 - m_padding, y2),
154 v2s32(x2 + m_padding, y2 + m_padding)
156 driver->draw2DRectangle(c_outside,
158 v2s32(x1 - m_padding, y1),
161 driver->draw2DRectangle(c_outside,
164 v2s32(x2 + m_padding, y2)
166 /*// Light inside borders
167 driver->draw2DRectangle(c_inside,
169 v2s32(x1 - padding/2, y1 - padding/2),
170 v2s32(x2 + padding/2, y1)
172 driver->draw2DRectangle(c_inside,
174 v2s32(x1 - padding/2, y2),
175 v2s32(x2 + padding/2, y2 + padding/2)
177 driver->draw2DRectangle(c_inside,
179 v2s32(x1 - padding/2, y1),
182 driver->draw2DRectangle(c_inside,
185 v2s32(x2 + padding/2, y2)
191 video::SColor bgcolor2(128, 0, 0, 0);
192 if (!use_hotbar_image)
193 driver->draw2DRectangle(bgcolor2, rect, NULL);
194 drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
195 client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
198 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
199 void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
200 s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
202 #ifdef HAVE_TOUCHSCREENGUI
203 if (g_touchscreengui && inv_offset == 0)
204 g_touchscreengui->resetHud();
207 s32 height = m_hotbar_imagesize + m_padding * 2;
208 s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
210 if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
216 // Position of upper left corner of bar
217 v2s32 pos = screen_offset * m_scale_factor;
220 // Store hotbar_image in member variable, used by drawItem()
221 if (hotbar_image != player->hotbar_image) {
222 hotbar_image = player->hotbar_image;
223 use_hotbar_image = !hotbar_image.empty();
226 // Store hotbar_selected_image in member variable, used by drawItem()
227 if (hotbar_selected_image != player->hotbar_selected_image) {
228 hotbar_selected_image = player->hotbar_selected_image;
229 use_hotbar_selected_image = !hotbar_selected_image.empty();
232 // draw customized item background
233 if (use_hotbar_image) {
234 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
235 width+m_padding/2, height+m_padding/2);
236 core::rect<s32> rect2 = imgrect2 + pos;
237 video::ITexture *texture = tsrc->getTexture(hotbar_image);
238 core::dimension2di imgsize(texture->getOriginalSize());
239 draw2DImageFilterScaled(driver, texture, rect2,
240 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
241 NULL, hbar_colors, true);
245 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
246 for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
247 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
251 case HUD_DIR_RIGHT_LEFT:
252 steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
254 case HUD_DIR_TOP_BOTTOM:
255 steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
257 case HUD_DIR_BOTTOM_TOP:
258 steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
261 steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
265 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
267 #ifdef HAVE_TOUCHSCREENGUI
268 if (g_touchscreengui)
269 g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
274 // Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false.
275 bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos)
277 v3f w_pos = e->world_pos * BS;
278 scene::ICameraSceneNode* camera =
279 RenderingEngine::get_scene_manager()->getActiveCamera();
280 w_pos -= intToFloat(camera_offset, BS);
281 core::matrix4 trans = camera->getProjectionMatrix();
282 trans *= camera->getViewMatrix();
283 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
284 trans.multiplyWith1x4Matrix(transformed_pos);
285 if (transformed_pos[3] < 0)
287 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
288 core::reciprocal(transformed_pos[3]);
289 pos->X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
290 pos->Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
294 void Hud::drawLuaElements(const v3s16 &camera_offset)
296 u32 text_height = g_fontengine->getTextHeight();
297 irr::gui::IGUIFont* font = g_fontengine->getFont();
299 // Reorder elements by z_index
300 std::vector<size_t> ids;
302 for (size_t i = 0; i != player->maxHudId(); i++) {
303 HudElement *e = player->getHud(i);
307 auto it = ids.begin();
308 while (it != ids.end() && player->getHud(*it)->z_index <= e->z_index)
314 for (size_t i : ids) {
315 HudElement *e = player->getHud(i);
317 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
318 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
320 case HUD_ELEM_TEXT: {
321 irr::gui::IGUIFont *textfont = font;
322 unsigned int font_size = g_fontengine->getDefaultFontSize();
325 font_size *= e->size.X;
327 if (font_size != g_fontengine->getDefaultFontSize())
328 textfont = g_fontengine->getFont(font_size);
330 video::SColor color(255, (e->number >> 16) & 0xFF,
331 (e->number >> 8) & 0xFF,
332 (e->number >> 0) & 0xFF);
333 std::wstring text = unescape_translate(utf8_to_wide(e->text));
334 core::dimension2d<u32> textsize = textfont->getDimension(text.c_str());
336 // The text size on Android is not proportional with the actual scaling
337 irr::gui::IGUIFont *font_scaled = font_size <= 3 ?
338 textfont : g_fontengine->getFont(font_size - 3);
339 if (e->offset.X < -20)
340 textsize = font_scaled->getDimension(text.c_str());
342 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
343 (e->align.Y - 1.0) * (textsize.Height / 2));
344 core::rect<s32> size(0, 0, e->scale.X * m_scale_factor,
345 text_height * e->scale.Y * m_scale_factor);
346 v2s32 offs(e->offset.X * m_scale_factor,
347 e->offset.Y * m_scale_factor);
349 if (e->offset.X < -20)
350 font_scaled->draw(text.c_str(), size + pos + offset + offs, color);
354 textfont->draw(text.c_str(), size + pos + offset + offs, color);
357 case HUD_ELEM_STATBAR: {
358 v2s32 offs(e->offset.X, e->offset.Y);
359 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2,
360 e->number, e->item, offs, e->size);
362 case HUD_ELEM_INVENTORY: {
363 InventoryList *inv = inventory->getList(e->text);
364 drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
365 inv, e->item, e->dir);
367 case HUD_ELEM_WAYPOINT: {
368 if (!calculateScreenPos(camera_offset, e, &pos))
370 v3f p_pos = player->getPosition() / BS;
371 pos += v2s32(e->offset.X, e->offset.Y);
372 video::SColor color(255, (e->number >> 16) & 0xFF,
373 (e->number >> 8) & 0xFF,
374 (e->number >> 0) & 0xFF);
375 std::wstring text = unescape_translate(utf8_to_wide(e->name));
376 const std::string &unit = e->text;
377 // waypoints reuse the item field to store precision, item = precision + 1
379 float precision = (item == 0) ? 10.0f : (item - 1.f);
380 bool draw_precision = precision > 0;
382 core::rect<s32> bounds(0, 0, font->getDimension(text.c_str()).Width, (draw_precision ? 2:1) * text_height);
383 pos.Y += (e->align.Y - 1.0) * bounds.getHeight() / 2;
385 font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0) * bounds.getWidth() / 2, 0), color);
386 if (draw_precision) {
387 std::ostringstream os;
388 float distance = std::floor(precision * p_pos.getDistanceFrom(e->world_pos)) / precision;
389 os << distance << unit;
390 text = unescape_translate(utf8_to_wide(os.str()));
391 bounds.LowerRightCorner.X = bounds.UpperLeftCorner.X + font->getDimension(text.c_str()).Width;
392 font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0f) * bounds.getWidth() / 2, text_height), color);
395 case HUD_ELEM_IMAGE_WAYPOINT: {
396 if (!calculateScreenPos(camera_offset, e, &pos))
399 case HUD_ELEM_IMAGE: {
400 video::ITexture *texture = tsrc->getTexture(e->text);
404 const video::SColor color(255, 255, 255, 255);
405 const video::SColor colors[] = {color, color, color, color};
406 core::dimension2di imgsize(texture->getOriginalSize());
407 v2s32 dstsize(imgsize.Width * e->scale.X * m_scale_factor,
408 imgsize.Height * e->scale.Y * m_scale_factor);
410 dstsize.X = m_screensize.X * (e->scale.X * -0.01);
412 dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
413 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
414 (e->align.Y - 1.0) * dstsize.Y / 2);
415 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
416 rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
417 e->offset.Y * m_scale_factor);
418 draw2DImageFilterScaled(driver, texture, rect,
419 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
423 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
424 " of hud element ID " << i << " due to unrecognized type" << std::endl;
430 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
431 const std::string &texture, const std::string &bgtexture,
432 s32 count, s32 maxcount, v2s32 offset, v2s32 size)
434 const video::SColor color(255, 255, 255, 255);
435 const video::SColor colors[] = {color, color, color, color};
437 video::ITexture *stat_texture = tsrc->getTexture(texture);
441 video::ITexture *stat_texture_bg = nullptr;
442 if (!bgtexture.empty()) {
443 stat_texture_bg = tsrc->getTexture(bgtexture);
446 core::dimension2di srcd(stat_texture->getOriginalSize());
447 core::dimension2di dstd;
448 if (size == v2s32()) {
450 dstd.Height *= m_scale_factor;
451 dstd.Width *= m_scale_factor;
452 offset.X *= m_scale_factor;
453 offset.Y *= m_scale_factor;
455 dstd.Height = size.Y * m_scale_factor;
456 dstd.Width = size.X * m_scale_factor;
457 offset.X *= m_scale_factor;
458 offset.Y *= m_scale_factor;
462 if (corner & HUD_CORNER_LOWER)
469 case HUD_DIR_RIGHT_LEFT:
470 steppos = v2s32(-1, 0);
472 case HUD_DIR_TOP_BOTTOM:
473 steppos = v2s32(0, 1);
475 case HUD_DIR_BOTTOM_TOP:
476 steppos = v2s32(0, -1);
479 // From left to right
480 steppos = v2s32(1, 0);
484 auto calculate_clipping_rect = [] (core::dimension2di src,
485 v2s32 steppos) -> core::rect<s32> {
487 // Create basic rectangle
488 core::rect<s32> rect(0, 0,
489 src.Width - std::abs(steppos.X) * src.Width / 2,
490 src.Height - std::abs(steppos.Y) * src.Height / 2
492 // Move rectangle left or down
494 rect += v2s32(src.Width / 2, 0);
496 rect += v2s32(0, src.Height / 2);
499 // Rectangles for 1/2 the actual value to display
500 core::rect<s32> srchalfrect, dsthalfrect;
501 // Rectangles for 1/2 the "off state" texture
502 core::rect<s32> srchalfrect2, dsthalfrect2;
504 if (count % 2 == 1) {
505 // Need to draw halves: Calculate rectangles
506 srchalfrect = calculate_clipping_rect(srcd, steppos);
507 dsthalfrect = calculate_clipping_rect(dstd, steppos);
508 srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1);
509 dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1);
512 steppos.X *= dstd.Width;
513 steppos.Y *= dstd.Height;
515 // Draw full textures
516 for (s32 i = 0; i < count / 2; i++) {
517 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
518 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
521 draw2DImageFilterScaled(driver, stat_texture,
522 dstrect, srcrect, NULL, colors, true);
526 if (count % 2 == 1) {
527 // Draw half a texture
528 draw2DImageFilterScaled(driver, stat_texture,
529 dsthalfrect + p, srchalfrect, NULL, colors, true);
531 if (stat_texture_bg && maxcount > count) {
532 draw2DImageFilterScaled(driver, stat_texture_bg,
533 dsthalfrect2 + p, srchalfrect2,
539 if (stat_texture_bg && maxcount > count / 2) {
540 // Draw "off state" textures
543 start_offset = count / 2 + 1;
545 start_offset = count / 2;
546 for (s32 i = start_offset; i < maxcount / 2; i++) {
547 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
548 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
551 draw2DImageFilterScaled(driver, stat_texture_bg,
557 if (maxcount % 2 == 1) {
558 draw2DImageFilterScaled(driver, stat_texture_bg,
559 dsthalfrect + p, srchalfrect,
566 void Hud::drawHotbar(u16 playeritem) {
568 v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
570 InventoryList *mainlist = inventory->getList("main");
571 if (mainlist == NULL) {
572 //silently ignore this we may not be initialized completely
576 s32 hotbar_itemcount = player->hud_hotbar_itemcount;
577 s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
578 v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
580 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
581 if ((float) width / (float) window_size.X <=
582 g_settings->getFloat("hud_hotbar_max_width")) {
583 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
584 drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
589 v2s32 secondpos = pos;
590 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
592 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
593 drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
594 mainlist, playeritem + 1, 0);
595 drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
596 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
602 void Hud::drawCrosshair()
604 if (use_crosshair_image) {
605 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
606 v2u32 size = crosshair->getOriginalSize();
607 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
608 m_displaycenter.Y - (size.Y / 2));
609 driver->draw2DImage(crosshair, lsize,
610 core::rect<s32>(0, 0, size.X, size.Y),
611 0, crosshair_argb, true);
613 driver->draw2DLine(m_displaycenter - v2s32(10, 0),
614 m_displaycenter + v2s32(10, 0), crosshair_argb);
615 driver->draw2DLine(m_displaycenter - v2s32(0, 10),
616 m_displaycenter + v2s32(0, 10), crosshair_argb);
620 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
622 m_camera_offset = camera_offset;
623 m_selection_pos = pos;
624 m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
627 void Hud::drawSelectionMesh()
629 if (m_mode == HIGHLIGHT_BOX) {
630 // Draw 3D selection boxes
631 video::SMaterial oldmaterial = driver->getMaterial2D();
632 driver->setMaterial(m_selection_material);
633 for (auto & selection_box : m_selection_boxes) {
635 selection_box.MinEdge + m_selection_pos_with_offset,
636 selection_box.MaxEdge + m_selection_pos_with_offset);
638 u32 r = (selectionbox_argb.getRed() *
639 m_selection_mesh_color.getRed() / 255);
640 u32 g = (selectionbox_argb.getGreen() *
641 m_selection_mesh_color.getGreen() / 255);
642 u32 b = (selectionbox_argb.getBlue() *
643 m_selection_mesh_color.getBlue() / 255);
644 driver->draw3DBox(box, video::SColor(255, r, g, b));
646 driver->setMaterial(oldmaterial);
647 } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
648 // Draw selection mesh
649 video::SMaterial oldmaterial = driver->getMaterial2D();
650 driver->setMaterial(m_selection_material);
651 setMeshColor(m_selection_mesh, m_selection_mesh_color);
652 video::SColor face_color(0,
653 MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
654 MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
655 MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
656 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
658 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
659 translateMesh(mesh, m_selection_pos_with_offset);
660 u32 mc = m_selection_mesh->getMeshBufferCount();
661 for (u32 i = 0; i < mc; i++) {
662 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
663 driver->drawMeshBuffer(buf);
666 driver->setMaterial(oldmaterial);
670 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
672 m_camera_offset = camera_offset;
673 if (m_mode != HIGHLIGHT_HALO)
676 if (m_selection_mesh) {
677 m_selection_mesh->drop();
678 m_selection_mesh = NULL;
681 if (m_selection_boxes.empty()) {
686 // New pointed object, create new mesh.
688 // Texture UV coordinates for selection boxes
689 static f32 texture_uv[24] = {
698 // Use single halo box instead of multiple overlapping boxes.
699 // Temporary solution - problem can be solved with multiple
700 // rendering targets, or some method to remove inner surfaces.
701 // Thats because of halo transparency.
703 aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
704 m_halo_boxes.clear();
706 for (const auto &selection_box : m_selection_boxes) {
707 halo_box.addInternalBox(selection_box);
710 m_halo_boxes.push_back(halo_box);
711 m_selection_mesh = convertNodeboxesToMesh(
712 m_halo_boxes, texture_uv, 0.5);
715 void Hud::resizeHotbar() {
716 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
718 if (m_screensize != window_size) {
719 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
720 RenderingEngine::getDisplayDensity() + 0.5);
721 m_hotbar_imagesize *= m_hud_scaling;
722 m_padding = m_hotbar_imagesize / 12;
723 m_screensize = window_size;
724 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
728 struct MeshTimeInfo {
730 scene::IMesh *mesh = nullptr;
734 video::IVideoDriver *driver,
736 const ItemStack &item,
737 const core::rect<s32> &rect,
738 const core::rect<s32> *clip,
740 ItemRotationKind rotation_kind,
742 const v3s16 &rotation_speed)
744 static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
747 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
748 rotation_time_infos[rotation_kind].mesh = NULL;
753 const ItemDefinition &def = item.getDefinition(client->idef());
754 ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
756 if (imesh && imesh->mesh) {
757 scene::IMesh *mesh = imesh->mesh;
758 driver->clearZBuffer();
760 if (rotation_kind < IT_ROT_NONE) {
761 MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
762 if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
764 ti.time = porting::getTimeMs();
766 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
769 core::rect<s32> oldViewPort = driver->getViewPort();
770 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
771 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
772 core::rect<s32> viewrect = rect;
774 viewrect.clipAgainst(*clip);
776 core::matrix4 ProjMatrix;
777 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
779 core::matrix4 ViewMatrix;
780 ViewMatrix.buildProjectionMatrixOrthoLH(
781 2.0f * viewrect.getWidth() / rect.getWidth(),
782 2.0f * viewrect.getHeight() / rect.getHeight(),
785 ViewMatrix.setTranslation(core::vector3df(
786 1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
787 viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
789 1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
790 rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
791 viewrect.getHeight(),
794 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
795 driver->setTransform(video::ETS_VIEW, ViewMatrix);
797 core::matrix4 matrix;
798 matrix.makeIdentity();
800 static thread_local bool enable_animations =
801 g_settings->getBool("inventory_items_animations");
803 if (enable_animations) {
804 float timer_f = (float) delta / 5000.f;
805 matrix.setRotationDegrees(v3f(
806 angle.X + rotation_speed.X * 3.60f * timer_f,
807 angle.Y + rotation_speed.Y * 3.60f * timer_f,
808 angle.Z + rotation_speed.Z * 3.60f * timer_f)
812 driver->setTransform(video::ETS_WORLD, matrix);
813 driver->setViewPort(viewrect);
815 video::SColor basecolor =
816 client->idef()->getItemstackColor(item, client);
818 u32 mc = mesh->getMeshBufferCount();
819 for (u32 j = 0; j < mc; ++j) {
820 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
821 // we can modify vertices relatively fast,
822 // because these meshes are not buffered.
823 assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
824 video::SColor c = basecolor;
826 if (imesh->buffer_colors.size() > j) {
827 ItemPartColor *p = &imesh->buffer_colors[j];
828 if (p->override_base)
832 if (imesh->needs_shading)
833 colorizeMeshBuffer(buf, &c);
835 setMeshBufferColor(buf, c);
837 video::SMaterial &material = buf->getMaterial();
838 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
839 material.Lighting = false;
840 driver->setMaterial(material);
841 driver->drawMeshBuffer(buf);
844 driver->setTransform(video::ETS_VIEW, oldViewMat);
845 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
846 driver->setViewPort(oldViewPort);
848 // draw the inventory_overlay
849 if (def.type == ITEM_NODE && def.inventory_image.empty() &&
850 !def.inventory_overlay.empty()) {
851 ITextureSource *tsrc = client->getTextureSource();
852 video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
853 core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
854 core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
855 draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
859 if (def.type == ITEM_TOOL && item.wear != 0) {
860 // Draw a progressbar
861 float barheight = rect.getHeight() / 16;
862 float barpad_x = rect.getWidth() / 16;
863 float barpad_y = rect.getHeight() / 16;
865 core::rect<s32> progressrect(
866 rect.UpperLeftCorner.X + barpad_x,
867 rect.LowerRightCorner.Y - barpad_y - barheight,
868 rect.LowerRightCorner.X - barpad_x,
869 rect.LowerRightCorner.Y - barpad_y);
871 // Shrink progressrect by amount of tool damage
872 float wear = item.wear / 65535.0f;
874 wear * progressrect.UpperLeftCorner.X +
875 (1 - wear) * progressrect.LowerRightCorner.X;
877 // Compute progressbar color
879 // wear = 0.5: yellow
881 video::SColor color(255, 255, 255, 255);
882 int wear_i = MYMIN(std::floor(wear * 600), 511);
883 wear_i = MYMIN(wear_i + 10, 511);
886 color.set(255, wear_i, 255, 0);
888 color.set(255, 255, 511 - wear_i, 0);
890 core::rect<s32> progressrect2 = progressrect;
891 progressrect2.LowerRightCorner.X = progressmid;
892 driver->draw2DRectangle(color, progressrect2, clip);
894 color = video::SColor(255, 0, 0, 0);
895 progressrect2 = progressrect;
896 progressrect2.UpperLeftCorner.X = progressmid;
897 driver->draw2DRectangle(color, progressrect2, clip);
900 if (font != NULL && item.count >= 2) {
901 // Get the item count as a string
902 std::string text = itos(item.count);
903 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
904 v2s32 sdim(dim.X, dim.Y);
906 core::rect<s32> rect2(
907 /*rect.UpperLeftCorner,
908 core::dimension2d<u32>(rect.getWidth(), 15)*/
909 rect.LowerRightCorner - sdim,
913 video::SColor bgcolor(128, 0, 0, 0);
914 driver->draw2DRectangle(bgcolor, rect2, clip);
916 video::SColor color(255, 255, 255, 255);
917 font->draw(text.c_str(), rect2, color, false, false, clip);
922 video::IVideoDriver *driver,
924 const ItemStack &item,
925 const core::rect<s32> &rect,
926 const core::rect<s32> *clip,
928 ItemRotationKind rotation_kind)
930 drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
931 v3s16(0, 0, 0), v3s16(0, 100, 0));