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<HudElement*> elems;
340 elems.reserve(player->maxHudId());
342 for (size_t i = 0; i != player->maxHudId(); i++) {
343 HudElement *e = player->getHud(i);
347 auto it = elems.begin();
348 while (it != elems.end() && (*it)->z_index <= e->z_index)
354 for (HudElement *e : elems) {
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 << " 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::rect<s32> oldViewPort = driver->getViewPort();
575 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
576 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
578 core::matrix4 Matrix;
579 Matrix.makeIdentity();
580 Matrix.setRotationDegrees(v3f(0.f, 0.f, angle));
582 driver->setViewPort(rect);
583 driver->setTransform(video::ETS_PROJECTION, core::matrix4());
584 driver->setTransform(video::ETS_VIEW, core::matrix4());
585 driver->setTransform(video::ETS_WORLD, Matrix);
587 video::SMaterial &material = m_rotation_mesh_buffer.getMaterial();
588 material.TextureLayer[0].Texture = texture;
589 driver->setMaterial(material);
590 driver->drawMeshBuffer(&m_rotation_mesh_buffer);
592 driver->setTransform(video::ETS_WORLD, core::matrix4());
593 driver->setTransform(video::ETS_VIEW, oldViewMat);
594 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
596 // restore the view area
597 driver->setViewPort(oldViewPort);
600 void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
601 const std::string &texture, const std::string &bgtexture,
602 s32 count, s32 maxcount, v2s32 offset, v2s32 size)
604 const video::SColor color(255, 255, 255, 255);
605 const video::SColor colors[] = {color, color, color, color};
607 video::ITexture *stat_texture = tsrc->getTexture(texture);
611 video::ITexture *stat_texture_bg = nullptr;
612 if (!bgtexture.empty()) {
613 stat_texture_bg = tsrc->getTexture(bgtexture);
616 core::dimension2di srcd(stat_texture->getOriginalSize());
617 core::dimension2di dstd;
618 if (size == v2s32()) {
620 dstd.Height *= m_scale_factor;
621 dstd.Width *= m_scale_factor;
622 offset.X *= m_scale_factor;
623 offset.Y *= m_scale_factor;
625 dstd.Height = size.Y * m_scale_factor;
626 dstd.Width = size.X * m_scale_factor;
627 offset.X *= m_scale_factor;
628 offset.Y *= m_scale_factor;
632 if (corner & HUD_CORNER_LOWER)
639 case HUD_DIR_RIGHT_LEFT:
640 steppos = v2s32(-1, 0);
642 case HUD_DIR_TOP_BOTTOM:
643 steppos = v2s32(0, 1);
645 case HUD_DIR_BOTTOM_TOP:
646 steppos = v2s32(0, -1);
649 // From left to right
650 steppos = v2s32(1, 0);
654 auto calculate_clipping_rect = [] (core::dimension2di src,
655 v2s32 steppos) -> core::rect<s32> {
657 // Create basic rectangle
658 core::rect<s32> rect(0, 0,
659 src.Width - std::abs(steppos.X) * src.Width / 2,
660 src.Height - std::abs(steppos.Y) * src.Height / 2
662 // Move rectangle left or down
664 rect += v2s32(src.Width / 2, 0);
666 rect += v2s32(0, src.Height / 2);
669 // Rectangles for 1/2 the actual value to display
670 core::rect<s32> srchalfrect, dsthalfrect;
671 // Rectangles for 1/2 the "off state" texture
672 core::rect<s32> srchalfrect2, dsthalfrect2;
674 if (count % 2 == 1) {
675 // Need to draw halves: Calculate rectangles
676 srchalfrect = calculate_clipping_rect(srcd, steppos);
677 dsthalfrect = calculate_clipping_rect(dstd, steppos);
678 srchalfrect2 = calculate_clipping_rect(srcd, steppos * -1);
679 dsthalfrect2 = calculate_clipping_rect(dstd, steppos * -1);
682 steppos.X *= dstd.Width;
683 steppos.Y *= dstd.Height;
685 // Draw full textures
686 for (s32 i = 0; i < count / 2; i++) {
687 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
688 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
691 draw2DImageFilterScaled(driver, stat_texture,
692 dstrect, srcrect, NULL, colors, true);
696 if (count % 2 == 1) {
697 // Draw half a texture
698 draw2DImageFilterScaled(driver, stat_texture,
699 dsthalfrect + p, srchalfrect, NULL, colors, true);
701 if (stat_texture_bg && maxcount > count) {
702 draw2DImageFilterScaled(driver, stat_texture_bg,
703 dsthalfrect2 + p, srchalfrect2,
709 if (stat_texture_bg && maxcount > count / 2) {
710 // Draw "off state" textures
713 start_offset = count / 2 + 1;
715 start_offset = count / 2;
716 for (s32 i = start_offset; i < maxcount / 2; i++) {
717 core::rect<s32> srcrect(0, 0, srcd.Width, srcd.Height);
718 core::rect<s32> dstrect(0, 0, dstd.Width, dstd.Height);
721 draw2DImageFilterScaled(driver, stat_texture_bg,
727 if (maxcount % 2 == 1) {
728 draw2DImageFilterScaled(driver, stat_texture_bg,
729 dsthalfrect + p, srchalfrect,
736 void Hud::drawHotbar(u16 playeritem) {
738 v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y);
740 InventoryList *mainlist = inventory->getList("main");
741 if (mainlist == NULL) {
742 //silently ignore this we may not be initialized completely
746 s32 hotbar_itemcount = player->hud_hotbar_itemcount;
747 s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2);
748 v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3);
750 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
751 if ((float) width / (float) window_size.X <=
752 g_settings->getFloat("hud_hotbar_max_width")) {
753 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
754 drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0);
759 v2s32 secondpos = pos;
760 pos = pos - v2s32(0, m_hotbar_imagesize + m_padding);
762 if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) {
763 drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0,
764 mainlist, playeritem + 1, 0);
765 drawItems(secondpos, v2s32(0, 0), hotbar_itemcount,
766 hotbar_itemcount / 2, mainlist, playeritem + 1, 0);
772 void Hud::drawCrosshair()
774 if (pointing_at_object) {
775 if (use_object_crosshair_image) {
776 video::ITexture *object_crosshair = tsrc->getTexture("object_crosshair.png");
777 v2u32 size = object_crosshair->getOriginalSize();
778 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
779 m_displaycenter.Y - (size.Y / 2));
780 driver->draw2DImage(object_crosshair, lsize,
781 core::rect<s32>(0, 0, size.X, size.Y),
782 nullptr, crosshair_argb, true);
785 m_displaycenter - v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
786 OBJECT_CROSSHAIR_LINE_SIZE),
787 m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
788 OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
790 m_displaycenter + v2s32(OBJECT_CROSSHAIR_LINE_SIZE,
791 -OBJECT_CROSSHAIR_LINE_SIZE),
792 m_displaycenter + v2s32(-OBJECT_CROSSHAIR_LINE_SIZE,
793 OBJECT_CROSSHAIR_LINE_SIZE), crosshair_argb);
799 if (use_crosshair_image) {
800 video::ITexture *crosshair = tsrc->getTexture("crosshair.png");
801 v2u32 size = crosshair->getOriginalSize();
802 v2s32 lsize = v2s32(m_displaycenter.X - (size.X / 2),
803 m_displaycenter.Y - (size.Y / 2));
804 driver->draw2DImage(crosshair, lsize,
805 core::rect<s32>(0, 0, size.X, size.Y),
806 nullptr, crosshair_argb, true);
808 driver->draw2DLine(m_displaycenter - v2s32(CROSSHAIR_LINE_SIZE, 0),
809 m_displaycenter + v2s32(CROSSHAIR_LINE_SIZE, 0), crosshair_argb);
810 driver->draw2DLine(m_displaycenter - v2s32(0, CROSSHAIR_LINE_SIZE),
811 m_displaycenter + v2s32(0, CROSSHAIR_LINE_SIZE), crosshair_argb);
815 void Hud::setSelectionPos(const v3f &pos, const v3s16 &camera_offset)
817 m_camera_offset = camera_offset;
818 m_selection_pos = pos;
819 m_selection_pos_with_offset = pos - intToFloat(camera_offset, BS);
822 void Hud::drawSelectionMesh()
824 if (m_mode == HIGHLIGHT_BOX) {
825 // Draw 3D selection boxes
826 video::SMaterial oldmaterial = driver->getMaterial2D();
827 driver->setMaterial(m_selection_material);
828 for (auto & selection_box : m_selection_boxes) {
830 selection_box.MinEdge + m_selection_pos_with_offset,
831 selection_box.MaxEdge + m_selection_pos_with_offset);
833 u32 r = (selectionbox_argb.getRed() *
834 m_selection_mesh_color.getRed() / 255);
835 u32 g = (selectionbox_argb.getGreen() *
836 m_selection_mesh_color.getGreen() / 255);
837 u32 b = (selectionbox_argb.getBlue() *
838 m_selection_mesh_color.getBlue() / 255);
839 driver->draw3DBox(box, video::SColor(255, r, g, b));
841 driver->setMaterial(oldmaterial);
842 } else if (m_mode == HIGHLIGHT_HALO && m_selection_mesh) {
843 // Draw selection mesh
844 video::SMaterial oldmaterial = driver->getMaterial2D();
845 driver->setMaterial(m_selection_material);
846 setMeshColor(m_selection_mesh, m_selection_mesh_color);
847 video::SColor face_color(0,
848 MYMIN(255, m_selection_mesh_color.getRed() * 1.5),
849 MYMIN(255, m_selection_mesh_color.getGreen() * 1.5),
850 MYMIN(255, m_selection_mesh_color.getBlue() * 1.5));
851 setMeshColorByNormal(m_selection_mesh, m_selected_face_normal,
853 scene::IMesh* mesh = cloneMesh(m_selection_mesh);
854 translateMesh(mesh, m_selection_pos_with_offset);
855 u32 mc = m_selection_mesh->getMeshBufferCount();
856 for (u32 i = 0; i < mc; i++) {
857 scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
858 driver->drawMeshBuffer(buf);
861 driver->setMaterial(oldmaterial);
865 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
867 m_camera_offset = camera_offset;
868 if (m_mode != HIGHLIGHT_HALO)
871 if (m_selection_mesh) {
872 m_selection_mesh->drop();
873 m_selection_mesh = NULL;
876 if (m_selection_boxes.empty()) {
881 // New pointed object, create new mesh.
883 // Texture UV coordinates for selection boxes
884 static f32 texture_uv[24] = {
893 // Use single halo box instead of multiple overlapping boxes.
894 // Temporary solution - problem can be solved with multiple
895 // rendering targets, or some method to remove inner surfaces.
896 // Thats because of halo transparency.
898 aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
899 m_halo_boxes.clear();
901 for (const auto &selection_box : m_selection_boxes) {
902 halo_box.addInternalBox(selection_box);
905 m_halo_boxes.push_back(halo_box);
906 m_selection_mesh = convertNodeboxesToMesh(
907 m_halo_boxes, texture_uv, 0.5);
910 void Hud::resizeHotbar() {
911 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
913 if (m_screensize != window_size) {
914 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
915 RenderingEngine::getDisplayDensity() + 0.5);
916 m_hotbar_imagesize *= m_hud_scaling;
917 m_padding = m_hotbar_imagesize / 12;
918 m_screensize = window_size;
919 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
923 struct MeshTimeInfo {
925 scene::IMesh *mesh = nullptr;
929 video::IVideoDriver *driver,
931 const ItemStack &item,
932 const core::rect<s32> &rect,
933 const core::rect<s32> *clip,
935 ItemRotationKind rotation_kind,
937 const v3s16 &rotation_speed)
939 static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
942 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
943 rotation_time_infos[rotation_kind].mesh = NULL;
948 const static thread_local bool enable_animations =
949 g_settings->getBool("inventory_items_animations");
951 const ItemDefinition &def = item.getDefinition(client->idef());
953 // Render as mesh if animated or no inventory image
954 if ((enable_animations && rotation_kind < IT_ROT_NONE) || def.inventory_image.empty()) {
955 ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
956 if (!imesh || !imesh->mesh)
958 scene::IMesh *mesh = imesh->mesh;
959 driver->clearBuffers(video::ECBF_DEPTH);
961 if (rotation_kind < IT_ROT_NONE) {
962 MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
963 if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
965 ti.time = porting::getTimeMs();
967 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
970 core::rect<s32> oldViewPort = driver->getViewPort();
971 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
972 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
973 core::rect<s32> viewrect = rect;
975 viewrect.clipAgainst(*clip);
977 core::matrix4 ProjMatrix;
978 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
980 core::matrix4 ViewMatrix;
981 ViewMatrix.buildProjectionMatrixOrthoLH(
982 2.0f * viewrect.getWidth() / rect.getWidth(),
983 2.0f * viewrect.getHeight() / rect.getHeight(),
986 ViewMatrix.setTranslation(core::vector3df(
987 1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
988 viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
990 1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
991 rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
992 viewrect.getHeight(),
995 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
996 driver->setTransform(video::ETS_VIEW, ViewMatrix);
998 core::matrix4 matrix;
999 matrix.makeIdentity();
1001 if (enable_animations) {
1002 float timer_f = (float) delta / 5000.f;
1003 matrix.setRotationDegrees(v3f(
1004 angle.X + rotation_speed.X * 3.60f * timer_f,
1005 angle.Y + rotation_speed.Y * 3.60f * timer_f,
1006 angle.Z + rotation_speed.Z * 3.60f * timer_f)
1010 driver->setTransform(video::ETS_WORLD, matrix);
1011 driver->setViewPort(viewrect);
1013 video::SColor basecolor =
1014 client->idef()->getItemstackColor(item, client);
1016 u32 mc = mesh->getMeshBufferCount();
1017 for (u32 j = 0; j < mc; ++j) {
1018 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1019 // we can modify vertices relatively fast,
1020 // because these meshes are not buffered.
1021 assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
1022 video::SColor c = basecolor;
1024 if (imesh->buffer_colors.size() > j) {
1025 ItemPartColor *p = &imesh->buffer_colors[j];
1026 if (p->override_base)
1030 if (imesh->needs_shading)
1031 colorizeMeshBuffer(buf, &c);
1033 setMeshBufferColor(buf, c);
1035 video::SMaterial &material = buf->getMaterial();
1036 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1037 material.Lighting = false;
1038 driver->setMaterial(material);
1039 driver->drawMeshBuffer(buf);
1042 driver->setTransform(video::ETS_VIEW, oldViewMat);
1043 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
1044 driver->setViewPort(oldViewPort);
1045 } else { // Otherwise just draw as 2D
1046 video::ITexture *texture = client->idef()->getInventoryTexture(def.name, client);
1049 video::SColor color =
1050 client->idef()->getItemstackColor(item, client);
1051 const video::SColor colors[] = { color, color, color, color };
1053 draw2DImageFilterScaled(driver, texture, rect,
1054 core::rect<s32>({0, 0}, core::dimension2di(texture->getOriginalSize())),
1055 clip, colors, true);
1058 // draw the inventory_overlay
1059 if (def.type == ITEM_NODE && def.inventory_image.empty() &&
1060 !def.inventory_overlay.empty()) {
1061 ITextureSource *tsrc = client->getTextureSource();
1062 video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
1063 core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
1064 core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
1065 draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
1068 if (def.type == ITEM_TOOL && item.wear != 0) {
1069 // Draw a progressbar
1070 float barheight = static_cast<float>(rect.getHeight()) / 16;
1071 float barpad_x = static_cast<float>(rect.getWidth()) / 16;
1072 float barpad_y = static_cast<float>(rect.getHeight()) / 16;
1074 core::rect<s32> progressrect(
1075 rect.UpperLeftCorner.X + barpad_x,
1076 rect.LowerRightCorner.Y - barpad_y - barheight,
1077 rect.LowerRightCorner.X - barpad_x,
1078 rect.LowerRightCorner.Y - barpad_y);
1080 // Shrink progressrect by amount of tool damage
1081 float wear = item.wear / 65535.0f;
1083 wear * progressrect.UpperLeftCorner.X +
1084 (1 - wear) * progressrect.LowerRightCorner.X;
1086 // Compute progressbar color
1087 // wear = 0.0: green
1088 // wear = 0.5: yellow
1090 video::SColor color(255, 255, 255, 255);
1091 int wear_i = MYMIN(std::floor(wear * 600), 511);
1092 wear_i = MYMIN(wear_i + 10, 511);
1095 color.set(255, wear_i, 255, 0);
1097 color.set(255, 255, 511 - wear_i, 0);
1099 core::rect<s32> progressrect2 = progressrect;
1100 progressrect2.LowerRightCorner.X = progressmid;
1101 driver->draw2DRectangle(color, progressrect2, clip);
1103 color = video::SColor(255, 0, 0, 0);
1104 progressrect2 = progressrect;
1105 progressrect2.UpperLeftCorner.X = progressmid;
1106 driver->draw2DRectangle(color, progressrect2, clip);
1109 if (font != NULL && item.count >= 2) {
1110 // Get the item count as a string
1111 std::string text = itos(item.count);
1112 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
1113 v2s32 sdim(dim.X, dim.Y);
1115 core::rect<s32> rect2(
1116 /*rect.UpperLeftCorner,
1117 core::dimension2d<u32>(rect.getWidth(), 15)*/
1118 rect.LowerRightCorner - sdim,
1122 video::SColor bgcolor(128, 0, 0, 0);
1123 driver->draw2DRectangle(bgcolor, rect2, clip);
1125 video::SColor color(255, 255, 255, 255);
1126 font->draw(text.c_str(), rect2, color, false, false, clip);
1131 video::IVideoDriver *driver,
1132 gui::IGUIFont *font,
1133 const ItemStack &item,
1134 const core::rect<s32> &rect,
1135 const core::rect<s32> *clip,
1137 ItemRotationKind rotation_kind)
1139 drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
1140 v3s16(0, 0, 0), v3s16(0, 100, 0));