From cd0e213a3640e980e15735f97dd874754f0dc679 Mon Sep 17 00:00:00 2001 From: v-rob Date: Tue, 11 Aug 2020 11:07:17 -0700 Subject: Add font styling options to tables and textlists (#10203) --- src/gui/guiFormSpecMenu.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/gui/guiFormSpecMenu.cpp') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 601c5c18e..98392f3c0 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -1225,6 +1225,7 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) auto style = getDefaultStyleForElement("table", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideFont(style.getFont()); m_tables.emplace_back(spec, e); m_fields.push_back(spec); @@ -1302,6 +1303,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element auto style = getDefaultStyleForElement("textlist", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideFont(style.getFont()); m_tables.emplace_back(spec, e); m_fields.push_back(spec); -- cgit v1.2.3 From 471497fa9198695becae26a83097ec80643b4cc5 Mon Sep 17 00:00:00 2001 From: v-rob Date: Wed, 19 Aug 2020 16:13:29 -0700 Subject: Optimize formspec form size (#10144) --- src/gui/guiFormSpecMenu.cpp | 66 ++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 33 deletions(-) (limited to 'src/gui/guiFormSpecMenu.cpp') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 98392f3c0..f702b40bf 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3112,42 +3112,42 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) // and default scaling (1.00). use_imgsize = 0.5555 * screen_dpi * gui_scaling; } else { - // In variable-size mode, we prefer to make the - // inventory image size 1/15 of screen height, - // multiplied by the gui_scaling config parameter. - // If the preferred size won't fit the whole - // form on the screen, either horizontally or - // vertically, then we scale it down to fit. - // (The magic numbers in the computation of what - // fits arise from the scaling factors in the - // following stanza, including the form border, - // help text space, and 0.1 inventory slot spare.) - // However, a minimum size is also set, that - // the image size can't be less than 0.3 inch - // multiplied by gui_scaling, even if this means - // the form doesn't fit the screen. + // Variables for the maximum imgsize that can fit in the screen. + double fitx_imgsize; + double fity_imgsize; + + // Pad the screensize with 5% of the screensize on all sides to ensure + // that even the largest formspecs don't touch the screen borders. + v2f padded_screensize( + mydata.screensize.X * 0.9f, + mydata.screensize.Y * 0.9f + ); + + if (mydata.real_coordinates) { + fitx_imgsize = padded_screensize.X / mydata.invsize.X; + fity_imgsize = padded_screensize.Y / mydata.invsize.Y; + } else { + // The maximum imgsize in the old coordinate system also needs to + // factor in padding and spacing along with 0.1 inventory slot spare + // and help text space, hence the magic numbers. + fitx_imgsize = padded_screensize.X / + ((5.0 / 4.0) * (0.5 + mydata.invsize.X)); + fity_imgsize = padded_screensize.Y / + ((15.0 / 13.0) * (0.85 + mydata.invsize.Y)); + } + #ifdef __ANDROID__ - // For mobile devices these magic numbers are - // different and forms should always use the - // maximum screen space available. - double prefer_imgsize = mydata.screensize.Y / 10 * gui_scaling; - double fitx_imgsize = mydata.screensize.X / - ((12.0 / 8.0) * (0.5 + mydata.invsize.X)); - double fity_imgsize = mydata.screensize.Y / - ((15.0 / 11.0) * (0.85 + mydata.invsize.Y)); - use_imgsize = MYMIN(prefer_imgsize, - MYMIN(fitx_imgsize, fity_imgsize)); + // In Android, the preferred imgsize should be larger to accommodate the + // smaller screensize. + double prefer_imgsize = padded_screensize.Y / 10 * gui_scaling; #else - double prefer_imgsize = mydata.screensize.Y / 15 * gui_scaling; - double fitx_imgsize = mydata.screensize.X / - ((5.0 / 4.0) * (0.5 + mydata.invsize.X)); - double fity_imgsize = mydata.screensize.Y / - ((15.0 / 13.0) * (0.85 * mydata.invsize.Y)); - double screen_dpi = RenderingEngine::getDisplayDensity() * 96; - double min_imgsize = 0.3 * screen_dpi * gui_scaling; - use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize, - MYMIN(fitx_imgsize, fity_imgsize))); + // Desktop computers have more space, so try to fit 15 coordinates. + double prefer_imgsize = padded_screensize.Y / 15 * gui_scaling; #endif + // Try to use the preferred imgsize, but if that's bigger than the maximum + // size, use the maximum size. + use_imgsize = std::min(prefer_imgsize, + std::min(fitx_imgsize, fity_imgsize)); } // Everything else is scaled in proportion to the -- cgit v1.2.3 From 83d0c360cc648e02650aa4d341dbf5205c1e708f Mon Sep 17 00:00:00 2001 From: v-rob Date: Wed, 19 Aug 2020 18:14:47 -0700 Subject: Add gradients and borders to FormSpec boxes (#8676) --- doc/lua_api.txt | 43 +++++++++++++++-------- src/gui/StyleSpec.h | 64 +++++++++++++++++++++++++++++++++ src/gui/guiBox.cpp | 86 ++++++++++++++++++++++++++++++++++++++++++--- src/gui/guiBox.h | 11 ++++-- src/gui/guiFormSpecMenu.cpp | 61 ++++++++++++++++++-------------- 5 files changed, 218 insertions(+), 47 deletions(-) (limited to 'src/gui/guiFormSpecMenu.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ba55033dd..49fbe0d94 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2442,6 +2442,8 @@ Elements * Simple colored box * `color` is color specified as a `ColorString`. If the alpha component is left blank, the box will be semitransparent. + If the color is not specified, the box will use the options specified by + its style. If the color is specified, all styling options will be ignored. ### `dropdown[,;;;,, ...,;;]` @@ -2708,21 +2710,23 @@ Setting a property to nothing will reset it to the default value. For example: Some types may inherit styles from parent types. * animated_image, inherits from image +* box * button * button_exit, inherits from button * checkbox -* scrollbar -* table -* textlist * dropdown * field -* pwdfield, inherits from field -* textarea -* label -* vertlabel, inherits from field +* image * image_button * item_image_button +* label +* pwdfield, inherits from field +* scrollbar * tabheader +* table +* textarea +* textlist +* vertlabel, inherits from label ### Valid Properties @@ -2731,7 +2735,18 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * box * noclip - boolean, set to true to allow the element to exceed formspec bounds. - * Default to false in formspec_version version 3 or higher + * Defaults to false in formspec_version version 3 or higher + * **Note**: `colors`, `bordercolors`, and `borderwidths` accept multiple input types: + * Single value (e.g. `#FF0`): All corners/borders. + * Two values (e.g. `red,#FFAAFF`): top-left and bottom-right,top-right and bottom-left/ + top and bottom,left and right. + * Four values (e.g. `blue,#A0F,green,#FFFA`): top-left/top and rotates clockwise. + * These work similarly to CSS borders. + * colors - `ColorString`. Sets the color(s) of the box corners. Default `black`. + * bordercolors - `ColorString`. Sets the color(s) of the borders. Default `black`. + * borderwidths - Integer. Sets the width(s) of the borders in pixels. If the width is + negative, the border will extend inside the box, whereas positive extends outside + the box. A width of zero results in no border; this is default. * button, button_exit, image_button, item_image_button * alpha - boolean, whether to draw alpha in bgimg. Default true. * bgcolor - color, sets button tint. @@ -2767,12 +2782,6 @@ Some types may inherit styles from parent types. * textcolor - color, default white. * checkbox * noclip - boolean, set to true to allow the element to exceed formspec bounds. -* scrollbar - * noclip - boolean, set to true to allow the element to exceed formspec bounds. -* table, textlist - * font - Sets font type. See button `font` property for more information. - * font_size - Sets font size. See button `font_size` property for more information. - * noclip - boolean, set to true to allow the element to exceed formspec bounds. * dropdown * noclip - boolean, set to true to allow the element to exceed formspec bounds. * field, pwdfield, textarea @@ -2797,9 +2806,15 @@ Some types may inherit styles from parent types. * fgimg_pressed - image when pressed. Defaults to fgimg when not provided. * This is deprecated, use states instead. * NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed +* scrollbar + * noclip - boolean, set to true to allow the element to exceed formspec bounds. * tabheader * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. +* table, textlist + * font - Sets font type. See button `font` property for more information. + * font_size - Sets font size. See button `font_size` property for more information. + * noclip - boolean, set to true to allow the element to exceed formspec bounds. ### Valid States diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 67caf4f7b..36ad51a89 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include #include +#include #pragma once @@ -50,6 +51,9 @@ public: PADDING, FONT, FONT_SIZE, + COLORS, + BORDERCOLORS, + BORDERWIDTHS, NUM_PROPERTIES, NONE }; @@ -106,6 +110,12 @@ public: return FONT; } else if (name == "font_size") { return FONT_SIZE; + } else if (name == "colors") { + return COLORS; + } else if (name == "bordercolors") { + return BORDERCOLORS; + } else if (name == "borderwidths") { + return BORDERWIDTHS; } else { return NONE; } @@ -187,6 +197,42 @@ public: return color; } + std::array getColorArray(Property prop, + std::array def) const + { + const auto &val = properties[prop]; + if (val.empty()) + return def; + + std::vector strs; + if (!parseArray(val, strs)) + return def; + + for (size_t i = 0; i <= 3; i++) { + video::SColor color; + if (parseColorString(strs[i], color, false, 0xff)) + def[i] = color; + } + + return def; + } + + std::array getIntArray(Property prop, std::array def) const + { + const auto &val = properties[prop]; + if (val.empty()) + return def; + + std::vector strs; + if (!parseArray(val, strs)) + return def; + + for (size_t i = 0; i <= 3; i++) + def[i] = stoi(strs[i]); + + return def; + } + irr::core::rect getRect(Property prop, irr::core::rect def) const { const auto &val = properties[prop]; @@ -334,6 +380,24 @@ public: } private: + bool parseArray(const std::string &value, std::vector &arr) const + { + std::vector strs = split(value, ','); + + if (strs.size() == 1) { + arr = {strs[0], strs[0], strs[0], strs[0]}; + } else if (strs.size() == 2) { + arr = {strs[0], strs[1], strs[0], strs[1]}; + } else if (strs.size() == 4) { + arr = strs; + } else { + warningstream << "Invalid array size (" << strs.size() + << " arguments): \"" << value << "\"" << std::endl; + return false; + } + return true; + } + bool parseRect(const std::string &value, irr::core::rect *parsed_rect) const { irr::core::rect rect; diff --git a/src/gui/guiBox.cpp b/src/gui/guiBox.cpp index 7f329cc32..99a115daf 100644 --- a/src/gui/guiBox.cpp +++ b/src/gui/guiBox.cpp @@ -20,9 +20,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiBox.h" GUIBox::GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - const core::rect &rectangle, const video::SColor &color) : + const core::rect &rectangle, + const std::array &colors, + const std::array &bordercolors, + const std::array &borderwidths) : gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle), - m_color(color) + m_colors(colors), + m_bordercolors(bordercolors), + m_borderwidths(borderwidths) { } @@ -31,8 +36,81 @@ void GUIBox::draw() if (!IsVisible) return; - Environment->getVideoDriver()->draw2DRectangle(m_color, AbsoluteRect, - &AbsoluteClippingRect); + std::array negative_borders = {0, 0, 0, 0}; + std::array positive_borders = {0, 0, 0, 0}; + + for (size_t i = 0; i <= 3; i++) { + if (m_borderwidths[i] > 0) + positive_borders[i] = m_borderwidths[i]; + else + negative_borders[i] = m_borderwidths[i]; + } + + v2s32 upperleft = AbsoluteRect.UpperLeftCorner; + v2s32 lowerright = AbsoluteRect.LowerRightCorner; + + v2s32 topleft_border = { + upperleft.X - positive_borders[3], + upperleft.Y - positive_borders[0] + }; + v2s32 topleft_rect = { + upperleft.X - negative_borders[3], + upperleft.Y - negative_borders[0] + }; + + v2s32 lowerright_border = { + lowerright.X + positive_borders[1], + lowerright.Y + positive_borders[2] + }; + v2s32 lowerright_rect = { + lowerright.X + negative_borders[1], + lowerright.Y + negative_borders[2] + }; + + core::rect main_rect( + topleft_rect.X, + topleft_rect.Y, + lowerright_rect.X, + lowerright_rect.Y + ); + + std::array, 4> border_rects; + + border_rects[0] = core::rect( + topleft_border.X, + topleft_border.Y, + lowerright_border.X, + topleft_rect.Y + ); + + border_rects[1] = core::rect( + lowerright_rect.X, + topleft_rect.Y, + lowerright_border.X, + lowerright_rect.Y + ); + + border_rects[2] = core::rect( + topleft_border.X, + lowerright_rect.Y, + lowerright_border.X, + lowerright_border.Y + ); + + border_rects[3] = core::rect( + topleft_border.X, + topleft_rect.Y, + topleft_rect.X, + lowerright_rect.Y + ); + + video::IVideoDriver *driver = Environment->getVideoDriver(); + + driver->draw2DRectangle(main_rect, m_colors[0], m_colors[1], m_colors[3], + m_colors[2], nullptr); + + for (size_t i = 0; i <= 3; i++) + driver->draw2DRectangle(m_bordercolors[i], border_rects[i], nullptr); IGUIElement::draw(); } diff --git a/src/gui/guiBox.h b/src/gui/guiBox.h index 5306fdf65..ca8f83771 100644 --- a/src/gui/guiBox.h +++ b/src/gui/guiBox.h @@ -19,16 +19,23 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include +#include #include "irrlichttypes_extrabloated.h" class GUIBox : public gui::IGUIElement { public: GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, - const core::rect &rectangle, const video::SColor &color); + const core::rect &rectangle, + const std::array &colors, + const std::array &bordercolors, + const std::array &borderwidths); virtual void draw() override; private: - video::SColor m_color; + std::array m_colors; + std::array m_bordercolors; + std::array m_borderwidths; }; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index f702b40bf..9bd5f27fd 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -2211,16 +2211,16 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) { - std::vector parts = split(element,';'); + std::vector parts = split(element, ';'); if ((parts.size() == 3) || ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) { - std::vector v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); + std::vector v_pos = split(parts[0], ','); + std::vector v_geom = split(parts[1], ','); - MY_CHECKPOS("box",0); - MY_CHECKGEOM("box",1); + MY_CHECKPOS("box", 0); + MY_CHECKGEOM("box", 1); v2s32 pos; v2s32 geom; @@ -2234,36 +2234,43 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) geom.Y = stof(v_geom[1]) * spacing.Y; } - video::SColor tmp_color; - - if (parseColorString(parts[2], tmp_color, false, 0x8C)) { - FieldSpec spec( - "", - L"", - L"", - 258 + m_fields.size(), - -2 - ); - spec.ftype = f_Box; + FieldSpec spec( + "", + L"", + L"", + 258 + m_fields.size(), + -2 + ); + spec.ftype = f_Box; - core::rect rect(pos, pos + geom); + auto style = getDefaultStyleForElement("box", spec.fname); - GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, - rect, tmp_color); + video::SColor tmp_color; + std::array colors; + std::array bordercolors = {0x0, 0x0, 0x0, 0x0}; + std::array borderwidths = {0, 0, 0, 0}; - auto style = getDefaultStyleForElement("box", spec.fname); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); + if (parseColorString(parts[2], tmp_color, true, 0x8C)) { + colors = {tmp_color, tmp_color, tmp_color, tmp_color}; + } else { + colors = style.getColorArray(StyleSpec::COLORS, {0x0, 0x0, 0x0, 0x0}); + bordercolors = style.getColorArray(StyleSpec::BORDERCOLORS, + {0x0, 0x0, 0x0, 0x0}); + borderwidths = style.getIntArray(StyleSpec::BORDERWIDTHS, {0, 0, 0, 0}); + } - e->drop(); + core::rect rect(pos, pos + geom); - m_fields.push_back(spec); + GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect, + colors, bordercolors, borderwidths); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); + e->drop(); - } else { - errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl; - } + m_fields.push_back(spec); return; } - errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl; + errorstream << "Invalid Box element(" << parts.size() << "): '" << element + << "'" << std::endl; } void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element) -- cgit v1.2.3 From 71287894adbf3cbe092ded1cbf02755678305afb Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Thu, 20 Aug 2020 12:25:02 +0200 Subject: Fix missing translation call in hypertext (#10296) --- src/gui/guiFormSpecMenu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/gui/guiFormSpecMenu.cpp') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 9bd5f27fd..76441c645 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -1740,7 +1740,7 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen FieldSpec spec( name, - utf8_to_wide(unescape_string(text)), + translate_string(utf8_to_wide(unescape_string(text))), L"", 258 + m_fields.size() ); -- cgit v1.2.3 From 287f3fb2e3317bb6bd6d8608f1aa051c601aeca4 Mon Sep 17 00:00:00 2001 From: EvidenceB <49488517+EvidenceBKidscode@users.noreply.github.com> Date: Sun, 23 Aug 2020 22:50:14 +0200 Subject: Avoid drawing clipped out formspec elements (#10095) --- src/gui/guiFormSpecMenu.cpp | 10 +++++++--- src/gui/guiScrollContainer.cpp | 12 ++++++++++++ src/gui/guiScrollContainer.h | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'src/gui/guiFormSpecMenu.cpp') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 76441c645..7e3ad3b15 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3483,10 +3483,14 @@ void GUIFormSpecMenu::drawMenu() e->setVisible(true); /* - Call base class - (This is where all the drawing happens.) + This is where all the drawing happens. */ - gui::IGUIElement::draw(); + core::list::Iterator it = Children.begin(); + for (; it != Children.end(); ++it) + if ((*it)->isNotClipped() || + AbsoluteClippingRect.isRectCollided( + (*it)->getAbsolutePosition())) + (*it)->draw(); for (gui::IGUIElement *e : m_clickthrough_elements) e->setVisible(false); diff --git a/src/gui/guiScrollContainer.cpp b/src/gui/guiScrollContainer.cpp index 88cdc7057..0fe4c41bd 100644 --- a/src/gui/guiScrollContainer.cpp +++ b/src/gui/guiScrollContainer.cpp @@ -56,6 +56,18 @@ bool GUIScrollContainer::OnEvent(const SEvent &event) return IGUIElement::OnEvent(event); } +void GUIScrollContainer::draw() +{ + if (isVisible()) { + core::list::Iterator it = Children.begin(); + for (; it != Children.end(); ++it) + if ((*it)->isNotClipped() || + AbsoluteClippingRect.isRectCollided( + (*it)->getAbsolutePosition())) + (*it)->draw(); + } +} + void GUIScrollContainer::updateScrolling() { s32 pos = m_scrollbar->getPos(); diff --git a/src/gui/guiScrollContainer.h b/src/gui/guiScrollContainer.h index a0306291e..9e3ec6e93 100644 --- a/src/gui/guiScrollContainer.h +++ b/src/gui/guiScrollContainer.h @@ -32,6 +32,8 @@ public: virtual bool OnEvent(const SEvent &event) override; + virtual void draw() override; + inline void onScrollEvent(gui::IGUIElement *caller) { if (caller == m_scrollbar) -- cgit v1.2.3 From 3356da01513860d899cde503408436f7e1918f63 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 4 Nov 2020 21:46:18 +0100 Subject: Add model[] formspec element (#10320) Formspec element to display models, written by @kilbith, rebased and tweaked. Co-authored-by: Jean-Patrick Guerrero Co-authored-by: sfan5 --- doc/lua_api.txt | 16 ++ games/devtest/mods/testformspec/LICENSE.txt | 14 ++ games/devtest/mods/testformspec/formspec.lua | 4 + .../testformspec/models/testformspec_character.b3d | Bin 0 -> 73433 bytes .../testformspec/models/testformspec_chest.obj | 79 +++++++ .../testformspec/textures/default_chest_front.png | Bin 0 -> 423 bytes .../testformspec/textures/default_chest_inside.png | Bin 0 -> 102 bytes .../testformspec/textures/default_chest_side.png | Bin 0 -> 375 bytes .../testformspec/textures/default_chest_top.png | Bin 0 -> 423 bytes .../textures/testformspec_character.png | Bin 0 -> 2754 bytes src/gui/CMakeLists.txt | 1 + src/gui/guiFormSpecMenu.cpp | 86 +++++++ src/gui/guiFormSpecMenu.h | 2 +- src/gui/guiScene.cpp | 257 +++++++++++++++++++++ src/gui/guiScene.h | 85 +++++++ util/ci/clang-format-whitelist.txt | 2 + 16 files changed, 545 insertions(+), 1 deletion(-) create mode 100644 games/devtest/mods/testformspec/LICENSE.txt create mode 100644 games/devtest/mods/testformspec/models/testformspec_character.b3d create mode 100644 games/devtest/mods/testformspec/models/testformspec_chest.obj create mode 100644 games/devtest/mods/testformspec/textures/default_chest_front.png create mode 100644 games/devtest/mods/testformspec/textures/default_chest_inside.png create mode 100644 games/devtest/mods/testformspec/textures/default_chest_side.png create mode 100644 games/devtest/mods/testformspec/textures/default_chest_top.png create mode 100644 games/devtest/mods/testformspec/textures/testformspec_character.png create mode 100644 src/gui/guiScene.cpp create mode 100644 src/gui/guiScene.h (limited to 'src/gui/guiFormSpecMenu.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index daf0da3d2..38fc3066a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2272,6 +2272,18 @@ Elements * `frame duration`: Milliseconds between each frame. `0` means the frames don't advance. * `frame start` (Optional): The index of the frame to start on. Default `1`. +### `model[,;,;;;;;;]` + +* Show a mesh model. +* `name`: Element name that can be used for styling +* `mesh`: The mesh model to use. +* `textures`: The mesh textures to use according to the mesh materials. + Texture names must be separated by commas. +* `rotation {X,Y}` (Optional): Initial rotation of the camera. + The axes are euler angles in degrees. +* `continuous` (Optional): Whether the rotation is continuous. Default `false`. +* `mouse control` (Optional): Whether the model can be controlled with the mouse. Default `true`. + ### `item_image[,;,;]` * Show an inventory image of registered item/node @@ -2842,6 +2854,10 @@ Some types may inherit styles from parent types. * font_size - Sets font size. See button `font_size` property for more information. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * textcolor - color. Default white. +* model + * bgcolor - color, sets background color. + * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * Default to false in formspec_version version 3 or higher * image * noclip - boolean, set to true to allow the element to exceed formspec bounds. * Default to false in formspec_version version 3 or higher diff --git a/games/devtest/mods/testformspec/LICENSE.txt b/games/devtest/mods/testformspec/LICENSE.txt new file mode 100644 index 000000000..07696cc30 --- /dev/null +++ b/games/devtest/mods/testformspec/LICENSE.txt @@ -0,0 +1,14 @@ +License of media files +---------------------- +Content imported from minetest_game. + + +BlockMen (CC BY-SA 3.0) + default_chest_front.png + default_chest_lock.png + default_chest_side.png + default_chest_top.png + +stujones11 (CC BY-SA 3.0) +An0n3m0us (CC BY-SA 3.0) + testformspec_character.b3d diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 87a05fc96..5495896ce 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -327,6 +327,10 @@ Number] animated_image[3,4.25;1,1;;testformspec_animation.png;4;0;3] animated_image[5.5,0.5;5,2;;testformspec_animation.png;4;100] animated_image[5.5,2.75;5,2;;testformspec_animation.jpg;4;100] + + style[m1;bgcolor=black] + model[0.5,6;4,4;m1;testformspec_character.b3d;testformspec_character.png] + model[5,6;4,4;m2;testformspec_chest.obj;default_chest_top.png,default_chest_top.png,default_chest_side.png,default_chest_side.png,default_chest_front.png,default_chest_inside.png;30,1;true;true] ]], -- Scroll containers diff --git a/games/devtest/mods/testformspec/models/testformspec_character.b3d b/games/devtest/mods/testformspec/models/testformspec_character.b3d new file mode 100644 index 000000000..8edbaf637 Binary files /dev/null and b/games/devtest/mods/testformspec/models/testformspec_character.b3d differ diff --git a/games/devtest/mods/testformspec/models/testformspec_chest.obj b/games/devtest/mods/testformspec/models/testformspec_chest.obj new file mode 100644 index 000000000..72ba175a0 --- /dev/null +++ b/games/devtest/mods/testformspec/models/testformspec_chest.obj @@ -0,0 +1,79 @@ +# Blender v2.78 (sub 0) OBJ File: 'chest-open.blend' +# www.blender.org +o Top_Cube.002_None_Top_Cube.002_None_bottom +v -0.500000 0.408471 0.720970 +v -0.500000 1.115578 0.013863 +v -0.500000 0.894607 -0.207108 +v -0.500000 0.187501 0.499999 +v 0.500000 1.115578 0.013863 +v 0.500000 0.408471 0.720970 +v 0.500000 0.187501 0.499999 +v 0.500000 0.894607 -0.207108 +v -0.500000 0.187500 -0.500000 +v -0.500000 -0.500000 -0.500000 +v -0.500000 -0.500000 0.500000 +v 0.500000 0.187500 -0.500000 +v 0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 -0.500000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vt 1.0000 1.0000 +vt 1.0000 0.6875 +vt 0.0000 0.6875 +vt 1.0000 1.0000 +vt 0.0000 0.6875 +vt 1.0000 0.6875 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 0.0000 0.0000 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 1.0000 0.6875 +vt 1.0000 0.0000 +vt 0.0000 1.0000 +vt 0.0000 0.6875 +vt 0.0000 0.6875 +vt 0.0000 0.0000 +vt 1.0000 0.5000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.5000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vn 0.0000 0.7071 0.7071 +vn -0.0000 -1.0000 -0.0000 +vn -1.0000 0.0000 0.0000 +vn 1.0000 0.0000 -0.0000 +vn 0.0000 -0.7071 0.7071 +vn 0.0000 0.0000 1.0000 +vn -0.0000 0.7071 -0.7071 +vn -0.0000 0.0000 -1.0000 +vn -0.0000 -0.7071 -0.7071 +vn -0.0000 1.0000 -0.0000 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Top +s off +f 6/1/1 5/2/1 2/3/1 1/4/1 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Bottom +f 11/5/2 10/6/2 14/7/2 13/8/2 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Right-Left +f 1/9/3 2/10/3 3/11/3 4/12/3 +f 5/13/4 6/1/4 7/14/4 8/15/4 +f 4/12/3 9/16/3 10/17/3 11/18/3 +f 12/19/4 7/14/4 13/8/4 14/20/4 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Back +f 6/21/5 1/9/5 4/12/5 7/22/5 +f 7/22/6 4/12/6 11/18/6 13/23/6 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Front +f 2/10/7 5/24/7 8/25/7 3/11/7 +f 9/16/8 12/26/8 14/27/8 10/17/8 +g Top_Cube.002_None_Top_Cube.002_None_bottom_Top_Cube.002_None_Top_Cube.002_None_bottom_Inside +f 4/28/9 3/29/9 8/30/9 7/31/9 +f 7/31/10 12/32/10 9/33/10 4/28/10 diff --git a/games/devtest/mods/testformspec/textures/default_chest_front.png b/games/devtest/mods/testformspec/textures/default_chest_front.png new file mode 100644 index 000000000..85227d8fd Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_front.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_inside.png b/games/devtest/mods/testformspec/textures/default_chest_inside.png new file mode 100644 index 000000000..5f7b6b132 Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_inside.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_side.png b/games/devtest/mods/testformspec/textures/default_chest_side.png new file mode 100644 index 000000000..44a65a43d Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_side.png differ diff --git a/games/devtest/mods/testformspec/textures/default_chest_top.png b/games/devtest/mods/testformspec/textures/default_chest_top.png new file mode 100644 index 000000000..f4a92ee07 Binary files /dev/null and b/games/devtest/mods/testformspec/textures/default_chest_top.png differ diff --git a/games/devtest/mods/testformspec/textures/testformspec_character.png b/games/devtest/mods/testformspec/textures/testformspec_character.png new file mode 100644 index 000000000..05021781e Binary files /dev/null and b/games/devtest/mods/testformspec/textures/testformspec_character.png differ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 147f445f4..5305e7ad3 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -15,6 +15,7 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 7e3ad3b15..039b28e79 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -65,6 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiScrollContainer.h" #include "intlGUIEditBox.h" #include "guiHyperText.h" +#include "guiScene.h" #define MY_CHECKPOS(a,b) \ if (v_pos.size() != 2) { \ @@ -2695,6 +2696,86 @@ void GUIFormSpecMenu::parseSetFocus(const std::string &element) << "'" << std::endl; } +void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) +{ + std::vector parts = split(element, ';'); + + if (parts.size() < 5 || (parts.size() > 8 && + m_formspec_version <= FORMSPEC_API_VERSION)) { + errorstream << "Invalid model element (" << parts.size() << "): '" << element + << "'" << std::endl; + return; + } + + // Avoid length checks by resizing + if (parts.size() < 8) + parts.resize(8); + + std::vector v_pos = split(parts[0], ','); + std::vector v_geom = split(parts[1], ','); + std::string name = unescape_string(parts[2]); + std::string meshstr = unescape_string(parts[3]); + std::vector textures = split(parts[4], ','); + std::vector vec_rot = split(parts[5], ','); + bool inf_rotation = is_yes(parts[6]); + bool mousectrl = is_yes(parts[7]) || parts[7].empty(); // default true + + MY_CHECKPOS("model", 0); + MY_CHECKGEOM("model", 1); + + v2s32 pos; + v2s32 geom; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + geom.X = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + } + + if (!data->explicit_size) + warningstream << "invalid use of model without a size[] element" << std::endl; + + scene::IAnimatedMesh *mesh = m_client->getMesh(meshstr); + + if (!mesh) { + errorstream << "Invalid model element: Unable to load mesh:" + << std::endl << "\t" << meshstr << std::endl; + return; + } + + FieldSpec spec( + name, + L"", + L"", + 258 + m_fields.size() + ); + + core::rect rect(pos, pos + geom); + + GUIScene *e = new GUIScene(Environment, RenderingEngine::get_scene_manager(), + data->current_parent, rect, spec.fid); + + auto meshnode = e->setMesh(mesh); + + for (u32 i = 0; i < textures.size() && i < meshnode->getMaterialCount(); ++i) + e->setTexture(i, m_tsrc->getTexture(textures[i])); + + if (vec_rot.size() >= 2) + e->setRotation(v2f(stof(vec_rot[0]), stof(vec_rot[1]))); + + e->enableContinuousRotation(inf_rotation); + e->enableMouseControl(mousectrl); + + auto style = getStyleForElement("model", spec.fname); + e->setStyles(style); + e->drop(); + + m_fields.push_back(spec); +} + void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) { //some prechecks @@ -2891,6 +2972,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "model") { + parseModel(data, description); + return; + } + // Ignore others infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\"" << std::endl; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 613acaa04..c5d662a69 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -38,7 +38,6 @@ with this program; if not, write to the Free Software Foundation, Inc., class InventoryManager; class ISimpleTextureSource; class Client; -class TexturePool; class GUIScrollContainer; typedef enum { @@ -444,6 +443,7 @@ private: void parseAnchor(parserData *data, const std::string &element); bool parseStyle(parserData *data, const std::string &element, bool style_type); void parseSetFocus(const std::string &element); + void parseModel(parserData *data, const std::string &element); void tryClose(); diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp new file mode 100644 index 000000000..08f119e07 --- /dev/null +++ b/src/gui/guiScene.cpp @@ -0,0 +1,257 @@ +/* +Minetest +Copyright (C) 2020 Jean-Patrick Guerrero + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "guiScene.h" + +#include +#include +#include +#include "porting.h" + +GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, + gui::IGUIElement *parent, core::recti rect, s32 id) + : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rect) +{ + m_driver = env->getVideoDriver(); + m_smgr = smgr->createNewSceneManager(false); + + m_cam = m_smgr->addCameraSceneNode(0, v3f(0.f, 0.f, -100.f), v3f(0.f)); + m_cam->setFOV(30.f * core::DEGTORAD); + + scene::ILightSceneNode *light = m_smgr->addLightSceneNode(m_cam); + light->setRadius(1000.f); + + m_smgr->getParameters()->setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true); +} + +GUIScene::~GUIScene() +{ + setMesh(nullptr); + + m_smgr->drop(); +} + +scene::IAnimatedMeshSceneNode *GUIScene::setMesh(scene::IAnimatedMesh *mesh) +{ + if (m_mesh) { + m_mesh->remove(); + m_mesh = nullptr; + } + + if (!mesh) + return nullptr; + + m_mesh = m_smgr->addAnimatedMeshSceneNode(mesh); + m_mesh->setPosition(-m_mesh->getBoundingBox().getCenter()); + m_mesh->animateJoints(); + return m_mesh; +} + +void GUIScene::setTexture(u32 idx, video::ITexture *texture) +{ + video::SMaterial &material = m_mesh->getMaterial(idx); + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + material.MaterialTypeParam = 0.5f; + material.TextureLayer[0].Texture = texture; + material.setFlag(video::EMF_LIGHTING, false); + material.setFlag(video::EMF_FOG_ENABLE, true); + material.setFlag(video::EMF_BILINEAR_FILTER, false); + material.setFlag(video::EMF_BACK_FACE_CULLING, false); +} + +void GUIScene::draw() +{ + // Control rotation speed based on time + u64 new_time = porting::getTimeMs(); + u64 dtime_ms = 0; + if (m_last_time != 0) + dtime_ms = porting::getDeltaMs(m_last_time, new_time); + m_last_time = new_time; + + core::rect oldViewPort = m_driver->getViewPort(); + m_driver->setViewPort(getAbsoluteClippingRect()); + core::recti borderRect = Environment->getRootGUIElement()->getAbsoluteClippingRect(); + + if (m_bgcolor != 0) { + Environment->getSkin()->draw3DSunkenPane( + this, m_bgcolor, false, true, borderRect, 0); + } + + core::dimension2d size = getAbsoluteClippingRect().getSize(); + m_smgr->getActiveCamera()->setAspectRatio((f32)size.Width / (f32)size.Height); + + if (!m_target) { + updateCamera(m_smgr->addEmptySceneNode()); + rotateCamera(v3f(0.f)); + m_cam->bindTargetAndRotation(true); + } + + cameraLoop(); + + // Continuous rotation + if (m_inf_rot) + rotateCamera(v3f(0.f, -0.03f * (float)dtime_ms, 0.f)); + + m_smgr->drawAll(); + + if (m_initial_rotation && m_mesh) { + rotateCamera(v3f(m_custom_rot.X, m_custom_rot.Y, 0.f)); + calcOptimalDistance(); + + m_initial_rotation = false; + } + + m_driver->setViewPort(oldViewPort); +} + +bool GUIScene::OnEvent(const SEvent &event) +{ + if (m_mouse_ctrl && event.EventType == EET_MOUSE_INPUT_EVENT) { + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { + m_last_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y); + return true; + } else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) { + if (event.MouseInput.isLeftPressed()) { + m_curr_pos = v2f((f32)event.MouseInput.X, (f32)event.MouseInput.Y); + + rotateCamera(v3f( + m_last_pos.Y - m_curr_pos.Y, + m_curr_pos.X - m_last_pos.X, 0.f)); + + m_last_pos = m_curr_pos; + return true; + } + } + } + + return gui::IGUIElement::OnEvent(event); +} + +void GUIScene::setStyles(const std::array &styles) +{ + StyleSpec::State state = StyleSpec::STATE_DEFAULT; + StyleSpec style = StyleSpec::getStyleFromStatePropagation(styles, state); + + setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + setBackgroundColor(style.getColor(StyleSpec::BGCOLOR, m_bgcolor)); +} + +/* Camera control functions */ + +inline void GUIScene::calcOptimalDistance() +{ + core::aabbox3df box = m_mesh->getBoundingBox(); + f32 width = box.MaxEdge.X - box.MinEdge.X; + f32 height = box.MaxEdge.Y - box.MinEdge.Y; + f32 depth = box.MaxEdge.Z - box.MinEdge.Z; + f32 max_width = width > depth ? width : depth; + + const scene::SViewFrustum *f = m_cam->getViewFrustum(); + f32 cam_far = m_cam->getFarValue(); + f32 far_width = core::line3df(f->getFarLeftUp(), f->getFarRightUp()).getLength(); + f32 far_height = core::line3df(f->getFarLeftUp(), f->getFarLeftDown()).getLength(); + + core::recti rect = getAbsolutePosition(); + f32 zoomX = rect.getWidth() / max_width; + f32 zoomY = rect.getHeight() / height; + f32 dist; + + if (zoomX < zoomY) + dist = (max_width / (far_width / cam_far)) + (0.5f * max_width); + else + dist = (height / (far_height / cam_far)) + (0.5f * max_width); + + m_cam_distance = dist; + m_update_cam = true; +} + +void GUIScene::updateCamera(scene::ISceneNode *target) +{ + m_target = target; + updateTargetPos(); + + m_last_target_pos = m_target_pos; + updateCameraPos(); + + m_update_cam = true; +} + +void GUIScene::updateTargetPos() +{ + m_last_target_pos = m_target_pos; + m_target->updateAbsolutePosition(); + m_target_pos = m_target->getAbsolutePosition(); +} + +void GUIScene::setCameraRotation(v3f rot) +{ + correctBounds(rot); + + core::matrix4 mat; + mat.setRotationDegrees(rot); + + m_cam_pos = v3f(0.f, 0.f, m_cam_distance); + mat.rotateVect(m_cam_pos); + + m_cam_pos += m_target_pos; + m_cam->setPosition(m_cam_pos); + m_update_cam = false; +} + +bool GUIScene::correctBounds(v3f &rot) +{ + const float ROTATION_MAX_1 = 60.0f; + const float ROTATION_MAX_2 = 300.0f; + + // Limit and correct the rotation when needed + if (rot.X < 90.f) { + if (rot.X > ROTATION_MAX_1) { + rot.X = ROTATION_MAX_1; + return true; + } + } else if (rot.X < ROTATION_MAX_2) { + rot.X = ROTATION_MAX_2; + return true; + } + + // Not modified + return false; +} + +void GUIScene::cameraLoop() +{ + updateCameraPos(); + updateTargetPos(); + + if (m_target_pos != m_last_target_pos) + m_update_cam = true; + + if (m_update_cam) { + m_cam_pos = m_target_pos + (m_cam_pos - m_target_pos).normalize() * m_cam_distance; + + v3f rot = getCameraRotation(); + if (correctBounds(rot)) + setCameraRotation(rot); + + m_cam->setPosition(m_cam_pos); + m_cam->setTarget(m_target_pos); + + m_update_cam = false; + } +} diff --git a/src/gui/guiScene.h b/src/gui/guiScene.h new file mode 100644 index 000000000..707e6f66a --- /dev/null +++ b/src/gui/guiScene.h @@ -0,0 +1,85 @@ +/* +Minetest +Copyright (C) 2020 Jean-Patrick Guerrero + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "irrlichttypes_extrabloated.h" +#include "ICameraSceneNode.h" +#include "StyleSpec.h" + +using namespace irr; + +class GUIScene : public gui::IGUIElement +{ +public: + GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, + gui::IGUIElement *parent, core::recti rect, s32 id = -1); + + ~GUIScene(); + + scene::IAnimatedMeshSceneNode *setMesh(scene::IAnimatedMesh *mesh = nullptr); + void setTexture(u32 idx, video::ITexture *texture); + void setBackgroundColor(const video::SColor &color) noexcept { m_bgcolor = color; }; + void enableMouseControl(bool enable) noexcept { m_mouse_ctrl = enable; }; + void setRotation(v2f rot) noexcept { m_custom_rot = rot; }; + void enableContinuousRotation(bool enable) noexcept { m_inf_rot = enable; }; + void setStyles(const std::array &styles); + + virtual void draw(); + virtual bool OnEvent(const SEvent &event); + +private: + void calcOptimalDistance(); + void updateTargetPos(); + void updateCamera(scene::ISceneNode *target); + void setCameraRotation(v3f rot); + /// @return true indicates that the rotation was corrected + bool correctBounds(v3f &rot); + void cameraLoop(); + + void updateCameraPos() { m_cam_pos = m_cam->getPosition(); }; + v3f getCameraRotation() const { return (m_cam_pos - m_target_pos).getHorizontalAngle(); }; + void rotateCamera(const v3f &delta) { setCameraRotation(getCameraRotation() + delta); }; + + scene::ISceneManager *m_smgr; + video::IVideoDriver *m_driver; + scene::ICameraSceneNode *m_cam; + scene::ISceneNode *m_target = nullptr; + scene::IAnimatedMeshSceneNode *m_mesh = nullptr; + + f32 m_cam_distance = 50.f; + + u64 m_last_time = 0; + + v3f m_cam_pos; + v3f m_target_pos; + v3f m_last_target_pos; + // Cursor positions + v2f m_curr_pos; + v2f m_last_pos; + // Initial rotation + v2f m_custom_rot; + + bool m_mouse_ctrl = true; + bool m_update_cam = false; + bool m_inf_rot = false; + bool m_initial_rotation = true; + + video::SColor m_bgcolor = 0; +}; diff --git a/util/ci/clang-format-whitelist.txt b/util/ci/clang-format-whitelist.txt index 3334257ae..75d99f4cd 100644 --- a/util/ci/clang-format-whitelist.txt +++ b/util/ci/clang-format-whitelist.txt @@ -183,6 +183,8 @@ src/gui/guiMainMenu.h src/gui/guiPasswordChange.cpp src/gui/guiPathSelectMenu.cpp src/gui/guiPathSelectMenu.h +src/gui/guiScene.cpp +src/gui/guiScene.h src/gui/guiScrollBar.cpp src/gui/guiSkin.cpp src/gui/guiSkin.h -- cgit v1.2.3 From 78273027bf4bbee7488c76c6d65f85381ec7c0ba Mon Sep 17 00:00:00 2001 From: Pierre-Yves Rollo Date: Wed, 16 Sep 2020 17:10:17 +0200 Subject: Add sound to press event of some formspecs elements (#10402) --- doc/lua_api.txt | 5 +++++ src/client/game.cpp | 12 ++++++------ src/gui/StyleSpec.h | 3 +++ src/gui/guiEngine.cpp | 1 + src/gui/guiFormSpecMenu.cpp | 40 +++++++++++++++++++++++++++++++++++++--- src/gui/guiFormSpecMenu.h | 6 +++++- 6 files changed, 57 insertions(+), 10 deletions(-) (limited to 'src/gui/guiFormSpecMenu.cpp') diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ef283f0c1..27f871618 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2843,11 +2843,14 @@ Some types may inherit styles from parent types. * noclip - boolean, set to true to allow the element to exceed formspec bounds. * padding - rect, adds space between the edges of the button and the content. This value is relative to bgimg_middle. + * sound - a sound to be played when clicked. * textcolor - color, default white. * checkbox * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * dropdown * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * field, pwdfield, textarea * border - set to false to hide the textbox background and border. Default true. * font - Sets font type. See button `font` property for more information. @@ -2874,10 +2877,12 @@ Some types may inherit styles from parent types. * fgimg_pressed - image when pressed. Defaults to fgimg when not provided. * This is deprecated, use states instead. * NOTE: The parameters of any given image_button will take precedence over fgimg/fgimg_pressed + * sound - a sound to be played when clicked. * scrollbar * noclip - boolean, set to true to allow the element to exceed formspec bounds. * tabheader * noclip - boolean, set to true to allow the element to exceed formspec bounds. + * sound - a sound to be played when clicked. * textcolor - color. Default white. * table, textlist * font - Sets font type. See button `font` property for more information. diff --git a/src/client/game.cpp b/src/client/game.cpp index 5c38e027d..b7bb0a330 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2071,7 +2071,7 @@ void Game::openInventory() TextDest *txt_dst = new TextDestPlayerInventory(client); auto *&formspec = m_game_ui->updateFormspec(""); GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); + txt_dst, client->getFormspecPrepend(), sound); formspec->setFormSpec(fs_src->getForm(), inventoryloc); } @@ -2603,7 +2603,7 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname)); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); } delete event->show_formspec.formspec; @@ -2616,7 +2616,7 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta LocalFormspecHandler *txt_dst = new LocalFormspecHandler(*event->show_formspec.formname, client); GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); delete event->show_formspec.formspec; delete event->show_formspec.formname; @@ -3336,7 +3336,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, auto *&formspec = m_game_ui->updateFormspec(""); GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); + txt_dst, client->getFormspecPrepend(), sound); formspec->setFormSpec(meta->getString("formspec"), inventoryloc); return false; @@ -4108,7 +4108,7 @@ void Game::showDeathFormspec() auto *&formspec = m_game_ui->getFormspecGUI(); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_respawn"); } @@ -4242,7 +4242,7 @@ void Game::showPauseMenu() auto *&formspec = m_game_ui->getFormspecGUI(); GUIFormSpecMenu::create(formspec, client, &input->joystick, - fs_src, txt_dst, client->getFormspecPrepend()); + fs_src, txt_dst, client->getFormspecPrepend(), sound); formspec->setFocus("btn_continue"); formspec->doPause = true; } diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index 36ad51a89..f2844ce28 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -54,6 +54,7 @@ public: COLORS, BORDERCOLORS, BORDERWIDTHS, + SOUND, NUM_PROPERTIES, NONE }; @@ -116,6 +117,8 @@ public: return BORDERCOLORS; } else if (name == "borderwidths") { return BORDERWIDTHS; + } else if (name == "sound") { + return SOUND; } else { return NONE; } diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 4a13f0b11..c5ad5c323 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -170,6 +170,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, m_menumanager, NULL /* &client */, m_texture_source, + m_sound_manager, m_formspecgui, m_buttonhandler, "", diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 039b28e79..632b15992 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -48,6 +48,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "client/client.h" #include "client/fontengine.h" +#include "client/sound.h" #include "util/hex.h" #include "util/numeric.h" #include "util/string.h" // for parseColorString() @@ -95,11 +96,13 @@ inline u32 clamp_u8(s32 value) GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, - Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst, + Client *client, ISimpleTextureSource *tsrc, ISoundManager *sound_manager, + IFormSource *fsrc, TextDest *tdst, const std::string &formspecPrepend, bool remap_dbl_click): GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr, remap_dbl_click), m_invmgr(client), m_tsrc(tsrc), + m_sound_manager(sound_manager), m_client(client), m_formspec_prepend(formspecPrepend), m_form_src(fsrc), @@ -143,11 +146,12 @@ GUIFormSpecMenu::~GUIFormSpecMenu() void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client, JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, - const std::string &formspecPrepend) + const std::string &formspecPrepend, ISoundManager *sound_manager) { if (cur_formspec == nullptr) { cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr, - client, client->getTextureSource(), fs_src, txt_dest, formspecPrepend); + client, client->getTextureSource(), sound_manager, fs_src, + txt_dest, formspecPrepend); cur_formspec->doPause = false; /* @@ -614,6 +618,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getDefaultStyleForElement("checkbox", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); if (spec.fname == m_focused_element) { @@ -1020,6 +1027,9 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, data->current_parent, spec.fid, spec.flabel.c_str()); auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); + + spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + e->setStyles(style); if (spec.fname == m_focused_element) { @@ -1381,6 +1391,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element e->setSelected(stoi(str_initial_selection)-1); auto style = getDefaultStyleForElement("dropdown", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); m_fields.push_back(spec); @@ -1747,6 +1760,10 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen ); spec.ftype = f_HyperText; + + auto style = getDefaultStyleForElement("hypertext", spec.fname); + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, data->current_parent, spec.fid, rect, m_client, m_tsrc); e->drop(); @@ -1999,6 +2016,8 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem auto style = getStyleForElement("image_button", spec.fname); + spec.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + // Override style properties with values specified directly in the element if (!image_name.empty()) style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, image_name); @@ -2107,6 +2126,9 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen e->setTabHeight(geom.Y); auto style = getDefaultStyleForElement("tabheader", name); + + spec.sound = style.get(StyleSpec::Property::SOUND, ""); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); for (const std::string &button : buttons) { @@ -2195,6 +2217,9 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & item_name, m_client); auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button"); + + spec_btn.sound = style[StyleSpec::STATE_DEFAULT].get(StyleSpec::Property::SOUND, ""); + e_btn->setStyles(style); if (spec_btn.fname == m_focused_element) { @@ -4486,6 +4511,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) for (GUIFormSpecMenu::FieldSpec &s : m_fields) { if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID())) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(); s.send = false; @@ -4529,6 +4556,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) continue; if (s.ftype == f_Button || s.ftype == f_CheckBox) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); + s.send = true; if (s.is_exit) { if (m_allowclose) { @@ -4551,6 +4581,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) s2.send = false; } } + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(quit_mode_no); @@ -4567,6 +4599,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) acceptInput(quit_mode_no); s.fdefault = L""; } else if (s.ftype == f_Unknown || s.ftype == f_HyperText) { + if (!s.sound.empty() && m_sound_manager) + m_sound_manager->playSound(s.sound, false, 1.0f); s.send = true; acceptInput(); s.send = false; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index c5d662a69..53076e3bd 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -39,6 +39,7 @@ class InventoryManager; class ISimpleTextureSource; class Client; class GUIScrollContainer; +class ISoundManager; typedef enum { f_Button, @@ -127,6 +128,7 @@ class GUIFormSpecMenu : public GUIModalMenu int priority; core::rect rect; gui::ECURSOR_ICON fcursor_icon; + std::string sound; }; struct TooltipSpec @@ -151,6 +153,7 @@ public: IMenuManager *menumgr, Client *client, ISimpleTextureSource *tsrc, + ISoundManager *sound_manager, IFormSource* fs_src, TextDest* txt_dst, const std::string &formspecPrepend, @@ -160,7 +163,7 @@ public: static void create(GUIFormSpecMenu *&cur_formspec, Client *client, JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest, - const std::string &formspecPrepend); + const std::string &formspecPrepend, ISoundManager *sound_manager); void setFormSpec(const std::string &formspec_string, const InventoryLocation ¤t_inventory_location) @@ -293,6 +296,7 @@ protected: InventoryManager *m_invmgr; ISimpleTextureSource *m_tsrc; + ISoundManager *m_sound_manager; Client *m_client; std::string m_formspec_string; -- cgit v1.2.3