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"
39 #include "client/minimap.h"
41 #ifdef HAVE_TOUCHSCREENGUI
42 #include "gui/touchscreengui.h"
45 #define OBJECT_CROSSHAIR_LINE_SIZE 8
46 #define CROSSHAIR_LINE_SIZE 10
48 Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
51 driver = RenderingEngine::get_video_driver();
52 this->guienv = guienv;
53 this->client = client;
54 this->player = player;
55 this->inventory = inventory;
57 m_hud_scaling = g_settings->getFloat("hud_scaling");
58 m_scale_factor = m_hud_scaling * RenderingEngine::getDisplayDensity();
59 m_hotbar_imagesize = std::floor(HOTBAR_IMAGE_SIZE *
60 RenderingEngine::getDisplayDensity() + 0.5f);
61 m_hotbar_imagesize *= m_hud_scaling;
62 m_padding = m_hotbar_imagesize / 12;
64 for (auto &hbar_color : hbar_colors)
65 hbar_color = video::SColor(255, 255, 255, 255);
67 tsrc = client->getTextureSource();
69 v3f crosshair_color = g_settings->getV3F("crosshair_color");
70 u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255);
71 u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255);
72 u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255);
73 u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255);
74 crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b);
76 v3f selectionbox_color = g_settings->getV3F("selectionbox_color");
77 u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255);
78 u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255);
79 u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255);
80 selectionbox_argb = video::SColor(255, sbox_r, sbox_g, sbox_b);
82 use_crosshair_image = tsrc->isKnownSourceImage("crosshair.png");
83 use_object_crosshair_image = tsrc->isKnownSourceImage("object_crosshair.png");
85 m_selection_boxes.clear();
88 std::string mode_setting = g_settings->get("node_highlighting");
90 if (mode_setting == "halo") {
91 m_mode = HIGHLIGHT_HALO;
92 } else if (mode_setting == "none") {
93 m_mode = HIGHLIGHT_NONE;
95 m_mode = HIGHLIGHT_BOX;
98 m_selection_material.Lighting = false;
100 if (g_settings->getBool("enable_shaders")) {
101 IShaderSource *shdrsrc = client->getShaderSource();
102 u16 shader_id = shdrsrc->getShader(
103 m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", TILE_MATERIAL_ALPHA);
104 m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material;
106 m_selection_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
109 if (m_mode == HIGHLIGHT_BOX) {
110 m_selection_material.Thickness =
111 rangelim(g_settings->getS16("selectionbox_width"), 1, 5);
112 } else if (m_mode == HIGHLIGHT_HALO) {
113 m_selection_material.setTexture(0, tsrc->getTextureForMesh("halo.png"));
114 m_selection_material.setFlag(video::EMF_BACK_FACE_CULLING, true);
116 m_selection_material.MaterialType = video::EMT_SOLID;
119 // Prepare mesh for compass drawing
120 m_rotation_mesh_buffer.Vertices.set_used(4);
121 m_rotation_mesh_buffer.Indices.set_used(6);
123 video::SColor white(255, 255, 255, 255);
124 v3f normal(0.f, 0.f, 1.f);
126 m_rotation_mesh_buffer.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f));
127 m_rotation_mesh_buffer.Vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f));
128 m_rotation_mesh_buffer.Vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f));
129 m_rotation_mesh_buffer.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f));
131 m_rotation_mesh_buffer.Indices[0] = 0;
132 m_rotation_mesh_buffer.Indices[1] = 1;
133 m_rotation_mesh_buffer.Indices[2] = 2;
134 m_rotation_mesh_buffer.Indices[3] = 2;
135 m_rotation_mesh_buffer.Indices[4] = 3;
136 m_rotation_mesh_buffer.Indices[5] = 0;
138 m_rotation_mesh_buffer.getMaterial().Lighting = false;
139 m_rotation_mesh_buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
144 if (m_selection_mesh)
145 m_selection_mesh->drop();
148 void Hud::drawItem(const ItemStack &item, const core::rect<s32>& rect,
152 /* draw hihlighting around selected item */
153 if (use_hotbar_selected_image) {
154 core::rect<s32> imgrect2 = rect;
155 imgrect2.UpperLeftCorner.X -= (m_padding*2);
156 imgrect2.UpperLeftCorner.Y -= (m_padding*2);
157 imgrect2.LowerRightCorner.X += (m_padding*2);
158 imgrect2.LowerRightCorner.Y += (m_padding*2);
159 video::ITexture *texture = tsrc->getTexture(hotbar_selected_image);
160 core::dimension2di imgsize(texture->getOriginalSize());
161 draw2DImageFilterScaled(driver, texture, imgrect2,
162 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
163 NULL, hbar_colors, true);
165 video::SColor c_outside(255,255,0,0);
166 //video::SColor c_outside(255,0,0,0);
167 //video::SColor c_inside(255,192,192,192);
168 s32 x1 = rect.UpperLeftCorner.X;
169 s32 y1 = rect.UpperLeftCorner.Y;
170 s32 x2 = rect.LowerRightCorner.X;
171 s32 y2 = rect.LowerRightCorner.Y;
172 // Black base borders
173 driver->draw2DRectangle(c_outside,
175 v2s32(x1 - m_padding, y1 - m_padding),
176 v2s32(x2 + m_padding, y1)
178 driver->draw2DRectangle(c_outside,
180 v2s32(x1 - m_padding, y2),
181 v2s32(x2 + m_padding, y2 + m_padding)
183 driver->draw2DRectangle(c_outside,
185 v2s32(x1 - m_padding, y1),
188 driver->draw2DRectangle(c_outside,
191 v2s32(x2 + m_padding, y2)
193 /*// Light inside borders
194 driver->draw2DRectangle(c_inside,
196 v2s32(x1 - padding/2, y1 - padding/2),
197 v2s32(x2 + padding/2, y1)
199 driver->draw2DRectangle(c_inside,
201 v2s32(x1 - padding/2, y2),
202 v2s32(x2 + padding/2, y2 + padding/2)
204 driver->draw2DRectangle(c_inside,
206 v2s32(x1 - padding/2, y1),
209 driver->draw2DRectangle(c_inside,
212 v2s32(x2 + padding/2, y2)
218 video::SColor bgcolor2(128, 0, 0, 0);
219 if (!use_hotbar_image)
220 driver->draw2DRectangle(bgcolor2, rect, NULL);
221 drawItemStack(driver, g_fontengine->getFont(), item, rect, NULL,
222 client, selected ? IT_ROT_SELECTED : IT_ROT_NONE);
225 //NOTE: selectitem = 0 -> no selected; selectitem 1-based
226 void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
227 s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction)
229 #ifdef HAVE_TOUCHSCREENGUI
230 if (g_touchscreengui && inv_offset == 0)
231 g_touchscreengui->resetHud();
234 s32 height = m_hotbar_imagesize + m_padding * 2;
235 s32 width = (itemcount - inv_offset) * (m_hotbar_imagesize + m_padding * 2);
237 if (direction == HUD_DIR_TOP_BOTTOM || direction == HUD_DIR_BOTTOM_TOP) {
243 // Position of upper left corner of bar
244 v2s32 pos = screen_offset * m_scale_factor;
247 // Store hotbar_image in member variable, used by drawItem()
248 if (hotbar_image != player->hotbar_image) {
249 hotbar_image = player->hotbar_image;
250 use_hotbar_image = !hotbar_image.empty();
253 // Store hotbar_selected_image in member variable, used by drawItem()
254 if (hotbar_selected_image != player->hotbar_selected_image) {
255 hotbar_selected_image = player->hotbar_selected_image;
256 use_hotbar_selected_image = !hotbar_selected_image.empty();
259 // draw customized item background
260 if (use_hotbar_image) {
261 core::rect<s32> imgrect2(-m_padding/2, -m_padding/2,
262 width+m_padding/2, height+m_padding/2);
263 core::rect<s32> rect2 = imgrect2 + pos;
264 video::ITexture *texture = tsrc->getTexture(hotbar_image);
265 core::dimension2di imgsize(texture->getOriginalSize());
266 draw2DImageFilterScaled(driver, texture, rect2,
267 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
268 NULL, hbar_colors, true);
272 core::rect<s32> imgrect(0, 0, m_hotbar_imagesize, m_hotbar_imagesize);
273 for (s32 i = inv_offset; i < itemcount && (size_t)i < mainlist->getSize(); i++) {
274 s32 fullimglen = m_hotbar_imagesize + m_padding * 2;
278 case HUD_DIR_RIGHT_LEFT:
279 steppos = v2s32(-(m_padding + (i - inv_offset) * fullimglen), m_padding);
281 case HUD_DIR_TOP_BOTTOM:
282 steppos = v2s32(m_padding, m_padding + (i - inv_offset) * fullimglen);
284 case HUD_DIR_BOTTOM_TOP:
285 steppos = v2s32(m_padding, -(m_padding + (i - inv_offset) * fullimglen));
288 steppos = v2s32(m_padding + (i - inv_offset) * fullimglen, m_padding);
292 drawItem(mainlist->getItem(i), (imgrect + pos + steppos), (i + 1) == selectitem);
294 #ifdef HAVE_TOUCHSCREENGUI
295 if (g_touchscreengui)
296 g_touchscreengui->registerHudItem(i, (imgrect + pos + steppos));
301 bool Hud::hasElementOfType(HudElementType type)
303 for (size_t i = 0; i != player->maxHudId(); i++) {
304 HudElement *e = player->getHud(i);
313 // Calculates screen position of waypoint. Returns true if waypoint is visible (in front of the player), else false.
314 bool Hud::calculateScreenPos(const v3s16 &camera_offset, HudElement *e, v2s32 *pos)
316 v3f w_pos = e->world_pos * BS;
317 scene::ICameraSceneNode* camera =
318 RenderingEngine::get_scene_manager()->getActiveCamera();
319 w_pos -= intToFloat(camera_offset, BS);
320 core::matrix4 trans = camera->getProjectionMatrix();
321 trans *= camera->getViewMatrix();
322 f32 transformed_pos[4] = { w_pos.X, w_pos.Y, w_pos.Z, 1.0f };
323 trans.multiplyWith1x4Matrix(transformed_pos);
324 if (transformed_pos[3] < 0)
326 f32 zDiv = transformed_pos[3] == 0.0f ? 1.0f :
327 core::reciprocal(transformed_pos[3]);
328 pos->X = m_screensize.X * (0.5 * transformed_pos[0] * zDiv + 0.5);
329 pos->Y = m_screensize.Y * (0.5 - transformed_pos[1] * zDiv * 0.5);
333 void Hud::drawLuaElements(const v3s16 &camera_offset)
335 u32 text_height = g_fontengine->getTextHeight();
336 irr::gui::IGUIFont* font = g_fontengine->getFont();
338 // Reorder elements by z_index
339 std::vector<size_t> ids;
341 for (size_t i = 0; i != player->maxHudId(); i++) {
342 HudElement *e = player->getHud(i);
346 auto it = ids.begin();
347 while (it != ids.end() && player->getHud(*it)->z_index <= e->z_index)
353 for (size_t i : ids) {
354 HudElement *e = player->getHud(i);
356 v2s32 pos(floor(e->pos.X * (float) m_screensize.X + 0.5),
357 floor(e->pos.Y * (float) m_screensize.Y + 0.5));
359 case HUD_ELEM_TEXT: {
360 irr::gui::IGUIFont *textfont = font;
361 unsigned int font_size = g_fontengine->getDefaultFontSize();
364 font_size *= e->size.X;
366 if (font_size != g_fontengine->getDefaultFontSize())
367 textfont = g_fontengine->getFont(font_size);
369 video::SColor color(255, (e->number >> 16) & 0xFF,
370 (e->number >> 8) & 0xFF,
371 (e->number >> 0) & 0xFF);
372 std::wstring text = unescape_translate(utf8_to_wide(e->text));
373 core::dimension2d<u32> textsize = textfont->getDimension(text.c_str());
375 // The text size on Android is not proportional with the actual scaling
376 irr::gui::IGUIFont *font_scaled = font_size <= 3 ?
377 textfont : g_fontengine->getFont(font_size - 3);
378 if (e->offset.X < -20)
379 textsize = font_scaled->getDimension(text.c_str());
381 v2s32 offset((e->align.X - 1.0) * (textsize.Width / 2),
382 (e->align.Y - 1.0) * (textsize.Height / 2));
383 core::rect<s32> size(0, 0, e->scale.X * m_scale_factor,
384 text_height * e->scale.Y * m_scale_factor);
385 v2s32 offs(e->offset.X * m_scale_factor,
386 e->offset.Y * m_scale_factor);
388 if (e->offset.X < -20)
389 font_scaled->draw(text.c_str(), size + pos + offset + offs, color);
393 textfont->draw(text.c_str(), size + pos + offset + offs, color);
396 case HUD_ELEM_STATBAR: {
397 v2s32 offs(e->offset.X, e->offset.Y);
398 drawStatbar(pos, HUD_CORNER_UPPER, e->dir, e->text, e->text2,
399 e->number, e->item, offs, e->size);
401 case HUD_ELEM_INVENTORY: {
402 InventoryList *inv = inventory->getList(e->text);
403 drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0,
404 inv, e->item, e->dir);
406 case HUD_ELEM_WAYPOINT: {
407 if (!calculateScreenPos(camera_offset, e, &pos))
409 v3f p_pos = player->getPosition() / BS;
410 pos += v2s32(e->offset.X, e->offset.Y);
411 video::SColor color(255, (e->number >> 16) & 0xFF,
412 (e->number >> 8) & 0xFF,
413 (e->number >> 0) & 0xFF);
414 std::wstring text = unescape_translate(utf8_to_wide(e->name));
415 const std::string &unit = e->text;
416 // waypoints reuse the item field to store precision, item = precision + 1
418 float precision = (item == 0) ? 10.0f : (item - 1.f);
419 bool draw_precision = precision > 0;
421 core::rect<s32> bounds(0, 0, font->getDimension(text.c_str()).Width, (draw_precision ? 2:1) * text_height);
422 pos.Y += (e->align.Y - 1.0) * bounds.getHeight() / 2;
424 font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0) * bounds.getWidth() / 2, 0), color);
425 if (draw_precision) {
426 std::ostringstream os;
427 float distance = std::floor(precision * p_pos.getDistanceFrom(e->world_pos)) / precision;
428 os << distance << unit;
429 text = unescape_translate(utf8_to_wide(os.str()));
430 bounds.LowerRightCorner.X = bounds.UpperLeftCorner.X + font->getDimension(text.c_str()).Width;
431 font->draw(text.c_str(), bounds + v2s32((e->align.X - 1.0f) * bounds.getWidth() / 2, text_height), color);
434 case HUD_ELEM_IMAGE_WAYPOINT: {
435 if (!calculateScreenPos(camera_offset, e, &pos))
438 case HUD_ELEM_IMAGE: {
439 video::ITexture *texture = tsrc->getTexture(e->text);
443 const video::SColor color(255, 255, 255, 255);
444 const video::SColor colors[] = {color, color, color, color};
445 core::dimension2di imgsize(texture->getOriginalSize());
446 v2s32 dstsize(imgsize.Width * e->scale.X * m_scale_factor,
447 imgsize.Height * e->scale.Y * m_scale_factor);
449 dstsize.X = m_screensize.X * (e->scale.X * -0.01);
451 dstsize.Y = m_screensize.Y * (e->scale.Y * -0.01);
452 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
453 (e->align.Y - 1.0) * dstsize.Y / 2);
454 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
455 rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
456 e->offset.Y * m_scale_factor);
457 draw2DImageFilterScaled(driver, texture, rect,
458 core::rect<s32>(core::position2d<s32>(0,0), imgsize),
461 case HUD_ELEM_COMPASS: {
462 video::ITexture *texture = tsrc->getTexture(e->text);
467 v2s32 dstsize(e->size.X, e->size.Y);
469 dstsize.X = m_screensize.X * (e->size.X * -0.01);
471 dstsize.Y = m_screensize.Y * (e->size.Y * -0.01);
473 if (dstsize.X <= 0 || dstsize.Y <= 0)
474 return; // Avoid zero divides
476 // Angle according to camera view
477 v3f fore(0.f, 0.f, 1.f);
478 scene::ICameraSceneNode *cam = RenderingEngine::get_scene_manager()->getActiveCamera();
479 cam->getAbsoluteTransformation().rotateVect(fore);
480 int angle = - fore.getHorizontalAngle().Y;
482 // Limit angle and ajust with given offset
483 angle = (angle + (int)e->number) % 360;
485 core::rect<s32> dstrect(0, 0, dstsize.X, dstsize.Y);
486 dstrect += pos + v2s32(
487 (e->align.X - 1.0) * dstsize.X / 2,
488 (e->align.Y - 1.0) * dstsize.Y / 2) +
489 v2s32(e->offset.X * m_hud_scaling, e->offset.Y * m_hud_scaling);
492 case HUD_COMPASS_ROTATE:
493 drawCompassRotate(e, texture, dstrect, angle);
495 case HUD_COMPASS_ROTATE_REVERSE:
496 drawCompassRotate(e, texture, dstrect, -angle);
498 case HUD_COMPASS_TRANSLATE:
499 drawCompassTranslate(e, texture, dstrect, angle);
501 case HUD_COMPASS_TRANSLATE_REVERSE:
502 drawCompassTranslate(e, texture, dstrect, -angle);
508 case HUD_ELEM_MINIMAP: {
509 if (e->size.X <= 0 || e->size.Y <= 0)
511 if (!client->getMinimap())
513 // Draw a minimap of size "size"
514 v2s32 dstsize(e->size.X * m_scale_factor,
515 e->size.Y * m_scale_factor);
516 // (no percent size as minimap would likely be anamorphosed)
517 v2s32 offset((e->align.X - 1.0) * dstsize.X / 2,
518 (e->align.Y - 1.0) * dstsize.Y / 2);
519 core::rect<s32> rect(0, 0, dstsize.X, dstsize.Y);
520 rect += pos + offset + v2s32(e->offset.X * m_scale_factor,
521 e->offset.Y * m_scale_factor);
522 client->getMinimap()->drawMinimap(rect);
525 infostream << "Hud::drawLuaElements: ignoring drawform " << e->type <<
526 " of hud element ID " << i << " due to unrecognized type" << std::endl;
531 void Hud::drawCompassTranslate(HudElement *e, video::ITexture *texture,
532 const core::rect<s32> &rect, int angle)
534 const video::SColor color(255, 255, 255, 255);
535 const video::SColor colors[] = {color, color, color, color};
537 // Compute source image scaling
538 core::dimension2di imgsize(texture->getOriginalSize());
539 core::rect<s32> srcrect(0, 0, imgsize.Width, imgsize.Height);
541 v2s32 dstsize(rect.getHeight() * e->scale.X * imgsize.Width / imgsize.Height,
542 rect.getHeight() * e->scale.Y);
544 // Avoid infinite loop
545 if (dstsize.X <= 0 || dstsize.Y <= 0)
548 core::rect<s32> tgtrect(0, 0, dstsize.X, dstsize.Y);
550 (rect.getWidth() - dstsize.X) / 2,
551 (rect.getHeight() - dstsize.Y) / 2) +
552 rect.UpperLeftCorner;
554 int offset = angle * dstsize.X / 360;
556 tgtrect += v2s32(offset, 0);
558 // Repeat image as much as needed
559 while (tgtrect.UpperLeftCorner.X > rect.UpperLeftCorner.X)
560 tgtrect -= v2s32(dstsize.X, 0);
562 draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true);
563 tgtrect += v2s32(dstsize.X, 0);
565 while (tgtrect.UpperLeftCorner.X < rect.LowerRightCorner.X) {
566 draw2DImageFilterScaled(driver, texture, tgtrect, srcrect, &rect, colors, true);
567 tgtrect += v2s32(dstsize.X, 0);
571 void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture,
572 const core::rect<s32> &rect, int angle)
574 core::dimension2di imgsize(texture->getOriginalSize());
576 core::rect<s32> oldViewPort = driver->getViewPort();
577 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
578 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
580 core::matrix4 Matrix;
581 Matrix.makeIdentity();
582 Matrix.setRotationDegrees(v3f(0.f, 0.f, angle));
584 driver->setViewPort(rect);
585 driver->setTransform(video::ETS_PROJECTION, core::matrix4());
586 driver->setTransform(video::ETS_VIEW, core::matrix4());
587 driver->setTransform(video::ETS_WORLD, Matrix);
589 video::SMaterial &material = m_rotation_mesh_buffer.getMaterial();
590 material.TextureLayer[0].Texture = texture;
591 driver->setMaterial(material);
592 driver->drawMeshBuffer(&m_rotation_mesh_buffer);
594 driver->setTransform(video::ETS_WORLD, core::matrix4());
595 driver->setTransform(video::ETS_VIEW, oldViewMat);
596 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
598 // restore the view area
599 driver->setViewPort(oldViewPort);
602 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
603 const std::string &texture, const std::string &bgtexture,
604 s32 count, s32 maxcount, v2s32 offset, v2s32 size)
606 const video::SColor color(255, 255, 255, 255);
607 const video::SColor colors[] = {color, color, color, color};
609 video::ITexture *stat_texture = tsrc->getTexture(texture);
613 video::ITexture *stat_texture_bg = nullptr;
614 if (!bgtexture.empty()) {
615 stat_texture_bg = tsrc->getTexture(bgtexture);
618 core::dimension2di srcd(stat_texture->getOriginalSize());
619 core::dimension2di dstd;
620 if (size == v2s32()) {
622 dstd.Height *= m_scale_factor;
623 dstd.Width *= m_scale_factor;
624 offset.X *= m_scale_factor;
625 offset.Y *= m_scale_factor;
627 dstd.Height = size.Y * m_scale_factor;
628 dstd.Width = size.X * m_scale_factor;
629 offset.X *= m_scale_factor;
630 offset.Y *= m_scale_factor;
634 if (corner & HUD_CORNER_LOWER)
641 case HUD_DIR_RIGHT_LEFT:
642 steppos = v2s32(-1, 0);
644 case HUD_DIR_TOP_BOTTOM:
645 steppos = v2s32(0, 1);
647 case HUD_DIR_BOTTOM_TOP:
648 steppos = v2s32(0, -1);
651 // From left to right
652 steppos = v2s32(1, 0);
656 auto calculate_clipping_rect = [] (core::dimension2di src,
657 v2s32 steppos) -> core::rect<s32> {
659 // Create basic rectangle
660 core::rect<s32> rect(0, 0,
661 src.Width - std::abs(steppos.X) * src.Width / 2,
662 src.Height - std::abs(steppos.Y) * src.Height / 2
664 // Move rectangle left or down
666 rect += v2s32(src.Width / 2, 0);
668 rect += v2s32(0, src.Height / 2);
671 // Rectangles for 1/2 the actual value to display
672 core::rect<s32> srchalfrect, dsthalfrect;
673 // Rectangles for 1/2 the "off state" texture
674 core::rect<s32> srchalfrect2, dsthalfrect2;
676 if (count % 2 == 1) {
677 // Need to draw halves: Calculate rectangles
678 srchalfrect = calculate_clipping_rect(srcd, steppos);
679 dsthalfrect = calculate_clipping_rect(dstd, steppos);
680 srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1);
681 dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1);
684 steppos.X *= dstd.Width;
685 steppos.Y *= dstd.Height;
687 // Draw full textures
688 for (s32 i = 0; i < count / 2; i++) {
689 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
690 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
693 draw2DImageFilterScaled(driver, stat_texture,
694 dstrect, srcrect, NULL, colors, true);
698 if (count % 2 == 1) {
699 // Draw half a texture
700 draw2DImageFilterScaled(driver, stat_texture,
701 dsthalfrect + p, srchalfrect, NULL, colors, true);
703 if (stat_texture_bg && maxcount > count) {
704 draw2DImageFilterScaled(driver, stat_texture_bg,
705 dsthalfrect2 + p, srchalfrect2,
711 if (stat_texture_bg && maxcount > count / 2) {
712 // Draw "off state" textures
715 start_offset = count / 2 + 1;
717 start_offset = count / 2;
718 for (s32 i = start_offset; i < maxcount / 2; i++) {
719 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
720 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
723 draw2DImageFilterScaled(driver, stat_texture_bg,
729 if (maxcount % 2 == 1) {
730 draw2DImageFilterScaled(driver, stat_texture_bg,
731 dsthalfrect + p, srchalfrect,
738 void Hud::drawHotbar(u16 playeritem) {
740 v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
742 InventoryList *mainlist = inventory->getList("main");
743 if (mainlist == NULL) {
744 //silently ignore this we may not be initialized completely
748 s32 hotbar_itemcount = player->hud_hotbar_itemcount;
749 s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
750 v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
752 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
753 if ((float) width / (float) window_size.X <=
754 g_settings->getFloat("hud_hotbar_max_width")) {
755 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
756 drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
761 v2s32 secondpos = pos;
762 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
764 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
765 drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
766 mainlist, playeritem + 1, 0);
767 drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
768 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
774 void Hud::drawCrosshair()
776 if (pointing_at_object) {
777 if (use_object_crosshair_image) {
778 video::ITexture *object_crosshair = tsrc->getTexture("object_crosshair.png");
779 v2u32 size = object_crosshair->getOriginalSize();
780 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
781 m_displaycenter.Y - (size.Y / 2));
782 driver->draw2DImage(object_crosshair, lsize,
783 core::rect<s32>(0, 0, size.X, size.Y),
784 nullptr, crosshair_argb, true);
787 m_displaycenter - v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
788 OBJECT_CROSSHAIR_LINE_SIZE),
789 m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
790 OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
792 m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
793 -OBJECT_CROSSHAIR_LINE_SIZE),
794 m_displaycenter + v2s32(-OBJECT_CROSSHAIR_LINE_SIZE,
795 OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
801 if (use_crosshair_image) {
802 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
803 v2u32 size = crosshair->getOriginalSize();
804 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
805 m_displaycenter.Y - (size.Y / 2));
806 driver->draw2DImage(crosshair, lsize,
807 core::rect<s32>(0, 0, size.X, size.Y),
808 nullptr, crosshair_argb, true);
810 driver->draw2DLine(m_displaycenter - v2s32(CROSSHAIR_LINE_SIZE, 0),
811 m_displaycenter + v2s32(CROSSHAIR_LINE_SIZE, 0), crosshair_argb);
812 driver->draw2DLine(m_displaycenter - v2s32(0, CROSSHAIR_LINE_SIZE),
813 m_displaycenter + v2s32(0, CROSSHAIR_LINE_SIZE), crosshair_argb);
817 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
819 m_camera_offset = camera_offset;
820 m_selection_pos = pos;
821 m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
824 void Hud::drawSelectionMesh()
826 if (m_mode == HIGHLIGHT_BOX) {
827 // Draw 3D selection boxes
828 video::SMaterial oldmaterial = driver->getMaterial2D();
829 driver->setMaterial(m_selection_material);
830 for (auto & selection_box : m_selection_boxes) {
832 selection_box.MinEdge + m_selection_pos_with_offset,
833 selection_box.MaxEdge + m_selection_pos_with_offset);
835 u32 r = (selectionbox_argb.getRed() *
836 m_selection_mesh_color.getRed() / 255);
837 u32 g = (selectionbox_argb.getGreen() *
838 m_selection_mesh_color.getGreen() / 255);
839 u32 b = (selectionbox_argb.getBlue() *
840 m_selection_mesh_color.getBlue() / 255);
841 driver->draw3DBox(box, video::SColor(255, r, g, b));
843 driver->setMaterial(oldmaterial);
844 } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
845 // Draw selection mesh
846 video::SMaterial oldmaterial = driver->getMaterial2D();
847 driver->setMaterial(m_selection_material);
848 setMeshColor(m_selection_mesh, m_selection_mesh_color);
849 video::SColor face_color(0,
850 MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
851 MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
852 MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
853 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
855 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
856 translateMesh(mesh, m_selection_pos_with_offset);
857 u32 mc = m_selection_mesh->getMeshBufferCount();
858 for (u32 i = 0; i < mc; i++) {
859 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
860 driver->drawMeshBuffer(buf);
863 driver->setMaterial(oldmaterial);
867 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
869 m_camera_offset = camera_offset;
870 if (m_mode != HIGHLIGHT_HALO)
873 if (m_selection_mesh) {
874 m_selection_mesh->drop();
875 m_selection_mesh = NULL;
878 if (m_selection_boxes.empty()) {
883 // New pointed object, create new mesh.
885 // Texture UV coordinates for selection boxes
886 static f32 texture_uv[24] = {
895 // Use single halo box instead of multiple overlapping boxes.
896 // Temporary solution - problem can be solved with multiple
897 // rendering targets, or some method to remove inner surfaces.
898 // Thats because of halo transparency.
900 aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
901 m_halo_boxes.clear();
903 for (const auto &selection_box : m_selection_boxes) {
904 halo_box.addInternalBox(selection_box);
907 m_halo_boxes.push_back(halo_box);
908 m_selection_mesh = convertNodeboxesToMesh(
909 m_halo_boxes, texture_uv, 0.5);
912 void Hud::resizeHotbar() {
913 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
915 if (m_screensize != window_size) {
916 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
917 RenderingEngine::getDisplayDensity() + 0.5);
918 m_hotbar_imagesize *= m_hud_scaling;
919 m_padding = m_hotbar_imagesize / 12;
920 m_screensize = window_size;
921 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
925 struct MeshTimeInfo {
927 scene::IMesh *mesh = nullptr;
931 video::IVideoDriver *driver,
933 const ItemStack &item,
934 const core::rect<s32> &rect,
935 const core::rect<s32> *clip,
937 ItemRotationKind rotation_kind,
939 const v3s16 &rotation_speed)
941 static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
944 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
945 rotation_time_infos[rotation_kind].mesh = NULL;
950 const ItemDefinition &def = item.getDefinition(client->idef());
951 ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
953 if (imesh && imesh->mesh) {
954 scene::IMesh *mesh = imesh->mesh;
955 driver->clearZBuffer();
957 if (rotation_kind < IT_ROT_NONE) {
958 MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
959 if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
961 ti.time = porting::getTimeMs();
963 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
966 core::rect<s32> oldViewPort = driver->getViewPort();
967 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
968 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
969 core::rect<s32> viewrect = rect;
971 viewrect.clipAgainst(*clip);
973 core::matrix4 ProjMatrix;
974 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
976 core::matrix4 ViewMatrix;
977 ViewMatrix.buildProjectionMatrixOrthoLH(
978 2.0f * viewrect.getWidth() / rect.getWidth(),
979 2.0f * viewrect.getHeight() / rect.getHeight(),
982 ViewMatrix.setTranslation(core::vector3df(
983 1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
984 viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
986 1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
987 rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
988 viewrect.getHeight(),
991 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
992 driver->setTransform(video::ETS_VIEW, ViewMatrix);
994 core::matrix4 matrix;
995 matrix.makeIdentity();
997 static thread_local bool enable_animations =
998 g_settings->getBool("inventory_items_animations");
1000 if (enable_animations) {
1001 float timer_f = (float) delta / 5000.f;
1002 matrix.setRotationDegrees(v3f(
1003 angle.X + rotation_speed.X * 3.60f * timer_f,
1004 angle.Y + rotation_speed.Y * 3.60f * timer_f,
1005 angle.Z + rotation_speed.Z * 3.60f * timer_f)
1009 driver->setTransform(video::ETS_WORLD, matrix);
1010 driver->setViewPort(viewrect);
1012 video::SColor basecolor =
1013 client->idef()->getItemstackColor(item, client);
1015 u32 mc = mesh->getMeshBufferCount();
1016 for (u32 j = 0; j < mc; ++j) {
1017 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1018 // we can modify vertices relatively fast,
1019 // because these meshes are not buffered.
1020 assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
1021 video::SColor c = basecolor;
1023 if (imesh->buffer_colors.size() > j) {
1024 ItemPartColor *p = &imesh->buffer_colors[j];
1025 if (p->override_base)
1029 if (imesh->needs_shading)
1030 colorizeMeshBuffer(buf, &c);
1032 setMeshBufferColor(buf, c);
1034 video::SMaterial &material = buf->getMaterial();
1035 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1036 material.Lighting = false;
1037 driver->setMaterial(material);
1038 driver->drawMeshBuffer(buf);
1041 driver->setTransform(video::ETS_VIEW, oldViewMat);
1042 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
1043 driver->setViewPort(oldViewPort);
1045 // draw the inventory_overlay
1046 if (def.type == ITEM_NODE && def.inventory_image.empty() &&
1047 !def.inventory_overlay.empty()) {
1048 ITextureSource *tsrc = client->getTextureSource();
1049 video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
1050 core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
1051 core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
1052 draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
1056 if (def.type == ITEM_TOOL && item.wear != 0) {
1057 // Draw a progressbar
1058 float barheight = static_cast<float>(rect.getHeight()) / 16;
1059 float barpad_x = static_cast<float>(rect.getWidth()) / 16;
1060 float barpad_y = static_cast<float>(rect.getHeight()) / 16;
1062 core::rect<s32> progressrect(
1063 rect.UpperLeftCorner.X + barpad_x,
1064 rect.LowerRightCorner.Y - barpad_y - barheight,
1065 rect.LowerRightCorner.X - barpad_x,
1066 rect.LowerRightCorner.Y - barpad_y);
1068 // Shrink progressrect by amount of tool damage
1069 float wear = item.wear / 65535.0f;
1071 wear * progressrect.UpperLeftCorner.X +
1072 (1 - wear) * progressrect.LowerRightCorner.X;
1074 // Compute progressbar color
1075 // wear = 0.0: green
1076 // wear = 0.5: yellow
1078 video::SColor color(255, 255, 255, 255);
1079 int wear_i = MYMIN(std::floor(wear * 600), 511);
1080 wear_i = MYMIN(wear_i + 10, 511);
1083 color.set(255, wear_i, 255, 0);
1085 color.set(255, 255, 511 - wear_i, 0);
1087 core::rect<s32> progressrect2 = progressrect;
1088 progressrect2.LowerRightCorner.X = progressmid;
1089 driver->draw2DRectangle(color, progressrect2, clip);
1091 color = video::SColor(255, 0, 0, 0);
1092 progressrect2 = progressrect;
1093 progressrect2.UpperLeftCorner.X = progressmid;
1094 driver->draw2DRectangle(color, progressrect2, clip);
1097 if (font != NULL && item.count >= 2) {
1098 // Get the item count as a string
1099 std::string text = itos(item.count);
1100 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
1101 v2s32 sdim(dim.X, dim.Y);
1103 core::rect<s32> rect2(
1104 /*rect.UpperLeftCorner,
1105 core::dimension2d<u32>(rect.getWidth(), 15)*/
1106 rect.LowerRightCorner - sdim,
1110 video::SColor bgcolor(128, 0, 0, 0);
1111 driver->draw2DRectangle(bgcolor, rect2, clip);
1113 video::SColor color(255, 255, 255, 255);
1114 font->draw(text.c_str(), rect2, color, false, false, clip);
1119 video::IVideoDriver *driver,
1120 gui::IGUIFont *font,
1121 const ItemStack &item,
1122 const core::rect<s32> &rect,
1123 const core::rect<s32> *clip,
1125 ItemRotationKind rotation_kind)
1127 drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
1128 v3s16(0, 0, 0), v3s16(0, 100, 0));