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 #define OBJECT_CROSSHAIR_LINE_SIZE 8
45 #define CROSSHAIR_LINE_SIZE 10
47 Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
50 driver = RenderingEngine::get_video_driver();
51 this->guienv = guienv;
52 this->client = client;
53 this->player = player;
54 this->inventory = inventory;
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;
63 for (auto &hbar_color : hbar_colors)
64 hbar_color = video::SColor(255, 255, 255, 255);
66 tsrc = client->getTextureSource();
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);
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);
81 use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
82 use_object_crosshair_image = tsrc->isKnownSourceImage("object_crosshair.png");
84 m_selection_boxes.clear();
87 std::string mode_setting = g_settings->get("node_highlighting");
89 if (mode_setting == "halo") {
90 m_mode = HIGHLIGHT_HALO;
91 } else if (mode_setting == "none") {
92 m_mode = HIGHLIGHT_NONE;
94 m_mode = HIGHLIGHT_BOX;
97 m_selection_material.Lighting = false;
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", 1, 1);
103 m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
105 m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
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);
115 m_selection_material.MaterialType = video::EMT_SOLID;
121 if (m_selection_mesh)
122 m_selection_mesh->drop();
125 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
129 /* draw hihlighting around selected item */
130 if (use_hotbar_selected_image) {
131 core::rect<s32> imgrect2 = rect;
132 imgrect2.UpperLeftCorner.X -= (m_padding*2);
133 imgrect2.UpperLeftCorner.Y -= (m_padding*2);
134 imgrect2.LowerRightCorner.X += (m_padding*2);
135 imgrect2.LowerRightCorner.Y += (m_padding*2);
136 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
137 core::dimension2di imgsize(texture->getOriginalSize());
138 draw2DImageFilterScaled(driver, texture, imgrect2,
139 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
140 NULL, hbar_colors, true);
142 video::SColor c_outside(255,255,0,0);
143 //video::SColor c_outside(255,0,0,0);
144 //video::SColor c_inside(255,192,192,192);
145 s32 x1 = rect.UpperLeftCorner.X;
146 s32 y1 = rect.UpperLeftCorner.Y;
147 s32 x2 = rect.LowerRightCorner.X;
148 s32 y2 = rect.LowerRightCorner.Y;
149 // Black base borders
150 driver->draw2DRectangle(c_outside,
152 v2s32(x1 - m_padding, y1 - m_padding),
153 v2s32(x2 + m_padding, y1)
155 driver->draw2DRectangle(c_outside,
157 v2s32(x1 - m_padding, y2),
158 v2s32(x2 + m_padding, y2 + m_padding)
160 driver->draw2DRectangle(c_outside,
162 v2s32(x1 - m_padding, y1),
165 driver->draw2DRectangle(c_outside,
168 v2s32(x2 + m_padding, y2)
170 /*// Light inside borders
171 driver->draw2DRectangle(c_inside,
173 v2s32(x1 - padding/2, y1 - padding/2),
174 v2s32(x2 + padding/2, y1)
176 driver->draw2DRectangle(c_inside,
178 v2s32(x1 - padding/2, y2),
179 v2s32(x2 + padding/2, y2 + padding/2)
181 driver->draw2DRectangle(c_inside,
183 v2s32(x1 - padding/2, y1),
186 driver->draw2DRectangle(c_inside,
189 v2s32(x2 + padding/2, y2)
195 video::SColor bgcolor2(128, 0, 0, 0);
196 if (!use_hotbar_image)
197 driver->draw2DRectangle(bgcolor2, rect, NULL);
198 drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
199 client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
202 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
203 void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
204 s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
206 #ifdef HAVE_TOUCHSCREENGUI
207 if (g_touchscreengui && inv_offset == 0)
208 g_touchscreengui->resetHud();
211 s32 height = m_hotbar_imagesize + m_padding * 2;
212 s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
214 if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
220 // Position of upper left corner of bar
221 v2s32 pos = screen_offset * m_scale_factor;
224 // Store hotbar_image in member variable, used by drawItem()
225 if (hotbar_image != player->hotbar_image) {
226 hotbar_image = player->hotbar_image;
227 use_hotbar_image = !hotbar_image.empty();
230 // Store hotbar_selected_image in member variable, used by drawItem()
231 if (hotbar_selected_image != player->hotbar_selected_image) {
232 hotbar_selected_image = player->hotbar_selected_image;
233 use_hotbar_selected_image = !hotbar_selected_image.empty();
236 // draw customized item background
237 if (use_hotbar_image) {
238 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
239 width+m_padding/2, height+m_padding/2);
240 core::rect<s32> rect2 = imgrect2 + pos;
241 video::ITexture *texture = tsrc->getTexture(hotbar_image);
242 core::dimension2di imgsize(texture->getOriginalSize());
243 draw2DImageFilterScaled(driver, texture, rect2,
244 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
245 NULL, hbar_colors, true);
249 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
250 for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
251 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
255 case HUD_DIR_RIGHT_LEFT:
256 steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
258 case HUD_DIR_TOP_BOTTOM:
259 steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
261 case HUD_DIR_BOTTOM_TOP:
262 steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
265 steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
269 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
271 #ifdef HAVE_TOUCHSCREENGUI
272 if (g_touchscreengui)
273 g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
278 // Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false.
279 bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos)
281 v3f w_pos = e->world_pos * BS;
282 scene::ICameraSceneNode* camera =
283 RenderingEngine::get_scene_manager()->getActiveCamera();
284 w_pos -= intToFloat(camera_offset, BS);
285 core::matrix4 trans = camera->getProjectionMatrix();
286 trans *= camera->getViewMatrix();
287 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
288 trans.multiplyWith1x4Matrix(transformed_pos);
289 if (transformed_pos[3] < 0)
291 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
292 core::reciprocal(transformed_pos[3]);
293 pos->X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
294 pos->Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
298 void Hud::drawLuaElements(const v3s16 &camera_offset)
300 u32 text_height = g_fontengine->getTextHeight();
301 irr::gui::IGUIFont* font = g_fontengine->getFont();
303 // Reorder elements by z_index
304 std::vector<size_t> ids;
306 for (size_t i = 0; i != player->maxHudId(); i++) {
307 HudElement *e = player->getHud(i);
311 auto it = ids.begin();
312 while (it != ids.end() && player->getHud(*it)->z_index <= e->z_index)
318 for (size_t i : ids) {
319 HudElement *e = player->getHud(i);
321 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
322 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
324 case HUD_ELEM_TEXT: {
325 irr::gui::IGUIFont *textfont = font;
326 unsigned int font_size = g_fontengine->getDefaultFontSize();
329 font_size *= e->size.X;
331 if (font_size != g_fontengine->getDefaultFontSize())
332 textfont = g_fontengine->getFont(font_size);
334 video::SColor color(255, (e->number >> 16) & 0xFF,
335 (e->number >> 8) & 0xFF,
336 (e->number >> 0) & 0xFF);
337 std::wstring text = unescape_translate(utf8_to_wide(e->text));
338 core::dimension2d<u32> textsize = textfont->getDimension(text.c_str());
340 // The text size on Android is not proportional with the actual scaling
341 irr::gui::IGUIFont *font_scaled = font_size <= 3 ?
342 textfont : g_fontengine->getFont(font_size - 3);
343 if (e->offset.X < -20)
344 textsize = font_scaled->getDimension(text.c_str());
346 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
347 (e->align.Y - 1.0) * (textsize.Height / 2));
348 core::rect<s32> size(0, 0, e->scale.X * m_scale_factor,
349 text_height * e->scale.Y * m_scale_factor);
350 v2s32 offs(e->offset.X * m_scale_factor,
351 e->offset.Y * m_scale_factor);
353 if (e->offset.X < -20)
354 font_scaled->draw(text.c_str(), size + pos + offset + offs, color);
358 textfont->draw(text.c_str(), size + pos + offset + offs, color);
361 case HUD_ELEM_STATBAR: {
362 v2s32 offs(e->offset.X, e->offset.Y);
363 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2,
364 e->number, e->item, offs, e->size);
366 case HUD_ELEM_INVENTORY: {
367 InventoryList *inv = inventory->getList(e->text);
368 drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
369 inv, e->item, e->dir);
371 case HUD_ELEM_WAYPOINT: {
372 if (!calculateScreenPos(camera_offset, e, &pos))
374 v3f p_pos = player->getPosition() / BS;
375 pos += v2s32(e->offset.X, e->offset.Y);
376 video::SColor color(255, (e->number >> 16) & 0xFF,
377 (e->number >> 8) & 0xFF,
378 (e->number >> 0) & 0xFF);
379 std::wstring text = unescape_translate(utf8_to_wide(e->name));
380 const std::string &unit = e->text;
381 // waypoints reuse the item field to store precision, item = precision + 1
383 float precision = (item == 0) ? 10.0f : (item - 1.f);
384 bool draw_precision = precision > 0;
386 core::rect<s32> bounds(0, 0, font->getDimension(text.c_str()).Width, (draw_precision ? 2:1) * text_height);
387 pos.Y += (e->align.Y - 1.0) * bounds.getHeight() / 2;
389 font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0) * bounds.getWidth() / 2, 0), color);
390 if (draw_precision) {
391 std::ostringstream os;
392 float distance = std::floor(precision * p_pos.getDistanceFrom(e->world_pos)) / precision;
393 os << distance << unit;
394 text = unescape_translate(utf8_to_wide(os.str()));
395 bounds.LowerRightCorner.X = bounds.UpperLeftCorner.X + font->getDimension(text.c_str()).Width;
396 font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0f) * bounds.getWidth() / 2, text_height), color);
399 case HUD_ELEM_IMAGE_WAYPOINT: {
400 if (!calculateScreenPos(camera_offset, e, &pos))
403 case HUD_ELEM_IMAGE: {
404 video::ITexture *texture = tsrc->getTexture(e->text);
408 const video::SColor color(255, 255, 255, 255);
409 const video::SColor colors[] = {color, color, color, color};
410 core::dimension2di imgsize(texture->getOriginalSize());
411 v2s32 dstsize(imgsize.Width * e->scale.X * m_scale_factor,
412 imgsize.Height * e->scale.Y * m_scale_factor);
414 dstsize.X = m_screensize.X * (e->scale.X * -0.01);
416 dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
417 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
418 (e->align.Y - 1.0) * dstsize.Y / 2);
419 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
420 rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
421 e->offset.Y * m_scale_factor);
422 draw2DImageFilterScaled(driver, texture, rect,
423 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
427 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
428 " of hud element ID " << i << " due to unrecognized type" << std::endl;
434 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
435 const std::string &texture, const std::string &bgtexture,
436 s32 count, s32 maxcount, v2s32 offset, v2s32 size)
438 const video::SColor color(255, 255, 255, 255);
439 const video::SColor colors[] = {color, color, color, color};
441 video::ITexture *stat_texture = tsrc->getTexture(texture);
445 video::ITexture *stat_texture_bg = nullptr;
446 if (!bgtexture.empty()) {
447 stat_texture_bg = tsrc->getTexture(bgtexture);
450 core::dimension2di srcd(stat_texture->getOriginalSize());
451 core::dimension2di dstd;
452 if (size == v2s32()) {
454 dstd.Height *= m_scale_factor;
455 dstd.Width *= m_scale_factor;
456 offset.X *= m_scale_factor;
457 offset.Y *= m_scale_factor;
459 dstd.Height = size.Y * m_scale_factor;
460 dstd.Width = size.X * m_scale_factor;
461 offset.X *= m_scale_factor;
462 offset.Y *= m_scale_factor;
466 if (corner & HUD_CORNER_LOWER)
473 case HUD_DIR_RIGHT_LEFT:
474 steppos = v2s32(-1, 0);
476 case HUD_DIR_TOP_BOTTOM:
477 steppos = v2s32(0, 1);
479 case HUD_DIR_BOTTOM_TOP:
480 steppos = v2s32(0, -1);
483 // From left to right
484 steppos = v2s32(1, 0);
488 auto calculate_clipping_rect = [] (core::dimension2di src,
489 v2s32 steppos) -> core::rect<s32> {
491 // Create basic rectangle
492 core::rect<s32> rect(0, 0,
493 src.Width - std::abs(steppos.X) * src.Width / 2,
494 src.Height - std::abs(steppos.Y) * src.Height / 2
496 // Move rectangle left or down
498 rect += v2s32(src.Width / 2, 0);
500 rect += v2s32(0, src.Height / 2);
503 // Rectangles for 1/2 the actual value to display
504 core::rect<s32> srchalfrect, dsthalfrect;
505 // Rectangles for 1/2 the "off state" texture
506 core::rect<s32> srchalfrect2, dsthalfrect2;
508 if (count % 2 == 1) {
509 // Need to draw halves: Calculate rectangles
510 srchalfrect = calculate_clipping_rect(srcd, steppos);
511 dsthalfrect = calculate_clipping_rect(dstd, steppos);
512 srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1);
513 dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1);
516 steppos.X *= dstd.Width;
517 steppos.Y *= dstd.Height;
519 // Draw full textures
520 for (s32 i = 0; i < count / 2; i++) {
521 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
522 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
525 draw2DImageFilterScaled(driver, stat_texture,
526 dstrect, srcrect, NULL, colors, true);
530 if (count % 2 == 1) {
531 // Draw half a texture
532 draw2DImageFilterScaled(driver, stat_texture,
533 dsthalfrect + p, srchalfrect, NULL, colors, true);
535 if (stat_texture_bg && maxcount > count) {
536 draw2DImageFilterScaled(driver, stat_texture_bg,
537 dsthalfrect2 + p, srchalfrect2,
543 if (stat_texture_bg && maxcount > count / 2) {
544 // Draw "off state" textures
547 start_offset = count / 2 + 1;
549 start_offset = count / 2;
550 for (s32 i = start_offset; i < maxcount / 2; i++) {
551 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
552 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
555 draw2DImageFilterScaled(driver, stat_texture_bg,
561 if (maxcount % 2 == 1) {
562 draw2DImageFilterScaled(driver, stat_texture_bg,
563 dsthalfrect + p, srchalfrect,
570 void Hud::drawHotbar(u16 playeritem) {
572 v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
574 InventoryList *mainlist = inventory->getList("main");
575 if (mainlist == NULL) {
576 //silently ignore this we may not be initialized completely
580 s32 hotbar_itemcount = player->hud_hotbar_itemcount;
581 s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
582 v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
584 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
585 if ((float) width / (float) window_size.X <=
586 g_settings->getFloat("hud_hotbar_max_width")) {
587 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
588 drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
593 v2s32 secondpos = pos;
594 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
596 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
597 drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
598 mainlist, playeritem + 1, 0);
599 drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
600 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
606 void Hud::drawCrosshair()
608 if (pointing_at_object) {
609 if (use_object_crosshair_image) {
610 video::ITexture *object_crosshair = tsrc->getTexture("object_crosshair.png");
611 v2u32 size = object_crosshair->getOriginalSize();
612 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
613 m_displaycenter.Y - (size.Y / 2));
614 driver->draw2DImage(object_crosshair, lsize,
615 core::rect<s32>(0, 0, size.X, size.Y),
616 nullptr, crosshair_argb, true);
619 m_displaycenter - v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
620 OBJECT_CROSSHAIR_LINE_SIZE),
621 m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
622 OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
624 m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
625 -OBJECT_CROSSHAIR_LINE_SIZE),
626 m_displaycenter + v2s32(-OBJECT_CROSSHAIR_LINE_SIZE,
627 OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
633 if (use_crosshair_image) {
634 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
635 v2u32 size = crosshair->getOriginalSize();
636 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
637 m_displaycenter.Y - (size.Y / 2));
638 driver->draw2DImage(crosshair, lsize,
639 core::rect<s32>(0, 0, size.X, size.Y),
640 nullptr, crosshair_argb, true);
642 driver->draw2DLine(m_displaycenter - v2s32(CROSSHAIR_LINE_SIZE, 0),
643 m_displaycenter + v2s32(CROSSHAIR_LINE_SIZE, 0), crosshair_argb);
644 driver->draw2DLine(m_displaycenter - v2s32(0, CROSSHAIR_LINE_SIZE),
645 m_displaycenter + v2s32(0, CROSSHAIR_LINE_SIZE), crosshair_argb);
649 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
651 m_camera_offset = camera_offset;
652 m_selection_pos = pos;
653 m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
656 void Hud::drawSelectionMesh()
658 if (m_mode == HIGHLIGHT_BOX) {
659 // Draw 3D selection boxes
660 video::SMaterial oldmaterial = driver->getMaterial2D();
661 driver->setMaterial(m_selection_material);
662 for (auto & selection_box : m_selection_boxes) {
664 selection_box.MinEdge + m_selection_pos_with_offset,
665 selection_box.MaxEdge + m_selection_pos_with_offset);
667 u32 r = (selectionbox_argb.getRed() *
668 m_selection_mesh_color.getRed() / 255);
669 u32 g = (selectionbox_argb.getGreen() *
670 m_selection_mesh_color.getGreen() / 255);
671 u32 b = (selectionbox_argb.getBlue() *
672 m_selection_mesh_color.getBlue() / 255);
673 driver->draw3DBox(box, video::SColor(255, r, g, b));
675 driver->setMaterial(oldmaterial);
676 } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
677 // Draw selection mesh
678 video::SMaterial oldmaterial = driver->getMaterial2D();
679 driver->setMaterial(m_selection_material);
680 setMeshColor(m_selection_mesh, m_selection_mesh_color);
681 video::SColor face_color(0,
682 MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
683 MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
684 MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
685 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
687 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
688 translateMesh(mesh, m_selection_pos_with_offset);
689 u32 mc = m_selection_mesh->getMeshBufferCount();
690 for (u32 i = 0; i < mc; i++) {
691 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
692 driver->drawMeshBuffer(buf);
695 driver->setMaterial(oldmaterial);
699 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
701 m_camera_offset = camera_offset;
702 if (m_mode != HIGHLIGHT_HALO)
705 if (m_selection_mesh) {
706 m_selection_mesh->drop();
707 m_selection_mesh = NULL;
710 if (m_selection_boxes.empty()) {
715 // New pointed object, create new mesh.
717 // Texture UV coordinates for selection boxes
718 static f32 texture_uv[24] = {
727 // Use single halo box instead of multiple overlapping boxes.
728 // Temporary solution - problem can be solved with multiple
729 // rendering targets, or some method to remove inner surfaces.
730 // Thats because of halo transparency.
732 aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
733 m_halo_boxes.clear();
735 for (const auto &selection_box : m_selection_boxes) {
736 halo_box.addInternalBox(selection_box);
739 m_halo_boxes.push_back(halo_box);
740 m_selection_mesh = convertNodeboxesToMesh(
741 m_halo_boxes, texture_uv, 0.5);
744 void Hud::resizeHotbar() {
745 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
747 if (m_screensize != window_size) {
748 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
749 RenderingEngine::getDisplayDensity() + 0.5);
750 m_hotbar_imagesize *= m_hud_scaling;
751 m_padding = m_hotbar_imagesize / 12;
752 m_screensize = window_size;
753 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
757 struct MeshTimeInfo {
759 scene::IMesh *mesh = nullptr;
763 video::IVideoDriver *driver,
765 const ItemStack &item,
766 const core::rect<s32> &rect,
767 const core::rect<s32> *clip,
769 ItemRotationKind rotation_kind,
771 const v3s16 &rotation_speed)
773 static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
776 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
777 rotation_time_infos[rotation_kind].mesh = NULL;
782 const ItemDefinition &def = item.getDefinition(client->idef());
783 ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
785 if (imesh && imesh->mesh) {
786 scene::IMesh *mesh = imesh->mesh;
787 driver->clearZBuffer();
789 if (rotation_kind < IT_ROT_NONE) {
790 MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
791 if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
793 ti.time = porting::getTimeMs();
795 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
798 core::rect<s32> oldViewPort = driver->getViewPort();
799 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
800 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
801 core::rect<s32> viewrect = rect;
803 viewrect.clipAgainst(*clip);
805 core::matrix4 ProjMatrix;
806 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
808 core::matrix4 ViewMatrix;
809 ViewMatrix.buildProjectionMatrixOrthoLH(
810 2.0f * viewrect.getWidth() / rect.getWidth(),
811 2.0f * viewrect.getHeight() / rect.getHeight(),
814 ViewMatrix.setTranslation(core::vector3df(
815 1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
816 viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
818 1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
819 rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
820 viewrect.getHeight(),
823 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
824 driver->setTransform(video::ETS_VIEW, ViewMatrix);
826 core::matrix4 matrix;
827 matrix.makeIdentity();
829 static thread_local bool enable_animations =
830 g_settings->getBool("inventory_items_animations");
832 if (enable_animations) {
833 float timer_f = (float) delta / 5000.f;
834 matrix.setRotationDegrees(v3f(
835 angle.X + rotation_speed.X * 3.60f * timer_f,
836 angle.Y + rotation_speed.Y * 3.60f * timer_f,
837 angle.Z + rotation_speed.Z * 3.60f * timer_f)
841 driver->setTransform(video::ETS_WORLD, matrix);
842 driver->setViewPort(viewrect);
844 video::SColor basecolor =
845 client->idef()->getItemstackColor(item, client);
847 u32 mc = mesh->getMeshBufferCount();
848 for (u32 j = 0; j < mc; ++j) {
849 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
850 // we can modify vertices relatively fast,
851 // because these meshes are not buffered.
852 assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
853 video::SColor c = basecolor;
855 if (imesh->buffer_colors.size() > j) {
856 ItemPartColor *p = &imesh->buffer_colors[j];
857 if (p->override_base)
861 if (imesh->needs_shading)
862 colorizeMeshBuffer(buf, &c);
864 setMeshBufferColor(buf, c);
866 video::SMaterial &material = buf->getMaterial();
867 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
868 material.Lighting = false;
869 driver->setMaterial(material);
870 driver->drawMeshBuffer(buf);
873 driver->setTransform(video::ETS_VIEW, oldViewMat);
874 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
875 driver->setViewPort(oldViewPort);
877 // draw the inventory_overlay
878 if (def.type == ITEM_NODE && def.inventory_image.empty() &&
879 !def.inventory_overlay.empty()) {
880 ITextureSource *tsrc = client->getTextureSource();
881 video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
882 core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
883 core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
884 draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
888 if (def.type == ITEM_TOOL && item.wear != 0) {
889 // Draw a progressbar
890 float barheight = rect.getHeight() / 16;
891 float barpad_x = rect.getWidth() / 16;
892 float barpad_y = rect.getHeight() / 16;
894 core::rect<s32> progressrect(
895 rect.UpperLeftCorner.X + barpad_x,
896 rect.LowerRightCorner.Y - barpad_y - barheight,
897 rect.LowerRightCorner.X - barpad_x,
898 rect.LowerRightCorner.Y - barpad_y);
900 // Shrink progressrect by amount of tool damage
901 float wear = item.wear / 65535.0f;
903 wear * progressrect.UpperLeftCorner.X +
904 (1 - wear) * progressrect.LowerRightCorner.X;
906 // Compute progressbar color
908 // wear = 0.5: yellow
910 video::SColor color(255, 255, 255, 255);
911 int wear_i = MYMIN(std::floor(wear * 600), 511);
912 wear_i = MYMIN(wear_i + 10, 511);
915 color.set(255, wear_i, 255, 0);
917 color.set(255, 255, 511 - wear_i, 0);
919 core::rect<s32> progressrect2 = progressrect;
920 progressrect2.LowerRightCorner.X = progressmid;
921 driver->draw2DRectangle(color, progressrect2, clip);
923 color = video::SColor(255, 0, 0, 0);
924 progressrect2 = progressrect;
925 progressrect2.UpperLeftCorner.X = progressmid;
926 driver->draw2DRectangle(color, progressrect2, clip);
929 if (font != NULL && item.count >= 2) {
930 // Get the item count as a string
931 std::string text = itos(item.count);
932 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
933 v2s32 sdim(dim.X, dim.Y);
935 core::rect<s32> rect2(
936 /*rect.UpperLeftCorner,
937 core::dimension2d<u32>(rect.getWidth(), 15)*/
938 rect.LowerRightCorner - sdim,
942 video::SColor bgcolor(128, 0, 0, 0);
943 driver->draw2DRectangle(bgcolor, rect2, clip);
945 video::SColor color(255, 255, 255, 255);
946 font->draw(text.c_str(), rect2, color, false, false, clip);
951 video::IVideoDriver *driver,
953 const ItemStack &item,
954 const core::rect<s32> &rect,
955 const core::rect<s32> *clip,
957 ItemRotationKind rotation_kind)
959 drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
960 v3s16(0, 0, 0), v3s16(0, 100, 0));