From 7ce21788f86d489d6dc08d9b2d3f6e3f8495b64e Mon Sep 17 00:00:00 2001 From: Hugues Ross Date: Sat, 15 Feb 2020 10:33:18 -0500 Subject: Add animated_image[] formspec element (#9258) --- src/gui/CMakeLists.txt | 1 + src/gui/guiAnimatedImage.cpp | 83 ++++++++++++++++++++++++++++++++++++++++++++ src/gui/guiAnimatedImage.h | 26 ++++++++++++++ src/gui/guiFormSpecMenu.cpp | 58 +++++++++++++++++++++++++++++++ src/gui/guiFormSpecMenu.h | 2 ++ 5 files changed, 170 insertions(+) create mode 100644 src/gui/guiAnimatedImage.cpp create mode 100644 src/gui/guiAnimatedImage.h (limited to 'src') diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a9df7848d..110a00595 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -1,4 +1,5 @@ set(gui_SRCS + ${CMAKE_CURRENT_SOURCE_DIR}/guiAnimatedImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiBackgroundImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiButton.cpp diff --git a/src/gui/guiAnimatedImage.cpp b/src/gui/guiAnimatedImage.cpp new file mode 100644 index 000000000..822304087 --- /dev/null +++ b/src/gui/guiAnimatedImage.cpp @@ -0,0 +1,83 @@ +#include "guiAnimatedImage.h" + +#include "client/guiscalingfilter.h" +#include "client/tile.h" // ITextureSource +#include "log.h" +#include "porting.h" +#include + +GUIAnimatedImage::GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, + s32 id, const core::rect &rectangle, const std::string &name, + ISimpleTextureSource *tsrc) : + gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle), + m_name(name), m_tsrc(tsrc), m_texture(nullptr), m_global_time(0), + m_frame_idx(0), m_frame_count(1), m_frame_duration(1), m_frame_time(0) +{ + // Expected format: "texture_name:frame_count,frame_duration" + // If this format is not met, the string will be loaded as a normal texture + + std::string::size_type colon_position = name.find(':', 0); + std::string::size_type comma_position = name.find(',', 0); + + if (comma_position != std::string::npos && + colon_position != std::string::npos && + comma_position < name.size()) { + m_texture = m_tsrc->getTexture(name.substr(0, colon_position)); + + m_frame_count = std::max(stoi(name.substr( + colon_position + 1, comma_position - colon_position - 1)), 1); + + m_frame_duration = std::max(stoi(name.substr(comma_position + 1)), 1); + } else { + // Leave the count/duration and display a static image + m_texture = m_tsrc->getTexture(name); + errorstream << "animated_image[]: Invalid texture format " << name << + ". Expected format: texture_name:frame_count,frame_duration" << std::endl; + } + + if (m_texture != nullptr) { + core::dimension2d size = m_texture->getOriginalSize(); + if (size.Height < (u64)m_frame_count) { + m_frame_count = size.Height; + } + } else { + // No need to step an animation if we have nothing to draw + m_frame_count = 1; + } +} + +void GUIAnimatedImage::draw() +{ + // Render the current frame + if (m_texture != nullptr) { + video::IVideoDriver *driver = Environment->getVideoDriver(); + + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + + core::dimension2d size = m_texture->getOriginalSize(); + size.Height /= m_frame_count; + + draw2DImageFilterScaled( driver, m_texture, AbsoluteRect, + core::rect(core::position2d(0, size.Height * m_frame_idx), size), + NoClip ? nullptr : &AbsoluteClippingRect, colors, true); + } + + // Step the animation + if (m_frame_count > 1) { + // Determine the delta time to step + u64 new_global_time = porting::getTimeMs(); + if (m_global_time > 0) + m_frame_time += new_global_time - m_global_time; + + m_global_time = new_global_time; + + // Advance by the number of elapsed frames, looping if necessary + m_frame_idx += u32(m_frame_time / m_frame_duration); + m_frame_idx %= m_frame_count; + + // If 1 or more frames have elapsed, reset the frame time counter with + // the remainder + m_frame_time %= m_frame_duration; + } +} diff --git a/src/gui/guiAnimatedImage.h b/src/gui/guiAnimatedImage.h new file mode 100644 index 000000000..8fb2977f2 --- /dev/null +++ b/src/gui/guiAnimatedImage.h @@ -0,0 +1,26 @@ +#pragma once + +#include "irrlichttypes_extrabloated.h" +#include "util/string.h" + +class ISimpleTextureSource; + +class GUIAnimatedImage : public gui::IGUIElement { +public: + GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, + const core::rect &rectangle, const std::string &name, + ISimpleTextureSource *tsrc); + + virtual void draw() override; + +private: + std::string m_name; + ISimpleTextureSource *m_tsrc; + + video::ITexture *m_texture; + u64 m_global_time; + s32 m_frame_idx; + s32 m_frame_count; + u64 m_frame_duration; + u64 m_frame_time; +}; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 98f4368f4..3d473550c 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -55,6 +55,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" // for parseColorString() #include "irrlicht_changes/static_text.h" #include "client/guiscalingfilter.h" +#include "guiAnimatedImage.h" #include "guiBackgroundImage.h" #include "guiBox.h" #include "guiButton.h" @@ -779,6 +780,58 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl; } +void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &element) +{ + std::vector parts = split(element, ';'); + + if (parts.size() != 3 && + !(parts.size() > 3 && m_formspec_version > FORMSPEC_API_VERSION)) { + errorstream << "Invalid animated image element(" << parts.size() + << "): '" << element << "'" << std::endl; + return; + } + + std::vector v_pos = split(parts[0], ','); + std::vector v_geom = split(parts[1], ','); + std::string name = unescape_string(parts[2]); + + MY_CHECKPOS("animated_image", 0); + MY_CHECKGEOM("animated_image", 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 animated_image without a size[] element" << std::endl; + + FieldSpec spec( + "", + L"", + L"", + 258 + m_fields.size() + ); + + core::rect rect = core::rect(pos, pos + geom); + + gui::IGUIElement *e = new GUIAnimatedImage(Environment, this, spec.fid, + rect, name, m_tsrc); + + auto style = getStyleForElement("animated_image", spec.fname); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->drop(); + + m_fields.push_back(spec); +} + void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -2500,6 +2553,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "animated_image") { + parseAnimatedImage(data, description); + return; + } + if (type == "item_image") { parseItemImage(data, description); return; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 67be4268a..7c52336c9 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -38,6 +38,7 @@ class InventoryManager; class ISimpleTextureSource; class Client; class GUIScrollBar; +class TexturePool; typedef enum { f_Button, @@ -388,6 +389,7 @@ private: void parseListRing(parserData* data, const std::string &element); void parseCheckbox(parserData* data, const std::string &element); void parseImage(parserData* data, const std::string &element); + void parseAnimatedImage(parserData *data, const std::string &element); void parseItemImage(parserData* data, const std::string &element); void parseButton(parserData* data, const std::string &element, const std::string &typ); -- cgit v1.2.3