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::toggleBlockBounds()
867 m_block_bounds_mode = static_cast<BlockBoundsMode>(m_block_bounds_mode + 1);
869 if (m_block_bounds_mode >= BLOCK_BOUNDS_MAX) {
870 m_block_bounds_mode = BLOCK_BOUNDS_OFF;
874 void Hud::drawBlockBounds()
876 if (m_block_bounds_mode == BLOCK_BOUNDS_OFF) {
880 video::SMaterial old_material = driver->getMaterial2D();
881 driver->setMaterial(m_selection_material);
883 v3s16 pos = player->getStandingNodePos();
886 floorf((float) pos.X / MAP_BLOCKSIZE),
887 floorf((float) pos.Y / MAP_BLOCKSIZE),
888 floorf((float) pos.Z / MAP_BLOCKSIZE)
891 v3f offset = intToFloat(client->getCamera()->getOffset(), BS);
893 s8 radius = m_block_bounds_mode == BLOCK_BOUNDS_ALL ? 2 : 0;
895 v3f halfNode = v3f(BS, BS, BS) / 2.0f;
897 for (s8 x = -radius; x <= radius; x++)
898 for (s8 y = -radius; y <= radius; y++)
899 for (s8 z = -radius; z <= radius; z++) {
900 v3s16 blockOffset(x, y, z);
903 intToFloat((blockPos + blockOffset) * MAP_BLOCKSIZE, BS) - offset - halfNode,
904 intToFloat(((blockPos + blockOffset) * MAP_BLOCKSIZE) + (MAP_BLOCKSIZE - 1), BS) - offset + halfNode
907 driver->draw3DBox(box, video::SColor(255, 255, 0, 0));
910 driver->setMaterial(old_material);
913 void Hud::updateSelectionMesh(const v3s16 &camera_offset)
915 m_camera_offset = camera_offset;
916 if (m_mode != HIGHLIGHT_HALO)
919 if (m_selection_mesh) {
920 m_selection_mesh->drop();
921 m_selection_mesh = NULL;
924 if (m_selection_boxes.empty()) {
929 // New pointed object, create new mesh.
931 // Texture UV coordinates for selection boxes
932 static f32 texture_uv[24] = {
941 // Use single halo box instead of multiple overlapping boxes.
942 // Temporary solution - problem can be solved with multiple
943 // rendering targets, or some method to remove inner surfaces.
944 // Thats because of halo transparency.
946 aabb3f halo_box(100.0, 100.0, 100.0, -100.0, -100.0, -100.0);
947 m_halo_boxes.clear();
949 for (const auto &selection_box : m_selection_boxes) {
950 halo_box.addInternalBox(selection_box);
953 m_halo_boxes.push_back(halo_box);
954 m_selection_mesh = convertNodeboxesToMesh(
955 m_halo_boxes, texture_uv, 0.5);
958 void Hud::resizeHotbar() {
959 const v2u32 &window_size = RenderingEngine::get_instance()->getWindowSize();
961 if (m_screensize != window_size) {
962 m_hotbar_imagesize = floor(HOTBAR_IMAGE_SIZE *
963 RenderingEngine::getDisplayDensity() + 0.5);
964 m_hotbar_imagesize *= m_hud_scaling;
965 m_padding = m_hotbar_imagesize / 12;
966 m_screensize = window_size;
967 m_displaycenter = v2s32(m_screensize.X/2,m_screensize.Y/2);
971 struct MeshTimeInfo {
973 scene::IMesh *mesh = nullptr;
977 video::IVideoDriver *driver,
979 const ItemStack &item,
980 const core::rect<s32> &rect,
981 const core::rect<s32> *clip,
983 ItemRotationKind rotation_kind,
985 const v3s16 &rotation_speed)
987 static MeshTimeInfo rotation_time_infos[IT_ROT_NONE];
990 if (rotation_kind < IT_ROT_NONE && rotation_kind != IT_ROT_OTHER) {
991 rotation_time_infos[rotation_kind].mesh = NULL;
996 const static thread_local bool enable_animations =
997 g_settings->getBool("inventory_items_animations");
999 const ItemDefinition &def = item.getDefinition(client->idef());
1001 // Render as mesh if animated or no inventory image
1002 if ((enable_animations && rotation_kind < IT_ROT_NONE) || def.inventory_image.empty()) {
1003 ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client);
1004 if (!imesh || !imesh->mesh)
1006 scene::IMesh *mesh = imesh->mesh;
1007 driver->clearBuffers(video::ECBF_DEPTH);
1009 if (rotation_kind < IT_ROT_NONE) {
1010 MeshTimeInfo &ti = rotation_time_infos[rotation_kind];
1011 if (mesh != ti.mesh && rotation_kind != IT_ROT_OTHER) {
1013 ti.time = porting::getTimeMs();
1015 delta = porting::getDeltaMs(ti.time, porting::getTimeMs()) % 100000;
1018 core::rect<s32> oldViewPort = driver->getViewPort();
1019 core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION);
1020 core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW);
1021 core::rect<s32> viewrect = rect;
1023 viewrect.clipAgainst(*clip);
1025 core::matrix4 ProjMatrix;
1026 ProjMatrix.buildProjectionMatrixOrthoLH(2.0f, 2.0f, -1.0f, 100.0f);
1028 core::matrix4 ViewMatrix;
1029 ViewMatrix.buildProjectionMatrixOrthoLH(
1030 2.0f * viewrect.getWidth() / rect.getWidth(),
1031 2.0f * viewrect.getHeight() / rect.getHeight(),
1034 ViewMatrix.setTranslation(core::vector3df(
1035 1.0f * (rect.LowerRightCorner.X + rect.UpperLeftCorner.X -
1036 viewrect.LowerRightCorner.X - viewrect.UpperLeftCorner.X) /
1037 viewrect.getWidth(),
1038 1.0f * (viewrect.LowerRightCorner.Y + viewrect.UpperLeftCorner.Y -
1039 rect.LowerRightCorner.Y - rect.UpperLeftCorner.Y) /
1040 viewrect.getHeight(),
1043 driver->setTransform(video::ETS_PROJECTION, ProjMatrix);
1044 driver->setTransform(video::ETS_VIEW, ViewMatrix);
1046 core::matrix4 matrix;
1047 matrix.makeIdentity();
1049 if (enable_animations) {
1050 float timer_f = (float) delta / 5000.f;
1051 matrix.setRotationDegrees(v3f(
1052 angle.X + rotation_speed.X * 3.60f * timer_f,
1053 angle.Y + rotation_speed.Y * 3.60f * timer_f,
1054 angle.Z + rotation_speed.Z * 3.60f * timer_f)
1058 driver->setTransform(video::ETS_WORLD, matrix);
1059 driver->setViewPort(viewrect);
1061 video::SColor basecolor =
1062 client->idef()->getItemstackColor(item, client);
1064 u32 mc = mesh->getMeshBufferCount();
1065 for (u32 j = 0; j < mc; ++j) {
1066 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1067 // we can modify vertices relatively fast,
1068 // because these meshes are not buffered.
1069 assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER);
1070 video::SColor c = basecolor;
1072 if (imesh->buffer_colors.size() > j) {
1073 ItemPartColor *p = &imesh->buffer_colors[j];
1074 if (p->override_base)
1078 if (imesh->needs_shading)
1079 colorizeMeshBuffer(buf, &c);
1081 setMeshBufferColor(buf, c);
1083 video::SMaterial &material = buf->getMaterial();
1084 material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1085 material.Lighting = false;
1086 driver->setMaterial(material);
1087 driver->drawMeshBuffer(buf);
1090 driver->setTransform(video::ETS_VIEW, oldViewMat);
1091 driver->setTransform(video::ETS_PROJECTION, oldProjMat);
1092 driver->setViewPort(oldViewPort);
1093 } else { // Otherwise just draw as 2D
1094 video::ITexture *texture = client->idef()->getInventoryTexture(def.name, client);
1097 video::SColor color =
1098 client->idef()->getItemstackColor(item, client);
1099 const video::SColor colors[] = { color, color, color, color };
1101 draw2DImageFilterScaled(driver, texture, rect,
1102 core::rect<s32>({0, 0}, core::dimension2di(texture->getOriginalSize())),
1103 clip, colors, true);
1106 // draw the inventory_overlay
1107 if (def.type == ITEM_NODE && def.inventory_image.empty() &&
1108 !def.inventory_overlay.empty()) {
1109 ITextureSource *tsrc = client->getTextureSource();
1110 video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
1111 core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
1112 core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
1113 draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
1116 if (def.type == ITEM_TOOL && item.wear != 0) {
1117 // Draw a progressbar
1118 float barheight = static_cast<float>(rect.getHeight()) / 16;
1119 float barpad_x = static_cast<float>(rect.getWidth()) / 16;
1120 float barpad_y = static_cast<float>(rect.getHeight()) / 16;
1122 core::rect<s32> progressrect(
1123 rect.UpperLeftCorner.X + barpad_x,
1124 rect.LowerRightCorner.Y - barpad_y - barheight,
1125 rect.LowerRightCorner.X - barpad_x,
1126 rect.LowerRightCorner.Y - barpad_y);
1128 // Shrink progressrect by amount of tool damage
1129 float wear = item.wear / 65535.0f;
1131 wear * progressrect.UpperLeftCorner.X +
1132 (1 - wear) * progressrect.LowerRightCorner.X;
1134 // Compute progressbar color
1135 // wear = 0.0: green
1136 // wear = 0.5: yellow
1138 video::SColor color(255, 255, 255, 255);
1139 int wear_i = MYMIN(std::floor(wear * 600), 511);
1140 wear_i = MYMIN(wear_i + 10, 511);
1143 color.set(255, wear_i, 255, 0);
1145 color.set(255, 255, 511 - wear_i, 0);
1147 core::rect<s32> progressrect2 = progressrect;
1148 progressrect2.LowerRightCorner.X = progressmid;
1149 driver->draw2DRectangle(color, progressrect2, clip);
1151 color = video::SColor(255, 0, 0, 0);
1152 progressrect2 = progressrect;
1153 progressrect2.UpperLeftCorner.X = progressmid;
1154 driver->draw2DRectangle(color, progressrect2, clip);
1157 if (font != NULL && item.count >= 2) {
1158 // Get the item count as a string
1159 std::string text = itos(item.count);
1160 v2u32 dim = font->getDimension(utf8_to_wide(text).c_str());
1161 v2s32 sdim(dim.X, dim.Y);
1163 core::rect<s32> rect2(
1164 /*rect.UpperLeftCorner,
1165 core::dimension2d<u32>(rect.getWidth(), 15)*/
1166 rect.LowerRightCorner - sdim,
1170 video::SColor bgcolor(128, 0, 0, 0);
1171 driver->draw2DRectangle(bgcolor, rect2, clip);
1173 video::SColor color(255, 255, 255, 255);
1174 font->draw(text.c_str(), rect2, color, false, false, clip);
1179 video::IVideoDriver *driver,
1180 gui::IGUIFont *font,
1181 const ItemStack &item,
1182 const core::rect<s32> &rect,
1183 const core::rect<s32> *clip,
1185 ItemRotationKind rotation_kind)
1187 drawItemStack(driver, font, item, rect, clip, client, rotation_kind,
1188 v3s16(0, 0, 0), v3s16(0, 100, 0));