From 7295b6c88cbadb188871162d4d76e7c50faef9d1 Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:02:53 +0100 Subject: [PATCH 01/68] Remove unused and rarely used irrlicht color functions (#15442) SColor.h contains many functions which are unused and/or perform linear operations on non-linear 8 bit sRGB color values, such as the plus operator and `SColor::getInterpolated()`, and there is no documentation about missing gamma correction. Some of these functions are not called or called only once: * `getAverage(s16 color)`: Unused * `SColor::getLightness()`: Unused * `SColor::getAverage()`: Claims to determine a color's average intensity but calculates something significantly different since SColor represents non-linear sRGB values. * `SColor::getInterpolated_quadratic()`: Claims to interpolate between colors but uses the sRGB color space, which is neither physically nor perceptually linear. * `SColorf::getInterpolated_quadratic()`: Unused * `SColorf::setColorComponentValue()`: Unused Removing or inlining these functions can simplify the code and documenting gamma-incorrect operations can reduce confusion about what the functions do. This commit does the following: * Remove the above-mentioned unused functions * Inline `SColor::getAverage()` into `CIrrDeviceLinux::TextureToMonochromeCursor()` * Rename `SColor::getLuminance()` into `SColor::getBrightness()` since it does not determine a color's luminance but calculates something which differs significantly from physical luminance since SColor represents non-linear sRGB values. * Inline `SColor::getInterpolated_quadratic()` into `GameUI::update()`, where it is only used for the alpha value calculation for fading * Document gamma-incorrect behaviour in docstrings --- irr/include/SColor.h | 105 +++++------------------------------- irr/src/CIrrDeviceLinux.cpp | 3 +- src/client/camera.h | 2 +- src/client/gameui.cpp | 8 +-- src/client/imagesource.cpp | 2 +- 5 files changed, 20 insertions(+), 100 deletions(-) diff --git a/irr/include/SColor.h b/irr/include/SColor.h index a2dd52603..41bca1a8c 100644 --- a/irr/include/SColor.h +++ b/irr/include/SColor.h @@ -224,12 +224,6 @@ inline u32 getBlue(u16 color) return (color & 0x1F); } -//! Returns the average from a 16 bit A1R5G5B5 color -inline s32 getAverage(s16 color) -{ - return ((getRed(color) << 3) + (getGreen(color) << 3) + (getBlue(color) << 3)) / 3; -} - //! Class representing a 32 bit ARGB color. /** The color values for alpha, red, green, and blue are stored in a single u32. So all four values may be between 0 and 255. @@ -275,24 +269,12 @@ public: 0 means no blue, 255 means full blue. */ u32 getBlue() const { return color & 0xff; } - //! Get lightness of the color in the range [0,255] - f32 getLightness() const - { - return 0.5f * (core::max_(core::max_(getRed(), getGreen()), getBlue()) + core::min_(core::min_(getRed(), getGreen()), getBlue())); - } - - //! Get luminance of the color in the range [0,255]. - f32 getLuminance() const + //! Get an approximate brightness value of the color in the range [0,255] + f32 getBrightness() const { return 0.3f * getRed() + 0.59f * getGreen() + 0.11f * getBlue(); } - //! Get average intensity of the color in the range [0,255]. - u32 getAverage() const - { - return (getRed() + getGreen() + getBlue()) / 3; - } - //! Sets the alpha component of the Color. /** The alpha component defines how transparent a color should be. \param a The alpha value of the color. 0 is fully transparent, 255 is fully opaque. */ @@ -362,9 +344,9 @@ public: /** \return True if this color is smaller than the other one */ bool operator<(const SColor &other) const { return (color < other.color); } - //! Adds two colors, result is clamped to 0..255 values + //! Adds two colors in a gamma-incorrect way /** \param other Color to add to this color - \return Addition of the two colors, clamped to 0..255 values */ + \return Sum of the two non-linear colors, clamped to 0..255 values */ SColor operator+(const SColor &other) const { return SColor(core::min_(getAlpha() + other.getAlpha(), 255u), @@ -374,7 +356,9 @@ public: } //! Interpolates the color with a f32 value to another color - /** \param other: Other color + /** Note that the interpolation is neither physically nor perceptually + linear since it happens directly in the sRGB color space. + \param other: Other color \param d: value between 0.0f and 1.0f. d=0 returns other, d=1 returns this, values between interpolate. \return Interpolated color. */ SColor getInterpolated(const SColor &other, f32 d) const @@ -387,34 +371,6 @@ public: (u32)core::round32(other.getBlue() * inv + getBlue() * d)); } - //! Returns interpolated color. ( quadratic ) - /** \param c1: first color to interpolate with - \param c2: second color to interpolate with - \param d: value between 0.0f and 1.0f. */ - SColor getInterpolated_quadratic(const SColor &c1, const SColor &c2, f32 d) const - { - // this*(1-d)*(1-d) + 2 * c1 * (1-d) + c2 * d * d; - d = core::clamp(d, 0.f, 1.f); - const f32 inv = 1.f - d; - const f32 mul0 = inv * inv; - const f32 mul1 = 2.f * d * inv; - const f32 mul2 = d * d; - - return SColor( - core::clamp(core::floor32( - getAlpha() * mul0 + c1.getAlpha() * mul1 + c2.getAlpha() * mul2), - 0, 255), - core::clamp(core::floor32( - getRed() * mul0 + c1.getRed() * mul1 + c2.getRed() * mul2), - 0, 255), - core::clamp(core::floor32( - getGreen() * mul0 + c1.getGreen() * mul1 + c2.getGreen() * mul2), - 0, 255), - core::clamp(core::floor32( - getBlue() * mul0 + c1.getBlue() * mul1 + c2.getBlue() * mul2), - 0, 255)); - } - //! set the color by expecting data in the given format /** \param data: must point to valid memory containing color information in the given format \param format: tells the format in which data is available @@ -508,7 +464,7 @@ public: SColorf(f32 r, f32 g, f32 b, f32 a = 1.0f) : r(r), g(g), b(b), a(a) {} - //! Constructs a color from 32 bit Color. + //! Constructs a color from 32 bit Color without gamma correction /** \param c: 32 bit color from which this SColorf class is constructed from. */ SColorf(SColor c) @@ -520,7 +476,7 @@ public: a = c.getAlpha() * inv; } - //! Converts this color to a SColor without floats. + //! Converts this color to a SColor without gamma correction SColor toSColor() const { return SColor((u32)core::round32(a * 255.0f), (u32)core::round32(r * 255.0f), (u32)core::round32(g * 255.0f), (u32)core::round32(b * 255.0f)); @@ -558,7 +514,9 @@ public: } //! Interpolates the color with a f32 value to another color - /** \param other: Other color + /** Note that the interpolation is neither physically nor perceptually + linear if it happens directly in the sRGB color space. + \param other: Other color \param d: value between 0.0f and 1.0f \return Interpolated color. */ SColorf getInterpolated(const SColorf &other, f32 d) const @@ -569,45 +527,6 @@ public: other.g * inv + g * d, other.b * inv + b * d, other.a * inv + a * d); } - //! Returns interpolated color. ( quadratic ) - /** \param c1: first color to interpolate with - \param c2: second color to interpolate with - \param d: value between 0.0f and 1.0f. */ - inline SColorf getInterpolated_quadratic(const SColorf &c1, const SColorf &c2, - f32 d) const - { - d = core::clamp(d, 0.f, 1.f); - // this*(1-d)*(1-d) + 2 * c1 * (1-d) + c2 * d * d; - const f32 inv = 1.f - d; - const f32 mul0 = inv * inv; - const f32 mul1 = 2.f * d * inv; - const f32 mul2 = d * d; - - return SColorf(r * mul0 + c1.r * mul1 + c2.r * mul2, - g * mul0 + c1.g * mul1 + c2.g * mul2, - b * mul0 + c1.b * mul1 + c2.b * mul2, - a * mul0 + c1.a * mul1 + c2.a * mul2); - } - - //! Sets a color component by index. R=0, G=1, B=2, A=3 - void setColorComponentValue(s32 index, f32 value) - { - switch (index) { - case 0: - r = value; - break; - case 1: - g = value; - break; - case 2: - b = value; - break; - case 3: - a = value; - break; - } - } - //! Returns the alpha component of the color in the range 0.0 (transparent) to 1.0 (opaque) f32 getAlpha() const { return a; } diff --git a/irr/src/CIrrDeviceLinux.cpp b/irr/src/CIrrDeviceLinux.cpp index e88902936..0b9ef5b71 100644 --- a/irr/src/CIrrDeviceLinux.cpp +++ b/irr/src/CIrrDeviceLinux.cpp @@ -1951,7 +1951,8 @@ Cursor CIrrDeviceLinux::TextureToMonochromeCursor(irr::video::ITexture *tex, con XPutPixel(sourceImage, x, y, 0); } else // color { - if (pixelCol.getAverage() >= 127) + if ((pixelCol.getRed() + pixelCol.getGreen() + + pixelCol.getBlue()) / 3 >= 127) XPutPixel(sourceImage, x, y, 1); else XPutPixel(sourceImage, x, y, 0); diff --git a/src/client/camera.h b/src/client/camera.h index 9a8e2d36a..3715bebad 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -48,7 +48,7 @@ struct Nametag return bgcolor.value(); else if (!use_fallback) return video::SColor(0, 0, 0, 0); - else if (textcolor.getLuminance() > 186) + else if (textcolor.getBrightness() > 186) // Dark background for light text return video::SColor(50, 50, 50, 50); else diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 3408ba196..c2693c6ae 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -201,10 +201,10 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ status_y - status_height, status_x + status_width, status_y)); // Fade out - video::SColor final_color = m_statustext_initial_color; - final_color.setAlpha(0); - video::SColor fade_color = m_statustext_initial_color.getInterpolated_quadratic( - m_statustext_initial_color, final_color, m_statustext_time / statustext_time_max); + video::SColor fade_color = m_statustext_initial_color; + f32 d = m_statustext_time / statustext_time_max; + fade_color.setAlpha(static_cast( + fade_color.getAlpha() * (1.0f - d * d))); guitext_status->setOverrideColor(fade_color); guitext_status->enableOverrideColor(true); } diff --git a/src/client/imagesource.cpp b/src/client/imagesource.cpp index 0f4e81f97..a14dac290 100644 --- a/src/client/imagesource.cpp +++ b/src/client/imagesource.cpp @@ -566,7 +566,7 @@ static void apply_hue_saturation(video::IImage *dst, v2u32 dst_pos, v2u32 size, for (u32 x = dst_pos.X; x < dst_pos.X + size.X; x++) { if (colorize) { - f32 lum = dst->getPixel(x, y).getLuminance() / 255.0f; + f32 lum = dst->getPixel(x, y).getBrightness() / 255.0f; if (norm_l < 0) { lum *= norm_l + 1.0f; From a8ea1650425f83a7573314effae061823ff52de8 Mon Sep 17 00:00:00 2001 From: veprogames <75524847+veprogames@users.noreply.github.com> Date: Mon, 18 Nov 2024 00:04:32 +0100 Subject: [PATCH 02/68] Replace occurences of 'wiki.minetest.net' with 'wiki.luanti.org' --- README.md | 2 +- builtin/mainmenu/settings/generate_from_settingtypes.lua | 2 +- doc/android.md | 2 +- doc/developing/README.md | 2 +- minetest.conf.example | 2 +- misc/net.minetest.minetest.metainfo.xml | 4 ++-- src/server.cpp | 2 +- src/serverenvironment.cpp | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b21153d54..aa8faccd5 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Table of Contents Further documentation ---------------------- - Website: https://www.luanti.org/ -- Wiki: https://wiki.minetest.net/ +- Wiki: https://wiki.luanti.org/ - Forum: https://forum.luanti.org/ - GitHub: https://github.com/minetest/minetest/ - [Developer documentation](doc/developing/) diff --git a/builtin/mainmenu/settings/generate_from_settingtypes.lua b/builtin/mainmenu/settings/generate_from_settingtypes.lua index 198037776..52dfe71b1 100644 --- a/builtin/mainmenu/settings/generate_from_settingtypes.lua +++ b/builtin/mainmenu/settings/generate_from_settingtypes.lua @@ -16,7 +16,7 @@ local minetest_example_header = [[ # to the program, eg. "luanti.exe --config ../minetest.conf.example". # Further documentation: -# https://wiki.minetest.net/ +# https://wiki.luanti.org/ ]] diff --git a/doc/android.md b/doc/android.md index 65cc0440c..353a7d1c8 100644 --- a/doc/android.md +++ b/doc/android.md @@ -42,7 +42,7 @@ configuration file can usually be found at: * After 5.4.2: * `/sdcard/Android/data/net.minetest.minetest/` or `/storage/emulated/0/Android/data/net.minetest.minetest/` if stored on the device * `/storage/emulated/(varying folder name)/Android/data/net.minetest.minetest/` if stored on the SD card -* [Learn more about Android directory](https://wiki.minetest.net/Accessing_Android_Data_Directory) +* [Learn more about Android directory](https://wiki.luanti.org/Accessing_Android_Data_Directory) ## Useful settings diff --git a/doc/developing/README.md b/doc/developing/README.md index 7e84de904..1aa424b00 100644 --- a/doc/developing/README.md +++ b/doc/developing/README.md @@ -23,4 +23,4 @@ Notable pages: Oftentimes knowledge hasn't been written down (yet) and your best bet is to ask someone experienced and/or the core developers. -Feel free to join the [#minetest-dev IRC](https://wiki.minetest.net/IRC) and ask questions related to **engine development**. +Feel free to join the [#minetest-dev IRC](https://wiki.luanti.org/IRC) and ask questions related to **engine development**. diff --git a/minetest.conf.example b/minetest.conf.example index 6e3e698be..f5fefbcf3 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -10,7 +10,7 @@ # to the program, eg. "luanti.exe --config ../minetest.conf.example". # Further documentation: -# https://wiki.minetest.net/ +# https://wiki.luanti.org/ # # Controls diff --git a/misc/net.minetest.minetest.metainfo.xml b/misc/net.minetest.minetest.metainfo.xml index ca61fe80c..9e7d927f1 100644 --- a/misc/net.minetest.minetest.metainfo.xml +++ b/misc/net.minetest.minetest.metainfo.xml @@ -135,8 +135,8 @@ https://www.minetest.net/get-involved/#reporting-issues https://dev.minetest.net/Translation https://www.minetest.net/get-involved/#donate - https://wiki.minetest.net/FAQ - https://wiki.minetest.net + https://wiki.luanti.org/FAQ + https://wiki.luanti.org https://github.com/minetest/minetest https://www.minetest.net/get-involved diff --git a/src/server.cpp b/src/server.cpp index a636364ed..d2aaf3432 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -4191,7 +4191,7 @@ ModStorageDatabase *Server::openModStorageDatabase(const std::string &world_path warningstream << "/!\\ You are using the old mod storage files backend. " << "This backend is deprecated and may be removed in a future release /!\\" << std::endl << "Switching to SQLite3 is advised, " - << "please read http://wiki.minetest.net/Database_backends." << std::endl; + << "please read https://wiki.luanti.org/Database_backends." << std::endl; return openModStorageDatabase(backend, world_path, world_mt); } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index d60e41ad9..44645ca34 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -526,14 +526,14 @@ void ServerEnvironment::init() warningstream << "/!\\ You are using old player file backend. " << "This backend is deprecated and will be removed in a future release /!\\" << std::endl << "Switching to SQLite3 or PostgreSQL is advised, " - << "please read http://wiki.minetest.net/Database_backends." << std::endl; + << "please read https://wiki.luanti.org/Database_backends." << std::endl; } if (auth_backend_name == "files") { warningstream << "/!\\ You are using old auth file backend. " << "This backend is deprecated and will be removed in a future release /!\\" << std::endl << "Switching to SQLite3 is advised, " - << "please read http://wiki.minetest.net/Database_backends." << std::endl; + << "please read https://wiki.luanti.org/Database_backends." << std::endl; } m_player_database = openPlayerDatabase(player_backend_name, world_path, conf); From 9b6a3990117c3e226031b8ad6a3679a35bc7ee6a Mon Sep 17 00:00:00 2001 From: grorp Date: Mon, 18 Nov 2024 14:06:48 +0100 Subject: [PATCH 03/68] Implement support for FSAA in combination with post-processing (#15392) - Actually it's MSAA I think, or perhaps the terms are equivalent - I've made it fit into the existing Irrlicht architecture, but that has resulted in code duplication compared to my original "hacky" approach - OpenGL 3.2+ and OpenGL ES 3.1+ are supported - EDT_OPENGL3 is not required, EDT_OPENGL works too - Helpful tutorial: https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing, section "Off-screen MSAA" - This may be rough around the edges, but in general it works --- builtin/settingtypes.txt | 6 ++-- irr/include/EDriverFeatures.h | 3 ++ irr/include/IRenderTarget.h | 1 + irr/include/ITexture.h | 3 ++ irr/include/IVideoDriver.h | 12 +++++++ irr/src/CNullDriver.cpp | 6 ++++ irr/src/CNullDriver.h | 6 ++++ irr/src/COpenGLCoreCacheHandler.h | 15 ++++++--- irr/src/COpenGLCoreRenderTarget.h | 44 +++++++++++++++++++----- irr/src/COpenGLCoreTexture.h | 50 +++++++++++++++++++++------ irr/src/COpenGLDriver.cpp | 35 +++++++++++++++++-- irr/src/COpenGLDriver.h | 5 +++ irr/src/COpenGLExtensionHandler.cpp | 2 ++ irr/src/OpenGL/Driver.cpp | 33 +++++++++++++++++- irr/src/OpenGL/Driver.h | 5 +++ irr/src/OpenGL/ExtensionHandler.h | 3 ++ irr/src/OpenGL3/Driver.cpp | 1 + irr/src/OpenGLES2/Driver.cpp | 1 + src/client/render/pipeline.cpp | 22 +++++++++--- src/client/render/pipeline.h | 8 +++-- src/client/render/secondstage.cpp | 52 +++++++++++++++++++++++++---- src/client/render/secondstage.h | 14 ++++++++ src/client/renderingengine.cpp | 5 ++- 23 files changed, 290 insertions(+), 42 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index f8f1dec05..418e883d1 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -410,10 +410,12 @@ anisotropic_filter (Anisotropic filtering) bool false # * None - No antialiasing (default) # # * FSAA - Hardware-provided full-screen antialiasing -# (incompatible with Post Processing and Undersampling) # A.K.A multi-sample antialiasing (MSAA) # Smoothens out block edges but does not affect the insides of textures. -# A restart is required to change this option. +# +# If Post Processing is disabled, changing FSAA requires a restart. +# Also, if Post Processing is disabled, FSAA will not work together with +# undersampling or a non-default "3d_mode" setting. # # * FXAA - Fast approximate antialiasing # Applies a post-processing filter to detect and smoothen high-contrast edges. diff --git a/irr/include/EDriverFeatures.h b/irr/include/EDriverFeatures.h index bde2c7698..0a35161cf 100644 --- a/irr/include/EDriverFeatures.h +++ b/irr/include/EDriverFeatures.h @@ -129,6 +129,9 @@ enum E_VIDEO_DRIVER_FEATURE //! Support for clamping vertices beyond far-plane to depth instead of capping them. EVDF_DEPTH_CLAMP, + //! Support for multisample textures. + EVDF_TEXTURE_MULTISAMPLE, + //! Only used for counting the elements of this enum EVDF_COUNT }; diff --git a/irr/include/IRenderTarget.h b/irr/include/IRenderTarget.h index 1e70c1e06..85b9738dc 100644 --- a/irr/include/IRenderTarget.h +++ b/irr/include/IRenderTarget.h @@ -26,6 +26,7 @@ enum E_CUBE_SURFACE }; //! Interface of a Render Target. +/** This is a framebuffer object (FBO) in OpenGL. */ class IRenderTarget : public virtual IReferenceCounted { public: diff --git a/irr/include/ITexture.h b/irr/include/ITexture.h index 8760c9f2a..869a325e0 100644 --- a/irr/include/ITexture.h +++ b/irr/include/ITexture.h @@ -156,6 +156,9 @@ enum E_TEXTURE_TYPE //! 2D texture. ETT_2D, + //! 2D texture with multisampling. + ETT_2D_MS, + //! Cubemap texture. ETT_CUBEMAP }; diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index 035f5ad77..3d4deace5 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -273,6 +273,14 @@ public: virtual ITexture *addRenderTargetTexture(const core::dimension2d &size, const io::path &name = "rt", const ECOLOR_FORMAT format = ECF_UNKNOWN) = 0; + //! Adds a multisampled render target texture to the texture cache. + /** \param msaa The number of samples to use, values that make sense are > 1. + Only works if the driver supports the EVDF_TEXTURE_MULTISAMPLE feature, + check via queryFeature. + \see addRenderTargetTexture */ + virtual ITexture *addRenderTargetTextureMs(const core::dimension2d &size, u8 msaa, + const io::path &name = "rt", const ECOLOR_FORMAT format = ECF_UNKNOWN) = 0; + //! Adds a new render target texture with 6 sides for a cubemap map to the texture cache. /** \param sideLen Length of one cubemap side. \param name A name for the texture. Later calls of getTexture() with this name will return this texture. @@ -358,6 +366,10 @@ public: //! Remove all render targets. virtual void removeAllRenderTargets() = 0; + //! Blit contents of one render target to another one. + /** This is glBlitFramebuffer in OpenGL. */ + virtual void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) = 0; + //! Sets a boolean alpha channel on the texture based on a color key. /** This makes the texture fully transparent at the texels where this color key can be found when using for example draw2DImage diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index acce8383b..b5da7ef29 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -1678,6 +1678,12 @@ ITexture *CNullDriver::addRenderTargetTexture(const core::dimension2d &size return 0; } +ITexture *CNullDriver::addRenderTargetTextureMs(const core::dimension2d &size, u8 msaa, + const io::path &name, const ECOLOR_FORMAT format) +{ + return 0; +} + ITexture *CNullDriver::addRenderTargetTextureCubemap(const irr::u32 sideLen, const io::path &name, const ECOLOR_FORMAT format) { diff --git a/irr/src/CNullDriver.h b/irr/src/CNullDriver.h index b8d45118f..5752b53cd 100644 --- a/irr/src/CNullDriver.h +++ b/irr/src/CNullDriver.h @@ -227,6 +227,10 @@ public: virtual ITexture *addRenderTargetTexture(const core::dimension2d &size, const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override; + //! Creates a multisampled render target texture. + virtual ITexture *addRenderTargetTextureMs(const core::dimension2d &size, u8 msaa, + const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override; + //! Creates a render target texture for a cubemap ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen, const io::path &name, const ECOLOR_FORMAT format) override; @@ -410,6 +414,8 @@ public: //! Create render target. IRenderTarget *addRenderTarget() override; + void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override {} + //! Remove render target. void removeRenderTarget(IRenderTarget *renderTarget) override; diff --git a/irr/src/COpenGLCoreCacheHandler.h b/irr/src/COpenGLCoreCacheHandler.h index a1277bfed..744511629 100644 --- a/irr/src/COpenGLCoreCacheHandler.h +++ b/irr/src/COpenGLCoreCacheHandler.h @@ -85,13 +85,19 @@ class COpenGLCoreCacheHandler GL.BindTexture(prevTextureType, 0); #if defined(IRR_COMPILE_GL_COMMON) - GL.Disable(prevTextureType); - GL.Enable(curTextureType); + // The "enable/disable texture" stuff is so legacy that + // it's not even allowed for multisample textures. + // (IRR_COMPILE_GL_COMMON is for the legacy driver.) + if (prevTextureType != GL_TEXTURE_2D_MULTISAMPLE) + GL.Disable(prevTextureType); + if (curTextureType != GL_TEXTURE_2D_MULTISAMPLE) + GL.Enable(curTextureType); #endif } #if defined(IRR_COMPILE_GL_COMMON) else if (!prevTexture) - GL.Enable(curTextureType); + if (curTextureType != GL_TEXTURE_2D_MULTISAMPLE) + GL.Enable(curTextureType); #endif GL.BindTexture(curTextureType, static_cast(texture)->getOpenGLTextureName()); @@ -110,7 +116,8 @@ class COpenGLCoreCacheHandler GL.BindTexture(prevTextureType, 0); #if defined(IRR_COMPILE_GL_COMMON) - GL.Disable(prevTextureType); + if (prevTextureType != GL_TEXTURE_2D_MULTISAMPLE) + GL.Disable(prevTextureType); #endif } diff --git a/irr/src/COpenGLCoreRenderTarget.h b/irr/src/COpenGLCoreRenderTarget.h index 6bfc98cc0..a174e926e 100644 --- a/irr/src/COpenGLCoreRenderTarget.h +++ b/irr/src/COpenGLCoreRenderTarget.h @@ -5,6 +5,7 @@ #pragma once #include "IRenderTarget.h" +#include #ifndef GL_FRAMEBUFFER_INCOMPLETE_FORMATS #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT @@ -122,7 +123,7 @@ public: TOpenGLTexture *currentTexture = (depthStencil && depthStencil->getDriverType() == DriverType) ? static_cast(depthStencil) : 0; if (currentTexture) { - if (currentTexture->getType() == ETT_2D) { + if (currentTexture->getType() == ETT_2D || currentTexture->getType() == ETT_2D_MS) { GLuint textureID = currentTexture->getOpenGLTextureName(); const ECOLOR_FORMAT textureFormat = (textureID != 0) ? depthStencil->getColorFormat() : ECF_UNKNOWN; @@ -172,7 +173,20 @@ public: if (textureID != 0) { AssignedTextures[i] = GL_COLOR_ATTACHMENT0 + i; - GLenum textarget = currentTexture->getType() == ETT_2D ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int)CubeSurfaces[i]; + GLenum textarget; + switch (currentTexture->getType()) { + case ETT_2D: + textarget = GL_TEXTURE_2D; + break; + case ETT_2D_MS: + textarget = GL_TEXTURE_2D_MULTISAMPLE; + break; + case ETT_CUBEMAP: + textarget = GL_TEXTURE_CUBE_MAP_POSITIVE_X + (int)CubeSurfaces[i]; + break; + default: + throw std::logic_error("not reachable"); + } Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, AssignedTextures[i], textarget, textureID, 0); TEST_GL_ERROR(Driver); } else if (AssignedTextures[i] != GL_NONE) { @@ -198,36 +212,50 @@ public: // Set depth and stencil attachments. if (RequestDepthStencilUpdate) { - const ECOLOR_FORMAT textureFormat = (DepthStencil) ? DepthStencil->getColorFormat() : ECF_UNKNOWN; + const ECOLOR_FORMAT textureFormat = DepthStencil ? DepthStencil->getColorFormat() : ECF_UNKNOWN; if (IImage::isDepthFormat(textureFormat)) { + GLenum textarget; + switch (DepthStencil->getType()) { + case ETT_2D: + textarget = GL_TEXTURE_2D; + break; + case ETT_2D_MS: + textarget = GL_TEXTURE_2D_MULTISAMPLE; + break; + default: + // ETT_CUBEMAP is rejected for depth/stencil by setTextures + throw std::logic_error("not reachable"); + } + GLuint textureID = static_cast(DepthStencil)->getOpenGLTextureName(); #ifdef _IRR_EMSCRIPTEN_PLATFORM_ // The WEBGL_depth_texture extension does not allow attaching stencil+depth separate. if (textureFormat == ECF_D24S8) { GLenum attachment = 0x821A; // GL_DEPTH_STENCIL_ATTACHMENT - Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, textureID, 0); + Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, attachment, textarget, textureID, 0); AssignedStencil = true; } else { - Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0); + Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, textarget, textureID, 0); AssignedStencil = false; } #else - Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, textureID, 0); + Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, textarget, textureID, 0); if (textureFormat == ECF_D24S8) { - Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, textureID, 0); + Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, textarget, textureID, 0); AssignedStencil = true; } else { if (AssignedStencil) - Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); + Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, textarget, 0, 0); AssignedStencil = false; } #endif AssignedDepth = true; } else { + // No (valid) depth/stencil texture. if (AssignedDepth) Driver->irrGlFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, 0, 0); diff --git a/irr/src/COpenGLCoreTexture.h b/irr/src/COpenGLCoreTexture.h index 35c8f472a..a37562543 100644 --- a/irr/src/COpenGLCoreTexture.h +++ b/irr/src/COpenGLCoreTexture.h @@ -45,12 +45,13 @@ public: COpenGLCoreTexture(const io::path &name, const std::vector &srcImages, E_TEXTURE_TYPE type, TOpenGLDriver *driver) : ITexture(name, type), Driver(driver), TextureType(GL_TEXTURE_2D), - TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_RGBA), PixelType(GL_UNSIGNED_BYTE), Converter(0), LockReadOnly(false), LockImage(0), LockLayer(0), + TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_RGBA), PixelType(GL_UNSIGNED_BYTE), MSAA(0), Converter(0), LockReadOnly(false), LockImage(0), LockLayer(0), KeepImage(false), MipLevelStored(0), LegacyAutoGenerateMipMaps(false) { _IRR_DEBUG_BREAK_IF(srcImages.empty()) DriverType = Driver->getDriverType(); + _IRR_DEBUG_BREAK_IF(Type == ETT_2D_MS); // not supported by this constructor TextureType = TextureTypeIrrToGL(Type); HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); KeepImage = Driver->getTextureCreationFlag(ETCF_ALLOW_MEMORY_COPY); @@ -141,10 +142,10 @@ public: TEST_GL_ERROR(Driver); } - COpenGLCoreTexture(const io::path &name, const core::dimension2d &size, E_TEXTURE_TYPE type, ECOLOR_FORMAT format, TOpenGLDriver *driver) : + COpenGLCoreTexture(const io::path &name, const core::dimension2d &size, E_TEXTURE_TYPE type, ECOLOR_FORMAT format, TOpenGLDriver *driver, u8 msaa = 0) : ITexture(name, type), Driver(driver), TextureType(GL_TEXTURE_2D), - TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_RGBA), PixelType(GL_UNSIGNED_BYTE), Converter(0), LockReadOnly(false), LockImage(0), LockLayer(0), KeepImage(false), + TextureName(0), InternalFormat(GL_RGBA), PixelFormat(GL_RGBA), PixelType(GL_UNSIGNED_BYTE), MSAA(msaa), Converter(0), LockReadOnly(false), LockImage(0), LockLayer(0), KeepImage(false), MipLevelStored(0), LegacyAutoGenerateMipMaps(false) { DriverType = Driver->getDriverType(); @@ -184,23 +185,47 @@ public: const COpenGLCoreTexture *prevTexture = Driver->getCacheHandler()->getTextureCache().get(0); Driver->getCacheHandler()->getTextureCache().set(0, this); - GL.TexParameteri(TextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - GL.TexParameteri(TextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + // An INVALID_ENUM error is generated by TexParameter* if target is either + // TEXTURE_2D_MULTISAMPLE or TEXTURE_2D_MULTISAMPLE_ARRAY, and pname is any + // sampler state from table 23.18. + // ~ https://registry.khronos.org/OpenGL/specs/gl/glspec46.core.pdf + if (Type != ETT_2D_MS) { + GL.TexParameteri(TextureType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + GL.TexParameteri(TextureType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); #if defined(GL_VERSION_1_2) - GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + GL.TexParameteri(TextureType, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); #endif - StatesCache.WrapU = ETC_CLAMP_TO_EDGE; - StatesCache.WrapV = ETC_CLAMP_TO_EDGE; - StatesCache.WrapW = ETC_CLAMP_TO_EDGE; + StatesCache.WrapU = ETC_CLAMP_TO_EDGE; + StatesCache.WrapV = ETC_CLAMP_TO_EDGE; + StatesCache.WrapW = ETC_CLAMP_TO_EDGE; + } switch (Type) { case ETT_2D: GL.TexImage2D(GL_TEXTURE_2D, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0); break; + case ETT_2D_MS: { + // glTexImage2DMultisample is supported by OpenGL 3.2+ + // glTexStorage2DMultisample is supported by OpenGL 4.3+ and OpenGL ES 3.1+ +#ifdef IRR_COMPILE_GL_COMMON // legacy driver + constexpr bool use_gl_impl = true; +#else + const bool use_gl_impl = Driver->Version.Spec != OpenGLSpec::ES; +#endif + GLint max_samples = 0; + GL.GetIntegerv(GL_MAX_SAMPLES, &max_samples); + MSAA = std::min(MSAA, (u8)max_samples); + + if (use_gl_impl) + GL.TexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, MSAA, InternalFormat, Size.Width, Size.Height, GL_TRUE); + else + GL.TexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, MSAA, InternalFormat, Size.Width, Size.Height, GL_TRUE); + break; + } case ETT_CUBEMAP: GL.TexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0); GL.TexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, InternalFormat, Size.Width, Size.Height, 0, PixelFormat, PixelType, 0); @@ -595,6 +620,8 @@ protected: switch (type) { case ETT_2D: return GL_TEXTURE_2D; + case ETT_2D_MS: + return GL_TEXTURE_2D_MULTISAMPLE; case ETT_CUBEMAP: return GL_TEXTURE_CUBE_MAP; } @@ -610,6 +637,7 @@ protected: GLint InternalFormat; GLenum PixelFormat; GLenum PixelType; + u8 MSAA; void (*Converter)(const void *, s32, void *); bool LockReadOnly; diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index 2cbf6d8e6..07d7f7bb1 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -651,6 +651,29 @@ IRenderTarget *COpenGLDriver::addRenderTarget() return renderTarget; } +void COpenGLDriver::blitRenderTarget(IRenderTarget *from, IRenderTarget *to) +{ + if (Version < 300) { + os::Printer::log("glBlitFramebuffer not supported by OpenGL < 3.0", ELL_ERROR); + return; + } + + GLuint prev_fbo_id; + CacheHandler->getFBO(prev_fbo_id); + + COpenGLRenderTarget *src = static_cast(from); + COpenGLRenderTarget *dst = static_cast(to); + GL.BindFramebuffer(GL.READ_FRAMEBUFFER, src->getBufferID()); + GL.BindFramebuffer(GL.DRAW_FRAMEBUFFER, dst->getBufferID()); + GL.BlitFramebuffer( + 0, 0, src->getSize().Width, src->getSize().Height, + 0, 0, dst->getSize().Width, dst->getSize().Height, + GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT | GL.STENCIL_BUFFER_BIT, GL.NEAREST); + + // This resets both read and draw framebuffer. Note that we bypass CacheHandler here. + GL.BindFramebuffer(GL.FRAMEBUFFER, prev_fbo_id); +} + // small helper function to create vertex buffer object address offsets static inline const GLvoid *buffer_offset(const size_t offset) { @@ -2091,7 +2114,9 @@ void COpenGLDriver::setBasicRenderStates(const SMaterial &material, const SMater else if (lastmaterial.AntiAliasing & EAAM_ALPHA_TO_COVERAGE) glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); - if ((AntiAlias >= 2) && (material.AntiAliasing & (EAAM_SIMPLE | EAAM_QUALITY))) { + // Enable MSAA even if it's not enabled in the OpenGL context, we might + // be rendering to an FBO with multisampling. + if (material.AntiAliasing & (EAAM_SIMPLE | EAAM_QUALITY)) { glEnable(GL_MULTISAMPLE_ARB); #ifdef GL_NV_multisample_filter_hint if (FeatureAvailable[IRR_NV_multisample_filter_hint]) { @@ -2694,6 +2719,12 @@ IVideoDriver *COpenGLDriver::getVideoDriver() ITexture *COpenGLDriver::addRenderTargetTexture(const core::dimension2d &size, const io::path &name, const ECOLOR_FORMAT format) +{ + return addRenderTargetTextureMs(size, 0, name, format); +} + +ITexture *COpenGLDriver::addRenderTargetTextureMs(const core::dimension2d &size, u8 msaa, + const io::path &name, const ECOLOR_FORMAT format) { if (IImage::isCompressedFormat(format)) return 0; @@ -2711,7 +2742,7 @@ ITexture *COpenGLDriver::addRenderTargetTexture(const core::dimension2d &si destSize = destSize.getOptimalSize((size == size.getOptimalSize()), false, false); } - COpenGLTexture *renderTargetTexture = new COpenGLTexture(name, destSize, ETT_2D, format, this); + COpenGLTexture *renderTargetTexture = new COpenGLTexture(name, destSize, msaa > 0 ? ETT_2D_MS : ETT_2D, format, this, msaa); addTexture(renderTargetTexture); renderTargetTexture->drop(); diff --git a/irr/src/COpenGLDriver.h b/irr/src/COpenGLDriver.h index 9c4ecd3b3..126f7aadc 100644 --- a/irr/src/COpenGLDriver.h +++ b/irr/src/COpenGLDriver.h @@ -108,6 +108,8 @@ public: //! Create render target. IRenderTarget *addRenderTarget() override; + void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override; + //! draws a vertex primitive list virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount, const void *indexList, u32 primitiveCount, @@ -270,6 +272,9 @@ public: virtual ITexture *addRenderTargetTexture(const core::dimension2d &size, const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override; + virtual ITexture *addRenderTargetTextureMs(const core::dimension2d &size, u8 msaa, + const io::path &name = "rt", const ECOLOR_FORMAT format = ECF_UNKNOWN) override; + //! Creates a render target texture for a cubemap ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen, const io::path &name, const ECOLOR_FORMAT format) override; diff --git a/irr/src/COpenGLExtensionHandler.cpp b/irr/src/COpenGLExtensionHandler.cpp index a20932c8f..4c7fe69b7 100644 --- a/irr/src/COpenGLExtensionHandler.cpp +++ b/irr/src/COpenGLExtensionHandler.cpp @@ -612,6 +612,8 @@ bool COpenGLExtensionHandler::queryFeature(E_VIDEO_DRIVER_FEATURE feature) const return FeatureAvailable[IRR_ARB_seamless_cube_map]; case EVDF_DEPTH_CLAMP: return FeatureAvailable[IRR_NV_depth_clamp] || FeatureAvailable[IRR_ARB_depth_clamp]; + case EVDF_TEXTURE_MULTISAMPLE: + return (Version >= 302) || FeatureAvailable[IRR_ARB_texture_multisample]; default: return false; diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index 4b85b1345..ee272ceee 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -675,6 +675,29 @@ IRenderTarget *COpenGL3DriverBase::addRenderTarget() return renderTarget; } +void COpenGL3DriverBase::blitRenderTarget(IRenderTarget *from, IRenderTarget *to) +{ + if (Version.Spec == OpenGLSpec::ES && Version.Major < 3) { + os::Printer::log("glBlitFramebuffer not supported by OpenGL ES < 3.0", ELL_ERROR); + return; + } + + GLuint prev_fbo_id; + CacheHandler->getFBO(prev_fbo_id); + + COpenGL3RenderTarget *src = static_cast(from); + COpenGL3RenderTarget *dst = static_cast(to); + GL.BindFramebuffer(GL.READ_FRAMEBUFFER, src->getBufferID()); + GL.BindFramebuffer(GL.DRAW_FRAMEBUFFER, dst->getBufferID()); + GL.BlitFramebuffer( + 0, 0, src->getSize().Width, src->getSize().Height, + 0, 0, dst->getSize().Width, dst->getSize().Height, + GL.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT | GL.STENCIL_BUFFER_BIT, GL.NEAREST); + + // This resets both read and draw framebuffer. Note that we bypass CacheHandler here. + GL.BindFramebuffer(GL.FRAMEBUFFER, prev_fbo_id); +} + //! draws a vertex primitive list void COpenGL3DriverBase::drawVertexPrimitiveList(const void *vertices, u32 vertexCount, const void *indexList, u32 primitiveCount, @@ -1317,6 +1340,8 @@ void COpenGL3DriverBase::setBasicRenderStates(const SMaterial &material, const S GL.LineWidth(core::clamp(static_cast(material.Thickness), DimAliasedLine[0], DimAliasedLine[1])); // Anti aliasing + // Deal with MSAA even if it's not enabled in the OpenGL context, we might be + // rendering to an FBO with multisampling. if (resetAllRenderStates || lastmaterial.AntiAliasing != material.AntiAliasing) { if (material.AntiAliasing & EAAM_ALPHA_TO_COVERAGE) GL.Enable(GL_SAMPLE_ALPHA_TO_COVERAGE); @@ -1631,12 +1656,18 @@ IGPUProgrammingServices *COpenGL3DriverBase::getGPUProgrammingServices() ITexture *COpenGL3DriverBase::addRenderTargetTexture(const core::dimension2d &size, const io::path &name, const ECOLOR_FORMAT format) +{ + return addRenderTargetTextureMs(size, 0, name, format); +} + +ITexture *COpenGL3DriverBase::addRenderTargetTextureMs(const core::dimension2d &size, u8 msaa, + const io::path &name, const ECOLOR_FORMAT format) { // disable mip-mapping bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false); - COpenGL3Texture *renderTargetTexture = new COpenGL3Texture(name, size, ETT_2D, format, this); + COpenGL3Texture *renderTargetTexture = new COpenGL3Texture(name, size, msaa > 0 ? ETT_2D_MS : ETT_2D, format, this, msaa); addTexture(renderTargetTexture); renderTargetTexture->drop(); diff --git a/irr/src/OpenGL/Driver.h b/irr/src/OpenGL/Driver.h index 3104b164d..f00ffa8af 100644 --- a/irr/src/OpenGL/Driver.h +++ b/irr/src/OpenGL/Driver.h @@ -74,6 +74,8 @@ public: IRenderTarget *addRenderTarget() override; + void blitRenderTarget(IRenderTarget *from, IRenderTarget *to) override; + //! draws a vertex primitive list virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount, const void *indexList, u32 primitiveCount, @@ -209,6 +211,9 @@ public: virtual ITexture *addRenderTargetTexture(const core::dimension2d &size, const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override; + virtual ITexture *addRenderTargetTextureMs(const core::dimension2d &size, u8 msaa, + const io::path &name, const ECOLOR_FORMAT format = ECF_UNKNOWN) override; + //! Creates a render target texture for a cubemap ITexture *addRenderTargetTextureCubemap(const irr::u32 sideLen, const io::path &name, const ECOLOR_FORMAT format) override; diff --git a/irr/src/OpenGL/ExtensionHandler.h b/irr/src/OpenGL/ExtensionHandler.h index 8f76c0eea..403b828b3 100644 --- a/irr/src/OpenGL/ExtensionHandler.h +++ b/irr/src/OpenGL/ExtensionHandler.h @@ -74,6 +74,8 @@ public: return false; case EVDF_STENCIL_BUFFER: return StencilBuffer; + case EVDF_TEXTURE_MULTISAMPLE: + return TextureMultisampleSupported; default: return false; }; @@ -161,6 +163,7 @@ public: bool AnisotropicFilterSupported = false; bool BlendMinMaxSupported = false; + bool TextureMultisampleSupported = false; }; } diff --git a/irr/src/OpenGL3/Driver.cpp b/irr/src/OpenGL3/Driver.cpp index ac0816445..b397846aa 100644 --- a/irr/src/OpenGL3/Driver.cpp +++ b/irr/src/OpenGL3/Driver.cpp @@ -69,6 +69,7 @@ void COpenGL3Driver::initFeatures() AnisotropicFilterSupported = isVersionAtLeast(4, 6) || queryExtension("GL_ARB_texture_filter_anisotropic") || queryExtension("GL_EXT_texture_filter_anisotropic"); BlendMinMaxSupported = true; + TextureMultisampleSupported = true; // COGLESCoreExtensionHandler::Feature static_assert(MATERIAL_MAX_TEXTURES <= 16, "Only up to 16 textures are guaranteed"); diff --git a/irr/src/OpenGLES2/Driver.cpp b/irr/src/OpenGLES2/Driver.cpp index 430be736d..3c034522c 100644 --- a/irr/src/OpenGLES2/Driver.cpp +++ b/irr/src/OpenGLES2/Driver.cpp @@ -124,6 +124,7 @@ void COpenGLES2Driver::initFeatures() const bool MRTSupported = Version.Major >= 3 || queryExtension("GL_EXT_draw_buffers"); AnisotropicFilterSupported = queryExtension("GL_EXT_texture_filter_anisotropic"); BlendMinMaxSupported = (Version.Major >= 3) || FeatureAvailable[IRR_GL_EXT_blend_minmax]; + TextureMultisampleSupported = isVersionAtLeast(3, 1); const bool TextureLODBiasSupported = queryExtension("GL_EXT_texture_lod_bias"); // COGLESCoreExtensionHandler::Feature diff --git a/src/client/render/pipeline.cpp b/src/client/render/pipeline.cpp index a37fd32fd..d24aa3ce8 100644 --- a/src/client/render/pipeline.cpp +++ b/src/client/render/pipeline.cpp @@ -26,7 +26,7 @@ video::ITexture *TextureBuffer::getTexture(u8 index) } -void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format, bool clear) +void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::string &name, video::ECOLOR_FORMAT format, bool clear, u8 msaa) { assert(index != NO_DEPTH_TEXTURE); @@ -41,9 +41,10 @@ void TextureBuffer::setTexture(u8 index, core::dimension2du size, const std::str definition.name = name; definition.format = format; definition.clear = clear; + definition.msaa = msaa; } -void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format, bool clear) +void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &name, video::ECOLOR_FORMAT format, bool clear, u8 msaa) { assert(index != NO_DEPTH_TEXTURE); @@ -58,6 +59,7 @@ void TextureBuffer::setTexture(u8 index, v2f scale_factor, const std::string &na definition.name = name; definition.format = format; definition.clear = clear; + definition.msaa = msaa; } void TextureBuffer::reset(PipelineContext &context) @@ -125,13 +127,19 @@ bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefini if (definition.valid) { if (definition.clear) { + // We're not able to clear a render target texture + // We're not able to create a normal texture with MSAA + // (could be solved by more refactoring in Irrlicht, but not needed for now) + sanity_check(definition.msaa < 1); + video::IImage *image = m_driver->createImage(definition.format, size); // Cannot use image->fill because it's not implemented for all formats. std::memset(image->getData(), 0, image->getDataSizeFromFormat(definition.format, size.Width, size.Height)); *texture = m_driver->addTexture(definition.name.c_str(), image); image->drop(); - } - else { + } else if (definition.msaa > 0) { + *texture = m_driver->addRenderTargetTextureMs(size, definition.msaa, definition.name.c_str(), definition.format); + } else { *texture = m_driver->addRenderTargetTexture(size, definition.name.c_str(), definition.format); } } @@ -189,6 +197,12 @@ void TextureBufferOutput::activate(PipelineContext &context) RenderTarget::activate(context); } +video::IRenderTarget *TextureBufferOutput::getIrrRenderTarget(PipelineContext &context) +{ + activate(context); // Needed to make sure that render_target is set up. + return render_target; +} + u8 DynamicSource::getTextureCount() { assert(isConfigured()); diff --git a/src/client/render/pipeline.h b/src/client/render/pipeline.h index 9fa47c329..17bed8b7b 100644 --- a/src/client/render/pipeline.h +++ b/src/client/render/pipeline.h @@ -117,7 +117,7 @@ public: * @param name unique name of the texture * @param format color format */ - void setTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format, bool clear = false); + void setTexture(u8 index, core::dimension2du size, const std::string& name, video::ECOLOR_FORMAT format, bool clear = false, u8 msaa = 0); /** * Configure relative-size texture for the specific index @@ -127,7 +127,7 @@ public: * @param name unique name of the texture * @param format color format */ - void setTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format, bool clear = false); + void setTexture(u8 index, v2f scale_factor, const std::string& name, video::ECOLOR_FORMAT format, bool clear = false, u8 msaa = 0); virtual u8 getTextureCount() override { return m_textures.size(); } virtual video::ITexture *getTexture(u8 index) override; @@ -146,6 +146,7 @@ private: core::dimension2du size; std::string name; video::ECOLOR_FORMAT format; + u8 msaa; }; /** @@ -174,6 +175,9 @@ public: TextureBufferOutput(TextureBuffer *buffer, const std::vector &texture_map, u8 depth_stencil); virtual ~TextureBufferOutput() override; void activate(PipelineContext &context) override; + + video::IRenderTarget *getIrrRenderTarget(PipelineContext &context); + private: static const u8 NO_DEPTH_TEXTURE = 255; diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp index 26ce6888e..f6a0cf78e 100644 --- a/src/client/render/secondstage.cpp +++ b/src/client/render/secondstage.cpp @@ -9,6 +9,7 @@ #include "client/shader.h" #include "client/tile.h" #include "settings.h" +#include "mt_opengl.h" PostProcessingStep::PostProcessingStep(u32 _shader_id, const std::vector &_texture_map) : shader_id(_shader_id), texture_map(_texture_map) @@ -102,21 +103,45 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep static const u8 TEXTURE_EXPOSURE_2 = 4; static const u8 TEXTURE_FXAA = 5; static const u8 TEXTURE_VOLUME = 6; + + static const u8 TEXTURE_MSAA_COLOR = 7; + static const u8 TEXTURE_MSAA_DEPTH = 8; + static const u8 TEXTURE_SCALE_DOWN = 10; static const u8 TEXTURE_SCALE_UP = 20; - // Super-sampling is simply rendering into a larger texture. - // Downscaling is done by the final step when rendering to the screen. - const std::string antialiasing = g_settings->get("antialiasing"); const bool enable_bloom = g_settings->getBool("enable_bloom"); + const bool enable_volumetric_light = g_settings->getBool("enable_volumetric_lighting") && enable_bloom; const bool enable_auto_exposure = g_settings->getBool("enable_auto_exposure"); + + const std::string antialiasing = g_settings->get("antialiasing"); + const u16 antialiasing_scale = MYMAX(2, g_settings->getU16("fsaa")); + + // This code only deals with MSAA in combination with post-processing. MSAA without + // post-processing works via a flag at OpenGL context creation instead. + // To make MSAA work with post-processing, we need multisample texture support, + // which has higher OpenGL (ES) version requirements. + // Note: This is not about renderbuffer objects, but about textures, + // since that's what we use and what Irrlicht allows us to use. + + const bool msaa_available = driver->queryFeature(video::EVDF_TEXTURE_MULTISAMPLE); + const bool enable_msaa = antialiasing == "fsaa" && msaa_available; + if (antialiasing == "fsaa" && !msaa_available) + warningstream << "Ignoring configured FSAA. FSAA is not supported in " + << "combination with post-processing by the current video driver." << std::endl; + const bool enable_ssaa = antialiasing == "ssaa"; const bool enable_fxaa = antialiasing == "fxaa"; - const bool enable_volumetric_light = g_settings->getBool("enable_volumetric_lighting") && enable_bloom; + // Super-sampling is simply rendering into a larger texture. + // Downscaling is done by the final step when rendering to the screen. if (enable_ssaa) { - u16 ssaa_scale = MYMAX(2, g_settings->getU16("fsaa")); - scale *= ssaa_scale; + scale *= antialiasing_scale; + } + + if (enable_msaa) { + buffer->setTexture(TEXTURE_MSAA_COLOR, scale, "3d_render_msaa", color_format, false, antialiasing_scale); + buffer->setTexture(TEXTURE_MSAA_DEPTH, scale, "3d_depthmap_msaa", depth_format, false, antialiasing_scale); } buffer->setTexture(TEXTURE_COLOR, scale, "3d_render", color_format); @@ -125,7 +150,14 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep buffer->setTexture(TEXTURE_DEPTH, scale, "3d_depthmap", depth_format); // attach buffer to the previous step - previousStep->setRenderTarget(pipeline->createOwned(buffer, std::vector { TEXTURE_COLOR }, TEXTURE_DEPTH)); + if (enable_msaa) { + TextureBufferOutput *msaa = pipeline->createOwned(buffer, std::vector { TEXTURE_MSAA_COLOR }, TEXTURE_MSAA_DEPTH); + previousStep->setRenderTarget(msaa); + TextureBufferOutput *normal = pipeline->createOwned(buffer, std::vector { TEXTURE_COLOR }, TEXTURE_DEPTH); + pipeline->addStep(msaa, normal); + } else { + previousStep->setRenderTarget(pipeline->createOwned(buffer, std::vector { TEXTURE_COLOR }, TEXTURE_DEPTH)); + } // shared variables u32 shader_id; @@ -234,3 +266,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep return effect; } + +void ResolveMSAAStep::run(PipelineContext &context) +{ + context.device->getVideoDriver()->blitRenderTarget(msaa_fbo->getIrrRenderTarget(context), + target_fbo->getIrrRenderTarget(context)); +} diff --git a/src/client/render/secondstage.h b/src/client/render/secondstage.h index 6b0e42289..1765a5ed8 100644 --- a/src/client/render/secondstage.h +++ b/src/client/render/secondstage.h @@ -44,4 +44,18 @@ private: void configureMaterial(); }; + +class ResolveMSAAStep : public TrivialRenderStep +{ +public: + ResolveMSAAStep(TextureBufferOutput *_msaa_fbo, TextureBufferOutput *_target_fbo) : + msaa_fbo(_msaa_fbo), target_fbo(_target_fbo) {}; + void run(PipelineContext &context) override; + +private: + TextureBufferOutput *msaa_fbo; + TextureBufferOutput *target_fbo; +}; + + RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep, v2f scale, Client *client); diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 4e45bd4cd..c3cb49eed 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -179,7 +179,10 @@ RenderingEngine::RenderingEngine(MyEventReceiver *receiver) // bpp, fsaa, vsync bool vsync = g_settings->getBool("vsync"); - bool enable_fsaa = g_settings->get("antialiasing") == "fsaa"; + // Don't enable MSAA in OpenGL context creation if post-processing is enabled, + // the post-processing pipeline handles it. + bool enable_fsaa = g_settings->get("antialiasing") == "fsaa" && + !g_settings->getBool("enable_post_processing"); u16 fsaa = enable_fsaa ? MYMAX(2, g_settings->getU16("fsaa")) : 0; // Determine driver From f493e73aeb65a0f4f4ad8a93ef4f4dbf197d606d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 18 Nov 2024 23:58:42 +0100 Subject: [PATCH 04/68] Fix changing secure settings from mainmenu forgotten in ea4ae55e24d2c27524490cb8e0d3c34aa46e3da3 closes #15454 --- src/script/lua_api/l_settings.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp index f3f1a57bf..c4395e390 100644 --- a/src/script/lua_api/l_settings.cpp +++ b/src/script/lua_api/l_settings.cpp @@ -12,11 +12,12 @@ #include "log.h" -/* This protects the following from being set: - * 'secure.*' settings - * some security-relevant settings +/* + * This protects the following from being set: + * - 'secure.*' settings + * - some security-relevant settings * (better solution pending) - * some mapgen settings + * - some mapgen settings * (not security-criticial, just to avoid messing up user configs) */ #define CHECK_SETTING_SECURITY(L, name) \ @@ -27,14 +28,16 @@ static inline int checkSettingSecurity(lua_State* L, const std::string &name) { +#if CHECK_CLIENT_BUILD() + // Main menu is allowed everything + if (ModApiBase::getGuiEngine(L) != nullptr) + return 0; +#endif + if (ScriptApiSecurity::isSecure(L) && name.compare(0, 7, "secure.") == 0) throw LuaError("Attempted to set secure setting."); - bool is_mainmenu = false; -#if CHECK_CLIENT_BUILD() - is_mainmenu = ModApiBase::getGuiEngine(L) != nullptr; -#endif - if (!is_mainmenu && (name == "mg_name" || name == "mg_flags")) { + if (name == "mg_name" || name == "mg_flags") { errorstream << "Tried to set global setting " << name << ", ignoring. " "minetest.set_mapgen_setting() should be used instead." << std::endl; infostream << script_get_backtrace(L) << std::endl; @@ -45,11 +48,9 @@ static inline int checkSettingSecurity(lua_State* L, const std::string &name) "main_menu_script", "shader_path", "texture_path", "screenshot_path", "serverlist_file", "serverlist_url", "map-dir", "contentdb_url", }; - if (!is_mainmenu) { - for (const char *name2 : disallowed) { - if (name == name2) - throw LuaError("Attempted to set disallowed setting."); - } + for (const char *name2 : disallowed) { + if (name == name2) + throw LuaError("Attempted to set disallowed setting."); } return 0; From 138052adfc2074fc6e42d0f8856d6cdca37dbd77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:30:17 +0100 Subject: [PATCH 05/68] Add particle blend mode "clip" (#15444) This lets modders avoid alpha blending rendering bugs as well as potential (future) performance issues. The appropriate blend modes are also used for node dig particles. --------- Co-authored-by: sfan5 --- builtin/game/features.lua | 1 + doc/breakages.md | 1 + doc/lua_api.md | 10 +++ games/devtest/mods/testtools/particles.lua | 28 ++++++-- .../textures/testtools_particle_clip.png | Bin 0 -> 179 bytes src/client/particles.cpp | 62 +++++++++++------- src/client/particles.h | 5 +- src/network/networkprotocol.cpp | 5 +- src/particles.cpp | 8 ++- src/particles.h | 3 +- src/script/lua_api/l_particleparams.h | 3 +- 11 files changed, 93 insertions(+), 33 deletions(-) create mode 100644 games/devtest/mods/testtools/textures/testtools_particle_clip.png diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 8d7753839..ea80a09fb 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -44,6 +44,7 @@ core.features = { bulk_lbms = true, abm_without_neighbors = true, biome_weights = true, + particle_blend_clip = true, } function core.has_feature(arg) diff --git a/doc/breakages.md b/doc/breakages.md index 0aded3d92..2d2994385 100644 --- a/doc/breakages.md +++ b/doc/breakages.md @@ -21,3 +21,4 @@ This list is largely advisory and items may be reevaluated once the time comes. * merge `sound` and `sounds` table in itemdef * remove `DIR_DELIM` from Lua * stop reading initial properties from bare entity def +* change particle default blend mode to `clip` diff --git a/doc/lua_api.md b/doc/lua_api.md index 48ef798c4..e82b075fb 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5657,6 +5657,8 @@ Utilities abm_without_neighbors = true, -- biomes have a weight parameter (5.11.0) biome_weights = true, + -- Particles can specify a "clip" blend mode (5.11.0) + particle_blend_clip = true, } ``` @@ -11483,6 +11485,14 @@ texture = { -- (default) blends transparent pixels with those they are drawn atop -- according to the alpha channel of the source texture. useful for -- e.g. material objects like rocks, dirt, smoke, or node chunks + -- note: there will be rendering bugs when particles interact with + -- translucent nodes. particles are also not transparency-sorted + -- relative to each other. + blend = "clip", + -- pixels are either fully opaque or fully transparent, + -- depending on whether alpha is greater than or less than 50% + -- (just like `use_texture_alpha = "clip"` for nodes). + -- you should prefer this if you don't need semi-transparency, as it's faster. blend = "add", -- adds the value of pixels to those underneath them, modulo the sources -- alpha channel. useful for e.g. bright light effects like sparks or fire diff --git a/games/devtest/mods/testtools/particles.lua b/games/devtest/mods/testtools/particles.lua index 17f4f5c80..da6b6cab9 100644 --- a/games/devtest/mods/testtools/particles.lua +++ b/games/devtest/mods/testtools/particles.lua @@ -1,14 +1,27 @@ +local function spawn_clip_test_particle(pos) + core.add_particle({ + pos = pos, + size = 5, + expirationtime = 10, + texture = { + name = "testtools_particle_clip.png", + blend = "clip", + }, + }) +end + core.register_tool("testtools:particle_spawner", { - description = "Particle Spawner".."\n".. + description = table.concat({ + "Particle Spawner", "Punch: Spawn random test particle", + "Place: Spawn clip test particle", + }, "\n"), inventory_image = "testtools_particle_spawner.png", groups = { testtool = 1, disable_repair = 1 }, on_use = function(itemstack, user, pointed_thing) local pos = core.get_pointed_thing_position(pointed_thing, true) if pos == nil then - if user then - pos = user:get_pos() - end + pos = assert(user):get_pos() end pos = vector.add(pos, {x=0, y=0.5, z=0}) local tex, anim @@ -32,5 +45,12 @@ core.register_tool("testtools:particle_spawner", { glow = math.random(0, 5), }) end, + on_place = function(itemstack, user, pointed_thing) + local pos = assert(core.get_pointed_thing_position(pointed_thing, true)) + spawn_clip_test_particle(pos) + end, + on_secondary_use = function(_, user) + spawn_clip_test_particle(assert(user):get_pos()) + end, }) diff --git a/games/devtest/mods/testtools/textures/testtools_particle_clip.png b/games/devtest/mods/testtools/textures/testtools_particle_clip.png new file mode 100644 index 0000000000000000000000000000000000000000..5fb9ad09a6c8596e60004b045007fdecb5039c08 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|ay?xfLoEE0 zQx*sqG=166BmR%y*uCay^f6}r*8kQW`&mynHhegJ`v3i|*GBt9=JAHJ3yIgyKE+|q zutabf<9tR1-vsa0^#Agv4QC7-`bxW)1k(g=UbM(!P|_<%Q=1_au*~V4&xSn>LLF0{ aWH5Bde-YerZTTjk#SEUVelF{r5}E*N6h21) literal 0 HcmV?d00001 diff --git a/src/client/particles.cpp b/src/client/particles.cpp index 6ba7fa701..c6891f8ef 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -22,6 +22,8 @@ #include "settings.h" #include "profiler.h" +using BlendMode = ParticleParamTypes::BlendMode; + ClientParticleTexture::ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *tsrc) { tex = p; @@ -603,8 +605,11 @@ video::S3DVertex *ParticleBuffer::getVertices(u16 index) void ParticleBuffer::OnRegisterSceneNode() { - if (IsVisible) - SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT); + if (IsVisible) { + SceneManager->registerNodeForRendering(this, + m_mesh_buffer->getMaterial().MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF + ? scene::ESNRP_SOLID : scene::ESNRP_TRANSPARENT_EFFECT); + } scene::ISceneNode::OnRegisterSceneNode(); } @@ -906,6 +911,9 @@ void ParticleManager::addNodeParticle(IGameDef *gamedef, if (!getNodeParticleParams(n, f, p, &ref, texpos, texsize, &color)) return; + p.texture.blendmode = f.alpha == ALPHAMODE_BLEND + ? BlendMode::alpha : BlendMode::clip; + p.expirationtime = myrand_range(0, 100) / 100.0f; // Physics @@ -940,40 +948,47 @@ void ParticleManager::reserveParticleSpace(size_t max_estimate) m_particles.reserve(m_particles.size() + max_estimate); } -video::SMaterial ParticleManager::getMaterialForParticle(const ClientParticleTexRef &texture) +static void setBlendMode(video::SMaterial &material, BlendMode blendmode) { - // translate blend modes to GL blend functions video::E_BLEND_FACTOR bfsrc, bfdst; video::E_BLEND_OPERATION blendop; - const auto blendmode = texture.tex ? texture.tex->blendmode : - ParticleParamTypes::BlendMode::alpha; - switch (blendmode) { - case ParticleParamTypes::BlendMode::add: + case BlendMode::add: bfsrc = video::EBF_SRC_ALPHA; bfdst = video::EBF_DST_ALPHA; blendop = video::EBO_ADD; break; - case ParticleParamTypes::BlendMode::sub: + case BlendMode::sub: bfsrc = video::EBF_SRC_ALPHA; bfdst = video::EBF_DST_ALPHA; blendop = video::EBO_REVSUBTRACT; break; - case ParticleParamTypes::BlendMode::screen: + case BlendMode::screen: bfsrc = video::EBF_ONE; bfdst = video::EBF_ONE_MINUS_SRC_COLOR; blendop = video::EBO_ADD; break; - default: // includes ParticleParamTypes::BlendMode::alpha + default: // includes BlendMode::alpha bfsrc = video::EBF_SRC_ALPHA; bfdst = video::EBF_ONE_MINUS_SRC_ALPHA; blendop = video::EBO_ADD; break; } + material.MaterialTypeParam = video::pack_textureBlendFunc( + bfsrc, bfdst, + video::EMFN_MODULATE_1X, + video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); + material.BlendOperation = blendop; +} + +video::SMaterial ParticleManager::getMaterialForParticle(const Particle *particle) +{ + const ClientParticleTexRef &texture = particle->getTextureRef(); + video::SMaterial material; // Texture @@ -984,17 +999,18 @@ video::SMaterial ParticleManager::getMaterialForParticle(const ClientParticleTex tex.MagFilter = video::ETMAGF_NEAREST; }); - // We don't have working transparency sorting. Disable Z-Write for - // correct results for clipped-alpha at least. - material.ZWriteEnable = video::EZW_OFF; - - // enable alpha blending and set blend mode - material.MaterialType = video::EMT_ONETEXTURE_BLEND; - material.MaterialTypeParam = video::pack_textureBlendFunc( - bfsrc, bfdst, - video::EMFN_MODULATE_1X, - video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); - material.BlendOperation = blendop; + const auto blendmode = particle->getBlendMode(); + if (blendmode == BlendMode::clip) { + material.ZWriteEnable = video::EZW_ON; + material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + material.MaterialTypeParam = 0.5f; + } else { + // We don't have working transparency sorting. Disable Z-Write for + // correct results for clipped-alpha at least. + material.ZWriteEnable = video::EZW_OFF; + material.MaterialType = video::EMT_ONETEXTURE_BLEND; + setBlendMode(material, blendmode); + } material.setTexture(0, texture.ref); return material; @@ -1004,7 +1020,7 @@ bool ParticleManager::addParticle(std::unique_ptr toadd) { MutexAutoLock lock(m_particle_list_lock); - auto material = getMaterialForParticle(toadd->getTextureRef()); + auto material = getMaterialForParticle(toadd.get()); ParticleBuffer *found = nullptr; // simple shortcut when multiple particles of the same type get added diff --git a/src/client/particles.h b/src/client/particles.h index 6a8d09a47..619877744 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -77,6 +77,9 @@ public: const ClientParticleTexRef &getTextureRef() const { return m_texture; } + ParticleParamTypes::BlendMode getBlendMode() const + { return m_texture.tex ? m_texture.tex->blendmode : m_p.texture.blendmode; } + ParticleBuffer *getBuffer() const { return m_buffer; } bool attachToBuffer(ParticleBuffer *buffer); @@ -231,7 +234,7 @@ protected: ParticleParameters &p, video::ITexture **texture, v2f &texpos, v2f &texsize, video::SColor *color, u8 tilenum = 0); - static video::SMaterial getMaterialForParticle(const ClientParticleTexRef &texture); + static video::SMaterial getMaterialForParticle(const Particle *texture); bool addParticle(std::unique_ptr toadd); diff --git a/src/network/networkprotocol.cpp b/src/network/networkprotocol.cpp index 40f8acef1..f77e85d3c 100644 --- a/src/network/networkprotocol.cpp +++ b/src/network/networkprotocol.cpp @@ -59,9 +59,12 @@ Rename TOSERVER_RESPAWN to TOSERVER_RESPAWN_LEGACY Support float animation frame numbers in TOCLIENT_LOCAL_PLAYER_ANIMATIONS [scheduled bump for 5.10.0] + PROTOCOL VERSION 47 + Add particle blend mode "clip" + [scheduled bump for 5.11.0] */ -const u16 LATEST_PROTOCOL_VERSION = 46; +const u16 LATEST_PROTOCOL_VERSION = 47; // See also formspec [Version History] in doc/lua_api.md const u16 FORMSPEC_API_VERSION = 8; diff --git a/src/particles.cpp b/src/particles.cpp index 2c470d2d3..660ea8d2f 100644 --- a/src/particles.cpp +++ b/src/particles.cpp @@ -190,8 +190,10 @@ void ServerParticleTexture::serialize(std::ostream &os, u16 protocol_ver, FlagT flags = 0; if (animated) flags |= FlagT(ParticleTextureFlags::animated); - if (blendmode != BlendMode::alpha) - flags |= FlagT(blendmode) << 1; + // Default to `blend = "alpha"` for older clients that don't support `blend = "clip"` + auto sent_blendmode = (protocol_ver < 47 && blendmode == BlendMode::clip) + ? BlendMode::alpha : blendmode; + flags |= FlagT(sent_blendmode) << 1; serializeParameterValue(os, flags); alpha.serialize(os); @@ -215,6 +217,8 @@ void ServerParticleTexture::deSerialize(std::istream &is, u16 protocol_ver, animated = !!(flags & FlagT(ParticleTextureFlags::animated)); blendmode = BlendMode((flags & FlagT(ParticleTextureFlags::blend)) >> 1); + if (blendmode >= BlendMode::BlendMode_END) + throw SerializationError("invalid blend mode"); alpha.deSerialize(is); scale.deSerialize(is); diff --git a/src/particles.h b/src/particles.h index 52fe26661..00d04dbeb 100644 --- a/src/particles.h +++ b/src/particles.h @@ -233,7 +233,8 @@ namespace ParticleParamTypes } enum class AttractorKind : u8 { none, point, line, plane }; - enum class BlendMode : u8 { alpha, add, sub, screen }; + // Note: Allows at most 8 enum members (due to how this is serialized) + enum class BlendMode : u8 { alpha, add, sub, screen, clip, BlendMode_END }; // these are consistently-named convenience aliases to make code more readable without `using ParticleParamTypes` declarations using v3fRange = RangedParameter; diff --git a/src/script/lua_api/l_particleparams.h b/src/script/lua_api/l_particleparams.h index 7303081db..0e508148f 100644 --- a/src/script/lua_api/l_particleparams.h +++ b/src/script/lua_api/l_particleparams.h @@ -118,13 +118,14 @@ namespace LuaParticleParams {(int)BlendMode::add, "add"}, {(int)BlendMode::sub, "sub"}, {(int)BlendMode::screen, "screen"}, + {(int)BlendMode::clip, "clip"}, {0, nullptr}, }; luaL_checktype(L, -1, LUA_TSTRING); int v = (int)BlendMode::alpha; if (!string_to_enum(opts, v, lua_tostring(L, -1))) { - throw LuaError("blend mode must be one of ('alpha', 'add', 'sub', 'screen')"); + throw LuaError("blend mode must be one of ('alpha', 'clip', 'add', 'sub', 'screen')"); } ret = (BlendMode)v; } From 15e8f9e6a06c3ad87bae3ffaf4ec58f551612ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Tue, 19 Nov 2024 13:37:05 +0100 Subject: [PATCH 06/68] Fix rendering regression with TGA type 1 files with BGRA8 color (#15402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TGA uses BGR(A)8, stored in memory in that order. Irrlicht typically expects 0xAARRGGBB, which depends on endianness. (This means that on little endian, no [B][G][R][A] -> 0xAARRGGBB conversion needs to be done, but Irrlicht was swapping the bytes.) This makes both conversion functions consistently convert from [B][G][R]([A]) to 0xAARRGGBB (SColor), documents them properly and moves them to CImageLoaderTGA.cpp so no poor soul shall be fooled by them in the near future. --------- Co-authored-by: Ælla Chiana Moskopp --- irr/src/CColorConverter.cpp | 38 +++++++++---------------------------- irr/src/CColorConverter.h | 2 -- irr/src/CImage.cpp | 2 ++ irr/src/CImageLoaderTGA.cpp | 23 ++++++++++++++++++++-- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/irr/src/CColorConverter.cpp b/irr/src/CColorConverter.cpp index 7749ececf..d1bd302c7 100644 --- a/irr/src/CColorConverter.cpp +++ b/irr/src/CColorConverter.cpp @@ -7,6 +7,15 @@ #include "os.h" #include "irrString.h" +// Warning: The naming of Irrlicht color formats +// is not consistent regarding actual component order in memory. +// E.g. in CImage, ECF_R8G8B8 is handled per-byte and stored as [R][G][B] in memory +// while ECF_A8R8G8B8 is handled as an u32 0xAARRGGBB so [B][G][R][A] (little endian) in memory. +// The conversions suffer from the same inconsistencies, e.g. +// convert_R8G8B8toA8R8G8B8 converts [R][G][B] into 0xFFRRGGBB = [B][G][R][FF] (little endian); +// convert_A1R5G5B5toR8G8B8 converts 0bARRRRRGGGGGBBBBB into [R][G][B]. +// This also means many conversions may be broken on big endian. + namespace irr { namespace video @@ -393,19 +402,6 @@ void CColorConverter::convert_R8G8B8toA1R5G5B5(const void *sP, s32 sN, void *dP) } } -void CColorConverter::convert_B8G8R8toA8R8G8B8(const void *sP, s32 sN, void *dP) -{ - u8 *sB = (u8 *)sP; - u32 *dB = (u32 *)dP; - - for (s32 x = 0; x < sN; ++x) { - *dB = 0xff000000 | (sB[2] << 16) | (sB[1] << 8) | sB[0]; - - sB += 3; - ++dB; - } -} - void CColorConverter::convert_A8R8G8B8toR8G8B8A8(const void *sP, s32 sN, void *dP) { const u32 *sB = (const u32 *)sP; @@ -428,22 +424,6 @@ void CColorConverter::convert_A8R8G8B8toA8B8G8R8(const void *sP, s32 sN, void *d } } -void CColorConverter::convert_B8G8R8A8toA8R8G8B8(const void *sP, s32 sN, void *dP) -{ - u8 *sB = (u8 *)sP; - u8 *dB = (u8 *)dP; - - for (s32 x = 0; x < sN; ++x) { - dB[0] = sB[3]; - dB[1] = sB[2]; - dB[2] = sB[1]; - dB[3] = sB[0]; - - sB += 4; - dB += 4; - } -} - void CColorConverter::convert_R8G8B8toB8G8R8(const void *sP, s32 sN, void *dP) { u8 *sB = (u8 *)sP; diff --git a/irr/src/CColorConverter.h b/irr/src/CColorConverter.h index af7b13726..8db7af097 100644 --- a/irr/src/CColorConverter.h +++ b/irr/src/CColorConverter.h @@ -66,8 +66,6 @@ public: static void convert_R8G8B8toA1R5G5B5(const void *sP, s32 sN, void *dP); static void convert_R8G8B8toB8G8R8(const void *sP, s32 sN, void *dP); static void convert_R8G8B8toR5G6B5(const void *sP, s32 sN, void *dP); - static void convert_B8G8R8toA8R8G8B8(const void *sP, s32 sN, void *dP); - static void convert_B8G8R8A8toA8R8G8B8(const void *sP, s32 sN, void *dP); static void convert_A8R8G8B8toR8G8B8A8(const void *sP, s32 sN, void *dP); static void convert_A8R8G8B8toA8B8G8R8(const void *sP, s32 sN, void *dP); diff --git a/irr/src/CImage.cpp b/irr/src/CImage.cpp index 209590174..0c6d705f3 100644 --- a/irr/src/CImage.cpp +++ b/irr/src/CImage.cpp @@ -95,6 +95,8 @@ SColor CImage::getPixel(u32 x, u32 y) const case ECF_A8R8G8B8: return ((u32 *)Data)[y * Size.Width + x]; case ECF_R8G8B8: { + // FIXME this interprets the memory as [R][G][B], whereas SColor is stored as + // 0xAARRGGBB, meaning it is lies in memory as [B][G][R][A] on a little endian machine. u8 *p = Data + (y * 3) * Size.Width + (x * 3); return SColor(255, p[0], p[1], p[2]); } diff --git a/irr/src/CImageLoaderTGA.cpp b/irr/src/CImageLoaderTGA.cpp index 274b15543..75b0b5679 100644 --- a/irr/src/CImageLoaderTGA.cpp +++ b/irr/src/CImageLoaderTGA.cpp @@ -93,6 +93,25 @@ bool CImageLoaderTGA::isALoadableFileFormat(io::IReadFile *file) const return (!strcmp(footer.Signature, "TRUEVISION-XFILE.")); // very old tgas are refused. } +/// Converts *byte order* BGR to *endianness order* ARGB (SColor "=" u32) +static void convert_BGR8_to_SColor(const u8 *src, u32 n, u32 *dst) +{ + for (u32 i = 0; i < n; ++i) { + const u8 *bgr = &src[3 * i]; + dst[i] = 0xff000000 | (bgr[2] << 16) | (bgr[1] << 8) | bgr[0]; + } +} + +/// Converts *byte order* BGRA to *endianness order* ARGB (SColor "=" u32) +/// Note: This just copies from src to dst on little endian. +static void convert_BGRA8_to_SColor(const u8 *src, u32 n, u32 *dst) +{ + for (u32 i = 0; i < n; ++i) { + const u8 *bgra = &src[4 * i]; + dst[i] = (bgra[3] << 24) | (bgra[2] << 16) | (bgra[1] << 8) | bgra[0]; + } +} + //! creates a surface from the file IImage *CImageLoaderTGA::loadImage(io::IReadFile *file) const { @@ -139,10 +158,10 @@ IImage *CImageLoaderTGA::loadImage(io::IReadFile *file) const CColorConverter::convert_A1R5G5B5toA8R8G8B8(colorMap, header.ColorMapLength, palette); break; case 24: - CColorConverter::convert_B8G8R8toA8R8G8B8(colorMap, header.ColorMapLength, palette); + convert_BGR8_to_SColor(colorMap, header.ColorMapLength, palette); break; case 32: - CColorConverter::convert_B8G8R8A8toA8R8G8B8(colorMap, header.ColorMapLength, palette); + convert_BGRA8_to_SColor(colorMap, header.ColorMapLength, palette); break; } delete[] colorMap; From b63e988bd60089b5f0595442b974886c17d5e1c9 Mon Sep 17 00:00:00 2001 From: Christian Betancourt Dias <128722328+PichiBeta@users.noreply.github.com> Date: Tue, 19 Nov 2024 07:38:19 -0500 Subject: [PATCH 07/68] Fix naming conventions for `CMatrix4::set(Inverse)RotationRadians()` (#15204) --- irr/include/matrix4.h | 68 +++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/irr/include/matrix4.h b/irr/include/matrix4.h index 8fce0157a..36f98bb70 100644 --- a/irr/include/matrix4.h +++ b/irr/include/matrix4.h @@ -856,27 +856,27 @@ inline CMatrix4 &CMatrix4::setInverseRotationDegrees(const vector3d &ro template inline CMatrix4 &CMatrix4::setRotationRadians(const vector3d &rotation) { - const f64 cr = cos(rotation.X); - const f64 sr = sin(rotation.X); - const f64 cp = cos(rotation.Y); - const f64 sp = sin(rotation.Y); - const f64 cy = cos(rotation.Z); - const f64 sy = sin(rotation.Z); + const f64 cPitch = cos(rotation.X); + const f64 sPitch = sin(rotation.X); + const f64 cYaw = cos(rotation.Y); + const f64 sYaw = sin(rotation.Y); + const f64 cRoll = cos(rotation.Z); + const f64 sRoll = sin(rotation.Z); - M[0] = (T)(cp * cy); - M[1] = (T)(cp * sy); - M[2] = (T)(-sp); + M[0] = (T)(cYaw * cRoll); + M[1] = (T)(cYaw * sRoll); + M[2] = (T)(-sYaw); - const f64 srsp = sr * sp; - const f64 crsp = cr * sp; + const f64 sPitch_sYaw = sPitch * sYaw; + const f64 cPitch_sYaw = cPitch * sYaw; - M[4] = (T)(srsp * cy - cr * sy); - M[5] = (T)(srsp * sy + cr * cy); - M[6] = (T)(sr * cp); + M[4] = (T)(sPitch_sYaw * cRoll - cPitch * sRoll); + M[5] = (T)(sPitch_sYaw * sRoll + cPitch * cRoll); + M[6] = (T)(sPitch * cYaw); - M[8] = (T)(crsp * cy + sr * sy); - M[9] = (T)(crsp * sy - sr * cy); - M[10] = (T)(cr * cp); + M[8] = (T)(cPitch_sYaw * cRoll + sPitch * sRoll); + M[9] = (T)(cPitch_sYaw * sRoll - sPitch * cRoll); + M[10] = (T)(cPitch * cYaw); #if defined(USE_MATRIX_TEST) definitelyIdentityMatrix = false; #endif @@ -960,27 +960,27 @@ inline core::vector3d CMatrix4::getRotationDegrees() const template inline CMatrix4 &CMatrix4::setInverseRotationRadians(const vector3d &rotation) { - f64 cr = cos(rotation.X); - f64 sr = sin(rotation.X); - f64 cp = cos(rotation.Y); - f64 sp = sin(rotation.Y); - f64 cy = cos(rotation.Z); - f64 sy = sin(rotation.Z); + f64 cPitch = cos(rotation.X); + f64 sPitch = sin(rotation.X); + f64 cYaw = cos(rotation.Y); + f64 sYaw = sin(rotation.Y); + f64 cRoll = cos(rotation.Z); + f64 sRoll = sin(rotation.Z); - M[0] = (T)(cp * cy); - M[4] = (T)(cp * sy); - M[8] = (T)(-sp); + M[0] = (T)(cYaw * cRoll); + M[4] = (T)(cYaw * sRoll); + M[8] = (T)(-sYaw); - f64 srsp = sr * sp; - f64 crsp = cr * sp; + f64 sPitch_sYaw = sPitch * sYaw; + f64 cPitch_sYaw = cPitch * sYaw; - M[1] = (T)(srsp * cy - cr * sy); - M[5] = (T)(srsp * sy + cr * cy); - M[9] = (T)(sr * cp); + M[1] = (T)(sPitch_sYaw * cRoll - cPitch * sRoll); + M[5] = (T)(sPitch_sYaw * sRoll + cPitch * cRoll); + M[9] = (T)(sPitch * cYaw); - M[2] = (T)(crsp * cy + sr * sy); - M[6] = (T)(crsp * sy - sr * cy); - M[10] = (T)(cr * cp); + M[2] = (T)(cPitch_sYaw * cRoll + sPitch * sRoll); + M[6] = (T)(cPitch_sYaw * sRoll - sPitch * cRoll); + M[10] = (T)(cPitch * cYaw); #if defined(USE_MATRIX_TEST) definitelyIdentityMatrix = false; #endif From 946b3a422272d680d5d9d9a208bb0ffedce0291f Mon Sep 17 00:00:00 2001 From: Richard Try Date: Wed, 20 Nov 2024 21:36:35 +0300 Subject: [PATCH 08/68] Typo in lua_api.md (#15440) Duplicate text removed. --- doc/lua_api.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index e82b075fb..b4ef79088 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -8892,7 +8892,7 @@ For `core.get_perlin_map()`, the actual seed used is the noiseparams seed plus the world seed, to create world-specific noise. Format of `size` is `{x=dimx, y=dimy, z=dimz}`. The `z` component is omitted -for 2D noise, and it must be must be larger than 1 for 3D noise (otherwise +for 2D noise, and it must be larger than 1 for 3D noise (otherwise `nil` is returned). For each of the functions with an optional `buffer` parameter: If `buffer` is From b6eaf7b5a4d8c2d818a0fc744d2c6db64e204efe Mon Sep 17 00:00:00 2001 From: siliconsniffer <97843108+siliconsniffer@users.noreply.github.com> Date: Thu, 21 Nov 2024 20:34:40 +0100 Subject: [PATCH 09/68] Main menu: Player list for public servers (#15425) --- LICENSE.txt | 1 + builtin/mainmenu/dlg_clients_list.lua | 49 +++++++++++++++++++++ builtin/mainmenu/init.lua | 1 + builtin/mainmenu/tab_online.lua | 42 +++++++++++++++--- textures/base/pack/server_view_clients.png | Bin 0 -> 218 bytes 5 files changed, 87 insertions(+), 6 deletions(-) create mode 100644 builtin/mainmenu/dlg_clients_list.lua create mode 100644 textures/base/pack/server_view_clients.png diff --git a/LICENSE.txt b/LICENSE.txt index e562a0b4c..fb055807b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -63,6 +63,7 @@ Zughy: textures/base/pack/settings_btn.png textures/base/pack/settings_info.png textures/base/pack/settings_reset.png + textures/base/pack/server_view_clients.png appgurueu: textures/base/pack/server_incompatible.png diff --git a/builtin/mainmenu/dlg_clients_list.lua b/builtin/mainmenu/dlg_clients_list.lua new file mode 100644 index 000000000..2ea021c5e --- /dev/null +++ b/builtin/mainmenu/dlg_clients_list.lua @@ -0,0 +1,49 @@ +-- Luanti +-- Copyright (C) 2024 siliconsniffer +-- SPDX-License-Identifier: LGPL-2.1-or-later + + +local function clients_list_formspec(dialogdata) + local TOUCH_GUI = core.settings:get_bool("touch_gui") + local clients_list = dialogdata.server.clients_list + local servername = dialogdata.server.name + + local function fmt_formspec_list(clients_list) + local escaped = {} + for i, str in ipairs(clients_list) do + escaped[i] = core.formspec_escape(str) + end + return table.concat(escaped, ",") + end + + local formspec = { + "formspec_version[8]", + "size[6,9.5]", + TOUCH_GUI and "padding[0.01,0.01]" or "", + "hypertext[0,0;6,1.5;;", + fgettext("This is the list of clients connected to\n$1", + "" .. core.hypertext_escape(servername) .. "") .. "]", + "textlist[0.5,1.5;5,6.8;;" .. fmt_formspec_list(clients_list) .. "]", + "button[1.5,8.5;3,0.8;quit;OK]" + } + return table.concat(formspec, "") +end + + +local function clients_list_buttonhandler(this, fields) + if fields.quit then + this:delete() + return true + end + return false +end + + +function create_clientslist_dialog(server) + local retval = dialog_create("dlg_clients_list", + clients_list_formspec, + clients_list_buttonhandler, + nil) + retval.data.server = server + return retval +end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 41519b2ee..4e1c201cd 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -55,6 +55,7 @@ dofile(menupath .. DIR_DELIM .. "dlg_register.lua") dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua") dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua") +dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua") local tabs = { content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"), diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index a73b863ce..558e2aff2 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -108,16 +108,38 @@ local function get_formspec(tabview, name, tabdata) end if tabdata.selected then - if gamedata.fav then - retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]" - retval = retval .. "style[btn_delete_favorite;padding=6]" - retval = retval .. "image_button[5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. - "server_favorite_delete.png") .. ";btn_delete_favorite;]" - end if gamedata.serverdescription then retval = retval .. "textarea[0.25,1.85;5.25,2.7;;;" .. core.formspec_escape(gamedata.serverdescription) .. "]" end + + local server = tabdata.lookup[tabdata.selected] + + local clients_list = server and server.clients_list + local can_view_clients_list = clients_list and #clients_list > 0 + if can_view_clients_list then + table.sort(clients_list, function(a, b) + return a:lower() < b:lower() + end) + local max_clients = 5 + if #clients_list > max_clients then + retval = retval .. "tooltip[btn_view_clients;" .. + fgettext("Clients:\n$1", table.concat(clients_list, "\n", 1, max_clients)) .. "\n..." .. "]" + else + retval = retval .. "tooltip[btn_view_clients;" .. + fgettext("Clients:\n$1", table.concat(clients_list, "\n")) .. "]" + end + retval = retval .. "style[btn_view_clients;padding=6]" + retval = retval .. "image_button[5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. + "server_view_clients.png") .. ";btn_view_clients;]" + end + + if gamedata.fav then + retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]" + retval = retval .. "style[btn_delete_favorite;padding=6]" + retval = retval .. "image_button[" .. (can_view_clients_list and "4.5" or "5") .. ",1.3;0.5,0.5;" .. + core.formspec_escape(defaulttexturedir .. "server_favorite_delete.png") .. ";btn_delete_favorite;]" + end end retval = retval .. "container_end[]" @@ -315,6 +337,14 @@ local function main_button_handler(tabview, fields, name, tabdata) return true end + if fields.btn_view_clients then + local dlg = create_clientslist_dialog(tabdata.lookup[tabdata.selected]) + dlg:set_parent(tabview) + tabview:hide() + dlg:show() + return true + end + if fields.btn_mp_clear then tabdata.search_for = "" menudata.search_result = nil diff --git a/textures/base/pack/server_view_clients.png b/textures/base/pack/server_view_clients.png new file mode 100644 index 0000000000000000000000000000000000000000..87b569f93129e5add2d820f3c47d987a99ac2db1 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3q4&NLo9leQw}iklzjP@EWE$q zsgUw632w8qfxr2aFP}f~Amg|ET>%lkxU&Hp&Ri5bs_OGYBq@0@^Bgt-?gFWXH=;H7 z4jCl;{N`;TnmqY%hZwW$-v2$R!IDb0UkyzDzdv=X!;6PGp>2V+wC$IVyBMa=-zeE6 z#AfjPF%wXgVUXt0iyY;AyWZYl5@egeEUc`baJhj$;0>#Y_@DQu)NV2`giQ0$m?ix@ Q5a?J2Pgg&ebxsLQ0K_FxY5)KL literal 0 HcmV?d00001 From 4faa16fe0ddf98659bfa3f852cb1d9b8ff95eb93 Mon Sep 17 00:00:00 2001 From: luk3yx Date: Sat, 23 Nov 2024 05:56:35 +1300 Subject: [PATCH 10/68] Clarify behaviour of core.hypertext_escape (#15464) --- doc/lua_api.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/lua_api.md b/doc/lua_api.md index b4ef79088..709bf2984 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -6699,6 +6699,9 @@ Formspec * `core.hypertext_escape(string)`: returns a string * escapes the characters "\", "<", and ">" to show text in a hypertext element. * not safe for use with tag attributes. + * this function does not do formspec escaping, you will likely need to do + `core.formspec_escape(core.hypertext_escape(string))` if the hypertext is + not already being formspec escaped. * `core.explode_table_event(string)`: returns a table * returns e.g. `{type="CHG", row=1, column=2}` * `type` is one of: From 6a1d22b2c516abbb9ce9670dedff47451317706f Mon Sep 17 00:00:00 2001 From: grorp Date: Sun, 24 Nov 2024 11:33:39 +0100 Subject: [PATCH 11/68] Implement an editor to customize the touchscreen controls (#14933) - The editor is accessible via the pause menu and the settings menu. - Buttons can be moved via drag & drop. - Buttons can be added/removed. The grid menu added by #14918 is used to show all buttons not included in the layout. - Custom layouts are responsive and adapt to changed screen size / DPI / hud_scaling. - The layout is saved as JSON in the "touch_layout" setting. --- builtin/mainmenu/settings/dlg_settings.lua | 18 + doc/menu_lua_api.md | 1 + src/client/game.cpp | 21 +- src/client/hud.cpp | 1 + src/defaultsettings.cpp | 1 + src/gui/CMakeLists.txt | 2 + src/gui/mainmenumanager.h | 7 + src/gui/touchcontrols.cpp | 203 ++++------- src/gui/touchcontrols.h | 81 ++--- src/gui/touchscreeneditor.cpp | 404 +++++++++++++++++++++ src/gui/touchscreeneditor.h | 81 +++++ src/gui/touchscreenlayout.cpp | 345 ++++++++++++++++++ src/gui/touchscreenlayout.h | 104 ++++++ src/irr_gui_ptr.h | 28 ++ src/script/lua_api/l_mainmenu.cpp | 18 + src/script/lua_api/l_mainmenu.h | 2 + textures/base/pack/down.png | Bin 1618 -> 3647 bytes textures/base/pack/jump_btn.png | Bin 1633 -> 3625 bytes 18 files changed, 1122 insertions(+), 195 deletions(-) create mode 100644 src/gui/touchscreeneditor.cpp create mode 100644 src/gui/touchscreeneditor.h create mode 100644 src/gui/touchscreenlayout.cpp create mode 100644 src/gui/touchscreenlayout.h create mode 100644 src/irr_gui_ptr.h diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index ef28398b1..f1861879a 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -123,6 +123,22 @@ local function load() end, } + local touchscreen_layout = { + query_text = "Touchscreen layout", + requires = { + touchscreen = true, + }, + get_formspec = function(self, avail_w) + local btn_w = math.min(avail_w, 6) + return ("button[0,0;%f,0.8;btn_touch_layout;%s]"):format(btn_w, fgettext("Touchscreen layout")), 0.8 + end, + on_submit = function(self, fields) + if fields.btn_touch_layout then + core.show_touchscreen_layout() + end + end, + } + add_page({ id = "accessibility", title = fgettext_ne("Accessibility"), @@ -151,6 +167,8 @@ local function load() load_settingtypes() table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys) + -- insert after "touch_controls" + table.insert(page_by_id.controls_touchscreen.content, 2, touchscreen_layout) do local content = page_by_id.graphics_and_audio_effects.content local idx = table.indexof(content, "enable_dynamic_shadows") diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index ae4afd998..b17e4b1c8 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -223,6 +223,7 @@ GUI * `core.set_clouds()` * `core.set_topleft_text(text)` * `core.show_keys_menu()` +* `core.show_touchscreen_layout()` * `core.show_path_select_dialog(formname, caption, is_file_select)` * shows a path select dialog * `formname` is base name of dialog response returned in fields diff --git a/src/client/game.cpp b/src/client/game.cpp index f9f175d1b..dc13095d1 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -26,6 +26,7 @@ #include "client/event_manager.h" #include "fontengine.h" #include "gui/touchcontrols.h" +#include "gui/touchscreeneditor.h" #include "itemdef.h" #include "log.h" #include "log_internal.h" @@ -144,6 +145,11 @@ struct LocalFormspecHandler : public TextDest return; } + if (fields.find("btn_touchscreen_layout") != fields.end()) { + g_gamecallback->touchscreenLayout(); + return; + } + if (fields.find("btn_exit_menu") != fields.end()) { g_gamecallback->disconnect(); return; @@ -1844,6 +1850,12 @@ inline bool Game::handleCallbacks() g_gamecallback->keyconfig_requested = false; } + if (g_gamecallback->touchscreenlayout_requested) { + (new GUITouchscreenLayout(guienv, guiroot, -1, + &g_menumgr, texture_src))->drop(); + g_gamecallback->touchscreenlayout_requested = false; + } + if (!g_gamecallback->show_open_url_dialog.empty()) { (void)make_irr(guienv, guiroot, -1, &g_menumgr, texture_src, g_gamecallback->show_open_url_dialog); @@ -4510,9 +4522,14 @@ void Game::showPauseMenu() << strgettext("Sound Volume") << "]"; } #endif - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" - << strgettext("Controls") << "]"; #endif + if (g_touchcontrols) { + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_touchscreen_layout;" + << strgettext("Touchscreen Layout") << "]"; + } else { + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" + << strgettext("Controls") << "]"; + } os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;" << strgettext("Exit to Menu") << "]"; os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;" diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 6929d4c9e..e49327d30 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -24,6 +24,7 @@ #include "wieldmesh.h" #include "client/renderingengine.h" #include "client/minimap.h" +#include "client/texturesource.h" #include "gui/touchcontrols.h" #include "util/enriched_string.h" #include "irrlicht_changes/CGUITTFont.h" diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 2142615e2..345e4a07b 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -542,6 +542,7 @@ void set_default_settings() settings->setDefault("keymap_sneak", "KEY_SHIFT"); #endif + settings->setDefault("touch_layout", ""); settings->setDefault("touchscreen_sensitivity", "0.2"); settings->setDefault("touchscreen_threshold", "20"); settings->setDefault("touch_long_tap_delay", "400"); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 04a03609d..876acab74 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -25,5 +25,7 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/modalMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp ${CMAKE_CURRENT_SOURCE_DIR}/touchcontrols.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/touchscreenlayout.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/touchscreeneditor.cpp PARENT_SCOPE ) diff --git a/src/gui/mainmenumanager.h b/src/gui/mainmenumanager.h index d5f43796d..9d10e3960 100644 --- a/src/gui/mainmenumanager.h +++ b/src/gui/mainmenumanager.h @@ -21,6 +21,7 @@ public: virtual void changeVolume() = 0; virtual void showOpenURLDialog(const std::string &url) = 0; virtual void signalKeyConfigChange() = 0; + virtual void touchscreenLayout() = 0; }; extern gui::IGUIEnvironment *guienv; @@ -133,6 +134,11 @@ public: keyconfig_changed = true; } + void touchscreenLayout() override + { + touchscreenlayout_requested = true; + } + void showOpenURLDialog(const std::string &url) override { show_open_url_dialog = url; @@ -142,6 +148,7 @@ public: bool changepassword_requested = false; bool changevolume_requested = false; bool keyconfig_requested = false; + bool touchscreenlayout_requested = false; bool shutdown_requested = false; bool keyconfig_changed = false; std::string show_open_url_dialog = ""; diff --git a/src/gui/touchcontrols.cpp b/src/gui/touchcontrols.cpp index d7e46787b..17352264d 100644 --- a/src/gui/touchcontrols.cpp +++ b/src/gui/touchcontrols.cpp @@ -5,6 +5,7 @@ // Copyright (C) 2024 grorp, Gregor Parzefall #include "touchcontrols.h" +#include "touchscreenlayout.h" #include "gettime.h" #include "irr_v2d.h" @@ -14,8 +15,10 @@ #include "client/guiscalingfilter.h" #include "client/keycode.h" #include "client/renderingengine.h" +#include "client/texturesource.h" #include "util/numeric.h" -#include "gettext.h" +#include "irr_gui_ptr.h" +#include "IGUIImage.h" #include "IGUIStaticText.h" #include "IGUIFont.h" #include @@ -26,59 +29,6 @@ TouchControls *g_touchcontrols; -static const char *button_image_names[] = { - "jump_btn.png", - "down.png", - "zoom.png", - "aux1_btn.png", - "overflow_btn.png", - - "fly_btn.png", - "noclip_btn.png", - "fast_btn.png", - "debug_btn.png", - "camera_btn.png", - "rangeview_btn.png", - "minimap_btn.png", - "", - - "chat_btn.png", - "inventory_btn.png", - "drop_btn.png", - "exit_btn.png", - - "joystick_off.png", - "joystick_bg.png", - "joystick_center.png", -}; - -// compare with GUIKeyChangeMenu::init_keys -static const char *button_titles[] = { - N_("Jump"), - N_("Sneak"), - N_("Zoom"), - N_("Aux1"), - N_("Overflow menu"), - - N_("Toggle fly"), - N_("Toggle noclip"), - N_("Toggle fast"), - N_("Toggle debug"), - N_("Change camera"), - N_("Range select"), - N_("Toggle minimap"), - N_("Toggle chat log"), - - N_("Chat"), - N_("Inventory"), - N_("Drop"), - N_("Exit"), - - N_("Joystick"), - N_("Joystick"), - N_("Joystick"), -}; - static void load_button_texture(IGUIImage *gui_button, const std::string &path, const recti &button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) { @@ -268,10 +218,22 @@ TouchControls::TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc) m_long_tap_delay = g_settings->getU16("touch_long_tap_delay"); m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick"); m_joystick_triggers_aux1 = g_settings->getBool("virtual_joystick_triggers_aux1"); + m_screensize = m_device->getVideoDriver()->getScreenSize(); - m_button_size = MYMIN(m_screensize.Y / 4.5f, - RenderingEngine::getDisplayDensity() * 65.0f * - g_settings->getFloat("hud_scaling")); + m_button_size = ButtonLayout::getButtonSize(m_screensize); + applyLayout(ButtonLayout::loadFromSettings()); +} + +void TouchControls::applyLayout(const ButtonLayout &layout) +{ + m_layout = layout; + + m_buttons.clear(); + m_overflow_btn = nullptr; + m_overflow_bg = nullptr; + m_overflow_buttons.clear(); + m_overflow_button_titles.clear(); + m_overflow_button_rects.clear(); // Initialize joystick display "button". // Joystick is placed on the bottom left of screen. @@ -298,47 +260,21 @@ TouchControls::TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc) m_joystick_btn_center = grab_gui_element(makeButtonDirect(joystick_center_id, recti(0, 0, m_button_size, m_button_size), false)); - // init jump button - addButton(m_buttons, jump_id, button_image_names[jump_id], - recti(m_screensize.X - 1.75f * m_button_size, - m_screensize.Y - m_button_size, - m_screensize.X - 0.25f * m_button_size, - m_screensize.Y)); + for (const auto &[id, meta] : m_layout.layout) { + if (!mayAddButton(id)) + continue; - // init sneak button - addButton(m_buttons, sneak_id, button_image_names[sneak_id], - recti(m_screensize.X - 3.25f * m_button_size, - m_screensize.Y - m_button_size, - m_screensize.X - 1.75f * m_button_size, - m_screensize.Y)); - - // init zoom button - addButton(m_buttons, zoom_id, button_image_names[zoom_id], - recti(m_screensize.X - 1.25f * m_button_size, - m_screensize.Y - 4 * m_button_size, - m_screensize.X - 0.25f * m_button_size, - m_screensize.Y - 3 * m_button_size)); - - // init aux1 button - if (!m_joystick_triggers_aux1) - addButton(m_buttons, aux1_id, button_image_names[aux1_id], - recti(m_screensize.X - 1.25f * m_button_size, - m_screensize.Y - 2.5f * m_button_size, - m_screensize.X - 0.25f * m_button_size, - m_screensize.Y - 1.5f * m_button_size)); - - // init overflow button - m_overflow_btn = grab_gui_element(makeButtonDirect(overflow_id, - recti(m_screensize.X - 1.25f * m_button_size, - m_screensize.Y - 5.5f * m_button_size, - m_screensize.X - 0.25f * m_button_size, - m_screensize.Y - 4.5f * m_button_size), true)); - - const static touch_gui_button_id overflow_buttons[] { - chat_id, inventory_id, drop_id, exit_id, - fly_id, noclip_id, fast_id, debug_id, camera_id, range_id, minimap_id, - toggle_chat_id, - }; + recti rect = m_layout.getRect(id, m_screensize, m_button_size, m_texturesource); + if (id == toggle_chat_id) + // Chat is shown by default, so chat_hide_btn.png is shown first. + addToggleButton(m_buttons, id, "chat_hide_btn.png", + "chat_show_btn.png", rect, true); + else if (id == overflow_id) + m_overflow_btn = grab_gui_element( + makeButtonDirect(id, rect, true)); + else + addButton(m_buttons, id, button_image_names[id], rect, true); + } IGUIStaticText *background = m_guienv->addStaticText(L"", recti(v2s32(0, 0), dimension2du(m_screensize))); @@ -346,32 +282,17 @@ TouchControls::TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc) background->setVisible(false); m_overflow_bg = grab_gui_element(background); - s32 cols = 4; - s32 rows = 3; - f32 screen_aspect = (f32)m_screensize.X / (f32)m_screensize.Y; - while ((s32)ARRLEN(overflow_buttons) > cols * rows) { - f32 aspect = (f32)cols / (f32)rows; - if (aspect > screen_aspect) - rows++; - else - cols++; - } - - v2s32 size(m_button_size, m_button_size); - v2s32 spacing(m_screensize.X / (cols + 1), m_screensize.Y / (rows + 1)); - v2s32 pos(spacing); - - for (auto id : overflow_buttons) { - if (id_to_keycode(id) == KEY_UNKNOWN) - continue; - - recti rect(pos - size / 2, dimension2du(size.X, size.Y)); - if (rect.LowerRightCorner.X > (s32)m_screensize.X) { - pos.X = spacing.X; - pos.Y += spacing.Y; - rect = recti(pos - size / 2, dimension2du(size.X, size.Y)); - } + auto overflow_buttons = m_layout.getMissingButtons(); + overflow_buttons.erase(std::remove_if( + overflow_buttons.begin(), overflow_buttons.end(), + [&](touch_gui_button_id id) { + // There's no sense in adding the overflow button to the overflow + // menu (also, it's impossible since it doesn't have a keycode). + return !mayAddButton(id) || id == overflow_id; + }), overflow_buttons.end()); + layout_button_grid(m_screensize, m_texturesource, overflow_buttons, + [&] (touch_gui_button_id id, v2s32 pos, recti rect) { if (id == toggle_chat_id) // Chat is shown by default, so chat_hide_btn.png is shown first. addToggleButton(m_overflow_buttons, id, "chat_hide_btn.png", @@ -379,27 +300,23 @@ TouchControls::TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc) else addButton(m_overflow_buttons, id, button_image_names[id], rect, false); - std::wstring str = wstrgettext(button_titles[id]); - IGUIStaticText *text = m_guienv->addStaticText(str.c_str(), recti()); - IGUIFont *font = text->getActiveFont(); - dimension2du dim = font->getDimension(str.c_str()); - dim = dimension2du(dim.Width * 1.25f, dim.Height * 1.25f); // avoid clipping - text->setRelativePosition(recti(pos.X - dim.Width / 2, pos.Y + size.Y / 2, - pos.X + dim.Width / 2, pos.Y + size.Y / 2 + dim.Height)); - text->setTextAlignment(EGUIA_CENTER, EGUIA_UPPERLEFT); + IGUIStaticText *text = m_guienv->addStaticText(L"", recti()); + make_button_grid_title(text, id, pos, rect); text->setVisible(false); m_overflow_button_titles.push_back(grab_gui_element(text)); rect.addInternalPoint(text->getRelativePosition().UpperLeftCorner); rect.addInternalPoint(text->getRelativePosition().LowerRightCorner); m_overflow_button_rects.push_back(rect); - - pos.X += spacing.X; - } + }); m_status_text = grab_gui_element( m_guienv->addStaticText(L"", recti(), false, false)); m_status_text->setVisible(false); + + // applyLayout can be called at any time, also e.g. while the overflow menu + // is open, so this is necessary to restore correct visibility. + updateVisibility(); } TouchControls::~TouchControls() @@ -407,6 +324,17 @@ TouchControls::~TouchControls() releaseAll(); } +bool TouchControls::mayAddButton(touch_gui_button_id id) +{ + if (!ButtonLayout::isButtonAllowed(id)) + return false; + if (id == aux1_id && m_joystick_triggers_aux1) + return false; + if (id != overflow_id && id_to_keycode(id) == KEY_UNKNOWN) + return false; + return true; +} + void TouchControls::addButton(std::vector &buttons, touch_gui_button_id id, const std::string &image, const recti &rect, bool visible) { @@ -701,6 +629,15 @@ void TouchControls::applyJoystickStatus() void TouchControls::step(float dtime) { + v2u32 screensize = m_device->getVideoDriver()->getScreenSize(); + s32 button_size = ButtonLayout::getButtonSize(screensize); + + if (m_screensize != screensize || m_button_size != button_size) { + m_screensize = screensize; + m_button_size = button_size; + applyLayout(m_layout); + } + // simulate keyboard repeats buttons_step(m_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); buttons_step(m_overflow_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); diff --git a/src/gui/touchcontrols.h b/src/gui/touchcontrols.h index cb3e8e8bf..701baba42 100644 --- a/src/gui/touchcontrols.h +++ b/src/gui/touchcontrols.h @@ -5,11 +5,8 @@ #pragma once -#include "IGUIStaticText.h" #include "irrlichttypes.h" -#include -#include -#include +#include "IEventReceiver.h" #include #include @@ -17,41 +14,29 @@ #include #include "itemdef.h" -#include "client/game.h" +#include "touchscreenlayout.h" #include "util/basic_macros.h" -#include "client/texturesource.h" namespace irr { class IrrlichtDevice; + namespace gui + { + class IGUIEnvironment; + class IGUIImage; + class IGUIStaticText; + } + namespace video + { + class IVideoDriver; + } } +class ISimpleTextureSource; using namespace irr::core; using namespace irr::gui; -// We cannot use irr_ptr for Irrlicht GUI elements we own. -// Option 1: Pass IGUIElement* returned by IGUIEnvironment::add* into irr_ptr -// constructor. -// -> We steal the reference owned by IGUIEnvironment and drop it later, -// causing the IGUIElement to be deleted while IGUIEnvironment still -// references it. -// Option 2: Pass IGUIElement* returned by IGUIEnvironment::add* into irr_ptr::grab. -// -> We add another reference and drop it later, but since IGUIEnvironment -// still references the IGUIElement, it is never deleted. -// To make IGUIEnvironment drop its reference to the IGUIElement, we have to call -// IGUIElement::remove, so that's what we'll do. -template -std::shared_ptr grab_gui_element(T *element) -{ - static_assert(std::is_base_of_v, - "grab_gui_element only works for IGUIElement"); - return std::shared_ptr(element, [](T *e) { - e->remove(); - }); -} - - enum class TapState { None, @@ -59,36 +44,6 @@ enum class TapState LongTap, }; -enum touch_gui_button_id -{ - jump_id = 0, - sneak_id, - zoom_id, - aux1_id, - overflow_id, - - // usually in the "settings bar" - fly_id, - noclip_id, - fast_id, - debug_id, - camera_id, - range_id, - minimap_id, - toggle_chat_id, - - // usually in the "rare controls bar" - chat_id, - inventory_id, - drop_id, - exit_id, - - // the joystick - joystick_off_id, - joystick_bg_id, - joystick_center_id, -}; - #define BUTTON_REPEAT_DELAY 0.5f #define BUTTON_REPEAT_INTERVAL 0.333f @@ -168,6 +123,9 @@ public: bool isStatusTextOverriden() { return m_overflow_open; } IGUIStaticText *getStatusText() { return m_status_text.get(); } + ButtonLayout getLayout() { return m_layout; } + void applyLayout(const ButtonLayout &layout); + private: IrrlichtDevice *m_device = nullptr; IGUIEnvironment *m_guienv = nullptr; @@ -233,13 +191,14 @@ private: void releaseAll(); // initialize a button + bool mayAddButton(touch_gui_button_id id); void addButton(std::vector &buttons, touch_gui_button_id id, const std::string &image, - const recti &rect, bool visible=true); + const recti &rect, bool visible); void addToggleButton(std::vector &buttons, touch_gui_button_id id, const std::string &image_1, const std::string &image_2, - const recti &rect, bool visible=true); + const recti &rect, bool visible); IGUIImage *makeButtonDirect(touch_gui_button_id id, const recti &rect, bool visible); @@ -268,6 +227,8 @@ private: bool m_place_pressed = false; u64 m_place_pressed_until = 0; + + ButtonLayout m_layout; }; extern TouchControls *g_touchcontrols; diff --git a/src/gui/touchscreeneditor.cpp b/src/gui/touchscreeneditor.cpp new file mode 100644 index 000000000..b30ce5a20 --- /dev/null +++ b/src/gui/touchscreeneditor.cpp @@ -0,0 +1,404 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 grorp, Gregor Parzefall + +#include "touchscreeneditor.h" +#include "touchcontrols.h" +#include "touchscreenlayout.h" + +#include "client/renderingengine.h" +#include "gettext.h" +#include "irr_gui_ptr.h" +#include "settings.h" + +#include "IGUIButton.h" +#include "IGUIFont.h" +#include "IGUIImage.h" +#include "IGUIStaticText.h" + +GUITouchscreenLayout::GUITouchscreenLayout(gui::IGUIEnvironment* env, + gui::IGUIElement* parent, s32 id, + IMenuManager *menumgr, ISimpleTextureSource *tsrc +): + GUIModalMenu(env, parent, id, menumgr), + m_tsrc(tsrc) +{ + if (g_touchcontrols) + m_layout = g_touchcontrols->getLayout(); + else + m_layout = ButtonLayout::loadFromSettings(); + + m_gui_help_text = grab_gui_element(Environment->addStaticText( + L"", core::recti(), false, false, this, -1)); + m_gui_help_text->setTextAlignment(EGUIA_CENTER, EGUIA_CENTER); + + m_gui_add_btn = grab_gui_element(Environment->addButton( + core::recti(), this, -1, wstrgettext("Add button").c_str())); + m_gui_reset_btn = grab_gui_element(Environment->addButton( + core::recti(), this, -1, wstrgettext("Reset").c_str())); + m_gui_done_btn = grab_gui_element(Environment->addButton( + core::recti(), this, -1, wstrgettext("Done").c_str())); + + m_gui_remove_btn = grab_gui_element(Environment->addButton( + core::recti(), this, -1, wstrgettext("Remove").c_str())); +} + +GUITouchscreenLayout::~GUITouchscreenLayout() +{ + ButtonLayout::clearTextureCache(); +} + +void GUITouchscreenLayout::regenerateGui(v2u32 screensize) +{ + DesiredRect = core::recti(0, 0, screensize.X, screensize.Y); + recalculateAbsolutePosition(false); + + s32 button_size = ButtonLayout::getButtonSize(screensize); + if (m_last_screensize != screensize || m_button_size != button_size) { + m_last_screensize = screensize; + m_button_size = button_size; + // Prevent interpolation when the layout scales. + clearGUIImages(); + } + + // Discard invalid selection. May happen when... + // 1. a button is removed. + // 2. adding a button fails and it disappears from the layout again. + if (m_selected_btn != touch_gui_button_id_END && + m_layout.layout.count(m_selected_btn) == 0) + m_selected_btn = touch_gui_button_id_END; + + if (m_mode == Mode::Add) + regenerateGUIImagesAddMode(screensize); + else + regenerateGUIImagesRegular(screensize); + regenerateMenu(screensize); +} + +void GUITouchscreenLayout::clearGUIImages() +{ + m_gui_images.clear(); + m_gui_images_target_pos.clear(); + + m_add_layout.layout.clear(); + m_add_button_titles.clear(); +} + +void GUITouchscreenLayout::regenerateGUIImagesRegular(v2u32 screensize) +{ + assert(m_mode != Mode::Add); + + auto old_gui_images = m_gui_images; + clearGUIImages(); + + for (const auto &[btn, meta] : m_layout.layout) { + core::recti rect = m_layout.getRect(btn, screensize, m_button_size, m_tsrc); + std::shared_ptr img; + + if (old_gui_images.count(btn) > 0) { + img = old_gui_images.at(btn); + // Update size, keep position. Position is interpolated in interpolateGUIImages. + img->setRelativePosition(core::recti( + img->getRelativePosition().UpperLeftCorner, rect.getSize())); + } else { + img = grab_gui_element(Environment->addImage(rect, this, -1)); + img->setImage(ButtonLayout::getTexture(btn, m_tsrc)); + img->setScaleImage(true); + } + + m_gui_images[btn] = img; + m_gui_images_target_pos[btn] = rect.UpperLeftCorner; + } +} + +void GUITouchscreenLayout::regenerateGUIImagesAddMode(v2u32 screensize) +{ + assert(m_mode == Mode::Add); + + clearGUIImages(); + + auto missing_buttons = m_layout.getMissingButtons(); + + layout_button_grid(screensize, m_tsrc, missing_buttons, + [&] (touch_gui_button_id btn, v2s32 pos, core::recti rect) { + auto img = grab_gui_element(Environment->addImage(rect, this, -1)); + img->setImage(ButtonLayout::getTexture(btn, m_tsrc)); + img->setScaleImage(true); + m_gui_images[btn] = img; + + ButtonMeta meta; + meta.setPos(pos, screensize, m_button_size); + m_add_layout.layout[btn] = meta; + + IGUIStaticText *text = Environment->addStaticText(L"", core::recti(), + false, false,this, -1); + make_button_grid_title(text, btn, pos, rect); + m_add_button_titles.push_back(grab_gui_element(text)); + }); +} + +void GUITouchscreenLayout::interpolateGUIImages() +{ + if (m_mode == Mode::Add) + return; + + for (auto &[btn, gui_image] : m_gui_images) { + bool interpolate = m_mode != Mode::Dragging || m_selected_btn != btn; + + v2s32 cur_pos_int = gui_image->getRelativePosition().UpperLeftCorner; + v2s32 tgt_pos_int = m_gui_images_target_pos.at(btn); + v2f cur_pos(cur_pos_int.X, cur_pos_int.Y); + v2f tgt_pos(tgt_pos_int.X, tgt_pos_int.Y); + + if (interpolate && cur_pos.getDistanceFrom(tgt_pos) > 2.0f) { + v2f pos = cur_pos.getInterpolated(tgt_pos, 0.5f); + gui_image->setRelativePosition(v2s32(core::round32(pos.X), core::round32(pos.Y))); + } else { + gui_image->setRelativePosition(tgt_pos_int); + } + } +} + +static void layout_menu_row(v2u32 screensize, + const std::vector> &row, + const std::vector> &full_row, bool bottom) +{ + s32 spacing = RenderingEngine::getDisplayDensity() * 4.0f; + + s32 btn_w = 0; + s32 btn_h = 0; + for (const auto &btn : full_row) { + IGUIFont *font = btn->getActiveFont(); + core::dimension2du dim = font->getDimension(btn->getText()); + btn_w = std::max(btn_w, (s32)(dim.Width * 1.5f)); + btn_h = std::max(btn_h, (s32)(dim.Height * 2.5f)); + } + + const s32 row_width = ((btn_w + spacing) * row.size()) - spacing; + s32 x = screensize.X / 2 - row_width / 2; + const s32 y = bottom ? screensize.Y - spacing - btn_h : spacing; + + for (const auto &btn : row) { + btn->setRelativePosition(core::recti( + v2s32(x, y), core::dimension2du(btn_w, btn_h))); + x += btn_w + spacing; + } +} + +void GUITouchscreenLayout::regenerateMenu(v2u32 screensize) +{ + bool have_selection = m_selected_btn != touch_gui_button_id_END; + + if (m_mode == Mode::Add) + m_gui_help_text->setText(wstrgettext("Start dragging a button to add. Tap outside to cancel.").c_str()); + else if (!have_selection) + m_gui_help_text->setText(wstrgettext("Tap a button to select it. Drag a button to move it.").c_str()); + else + m_gui_help_text->setText(wstrgettext("Tap outside to deselect.").c_str()); + + IGUIFont *font = m_gui_help_text->getActiveFont(); + core::dimension2du dim = font->getDimension(m_gui_help_text->getText()); + s32 height = dim.Height * 2.5f; + s32 pos_y = (m_mode == Mode::Add || have_selection) ? 0 : screensize.Y - height; + m_gui_help_text->setRelativePosition(core::recti( + v2s32(0, pos_y), + core::dimension2du(screensize.X, height))); + + bool normal_buttons_visible = m_mode != Mode::Add && !have_selection; + bool add_visible = normal_buttons_visible && !m_layout.getMissingButtons().empty(); + + m_gui_add_btn->setVisible(add_visible); + m_gui_reset_btn->setVisible(normal_buttons_visible); + m_gui_done_btn->setVisible(normal_buttons_visible); + + if (normal_buttons_visible) { + std::vector row1{m_gui_add_btn, m_gui_reset_btn, m_gui_done_btn}; + if (add_visible) { + layout_menu_row(screensize, row1, row1, false); + } else { + std::vector row1_reduced{m_gui_reset_btn, m_gui_done_btn}; + layout_menu_row(screensize, row1_reduced, row1, false); + } + } + + bool remove_visible = m_mode != Mode::Add && have_selection && + !ButtonLayout::isButtonRequired(m_selected_btn); + + m_gui_remove_btn->setVisible(remove_visible); + + if (remove_visible) { + std::vector row2{m_gui_remove_btn}; + layout_menu_row(screensize, row2, row2, true); + } +} + +void GUITouchscreenLayout::drawMenu() +{ + video::IVideoDriver *driver = Environment->getVideoDriver(); + + video::SColor bgcolor(140, 0, 0, 0); + video::SColor selection_color(255, 128, 128, 128); + video::SColor error_color(255, 255, 0, 0); + + driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect); + + // Done here instead of in OnPostRender to avoid drag&drop lagging behind + // input by one frame. + // Must be done before drawing selection rectangle. + interpolateGUIImages(); + + bool draw_selection = m_gui_images.count(m_selected_btn) > 0; + if (draw_selection) + driver->draw2DRectangle(selection_color, + m_gui_images.at(m_selected_btn)->getAbsolutePosition(), + &AbsoluteClippingRect); + + if (m_mode == Mode::Dragging) { + for (const auto &rect : m_error_rects) + driver->draw2DRectangle(error_color, rect, &AbsoluteClippingRect); + } + + IGUIElement::draw(); +} + +void GUITouchscreenLayout::updateDragState(v2u32 screensize, v2s32 mouse_movement) +{ + assert(m_mode == Mode::Dragging); + + core::recti rect = m_layout.getRect(m_selected_btn, screensize, m_button_size, m_tsrc); + rect += mouse_movement; + rect.constrainTo(core::recti(v2s32(0, 0), core::dimension2du(screensize))); + + ButtonMeta &meta = m_layout.layout.at(m_selected_btn); + meta.setPos(rect.getCenter(), screensize, m_button_size); + + rect = m_layout.getRect(m_selected_btn, screensize, m_button_size, m_tsrc); + + m_error_rects.clear(); + for (const auto &[other_btn, other_meta] : m_layout.layout) { + if (other_btn == m_selected_btn) + continue; + core::recti other_rect = m_layout.getRect(other_btn, screensize, m_button_size, m_tsrc); + if (other_rect.isRectCollided(rect)) + m_error_rects.push_back(other_rect); + } + if (m_error_rects.empty()) + m_last_good_layout = m_layout; +} + +bool GUITouchscreenLayout::OnEvent(const SEvent& event) +{ + if (event.EventType == EET_KEY_INPUT_EVENT) { + if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) { + quitMenu(); + return true; + } + } + + core::dimension2du screensize = Environment->getVideoDriver()->getScreenSize(); + + if (event.EventType == EET_MOUSE_INPUT_EVENT) { + v2s32 mouse_pos = v2s32(event.MouseInput.X, event.MouseInput.Y); + + switch (event.MouseInput.Event) { + case EMIE_LMOUSE_PRESSED_DOWN: { + m_mouse_down = true; + m_last_mouse_pos = mouse_pos; + + IGUIElement *el = Environment->getRootGUIElement()->getElementFromPoint(mouse_pos); + // Clicking on nothing deselects. + m_selected_btn = touch_gui_button_id_END; + for (const auto &[btn, gui_image] : m_gui_images) { + if (el == gui_image.get()) { + m_selected_btn = btn; + break; + } + } + + if (m_mode == Mode::Add) { + if (m_selected_btn != touch_gui_button_id_END) { + m_mode = Mode::Dragging; + m_last_good_layout = m_layout; + m_layout.layout[m_selected_btn] = m_add_layout.layout.at(m_selected_btn); + updateDragState(screensize, v2s32(0, 0)); + } else { + // Clicking on nothing quits add mode without adding a button. + m_mode = Mode::Default; + } + } + + regenerateGui(screensize); + return true; + } + case EMIE_MOUSE_MOVED: { + if (m_mouse_down && m_selected_btn != touch_gui_button_id_END) { + if (m_mode != Mode::Dragging) { + m_mode = Mode::Dragging; + m_last_good_layout = m_layout; + } + updateDragState(screensize, mouse_pos - m_last_mouse_pos); + + regenerateGui(screensize); + } + + m_last_mouse_pos = mouse_pos; + return true; + } + case EMIE_LMOUSE_LEFT_UP: { + m_mouse_down = false; + + if (m_mode == Mode::Dragging) { + m_mode = Mode::Default; + if (!m_error_rects.empty()) + m_layout = m_last_good_layout; + + regenerateGui(screensize); + } + + return true; + } + default: + break; + } + } + + if (event.EventType == EET_GUI_EVENT) { + switch (event.GUIEvent.EventType) { + case EGET_BUTTON_CLICKED: { + if (event.GUIEvent.Caller == m_gui_add_btn.get()) { + m_mode = Mode::Add; + regenerateGui(screensize); + return true; + } + + if (event.GUIEvent.Caller == m_gui_reset_btn.get()) { + m_layout = ButtonLayout::predefined; + regenerateGui(screensize); + return true; + } + + if (event.GUIEvent.Caller == m_gui_done_btn.get()) { + if (g_touchcontrols) + g_touchcontrols->applyLayout(m_layout); + std::ostringstream oss; + m_layout.serializeJson(oss); + g_settings->set("touch_layout", oss.str()); + quitMenu(); + return true; + } + + if (event.GUIEvent.Caller == m_gui_remove_btn.get()) { + m_layout.layout.erase(m_selected_btn); + regenerateGui(screensize); + return true; + } + + break; + } + default: + break; + } + } + + return Parent ? Parent->OnEvent(event) : false; +} diff --git a/src/gui/touchscreeneditor.h b/src/gui/touchscreeneditor.h new file mode 100644 index 000000000..dc06fb224 --- /dev/null +++ b/src/gui/touchscreeneditor.h @@ -0,0 +1,81 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 grorp, Gregor Parzefall + +#pragma once + +#include "touchscreenlayout.h" +#include "modalMenu.h" + +#include +#include + +class ISimpleTextureSource; +namespace irr::gui +{ + class IGUIImage; +} + +class GUITouchscreenLayout : public GUIModalMenu +{ +public: + GUITouchscreenLayout(gui::IGUIEnvironment* env, + gui::IGUIElement* parent, s32 id, + IMenuManager *menumgr, ISimpleTextureSource *tsrc); + ~GUITouchscreenLayout(); + + void regenerateGui(v2u32 screensize); + void drawMenu(); + bool OnEvent(const SEvent& event); + +protected: + std::wstring getLabelByID(s32 id) { return L""; } + std::string getNameByID(s32 id) { return ""; } + +private: + ISimpleTextureSource *m_tsrc; + + ButtonLayout m_layout; + v2u32 m_last_screensize; + s32 m_button_size; + + enum class Mode { + Default, + Dragging, + Add, + }; + Mode m_mode = Mode::Default; + + std::unordered_map> m_gui_images; + // unused if m_mode == Mode::Add + std::unordered_map m_gui_images_target_pos; + void clearGUIImages(); + void regenerateGUIImagesRegular(v2u32 screensize); + void regenerateGUIImagesAddMode(v2u32 screensize); + void interpolateGUIImages(); + + // interaction state + bool m_mouse_down = false; + v2s32 m_last_mouse_pos; + touch_gui_button_id m_selected_btn = touch_gui_button_id_END; + + // dragging + ButtonLayout m_last_good_layout; + std::vector m_error_rects; + void updateDragState(v2u32 screensize, v2s32 mouse_movement); + + // add mode + ButtonLayout m_add_layout; + std::vector> m_add_button_titles; + + // Menu GUI elements + std::shared_ptr m_gui_help_text; + + std::shared_ptr m_gui_add_btn; + std::shared_ptr m_gui_reset_btn; + std::shared_ptr m_gui_done_btn; + + std::shared_ptr m_gui_remove_btn; + + void regenerateMenu(v2u32 screensize); +}; diff --git a/src/gui/touchscreenlayout.cpp b/src/gui/touchscreenlayout.cpp new file mode 100644 index 000000000..0a88f5dd5 --- /dev/null +++ b/src/gui/touchscreenlayout.cpp @@ -0,0 +1,345 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 grorp, Gregor Parzefall + +#include "touchscreenlayout.h" +#include "client/renderingengine.h" +#include "client/texturesource.h" +#include "convert_json.h" +#include "gettext.h" +#include "settings.h" +#include + +#include "IGUIFont.h" +#include "IGUIStaticText.h" + +const char *button_names[] = { + "jump", + "sneak", + "zoom", + "aux1", + "overflow", + + "chat", + "inventory", + "drop", + "exit", + + "fly", + "fast", + "noclip", + "debug", + "camera", + "range", + "minimap", + "toggle_chat", + + "joystick_off", + "joystick_bg", + "joystick_center", +}; + +// compare with GUIKeyChangeMenu::init_keys +const char *button_titles[] = { + N_("Jump"), + N_("Sneak"), + N_("Zoom"), + N_("Aux1"), + N_("Overflow menu"), + + N_("Chat"), + N_("Inventory"), + N_("Drop"), + N_("Exit"), + + N_("Toggle fly"), + N_("Toggle fast"), + N_("Toggle noclip"), + N_("Toggle debug"), + N_("Change camera"), + N_("Range select"), + N_("Toggle minimap"), + N_("Toggle chat log"), + + N_("Joystick"), + N_("Joystick"), + N_("Joystick"), +}; + +const char *button_image_names[] = { + "jump_btn.png", + "down.png", + "zoom.png", + "aux1_btn.png", + "overflow_btn.png", + + "chat_btn.png", + "inventory_btn.png", + "drop_btn.png", + "exit_btn.png", + + "fly_btn.png", + "fast_btn.png", + "noclip_btn.png", + "debug_btn.png", + "camera_btn.png", + "rangeview_btn.png", + "minimap_btn.png", + // toggle button: switches between "chat_hide_btn.png" and "chat_show_btn.png" + "chat_hide_btn.png", + + "joystick_off.png", + "joystick_bg.png", + "joystick_center.png", +}; + +v2s32 ButtonMeta::getPos(v2u32 screensize, s32 button_size) const +{ + return v2s32((position.X * screensize.X) + (offset.X * button_size), + (position.Y * screensize.Y) + (offset.Y * button_size)); +} + +void ButtonMeta::setPos(v2s32 pos, v2u32 screensize, s32 button_size) +{ + v2s32 third(screensize.X / 3, screensize.Y / 3); + + if (pos.X < third.X) + position.X = 0.0f; + else if (pos.X < 2 * third.X) + position.X = 0.5f; + else + position.X = 1.0f; + + if (pos.Y < third.Y) + position.Y = 0.0f; + else if (pos.Y < 2 * third.Y) + position.Y = 0.5f; + else + position.Y = 1.0f; + + offset.X = (pos.X - (position.X * screensize.X)) / button_size; + offset.Y = (pos.Y - (position.Y * screensize.Y)) / button_size; +} + +bool ButtonLayout::isButtonAllowed(touch_gui_button_id id) +{ + return id != joystick_off_id && id != joystick_bg_id && id != joystick_center_id && + id != touch_gui_button_id_END; +} + +bool ButtonLayout::isButtonRequired(touch_gui_button_id id) +{ + return id == overflow_id; +} + +s32 ButtonLayout::getButtonSize(v2u32 screensize) +{ + return std::min(screensize.Y / 4.5f, + RenderingEngine::getDisplayDensity() * 65.0f * + g_settings->getFloat("hud_scaling")); +} + +const ButtonLayout ButtonLayout::predefined {{ + {jump_id, { + v2f(1.0f, 1.0f), + v2f(-1.0f, -0.5f), + }}, + {sneak_id, { + v2f(1.0f, 1.0f), + v2f(-2.5f, -0.5f), + }}, + {zoom_id, { + v2f(1.0f, 1.0f), + v2f(-0.75f, -3.5f), + }}, + {aux1_id, { + v2f(1.0f, 1.0f), + v2f(-0.75f, -2.0f), + }}, + {overflow_id, { + v2f(1.0f, 1.0f), + v2f(-0.75f, -5.0f), + }}, +}}; + +ButtonLayout ButtonLayout::loadFromSettings() +{ + bool restored = false; + ButtonLayout layout; + + std::string str = g_settings->get("touch_layout"); + if (!str.empty()) { + std::istringstream iss(str); + try { + layout.deserializeJson(iss); + restored = true; + } catch (const Json::Exception &e) { + warningstream << "Could not parse touchscreen layout: " << e.what() << std::endl; + } + } + + if (!restored) + return predefined; + + return layout; +} + +std::unordered_map> ButtonLayout::texture_cache; + +video::ITexture *ButtonLayout::getTexture(touch_gui_button_id btn, ISimpleTextureSource *tsrc) +{ + if (texture_cache.count(btn) > 0) + return texture_cache.at(btn).get(); + + video::ITexture *tex = tsrc->getTexture(button_image_names[btn]); + if (!tex) + // necessary in the mainmenu + tex = tsrc->getTexture(porting::path_share + "/textures/base/pack/" + + button_image_names[btn]); + irr_ptr ptr; + ptr.grab(tex); + texture_cache[btn] = ptr; + return tex; +} + +void ButtonLayout::clearTextureCache() +{ + texture_cache.clear(); +} + +core::recti ButtonLayout::getRect(touch_gui_button_id btn, + v2u32 screensize, s32 button_size, ISimpleTextureSource *tsrc) +{ + const ButtonMeta &meta = layout.at(btn); + v2s32 pos = meta.getPos(screensize, button_size); + + v2u32 orig_size = getTexture(btn, tsrc)->getOriginalSize(); + v2s32 size((button_size * orig_size.X) / orig_size.Y, button_size); + + return core::recti(pos - size / 2, core::dimension2di(size)); +} + +std::vector ButtonLayout::getMissingButtons() +{ + std::vector missing_buttons; + for (u8 i = 0; i < touch_gui_button_id_END; i++) { + touch_gui_button_id btn = (touch_gui_button_id)i; + if (isButtonAllowed(btn) && layout.count(btn) == 0) + missing_buttons.push_back(btn); + } + return missing_buttons; +} + +void ButtonLayout::serializeJson(std::ostream &os) const +{ + Json::Value root = Json::objectValue; + root["layout"] = Json::objectValue; + + for (const auto &[id, meta] : layout) { + Json::Value button = Json::objectValue; + button["position_x"] = meta.position.X; + button["position_y"] = meta.position.Y; + button["offset_x"] = meta.offset.X; + button["offset_y"] = meta.offset.Y; + + root["layout"][button_names[id]] = button; + } + + fastWriteJson(root, os); +} + +static touch_gui_button_id button_name_to_id(const std::string &name) +{ + for (u8 i = 0; i < touch_gui_button_id_END; i++) { + if (name == button_names[i]) + return (touch_gui_button_id)i; + } + return touch_gui_button_id_END; +} + +void ButtonLayout::deserializeJson(std::istream &is) +{ + layout.clear(); + + Json::Value root; + is >> root; + + if (!root["layout"].isObject()) + throw Json::RuntimeError("invalid type for layout"); + + Json::Value &obj = root["layout"]; + Json::ValueIterator iter; + for (iter = obj.begin(); iter != obj.end(); iter++) { + touch_gui_button_id id = button_name_to_id(iter.name()); + if (!isButtonAllowed(id)) + throw Json::RuntimeError("invalid button name"); + + Json::Value &value = *iter; + if (!value.isObject()) + throw Json::RuntimeError("invalid type for button metadata"); + + ButtonMeta meta; + + if (!value["position_x"].isNumeric() || !value["position_y"].isNumeric()) + throw Json::RuntimeError("invalid type for position_x or position_y in button metadata"); + meta.position.X = value["position_x"].asFloat(); + meta.position.Y = value["position_y"].asFloat(); + + if (!value["offset_x"].isNumeric() || !value["offset_y"].isNumeric()) + throw Json::RuntimeError("invalid type for offset_x or offset_y in button metadata"); + meta.offset.X = value["offset_x"].asFloat(); + meta.offset.Y = value["offset_y"].asFloat(); + + layout.emplace(id, meta); + } +} + +void layout_button_grid(v2u32 screensize, ISimpleTextureSource *tsrc, + const std::vector &buttons, + // pos refers to the center of the button + const std::function &callback) +{ + s32 cols = 4; + s32 rows = 3; + f32 screen_aspect = (f32)screensize.X / (f32)screensize.Y; + while ((s32)buttons.size() > cols * rows) { + f32 aspect = (f32)cols / (f32)rows; + if (aspect > screen_aspect) + rows++; + else + cols++; + } + + s32 button_size = ButtonLayout::getButtonSize(screensize); + v2s32 spacing(screensize.X / (cols + 1), screensize.Y / (rows + 1)); + v2s32 pos(spacing); + + for (touch_gui_button_id btn : buttons) { + v2u32 orig_size = ButtonLayout::getTexture(btn, tsrc)->getOriginalSize(); + v2s32 size((button_size * orig_size.X) / orig_size.Y, button_size); + + core::recti rect(pos - size / 2, core::dimension2di(size)); + + if (rect.LowerRightCorner.X > (s32)screensize.X) { + pos.X = spacing.X; + pos.Y += spacing.Y; + rect = core::recti(pos - size / 2, core::dimension2di(size)); + } + + callback(btn, pos, rect); + + pos.X += spacing.X; + } +} + +void make_button_grid_title(gui::IGUIStaticText *text, touch_gui_button_id btn, v2s32 pos, core::recti rect) +{ + std::wstring str = wstrgettext(button_titles[btn]); + text->setText(str.c_str()); + gui::IGUIFont *font = text->getActiveFont(); + core::dimension2du dim = font->getDimension(str.c_str()); + dim = core::dimension2du(dim.Width * 1.25f, dim.Height * 1.25f); // avoid clipping + text->setRelativePosition(core::recti(pos.X - dim.Width / 2, rect.LowerRightCorner.Y, + pos.X + dim.Width / 2, rect.LowerRightCorner.Y + dim.Height)); + text->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT); +} diff --git a/src/gui/touchscreenlayout.h b/src/gui/touchscreenlayout.h new file mode 100644 index 000000000..9c7d72fbf --- /dev/null +++ b/src/gui/touchscreenlayout.h @@ -0,0 +1,104 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 grorp, Gregor Parzefall + +#pragma once + +#include "irr_ptr.h" +#include "irrlichttypes_bloated.h" +#include "rect.h" +#include +#include + +class ISimpleTextureSource; +namespace irr::gui +{ + class IGUIStaticText; +} +namespace irr::video +{ + class ITexture; +} + +enum touch_gui_button_id : u8 +{ + jump_id = 0, + sneak_id, + zoom_id, + aux1_id, + overflow_id, + + // formerly "rare controls bar" + chat_id, + inventory_id, + drop_id, + exit_id, + + // formerly "settings bar" + fly_id, + fast_id, + noclip_id, + debug_id, + camera_id, + range_id, + minimap_id, + toggle_chat_id, + + // the joystick + joystick_off_id, + joystick_bg_id, + joystick_center_id, + + touch_gui_button_id_END, +}; + +extern const char *button_names[]; +extern const char *button_titles[]; +extern const char *button_image_names[]; + +struct ButtonMeta { + // Position, specified as a percentage of the screensize in the range [0,1]. + // The editor currently writes the values 0, 0.5 and 1. + v2f position; + // Offset, multiplied by the global button size before it is applied. + // Together, position and offset define the position of the button's center. + v2f offset; + + // Returns the button's effective center position in pixels. + v2s32 getPos(v2u32 screensize, s32 button_size) const; + // Sets the button's effective center position in pixels. + void setPos(v2s32 pos, v2u32 screensize, s32 button_size); +}; + +struct ButtonLayout { + static bool isButtonAllowed(touch_gui_button_id id); + static bool isButtonRequired(touch_gui_button_id id); + static s32 getButtonSize(v2u32 screensize); + static ButtonLayout loadFromSettings(); + + static video::ITexture *getTexture(touch_gui_button_id btn, ISimpleTextureSource *tsrc); + static void clearTextureCache(); + + std::unordered_map layout; + + core::recti getRect(touch_gui_button_id btn, + v2u32 screensize, s32 button_size, ISimpleTextureSource *tsrc); + + std::vector getMissingButtons(); + + void serializeJson(std::ostream &os) const; + void deserializeJson(std::istream &is); + + static const ButtonLayout predefined; + +private: + static std::unordered_map> texture_cache; +}; + +void layout_button_grid(v2u32 screensize, ISimpleTextureSource *tsrc, + const std::vector &buttons, + // pos refers to the center of the button. + const std::function &callback); + +void make_button_grid_title(gui::IGUIStaticText *text, + touch_gui_button_id btn,v2s32 pos, core::recti rect); diff --git a/src/irr_gui_ptr.h b/src/irr_gui_ptr.h new file mode 100644 index 000000000..8c16a5b1e --- /dev/null +++ b/src/irr_gui_ptr.h @@ -0,0 +1,28 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 grorp, Gregor Parzefall + +#pragma once +#include +#include "IGUIElement.h" + +// We cannot use irr_ptr for Irrlicht GUI elements we own. +// Option 1: Pass IGUIElement* returned by IGUIEnvironment::add* into irr_ptr +// constructor. +// -> We steal the reference owned by IGUIEnvironment and drop it later, +// causing the IGUIElement to be deleted while IGUIEnvironment still +// references it. +// Option 2: Pass IGUIElement* returned by IGUIEnvironment::add* into irr_ptr::grab. +// -> We add another reference and drop it later, but since IGUIEnvironment +// still references the IGUIElement, it is never deleted. +// To make IGUIEnvironment drop its reference to the IGUIElement, we have to call +// IGUIElement::remove, so that's what we'll do. +template +std::shared_ptr grab_gui_element(T *element) +{ + static_assert(std::is_base_of_v, + "grab_gui_element only works for IGUIElement"); + return std::shared_ptr(element, [](T *e) { + e->remove(); + }); +} diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index e8c969268..0353efe1d 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -11,6 +11,7 @@ #include "gui/guiMainMenu.h" #include "gui/guiKeyChangeMenu.h" #include "gui/guiPathSelectMenu.h" +#include "gui/touchscreeneditor.h" #include "version.h" #include "porting.h" #include "filesys.h" @@ -535,6 +536,22 @@ int ModApiMainMenu::l_show_keys_menu(lua_State *L) return 0; } +/******************************************************************************/ +int ModApiMainMenu::l_show_touchscreen_layout(lua_State *L) +{ + GUIEngine *engine = getGuiEngine(L); + sanity_check(engine != NULL); + + GUITouchscreenLayout *gui = new GUITouchscreenLayout( + engine->m_rendering_engine->get_gui_env(), + engine->m_parent, + -1, + engine->m_menumanager, + engine->m_texture_source.get()); + gui->drop(); + return 0; +} + /******************************************************************************/ int ModApiMainMenu::l_create_world(lua_State *L) { @@ -1080,6 +1097,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top) API_FCT(start); API_FCT(close); API_FCT(show_keys_menu); + API_FCT(show_touchscreen_layout); API_FCT(create_world); API_FCT(delete_world); API_FCT(set_background); diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h index d0f72b6c4..704924bf3 100644 --- a/src/script/lua_api/l_mainmenu.h +++ b/src/script/lua_api/l_mainmenu.h @@ -69,6 +69,8 @@ private: static int l_show_keys_menu(lua_State *L); + static int l_show_touchscreen_layout(lua_State *L); + static int l_show_path_select_dialog(lua_State *L); static int l_set_topleft_text(lua_State *L); diff --git a/textures/base/pack/down.png b/textures/base/pack/down.png index 7183ee5364c4f54d0f3dff7a6908ef36fe5a47a8..594863dba86ea4e9cbc563f9e8c574ac5830d2f5 100644 GIT binary patch literal 3647 zcmeH{*H;q=7REyeLXeJt6a__y1Ps!`5K0hX=pCd40YL&7iu4|WgLFg%snQH(=pZ4| z!-OVMl-@yHni&GpYhaz-hy4R~-*$fI+;h)&&$-{jcka3ICPvz<%mT~+0Dx6jN7D=d zpu_zv02BSsNphK~B>(_CG%>Ky`Y9_bD=8Gp#ozp$!2eAGy-zb@f1duCznPKw&kmpi z(t{W-F*1RfFSD?+v2$>8abMx#<-5u+ASiV0y0FNNo1(YG#38pOppsH|VA6MGWaZ=) z;EKN}A(T~A)sX7~_tEX>ZXk=_+YG!U>`M}EB#ujDw(BYAj^J5pZtDE~1 zjEAR}_fsEVKmUNhpx}_uu<(dTT-3AZ=P|Ky@d=4Z$uCk~zIvUS_9i_eGb=kMH!r`S z@EyLWxTLhKyrQzIx~8`7eLbOp_@S|>xuvzOoz(FMxwEUgr}tyur~ZM#q0e82M@GlS zCnl$+XJ+T-7Z$%Ref!7q%Iezscgmj|)Xl9Q+dI2^`v-?d$0w&}v~v?ZnGFB{e5$K? z&mwq+nj2PP^3h4#jlE0zl&~*LRi7-s#*L`BGd>_78YEq^kg@7bZhd&*YI6%wI_dQlP5| z0N2Tr`v4{Yoe0j2p&%K-dL)ey^+eIFm$XK|1Q+DLjBXX5Ap+g}Zc2dHV-p63XKZmB zOh&J(frew+kN>?10O0@}0HFH6Z2luU-T<0~n0{OUm)AX{)JI>%x(HboGS{J%4&st& zxCQ&crLpa5{0%y-cBq#<0jw0*msSKBtu$Hk%8+ zvn%F`eS@10v%|x~3lxSAvvyoZsw8oTpHk`HhzBaB+Y^+*QdP>Q!vt% zD%n;A#bE}ncdD=O`CI94PXx2~DyL+nTCH-D8XAieue;f?ggMz&K>;hR6f?;}wuXk~ zf8PAzxnc}C=N~EGTo=X~KjvvlPJ>}Ab{Nr_t zHrdfvpnsDTl zqmVUl=J!67;>cA3Zh@OA7c{3vGlHoE zcTlsjj=KKH-wjW*DHz*LEunC^%jmXE&7F-Ky2ku{*e_18)}C!W!=DZOJIH;oVFSTj;dE$0!=5y&RK5iGMVm z+gYJ82a=-8p|XdHw0mWfNB8VuBShN4F!gO~f2ykvrO@6qcgn zV%3IMb@w;Ud@4cQd00|*u!1*qN@dUxSHg-RYE~o5L}O?{DM;lkz8DeFsQSo}1wGNF zG|I@s1>TBuFovfV-M{DX(BoEDE}@R{HNgiKZZqTY7Vfp3hw_ZJ{4yW9-@pJ}%7j-= ze^W|yBkmm#BAcWoUOJeFr>|y^S3gT}(xjlopI0?iBb)QC zC<_YiPCReXIc~yfE61v4%&MP_DjZ{k{nsK(a@*QL$WwcT?SrYO2d?HSArQJ8>-eL- z*nlon#NcVg{rTu6g}UR^f;5`K@>w5*l-Vaa8Z(G#>h(CX)*ir&w`T}Ii&}a8(vu$& zwh+^Vrq*3f9&ZCdI4*Ji#79o19<{ysOc!2#Dl)^C3MX*gt&bwV-71Xa|o~0 zQ?7;aJe~Y?FGlc`p#DdSXb^}O=J)gu8TTEB+GB>bipzM@V7^~v?6#*7R&=EX(Rs^G zsWCESBLU`L1|`!8O(w}~i9M#@^BJ6lSnF$`F#JdR2M(kFTV0|Na#n59e2(T7fj*R$ zeln0XPdAr2pPtB=KxD2C#-;kEsK7p{YNFKS40=VJ8VoPzT{2ZqhmQ~XG@pCg?Lbbr z^k510If<1Pdr#`(8t=1cb-hiC;6vdzPVM<=kY(8u!7UZo3)xVZfbgfFL19x^`{&e5 znyq{NefifcANJQcFIOSkI}NRr?In3g*^5H;Sx-UK{34EF6J*kXaX*#7hlLFV&MYaa zugxor0%+u-LV5K?%xxsPr0{Gnlv6RyZx_^Yvi^sWwMyCDC!iEZ|I7%GGe?QxBK~Gh zd`iNXT+7%zX6_68%}#mG@QjW2VO{K{)sE6A-65oKeG^!6mHR z2d5E1pVvH#c*X+vjdxpD;*=R(X6&AbXTh(HM>_EOU#z^zrP1jxw$B?O?%V2VdK0Z> zp!;v%e>b2XCX@{svYt0Pca-GNV;e8vGHo-#H$+2|Sn$YTu-(mxToJWF=StOA0_RBZ z)KY%q&U&uVh^j^gDWqH!c~|)vBr4vyT(zz4V%R?L2$Ca*nu4Uya~!escFDF>xD9!4 z&RP~-H_c&=L-zwtd9G?$2-M2=Z!{sai?iJiy08!>ofoM=k4pw;Cv{%A7^8-x=(UzJ z8&o8R9ipltiz?I=f_ldTNAPWz=p|+sP4lvT@2Tn+5ct^IPSXQ_AGRm|P<+#lnAMK) zuOBzaaofSS(W5tsy`dj#kFCcES=ql1++ECQ!TKNuc-at8@`O;0%y4=PX>;t$oZwy1 zsQBY;ju(47z!H}u3>*XbC>j} zW1?1Mb6M>nRXNh?SW$oe1l#Q_MaBwd!OI&Lu~joK%bdd_(gn2C;1PtNQHJ@p-_zif zX+8VQJ47r&5qjsaebz=U;nbx)iTno)^L3GPwDtKyZ`_I literal 1618 zcmbV_Ydq5n7{~v1Msf+IGm*>Dl|!gWE;}WorVN|4!Zg*Q879j$S-Eu5bfsx-Wek(c zsb;x5r=)`{mp?_QCTkeZ+9vnoyf`n;i}R-Mo9FpGzvuJ&UGqX8(p1w|0{}o1br^{O z0HxJc2~dTu4n;gs0stzTP)KK=m;uQUih?8Qz5J^WQ^dpkUcE1{+~!oM4SJsswgsLR zq2{*Uw(X8`b}Ej~)D%}!RQ-Chh~=(ql~39Xy`qvA?i0{oH*~tTq#ciW9}Gk;6 zv+A*z6q2>_)7+5qGL zAXRP78k34f!#e~3py@0CqC>3PCtF=y@VLjG70T=8o+)W5Olfa^OGh0 z zn@qpyPjzuBxPoREZ6lE6<5dqIkBEn@cX=1$X+Ndg_R$&x;2`aK=T{*euNQ_XOo~e$ z)9237FIoG1jg~t!Oj(_O!<;Y_&VnbN#;_dvLYjSAy)C7ZH$kP=lBBl8$sSAiysmA1 zfN}QJBNbY0{toJ0g&z8Yr~HZUOw#~Z;(15|T=3`5UDQvHdoR;bRPQPhw}R!k%It9k z4N#x*t;p9Xma3O2x8gda{&Eq~oO3L6&*xY#(bkbgH$)&8^m9%)%XLebfBw{$itm;* z5l$lG!NQjLS1}&(Zg*Z+;+zj@ZX77fc0sf_2nmukwYzH+{0V~a?%mWXPul%PJ4FBZ zks#gykfUkTootl$ktUi@#`f{Y7qV@|+cWPBSr8sazWIGf;4X)gJmKkJ7Hqm9(%1)v-H6S8|OjU_6nE7*LtU zx3>f)AUV>Ft*#h*Sj`&YvsbSRi0TYJ%1Z1%Ehs_>I8oW{Nq0t#X(ViYvvDS8xy14M zdX@U)1?zsSdu(=w-dCy5XsHEb5{|w|A$BDKYW#A9vCWU6=|fgyG~;^>$G$v!k*uR{4gpPKiL&naw25 z=|V)6-s+;7Gv+$fZ!*H`vCOpr$zp4W0$+NMxBRkAT<{cos6ee zn9Yq$oypk4(3X8EdldcgeiN|~M9@rDRN?2|%5YUf)mnIU8QCH}qi3!2S;xO5ST)Mb z%+HX}fq*Fs^x8=GMT@+vIy)ovJ?&mNmS&+xY?|+U3$TW zofDBlj5QN-#%42}+(^%_(J%D$-AX;WGPIY60jo@jhIXWkJ)gAwi?!OrR*bIhZ)0?9 zSv{u92}9O5v-2osbCgHxuTI32Z>ky25|}L!~g&Q diff --git a/textures/base/pack/jump_btn.png b/textures/base/pack/jump_btn.png index 17b7cb586dbb602e8f37fac463e5630b316826e0..dbb455b9598328eda2b68d4e3b903fbc29ace1c1 100644 GIT binary patch literal 3625 zcmeH_X*d*$8plVrL|5sMD2b3QI*PK*kR`jpU_$m8V~mt#jNM2GCCPd;smLzF*mq+| zmdThVV~~9t`_ABY?#KJ}e7yJfJn!>9|M$cH!|!?H%uV&ro)J0&007P!JkYTK09Yb^ z58yQG?@M9<7yhqqB>vn>!5df$&6n zp}ZgaJbCKt=O2I$!~_M0goZ~%VxL7nkBNSC7TtcGL%hf+Qi6SZi)Qve1YX&5-7i^>c{y ziEzh5*ix1#anqxcD<6CV2tI?9bee<^=#+VxkJvi$W40i zxbJ{(zxz+czs3J!1YU&)$?}URMg*V{9rBO zJx{q1hB6!)@ndvwjddGh2GklQ;i2?FS5f8>U*)?%>DHJx0HL*1blo0tE~Wc0;dHY# zwyA0`(SYF+@pB!|bYM0g(%2E>C0g&3ZJIR2N$m9zF%4wMZ6rDO=GzrYnS~7G8QUlY zQxH}oNMD_p5=cH+nK6}FWyp;aiW;JlK%ly1r;7Bc6&DK_b!U_J%y@>d`)Po zFV0|_N)u_&DwL3a1gv$Gce39Ll`2vDu05SyXUZQn2U*Ivyo`yS7qavgd0o6)e^92Y z+EjGp$Utw0mBaVjDsQ!Utp|I3!>Cp;H*i2fUAI*Gej(-&%F+B@QQ_vGBb>8lLUa&^ z<^aCil{g=kSh)0bOkD$AbZj@$8J|roF&Twc_#b=F`{viUqXfJg3*TFRVK7(Uu<}@4 ztf`A0Zx`;72(_xM3xMmL?xD2%WyrT@3|owpNG>7EG#IWrp@~cU8(}^g&|311#U1qv zGhF1^5txzH>^oR)X0c*foJv(&Z*i0XPy$KK6nXM}G43b(2@W0f>YGfRLGDpw;?=YL zuFiZqrFVxw1|(`oh)E@{aaKLdsBOT^Am2keYu8PWi?h~gIQ7gx;7J~qW`S2~HpU{9 zoT{!*r&NOD;wOV9K9tij>k(W)sNlg+sZdX=4I#}tgh*|*i>qOu=Ubc48*sN8{6GrG zOb@iL5D_@RU?`(0t|V&$2A0GM&y_>%28Z}UF7m(MS(Nw1u1bN7f3;kCY*hu`ty__` z!}0hGJth3``dTLWD)!|IpouVMT%Qe1+&3My`eAD5v)w4wS)(?lMSK}U`dm%@jsPxI z&rFFAkAFSfKtL5B_I2gRueK^(J%&&MkWq_OrFq@k?)l^VV}zIz%7q%HovGZYvZE01 zBia=a4TSBNOG+TBF1y0GaqCVqi(CiBS*d1tNwruu`+^pwfIhG^;qqIR_;b;FVc>(l<5dj!@X_jEkxDO@}oaH+vSX2ziH99jtWNcu2WXuq|Qn1 z4-@{@mr`vBGon(}2DSUr`0)+sEi8)g%=JCkKg64wcXoH~aG<$G8GxXDpe0Ym7Lc{t;bz)x0&&TRV6rB1!nhyOi_W|UD&?f z-pY)Im70WJ;^MCSOMI61&D2nx#xK>;#)lmp3Yzn%i5^8?o=NM=k0lj2N_2}pYA{-F zIYmdL<3i~=S@Qb$u3+9)7gs(VIiOEeLt^z`%;>&>@Cwxg^d!x!z%K3K_dPAxC!g3I zRjEk3z|MXSyoiLt5ydJ?8D-n{I^|H>fv0qTx6OP7sgly1<1@s(V&g%crO)c?DtppA zkn7}=K(Jo4<2vM+9C<%`wVYwwjM(hRs)$07DsSz@5FD$LfnH8I_Q`hJ+U2VysJ;A< z@K%z@+b9*gwyiLFPcdT(W{-Cg&pTVR69YWaYD~;UbC74A^QP$sLBp+s4zV9A8@XTR zaJ#;$34Gh3kPy{889cnkKfY)vDfvgO2!2^C4Z?r+N<}YH-raXB@nvC&va6qM)DyR4 z3Ocds;AZ138#gs;TSHvhM61<^gOls8Y*YHb^+qdYf{FM6+44lAJXx9ID(cQ-8rK4OD%s~ z9&r*rx~EB-H_6Wd?gZu1uW?-E;Z3$b-rSX2K;0die-)78nHdQ#1Q}`2meSiF&k9Rk zX6g`**9R)fe$MU9Wj}ei;X-n2yIaCVIqv4?+~xtD>`JJKM66uB0UXSSzYn1VDrwT< zr2q*S3upg^3}fMCalx%kf~W527+h@m?kf#9^&{bv$`b2>k914Lk2%Gc8%AyqR)W2a zwx%Hw5xM#hRa}&M_63Efq!@HM;sVb1;b+80)wn9^R7nP>aivD6YJANclIQKPSFGO)CJ2MgfD>PQ>WKVSk7EO2RH=+oCdIn0@xw|{~gke zBHC;tF9?Fp*?AyXrgf#yPqhHfOX~{ku-?_Tkl{cljgiX`EJl3utf&6d!Z3*VRm^u< zQIN$DkHzq}0H>19INuSWA^;Wc)2b3 u?(5EGX22ctOT9q~rTz0z4!}RhsgKERidg#jpf!SJ_rE(8J7>YEX+&BwhA*X*TFjrP zr!*6Tx)^nmZ~xu;MrUJ8_n}ad&975lr&Nq7_3J#8N~Zm7+A9lt)rMM0GrvaNIxZ`} zt5@fsT5{}f((@{pS9BT5CBy!E^}Qit)AoChb#D_A7^y z@x1Np(I2V?e=4t96pfet&Dv=)cSu8~@9k7Gs>a9uF8yLKbwWLvOeUjsI%@d#uX-G1 z<6!<=Z8e?K?`g@u+gzo$OxtWzJqr76|8jj{Fmh6TI`_2gW~o;v4O<@6ov7@q`LmS& zXN12vU#Vlp?SF68=Z39MssHrwfFJfR(ypMFar58DZY@P|zwEc&$Q{vRCHJ(}dIPB% zboayj1`V6e+g-MaUs|ZwP5XOEm!N+1_IK*LYIo+SMm;)V*z1p4g#%HVf4=%kOz`-d zQQc7XzT5SYVb4?As@XUZjs4aM9s|0d!g$#Rl0MP(xPRf=eCtK}kRA;5JNKA|l20nn z1QM6_FVG97bN}VwlRDI>=S;u#s;T~E* z9gbC3`hRAX{@B_S>JlBP-odHkw|E zG}s@TzBjFQ-7lGZ^P9}Q@0WYgUWX$SX|Uf`%71ynrvh$s@p-_p$>f`#HIKkw=Uv;| z^kJB@6VhP+vUtzIDPw-pjkx{)i>R%X^9SAU^>K9STgpw#mu;cmNXk51HRxH>vAoUV zl+%)}EvJ+-CJ(Vys!~Y4`Sx0zC9yiLMUs~I1hFOy#O*?I;}+vhr5FwvUS2lO4jF&+k}^(hr3!ou|oe0S47*Cw}<2Z zpJ(CY^i7&ni#|yBdgV-;E}@Tg+WUcMrhmN$J2-pcM5^D@RrDQZ@hvx{bgB%kl2>j@ z@ni*xjhn_sy5o@NCro*Mm-6W|u9>#-_h-C*)wOQZeil#&aMxs80OKw{4}c|9f@^q- zc?lffCXld*Qk8VHFM_qqr)JwqIgjX3EHW?y3=MYE<0^rErc^ncQ* zo61tE;pl2#3ipBN8fjfjxxiw|73mxDV%R~O*Q$c)j&-xLm|3C5S#36+Vy&*5)^6(U zV~M2TO0U$ILFvP4cl!^u$Iz%9YQ#Wix7m9fJ`*TcvN^}->94BdmI);8UUIDsyjIP5 z1eR)d3Dq~aYs|)?Z{^%XPk$(?*Dh&O(a!dbJ_LYQtA?w5v2s%n0x58+e2Dh_C*b&w`UBy?Tl>FD4Ki3sG3~&N7rkM zCQ#l3CbT7@*K2Bya_Zz0=_eQ47fJvC000000002MU+uvG2><{9fS~`c+YzV$00000 elePpK7F+}k^M@G+J9DQ10000 Date: Sun, 24 Nov 2024 19:23:32 +0100 Subject: [PATCH 12/68] Doc: Add missing jpeg package for Arch Linux (#15461) --- doc/compiling/linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/compiling/linux.md b/doc/compiling/linux.md index 573c6908e..54f7de2a0 100644 --- a/doc/compiling/linux.md +++ b/doc/compiling/linux.md @@ -33,7 +33,7 @@ For openSUSE users: For Arch users: - sudo pacman -S --needed base-devel libcurl-gnutls cmake libpng sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd gettext sdl2 + sudo pacman -S --needed base-devel libcurl-gnutls cmake libpng libjpeg-turbo sqlite libogg libvorbis openal freetype2 jsoncpp gmp luajit leveldb ncurses zstd gettext sdl2 For Alpine users: From b77ad82fb952e728b43307d1f3fc13671d0f0236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sun, 24 Nov 2024 19:23:53 +0100 Subject: [PATCH 13/68] Sanitize invalid particle spawner time (#15465) --- src/client/particles.cpp | 1 + src/network/clientpackethandler.cpp | 3 +++ src/script/lua_api/l_particles.cpp | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/src/client/particles.cpp b/src/client/particles.cpp index c6891f8ef..472ac0321 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -270,6 +270,7 @@ ParticleSpawner::ParticleSpawner( } size_t max_particles = 0; // maximum number of particles likely to be visible at any given time + assert(p.time >= 0); if (p.time != 0) { auto maxGenerations = p.time / std::min(p.exptime.start.min, p.exptime.end.min); max_particles = p.amount / maxGenerations; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 9f987ccea..fbd7bdd9e 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -4,6 +4,7 @@ #include "client/client.h" +#include "exceptions.h" #include "irr_v2d.h" #include "util/base64.h" #include "client/camera.h" @@ -1007,6 +1008,8 @@ void Client::handleCommand_AddParticleSpawner(NetworkPacket* pkt) p.amount = readU16(is); p.time = readF32(is); + if (p.time < 0) + throw SerializationError("particle spawner time < 0"); bool missing_end_values = false; if (m_proto_ver >= 42) { diff --git a/src/script/lua_api/l_particles.cpp b/src/script/lua_api/l_particles.cpp index 80c3f27c2..efcf3725e 100644 --- a/src/script/lua_api/l_particles.cpp +++ b/src/script/lua_api/l_particles.cpp @@ -3,6 +3,7 @@ // Copyright (C) 2013 celeron55, Perttu Ahola #include "lua_api/l_particles.h" +#include "common/c_types.h" #include "lua_api/l_object.h" #include "lua_api/l_internal.h" #include "lua_api/l_particleparams.h" @@ -280,6 +281,9 @@ int ModApiParticles::l_add_particlespawner(lua_State *L) p.node_tile = getintfield_default(L, 1, "node_tile", p.node_tile); } + if (p.time < 0) + throw LuaError("particle spawner 'time' must be >= 0"); + u32 id = getServer(L)->addParticleSpawner(p, attached, playername); lua_pushnumber(L, id); From 8c56434bd3e93e89e258d82171b9af71b558145e Mon Sep 17 00:00:00 2001 From: lhofhansl Date: Mon, 25 Nov 2024 11:56:32 -0800 Subject: [PATCH 14/68] Clamp pre-bloom color to valid range (#15453) That avoids flashing artifacts when combining MSAA and bloom. --- client/shaders/extract_bloom/opengl_fragment.glsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/shaders/extract_bloom/opengl_fragment.glsl b/client/shaders/extract_bloom/opengl_fragment.glsl index 36671b06c..281884cee 100644 --- a/client/shaders/extract_bloom/opengl_fragment.glsl +++ b/client/shaders/extract_bloom/opengl_fragment.glsl @@ -23,7 +23,7 @@ void main(void) vec2 uv = varTexCoord.st; vec3 color = texture2D(rendered, uv).rgb; // translate to linear colorspace (approximate) - color = pow(color, vec3(2.2)); + color = pow(clamp(color, 0.0, 1.0), vec3(2.2)); color *= exposureParams.compensationFactor * bloomStrength; From 6c324cb871dc04aff487dd3330aa79ab902dc1b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:39:28 +0100 Subject: [PATCH 15/68] Main menu: Fix server selection (#15466) Server selections are now always correct (no more arbitrarily changing selections if the order of the serverlist changes) and consistent with the address + port in the sidebar. --- builtin/mainmenu/tab_online.lua | 96 +++++++++++++++++---------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index 558e2aff2..6545569b2 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -55,6 +55,36 @@ local function get_sorted_servers() return servers end +-- Persists the selected server in the "address" and "remote_port" settings +local function set_selected_server(server) + local address = server.address + local port = server.port + gamedata.serverdescription = server.description + + gamedata.fav = false + for _, fav in ipairs(serverlistmgr.get_favorites()) do + if address == fav.address and port == fav.port then + gamedata.fav = true + break + end + end + + if address and port then + core.settings:set("address", address) + core.settings:set("remote_port", port) + end +end + +local function find_selected_server() + local address = core.settings:get("address") + local port = tonumber(core.settings:get("remote_port")) + for _, server in ipairs(serverlistmgr.servers) do + if server.address == address and server.port == port then + return server + end + end +end + local function get_formspec(tabview, name, tabdata) -- Update the cached supported proto info, -- it may have changed after a change by the settings menu. @@ -107,15 +137,15 @@ local function get_formspec(tabview, name, tabdata) retval = retval .. "button[0.25,6;2.5,0.75;btn_mp_register;" .. fgettext("Register") .. "]" end - if tabdata.selected then + local selected_server = find_selected_server() + + if selected_server then if gamedata.serverdescription then retval = retval .. "textarea[0.25,1.85;5.25,2.7;;;" .. core.formspec_escape(gamedata.serverdescription) .. "]" end - local server = tabdata.lookup[tabdata.selected] - - local clients_list = server and server.clients_list + local clients_list = selected_server.clients_list local can_view_clients_list = clients_list and #clients_list > 0 if can_view_clients_list then table.sort(clients_list, function(a, b) @@ -197,11 +227,17 @@ local function get_formspec(tabview, name, tabdata) retval = retval .. table.concat(rows, ",") - if tabdata.selected then - retval = retval .. ";" .. tabdata.selected .. "]" - else - retval = retval .. ";0]" + local selected_row_idx = 0 + if selected_server then + for i, server in pairs(tabdata.lookup) do + if selected_server.address == server.address and + selected_server.port == server.port then + selected_row_idx = i + break + end + end end + retval = retval .. ";" .. selected_row_idx .. "]" return retval end @@ -253,35 +289,6 @@ local function search_server_list(input) menudata.search_result = search_result end -local function set_selected_server(tabdata, idx, server) - -- reset selection - if idx == nil or server == nil then - tabdata.selected = nil - - core.settings:set("address", "") - core.settings:set("remote_port", "30000") - return - end - - local address = server.address - local port = server.port - gamedata.serverdescription = server.description - - gamedata.fav = false - for _, fav in ipairs(serverlistmgr.get_favorites()) do - if address == fav.address and port == fav.port then - gamedata.fav = true - break - end - end - - if address and port then - core.settings:set("address", address) - core.settings:set("remote_port", port) - end - tabdata.selected = idx -end - local function main_button_handler(tabview, fields, name, tabdata) if fields.te_name then gamedata.playername = fields.te_name @@ -312,14 +319,13 @@ local function main_button_handler(tabview, fields, name, tabdata) gamedata.serverdescription = server.description if gamedata.address and gamedata.port then - core.settings:set("address", gamedata.address) - core.settings:set("remote_port", gamedata.port) + set_selected_server(server) core.start() end return true end if event.type == "CHG" then - set_selected_server(tabdata, event.row, server) + set_selected_server(server) return true end end @@ -332,13 +338,12 @@ local function main_button_handler(tabview, fields, name, tabdata) if not server then return end serverlistmgr.delete_favorite(server) - -- the server at [idx+1] will be at idx once list is refreshed - set_selected_server(tabdata, idx, tabdata.lookup[idx+1]) + set_selected_server(server) return true end if fields.btn_view_clients then - local dlg = create_clientslist_dialog(tabdata.lookup[tabdata.selected]) + local dlg = create_clientslist_dialog(find_selected_server()) dlg:set_parent(tabview) tabview:hide() dlg:show() @@ -355,8 +360,7 @@ local function main_button_handler(tabview, fields, name, tabdata) tabdata.search_for = fields.te_search search_server_list(fields.te_search:lower()) if menudata.search_result then - -- first server in row 2 due to header - set_selected_server(tabdata, 2, menudata.search_result[1]) + set_selected_server(menudata.search_result[1]) end return true @@ -383,8 +387,6 @@ local function main_button_handler(tabview, fields, name, tabdata) local idx = core.get_table_index("servers") local server = idx and tabdata.lookup[idx] - set_selected_server(tabdata) - if server and server.address == gamedata.address and server.port == gamedata.port then From c175046d300b98213598d2373e750ee07fba8a21 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 27 Nov 2024 18:39:57 +0100 Subject: [PATCH 16/68] Network: Fix serialization version checks (#15477) This fixes some incorrect assumptions that the read and write version ranges are identical - whereas they're in fact not. --- src/client/client.cpp | 2 +- src/main.cpp | 1 + src/mapblock.cpp | 6 ++---- src/mapnode.cpp | 20 +++++++------------ src/network/clientpackethandler.cpp | 6 +++--- src/network/serverpackethandler.cpp | 31 ++++++++++++----------------- src/serialization.h | 19 ++++++++++++------ src/server.cpp | 1 + src/server.h | 1 - src/server/clientiface.cpp | 3 +++ src/server/clientiface.h | 5 ++--- 11 files changed, 46 insertions(+), 49 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index b308a9276..f17d4debf 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -1128,7 +1128,7 @@ void Client::sendInit(const std::string &playerName) { NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size())); - pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) 0; + pkt << SER_FMT_VER_HIGHEST_READ << (u16) 0 /* unused */; pkt << CLIENT_PROTOCOL_VERSION_MIN << LATEST_PROTOCOL_VERSION; pkt << playerName; diff --git a/src/main.cpp b/src/main.cpp index 6d30b2f07..2d0c2aa79 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,6 +24,7 @@ #include "config.h" #include "player.h" #include "porting.h" +#include "serialization.h" // SER_FMT_VER_HIGHEST_* #include "network/socket.h" #include "mapblock.h" #if USE_CURSES diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 9ead0fa5a..f68657d9d 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -307,11 +307,9 @@ static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes, void MapBlock::serialize(std::ostream &os_compressed, u8 version, bool disk, int compression_level) { - if(!ser_ver_supported(version)) + if (!ser_ver_supported_write(version)) throw VersionMismatchException("ERROR: MapBlock format not supported"); - FATAL_ERROR_IF(version < SER_FMT_VER_LOWEST_WRITE, "Serialization version error"); - std::ostringstream os_raw(std::ios_base::binary); std::ostream &os = version >= 29 ? os_raw : os_compressed; @@ -423,7 +421,7 @@ void MapBlock::serializeNetworkSpecific(std::ostream &os) void MapBlock::deSerialize(std::istream &in_compressed, u8 version, bool disk) { - if(!ser_ver_supported(version)) + if (!ser_ver_supported_read(version)) throw VersionMismatchException("ERROR: MapBlock format not supported"); TRACESTREAM(<<"MapBlock::deSerialize "< MapNode::serializeBulk(int version, const MapNode *nodes, u32 nodecount, u8 content_width, u8 params_width) { - if (!ser_ver_supported(version)) + if (!ser_ver_supported_write(version)) throw VersionMismatchException("ERROR: MapNode format not supported"); sanity_check(content_width == 2); sanity_check(params_width == 2); - // Can't do this anymore; we have 16-bit dynamically allocated node IDs - // in memory; conversion just won't work in this direction. - if (version < 24) - throw SerializationError("MapNode::serializeBulk: serialization to " - "version < 24 not possible"); - Buffer databuf(nodecount * (content_width + params_width)); // Writing to the buffer linearly is faster @@ -712,13 +706,13 @@ void MapNode::deSerializeBulk(std::istream &is, int version, MapNode *nodes, u32 nodecount, u8 content_width, u8 params_width) { - if(!ser_ver_supported(version)) + if (!ser_ver_supported_read(version)) throw VersionMismatchException("ERROR: MapNode format not supported"); if (version < 22 || (content_width != 1 && content_width != 2) || params_width != 2) - FATAL_ERROR("Deserialize bulk node data error"); + throw SerializationError("Deserialize bulk node data error"); // read data const u32 len = nodecount * (content_width + params_width); diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index fbd7bdd9e..4b899e598 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -63,7 +63,7 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) if (pkt->getSize() < 1) return; - u8 serialization_ver; + u8 serialization_ver; // negotiated value u16 proto_ver; u16 unused_compression_mode; u32 auth_mechs; @@ -80,9 +80,9 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) << ", proto_ver=" << proto_ver << ". Doing auth with mech " << chosen_auth_mechanism << std::endl; - if (!ser_ver_supported(serialization_ver)) { + if (!ser_ver_supported_read(serialization_ver)) { infostream << "Client: TOCLIENT_HELLO: Server sent " - << "unsupported ser_fmt_ver"<< std::endl; + << "unsupported ser_fmt_ver=" << (int)serialization_ver << std::endl; return; } diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 47fd8ef5b..cf1dcacb1 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -12,6 +12,7 @@ #include "remoteplayer.h" #include "rollback_interface.h" #include "scripting_server.h" +#include "serialization.h" #include "settings.h" #include "tool.h" #include "version.h" @@ -83,34 +84,27 @@ void Server::handleCommand_Init(NetworkPacket* pkt) if (denyIfBanned(peer_id)) return; - // First byte after command is maximum supported - // serialization version - u8 client_max; + u8 max_ser_ver; // SER_FMT_VER_HIGHEST_READ (of client) u16 unused; - u16 min_net_proto_version = 0; + u16 min_net_proto_version; u16 max_net_proto_version; std::string playerName; - *pkt >> client_max >> unused >> min_net_proto_version - >> max_net_proto_version >> playerName; + *pkt >> max_ser_ver >> unused + >> min_net_proto_version >> max_net_proto_version + >> playerName; - u8 our_max = SER_FMT_VER_HIGHEST_READ; // Use the highest version supported by both - u8 depl_serial_v = std::min(client_max, our_max); - // If it's lower than the lowest supported, give up. -#if SER_FMT_VER_LOWEST_READ > 0 - if (depl_serial_v < SER_FMT_VER_LOWEST_READ) - depl_serial_v = SER_FMT_VER_INVALID; -#endif + const u8 serialization_ver = std::min(max_ser_ver, SER_FMT_VER_HIGHEST_WRITE); - if (depl_serial_v == SER_FMT_VER_INVALID) { + if (!ser_ver_supported_write(serialization_ver)) { actionstream << "Server: A mismatched client tried to connect from " << - addr_s << " ser_fmt_max=" << (int)client_max << std::endl; + addr_s << " ser_fmt_max=" << (int)serialization_ver << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_WRONG_VERSION); return; } - client->setPendingSerializationVersion(depl_serial_v); + client->setPendingSerializationVersion(serialization_ver); /* Read and check network protocol version @@ -263,8 +257,9 @@ void Server::handleCommand_Init(NetworkPacket* pkt) NetworkPacket resp_pkt(TOCLIENT_HELLO, 0, peer_id); - resp_pkt << depl_serial_v << u16(0) << net_proto_version - << auth_mechs << std::string_view(); + resp_pkt << serialization_ver << u16(0) /* unused */ + << net_proto_version + << auth_mechs << std::string_view() /* unused */; Send(&resp_pkt); diff --git a/src/serialization.h b/src/serialization.h index 7e63ec23c..48c7464c1 100644 --- a/src/serialization.h +++ b/src/serialization.h @@ -50,23 +50,30 @@ 28: Added "private" flag to NodeMetadata 29: Switched compression to zstd, a bit of reorganization */ + // This represents an uninitialized or invalid format -#define SER_FMT_VER_INVALID 255 +constexpr u8 SER_FMT_VER_INVALID = 255; // Highest supported serialization version -#define SER_FMT_VER_HIGHEST_READ 29 +constexpr u8 SER_FMT_VER_HIGHEST_READ = 29; // Saved on disk version -#define SER_FMT_VER_HIGHEST_WRITE 29 +constexpr u8 SER_FMT_VER_HIGHEST_WRITE = 29; // Lowest supported serialization version -#define SER_FMT_VER_LOWEST_READ 0 +constexpr u8 SER_FMT_VER_LOWEST_READ = 0; // Lowest serialization version for writing // Can't do < 24 anymore; we have 16-bit dynamically allocated node IDs // in memory; conversion just won't work in this direction. -#define SER_FMT_VER_LOWEST_WRITE 24 +constexpr u8 SER_FMT_VER_LOWEST_WRITE = 24; -inline bool ser_ver_supported(s32 v) { +inline bool ser_ver_supported_read(s32 v) +{ return v >= SER_FMT_VER_LOWEST_READ && v <= SER_FMT_VER_HIGHEST_READ; } +inline bool ser_ver_supported_write(s32 v) +{ + return v >= SER_FMT_VER_LOWEST_WRITE && v <= SER_FMT_VER_HIGHEST_WRITE; +} + /* Compression functions */ diff --git a/src/server.cpp b/src/server.cpp index d2aaf3432..ee9b0a859 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -21,6 +21,7 @@ #include "filesys.h" #include "mapblock.h" #include "server/serveractiveobject.h" +#include "serialization.h" // SER_FMT_VER_INVALID #include "settings.h" #include "profiler.h" #include "log.h" diff --git a/src/server.h b/src/server.h index f2d767e99..a9ba35c63 100644 --- a/src/server.h +++ b/src/server.h @@ -8,7 +8,6 @@ #include "map.h" #include "hud.h" #include "gamedef.h" -#include "serialization.h" // For SER_FMT_VER_INVALID #include "content/mods.h" #include "inventorymanager.h" #include "content/subgames.h" diff --git a/src/server/clientiface.cpp b/src/server/clientiface.cpp index f50b4273f..b114c6c84 100644 --- a/src/server/clientiface.cpp +++ b/src/server/clientiface.cpp @@ -8,6 +8,7 @@ #include "network/connection.h" #include "network/serveropcodes.h" #include "remoteplayer.h" +#include "serialization.h" // SER_FMT_VER_INVALID #include "settings.h" #include "mapblock.h" #include "serverenvironment.h" @@ -51,6 +52,8 @@ std::string ClientInterface::state2Name(ClientState state) } RemoteClient::RemoteClient() : + serialization_version(SER_FMT_VER_INVALID), + m_pending_serialization_version(SER_FMT_VER_INVALID), m_max_simul_sends(g_settings->getU16("max_simultaneous_block_sends_per_client")), m_min_time_from_building( g_settings->getFloat("full_block_send_enable_min_time_from_building")), diff --git a/src/server/clientiface.h b/src/server/clientiface.h index 4546f045e..294bcbd26 100644 --- a/src/server/clientiface.h +++ b/src/server/clientiface.h @@ -7,7 +7,6 @@ #include "irr_v3d.h" // for irrlicht datatypes #include "constants.h" -#include "serialization.h" // for SER_FMT_VER_INVALID #include "network/networkpacket.h" #include "network/networkprotocol.h" #include "network/address.h" @@ -219,7 +218,7 @@ public: // Also, the client must be moved to some other container. session_t peer_id = PEER_ID_INEXISTENT; // The serialization version to use with the client - u8 serialization_version = SER_FMT_VER_INVALID; + u8 serialization_version; // u16 net_proto_version = 0; @@ -333,7 +332,7 @@ public: private: // Version is stored in here after INIT before INIT2 - u8 m_pending_serialization_version = SER_FMT_VER_INVALID; + u8 m_pending_serialization_version; /* current state of client */ ClientState m_state = CS_Created; From df4e70b2c7631317430bd78061507270d23db67b Mon Sep 17 00:00:00 2001 From: DS Date: Thu, 28 Nov 2024 14:22:53 +0100 Subject: [PATCH 17/68] Add a setting to group transparency sorted triangles by buffer (#15115) --- builtin/settingtypes.txt | 5 ++++ src/client/clientmap.cpp | 7 ++++- src/client/clientmap.h | 1 + src/client/mapblock_mesh.cpp | 56 +++++++++++++++++++++++++++++------- src/client/mapblock_mesh.h | 10 +++++-- src/defaultsettings.cpp | 1 + 6 files changed, 66 insertions(+), 14 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 418e883d1..d23704694 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1843,6 +1843,11 @@ video_driver (Video driver) enum ,opengl,opengl3,ogles2 # Set to 0 to disable it entirely. transparency_sorting_distance (Transparency Sorting Distance) int 16 0 128 +# Draw transparency sorted triangles grouped by their mesh buffers. +# This breaks transparency sorting between mesh buffers, but avoids situations +# where transparency sorting would be very slow otherwise. +transparency_sorting_group_by_buffers (Transparency Sorting Group by Buffers) bool true + # Radius of cloud area stated in number of 64 node cloud squares. # Values larger than 26 will start to produce sharp cutoffs at cloud area corners. cloud_radius (Cloud radius) int 12 1 62 diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 9a788e3cc..c3207efd1 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -73,6 +73,7 @@ static const std::string ClientMap_settings[] = { "trilinear_filter", "bilinear_filter", "anisotropic_filter", + "transparency_sorting_group_by_buffers", "transparency_sorting_distance", "occlusion_culler", "enable_raytraced_culling", @@ -115,6 +116,9 @@ void ClientMap::onSettingChanged(std::string_view name, bool all) m_cache_bilinear_filter = g_settings->getBool("bilinear_filter"); if (all || name == "anisotropic_filter") m_cache_anistropic_filter = g_settings->getBool("anisotropic_filter"); + if (all || name == "transparency_sorting_group_by_buffers") + m_cache_transparency_sorting_group_by_buffers = + g_settings->getBool("transparency_sorting_group_by_buffers"); if (all || name == "transparency_sorting_distance") m_cache_transparency_sorting_distance = g_settings->getU16("transparency_sorting_distance"); if (all || name == "occlusion_culler") @@ -1337,7 +1341,8 @@ void ClientMap::updateTransparentMeshBuffers() } if (do_sort_block) { - blockmesh->updateTransparentBuffers(m_camera_position, block->getPos()); + blockmesh->updateTransparentBuffers(m_camera_position, block->getPos(), + m_cache_transparency_sorting_group_by_buffers); ++sorted_blocks; } else { blockmesh->consolidateTransparentBuffers(); diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 4855575f6..a72b6f50d 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -182,6 +182,7 @@ private: bool m_cache_trilinear_filter; bool m_cache_bilinear_filter; bool m_cache_anistropic_filter; + bool m_cache_transparency_sorting_group_by_buffers; u16 m_cache_transparency_sorting_distance; bool m_loops_occlusion_culler; diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index c212bd148..c8cd79248 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -3,6 +3,7 @@ // Copyright (C) 2010-2013 celeron55, Perttu Ahola #include "mapblock_mesh.h" +#include "CMeshBuffer.h" #include "client.h" #include "mapblock.h" #include "map.h" @@ -818,7 +819,8 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, return true; } -void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) +void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos, + bool group_by_buffers) { // nothing to do if the entire block is opaque if (m_transparent_triangles.empty()) @@ -834,24 +836,56 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) m_transparent_buffers_consolidated = false; m_transparent_buffers.clear(); + std::vector>> ordered_strains; + std::unordered_map strain_idxs; + + if (group_by_buffers) { + // find (reversed) order for strains, by iterating front-to-back + // (if a buffer A has a triangle nearer than all triangles of another + // buffer B, A should be drawn in front of (=after) B) + scene::SMeshBuffer *current_buffer = nullptr; + for (auto it = triangle_refs.rbegin(); it != triangle_refs.rend(); ++it) { + const auto &t = m_transparent_triangles[*it]; + if (current_buffer == t.buffer) + continue; + current_buffer = t.buffer; + auto [_it2, is_new] = + strain_idxs.emplace(current_buffer, ordered_strains.size()); + if (is_new) + ordered_strains.emplace_back(current_buffer, std::vector{}); + } + } + + // find order for triangles, by iterating back-to-front scene::SMeshBuffer *current_buffer = nullptr; - std::vector current_strain; + std::vector *current_strain = nullptr; for (auto i : triangle_refs) { const auto &t = m_transparent_triangles[i]; if (current_buffer != t.buffer) { - if (current_buffer) { - m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); - current_strain.clear(); - } current_buffer = t.buffer; + if (group_by_buffers) { + auto it = strain_idxs.find(current_buffer); + assert(it != strain_idxs.end()); + current_strain = &ordered_strains[it->second].second; + } else { + ordered_strains.emplace_back(current_buffer, std::vector{}); + current_strain = &ordered_strains.back().second; + } } - current_strain.push_back(t.p1); - current_strain.push_back(t.p2); - current_strain.push_back(t.p3); + current_strain->push_back(t.p1); + current_strain->push_back(t.p2); + current_strain->push_back(t.p3); } - if (!current_strain.empty()) - m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); + m_transparent_buffers.reserve(ordered_strains.size()); + if (group_by_buffers) { + // the order was reversed + for (auto it = ordered_strains.rbegin(); it != ordered_strains.rend(); ++it) + m_transparent_buffers.emplace_back(it->first, std::move(it->second)); + } else { + for (auto it = ordered_strains.begin(); it != ordered_strains.end(); ++it) + m_transparent_buffers.emplace_back(it->first, std::move(it->second)); + } } void MapBlockMesh::consolidateTransparentBuffers() diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 5a8daf50c..e7cadb3db 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -209,8 +209,14 @@ public: /// Center of the bounding-sphere, in BS-space, relative to block pos. v3f getBoundingSphereCenter() const { return m_bounding_sphere_center; } - /// update transparent buffers to render towards the camera - void updateTransparentBuffers(v3f camera_pos, v3s16 block_pos); + /** Update transparent buffers to render towards the camera. + * @param group_by_buffers If true, triangles in the same buffer are batched + * into the same PartialMeshBuffer, resulting in fewer draw calls, but + * wrong order. Triangles within a single buffer are still ordered, and + * buffers are ordered relative to each other (with respect to their nearest + * triangle). + */ + void updateTransparentBuffers(v3f camera_pos, v3s16 block_pos, bool group_by_buffers); void consolidateTransparentBuffers(); /// get the list of transparent buffers diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 345e4a07b..e6b26bd4a 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -302,6 +302,7 @@ void set_default_settings() settings->setDefault("arm_inertia", "true"); settings->setDefault("show_nametag_backgrounds", "true"); settings->setDefault("show_block_bounds_radius_near", "4"); + settings->setDefault("transparency_sorting_group_by_buffers", "true"); settings->setDefault("transparency_sorting_distance", "16"); settings->setDefault("enable_minimap", "true"); From d068f34753ee187f5adccbf31e214ab034766d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:00:09 +0100 Subject: [PATCH 18/68] Get rid of `definitelyIdentityMatrix` (#15480) --- irr/include/matrix4.h | 177 +-------------------------------------- irr/include/quaternion.h | 6 -- 2 files changed, 4 insertions(+), 179 deletions(-) diff --git a/irr/include/matrix4.h b/irr/include/matrix4.h index 36f98bb70..7897b9acb 100644 --- a/irr/include/matrix4.h +++ b/irr/include/matrix4.h @@ -13,11 +13,6 @@ #include "rect.h" #include "IrrCompileConfig.h" // for IRRLICHT_API -// enable this to keep track of changes to the matrix -// and make simpler identity check for seldom changing matrices -// otherwise identity check will always compare the elements -// #define USE_MATRIX_TEST - namespace irr { namespace core @@ -81,9 +76,6 @@ public: //! Simple operator for directly accessing every element of the matrix. T &operator()(const s32 row, const s32 col) { -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return M[row * 4 + col]; } @@ -93,9 +85,6 @@ public: //! Simple operator for linearly accessing every element of the matrix. T &operator[](u32 index) { -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return M[index]; } @@ -112,19 +101,12 @@ public: const T *pointer() const { return M; } T *pointer() { -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return M; } //! Returns true if other matrix is equal to this matrix. constexpr bool operator==(const CMatrix4 &other) const { -#if defined(USE_MATRIX_TEST) - if (definitelyIdentityMatrix && other.definitelyIdentityMatrix) - return true; -#endif for (s32 i = 0; i < 16; ++i) if (M[i] != other.M[i]) return false; @@ -443,31 +425,17 @@ public: //! Sets all matrix data members at once CMatrix4 &setM(const T *data); - //! Sets if the matrix is definitely identity matrix - void setDefinitelyIdentityMatrix(bool isDefinitelyIdentityMatrix); - - //! Gets if the matrix is definitely identity matrix - bool getDefinitelyIdentityMatrix() const; - //! Compare two matrices using the equal method bool equals(const core::CMatrix4 &other, const T tolerance = (T)ROUNDING_ERROR_f64) const; private: //! Matrix data, stored in row-major order T M[16]; -#if defined(USE_MATRIX_TEST) - //! Flag is this matrix is identity matrix - mutable u32 definitelyIdentityMatrix; -#endif }; // Default constructor template inline CMatrix4::CMatrix4(eConstructor constructor) -#if defined(USE_MATRIX_TEST) - : - definitelyIdentityMatrix(BIT_UNTESTED) -#endif { switch (constructor) { case EM4CONST_NOTHING: @@ -484,10 +452,6 @@ inline CMatrix4::CMatrix4(eConstructor constructor) // Copy constructor template inline CMatrix4::CMatrix4(const CMatrix4 &other, eConstructor constructor) -#if defined(USE_MATRIX_TEST) - : - definitelyIdentityMatrix(BIT_UNTESTED) -#endif { switch (constructor) { case EM4CONST_IDENTITY: @@ -713,9 +677,6 @@ inline CMatrix4 &CMatrix4::setbyproduct_nocheck(const CMatrix4 &other_a M[13] = m1[1] * m2[12] + m1[5] * m2[13] + m1[9] * m2[14] + m1[13] * m2[15]; M[14] = m1[2] * m2[12] + m1[6] * m2[13] + m1[10] * m2[14] + m1[14] * m2[15]; M[15] = m1[3] * m2[12] + m1[7] * m2[13] + m1[11] * m2[14] + m1[15] * m2[15]; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -787,9 +748,6 @@ inline CMatrix4 &CMatrix4::setTranslation(const vector3d &translation) M[12] = translation.X; M[13] = translation.Y; M[14] = translation.Z; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -799,9 +757,6 @@ inline CMatrix4 &CMatrix4::setInverseTranslation(const vector3d &transl M[12] = -translation.X; M[13] = -translation.Y; M[14] = -translation.Z; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -811,9 +766,6 @@ inline CMatrix4 &CMatrix4::setScale(const vector3d &scale) M[0] = scale.X; M[5] = scale.Y; M[10] = scale.Z; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -877,9 +829,6 @@ inline CMatrix4 &CMatrix4::setRotationRadians(const vector3d &rotation) M[8] = (T)(cPitch_sYaw * cRoll + sPitch * sRoll); M[9] = (T)(cPitch_sYaw * sRoll - sPitch * cRoll); M[10] = (T)(cPitch * cYaw); -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -981,9 +930,6 @@ inline CMatrix4 &CMatrix4::setInverseRotationRadians(const vector3d &ro M[2] = (T)(cPitch_sYaw * cRoll + sPitch * sRoll); M[6] = (T)(cPitch_sYaw * sRoll - sPitch * cRoll); M[10] = (T)(cPitch * cYaw); -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1015,9 +961,6 @@ inline CMatrix4 &CMatrix4::setRotationAxisRadians(const T &angle, const ve M[9] = (T)(tz * axis.Y - sx); M[10] = (T)(tz * axis.Z + c); -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1028,9 +971,6 @@ inline CMatrix4 &CMatrix4::makeIdentity() { memset(M, 0, 16 * sizeof(T)); M[0] = M[5] = M[10] = M[15] = (T)1; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = true; -#endif return *this; } @@ -1041,10 +981,6 @@ inline CMatrix4 &CMatrix4::makeIdentity() template inline bool CMatrix4::isIdentity() const { -#if defined(USE_MATRIX_TEST) - if (definitelyIdentityMatrix) - return true; -#endif if (!core::equals(M[12], (T)0) || !core::equals(M[13], (T)0) || !core::equals(M[14], (T)0) || !core::equals(M[15], (T)1)) return false; @@ -1068,9 +1004,6 @@ inline bool CMatrix4::isIdentity() const if ((j != i) && (!iszero((*this)(i,j)))) return false; */ -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = true; -#endif return true; } @@ -1106,10 +1039,6 @@ inline bool CMatrix4::isOrthogonal() const template inline bool CMatrix4::isIdentity_integer_base() const { -#if defined(USE_MATRIX_TEST) - if (definitelyIdentityMatrix) - return true; -#endif if (IR(M[0]) != F32_VALUE_1) return false; if (IR(M[1]) != 0) @@ -1146,9 +1075,6 @@ inline bool CMatrix4::isIdentity_integer_base() const if (IR(M[15]) != F32_VALUE_1) return false; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = true; -#endif return true; } @@ -1402,9 +1328,6 @@ inline bool CMatrix4::getInverse(CMatrix4 &out) const m[1] * (m[6] * m[8] - m[4] * m[10]) + m[2] * (m[4] * m[9] - m[5] * m[8])); -#if defined(USE_MATRIX_TEST) - out.definitelyIdentityMatrix = definitelyIdentityMatrix; -#endif return true; } @@ -1433,9 +1356,6 @@ inline bool CMatrix4::getInversePrimitive(CMatrix4 &out) const out.M[14] = (T) - (M[12] * M[8] + M[13] * M[9] + M[14] * M[10]); out.M[15] = 1; -#if defined(USE_MATRIX_TEST) - out.definitelyIdentityMatrix = definitelyIdentityMatrix; -#endif return true; } @@ -1444,10 +1364,6 @@ inline bool CMatrix4::getInversePrimitive(CMatrix4 &out) const template inline bool CMatrix4::makeInverse() { -#if defined(USE_MATRIX_TEST) - if (definitelyIdentityMatrix) - return true; -#endif CMatrix4 temp(EM4CONST_NOTHING); if (getInverse(temp)) { @@ -1464,9 +1380,6 @@ inline CMatrix4 &CMatrix4::operator=(const T &scalar) for (s32 i = 0; i < 16; ++i) M[i] = scalar; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1509,9 +1422,6 @@ inline CMatrix4 &CMatrix4::buildProjectionMatrixPerspectiveFovRH( M[14] = (T)(2.0f * zNear * zFar / (zNear - zFar)); } -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1554,9 +1464,6 @@ inline CMatrix4 &CMatrix4::buildProjectionMatrixPerspectiveFovLH( M[14] = (T)(2.0f * zNear * zFar / (zNear - zFar)); } -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1589,9 +1496,6 @@ inline CMatrix4 &CMatrix4::buildProjectionMatrixPerspectiveFovInfinityLH( M[14] = (T)(zNear * (epsilon - 1.f)); M[15] = 0; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1631,9 +1535,6 @@ inline CMatrix4 &CMatrix4::buildProjectionMatrixOrthoLH( M[14] = (T) - (zFar + zNear) / (zFar - zNear); } -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1673,9 +1574,6 @@ inline CMatrix4 &CMatrix4::buildProjectionMatrixOrthoRH( M[14] = (T) - (zFar + zNear) / (zFar - zNear); } -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1716,9 +1614,6 @@ inline CMatrix4 &CMatrix4::buildProjectionMatrixPerspectiveRH( M[14] = (T)(2.0f * zNear * zFar / (zNear - zFar)); } -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1759,9 +1654,6 @@ inline CMatrix4 &CMatrix4::buildProjectionMatrixPerspectiveLH( M[14] = (T)(2.0f * zNear * zFar / (zNear - zFar)); } -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } @@ -1791,9 +1683,7 @@ inline CMatrix4 &CMatrix4::buildShadowMatrix(const core::vector3df &light, M[13] = (T)(-plane.D * light.Y); M[14] = (T)(-plane.D * light.Z); M[15] = (T)(-plane.D * point + d); -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif + return *this; } @@ -1831,9 +1721,7 @@ inline CMatrix4 &CMatrix4::buildCameraLookAtMatrixLH( M[13] = (T)-yaxis.dotProduct(position); M[14] = (T)-zaxis.dotProduct(position); M[15] = 1; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif + return *this; } @@ -1871,9 +1759,7 @@ inline CMatrix4 &CMatrix4::buildCameraLookAtMatrixRH( M[13] = (T)-yaxis.dotProduct(position); M[14] = (T)-zaxis.dotProduct(position); M[15] = 1; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif + return *this; } @@ -1924,9 +1810,6 @@ inline void CMatrix4::getTransposed(CMatrix4 &o) const o[13] = M[7]; o[14] = M[11]; o[15] = M[15]; -#if defined(USE_MATRIX_TEST) - o.definitelyIdentityMatrix = definitelyIdentityMatrix; -#endif } // used to scale <-1,-1><1,1> to viewport @@ -2064,9 +1947,6 @@ inline void CMatrix4::setRotationCenter(const core::vector3df ¢er, const M[13] = -M[1] * center.X - M[5] * center.Y - M[9] * center.Z + (center.Y - translation.Y); M[14] = -M[2] * center.X - M[6] * center.Y - M[10] * center.Z + (center.Z - translation.Z); M[15] = (T)1.0; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif } /*! @@ -2108,9 +1988,7 @@ inline CMatrix4 &CMatrix4::buildTextureTransform(f32 rotateRad, M[13] = 0; M[14] = 0; M[15] = 1; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif + return *this; } @@ -2129,9 +2007,6 @@ inline CMatrix4 &CMatrix4::setTextureRotationCenter(f32 rotateRad) M[8] = (T)(0.5f * (s - c) + 0.5f); M[9] = (T)(-0.5f * (s + c) + 0.5f); -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = definitelyIdentityMatrix && (rotateRad == 0.0f); -#endif return *this; } @@ -2141,9 +2016,6 @@ inline CMatrix4 &CMatrix4::setTextureTranslate(f32 x, f32 y) M[8] = (T)x; M[9] = (T)y; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = definitelyIdentityMatrix && (x == 0.0f) && (y == 0.0f); -#endif return *this; } @@ -2159,10 +2031,6 @@ inline CMatrix4 &CMatrix4::setTextureTranslateTransposed(f32 x, f32 y) { M[2] = (T)x; M[6] = (T)y; - -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = definitelyIdentityMatrix && (x == 0.0f) && (y == 0.0f); -#endif return *this; } @@ -2171,9 +2039,6 @@ inline CMatrix4 &CMatrix4::setTextureScale(f32 sx, f32 sy) { M[0] = (T)sx; M[5] = (T)sy; -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = definitelyIdentityMatrix && (sx == 1.0f) && (sy == 1.0f); -#endif return *this; } @@ -2191,10 +2056,6 @@ inline CMatrix4 &CMatrix4::setTextureScaleCenter(f32 sx, f32 sy) M[5] = (T)sy; M[8] = (T)(0.5f - 0.5f * sx); M[9] = (T)(0.5f - 0.5f * sy); - -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = definitelyIdentityMatrix && (sx == 1.0f) && (sy == 1.0f); -#endif return *this; } @@ -2203,43 +2064,13 @@ template inline CMatrix4 &CMatrix4::setM(const T *data) { memcpy(M, data, 16 * sizeof(T)); - -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = false; -#endif return *this; } -// sets if the matrix is definitely identity matrix -template -inline void CMatrix4::setDefinitelyIdentityMatrix(bool isDefinitelyIdentityMatrix) -{ -#if defined(USE_MATRIX_TEST) - definitelyIdentityMatrix = isDefinitelyIdentityMatrix; -#else - (void)isDefinitelyIdentityMatrix; // prevent compiler warning -#endif -} - -// gets if the matrix is definitely identity matrix -template -inline bool CMatrix4::getDefinitelyIdentityMatrix() const -{ -#if defined(USE_MATRIX_TEST) - return definitelyIdentityMatrix; -#else - return false; -#endif -} - //! Compare two matrices using the equal method template inline bool CMatrix4::equals(const core::CMatrix4 &other, const T tolerance) const { -#if defined(USE_MATRIX_TEST) - if (definitelyIdentityMatrix && other.definitelyIdentityMatrix) - return true; -#endif for (s32 i = 0; i < 16; ++i) if (!core::equals(M[i], other.M[i], tolerance)) return false; diff --git a/irr/include/quaternion.h b/irr/include/quaternion.h index 4e90fa067..e23b1317d 100644 --- a/irr/include/quaternion.h +++ b/irr/include/quaternion.h @@ -358,8 +358,6 @@ inline void quaternion::getMatrixFast(matrix4 &dest) const dest[13] = 0.f; dest[14] = 0.f; dest[15] = 1.f; - - dest.setDefinitelyIdentityMatrix(false); } /*! @@ -397,8 +395,6 @@ inline void quaternion::getMatrix(matrix4 &dest, dest[13] = center.Y; dest[14] = center.Z; dest[15] = 1.f; - - dest.setDefinitelyIdentityMatrix(false); } /*! @@ -471,8 +467,6 @@ inline void quaternion::getMatrix_transposed(matrix4 &dest) const dest[7] = 0.f; dest[11] = 0.f; dest[15] = 1.f; - - dest.setDefinitelyIdentityMatrix(false); } // Inverts this quaternion From c3db9492a75b72d764ed3d2893ea87fb34eea861 Mon Sep 17 00:00:00 2001 From: JosiahWI <41302989+JosiahWI@users.noreply.github.com> Date: Fri, 29 Nov 2024 05:02:48 -0600 Subject: [PATCH 19/68] Update CMakeLists to use `add_compile_definitions` (#15483) This is a newer feature introduced in CMake 3.12, which is now our minimum version. It supercedes `add_definitions`. I've also replaced some calls to set `CMAKE__FLAGS` that were used to set definitions. This is a fairly trivial routine build maintenance that is not intended to have any behavioral effects. --- irr/src/CMakeLists.txt | 46 ++++++++++++++++++++---------------------- src/CMakeLists.txt | 23 ++++++++++++--------- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index 1fbfe5c9f..a05607830 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -9,7 +9,7 @@ option(USE_SDL2 "Use the SDL2 backend" ${DEFAULT_SDL2}) # Compiler flags if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_definitions(-D_DEBUG) + add_compile_definitions(_DEBUG) endif() set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) @@ -43,9 +43,7 @@ elseif(MSVC) endif() # Platform-independent configuration (hard-coded currently) -add_definitions( - -DIRR_ENABLE_BUILTIN_FONT -) +add_compile_definitions(IRR_ENABLE_BUILTIN_FONT) # Platform-specific configuration @@ -56,35 +54,35 @@ endif() # Device if(WIN32) - add_definitions(-D_IRR_WINDOWS_ -D_IRR_WINDOWS_API_) + add_compile_definitions(_IRR_WINDOWS_ _IRR_WINDOWS_API_) set(DEVICE "WINDOWS") elseif(APPLE) - add_definitions(-D_IRR_OSX_PLATFORM_) + add_compile_definitions(_IRR_OSX_PLATFORM_) set(DEVICE "OSX") elseif(ANDROID) - add_definitions(-D_IRR_ANDROID_PLATFORM_) + add_compile_definitions(_IRR_ANDROID_PLATFORM_) if(NOT USE_SDL2) message(FATAL_ERROR "The Android build requires SDL2") endif() elseif(EMSCRIPTEN) - add_definitions(-D_IRR_EMSCRIPTEN_PLATFORM_ -D_IRR_COMPILE_WITH_EGL_MANAGER_) + add_compile_definitions(_IRR_EMSCRIPTEN_PLATFORM_ _IRR_COMPILE_WITH_EGL_MANAGER_) set(LINUX_PLATFORM TRUE) set(DEVICE "SDL") elseif(SOLARIS) - add_definitions(-D_IRR_SOLARIS_PLATFORM_ -D_IRR_POSIX_API_) + add_compile_definitions(_IRR_SOLARIS_PLATFORM_ _IRR_POSIX_API_) set(DEVICE "X11") else() - add_definitions(-D_IRR_POSIX_API_) + add_compile_definitions(_IRR_POSIX_API_) set(LINUX_PLATFORM TRUE) set(DEVICE "X11") endif() if(LINUX_PLATFORM) - add_definitions(-D_IRR_LINUX_PLATFORM_) + add_compile_definitions(_IRR_LINUX_PLATFORM_) endif() if(MSVC) - add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) endif() if(USE_SDL2) @@ -93,7 +91,7 @@ elseif(DEVICE STREQUAL "SDL") message(FATAL_ERROR "SDL was used but not enabled?!") endif() -add_definitions("-D_IRR_COMPILE_WITH_${DEVICE}_DEVICE_") +add_compile_definitions("_IRR_COMPILE_WITH_${DEVICE}_DEVICE_") # X11 @@ -114,7 +112,7 @@ endif() # Joystick if(NOT (BSD OR SOLARIS OR EMSCRIPTEN)) - add_definitions(-D_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) + add_compile_definitions(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) endif() # OpenGL @@ -154,15 +152,15 @@ endif() if(ENABLE_OPENGL OR (ENABLE_OPENGL3 AND NOT USE_SDL2)) if(ENABLE_OPENGL) - add_definitions(-D_IRR_COMPILE_WITH_OPENGL_) + add_compile_definitions(_IRR_COMPILE_WITH_OPENGL_) set(OPENGL_DIRECT_LINK TRUE) # driver relies on this endif() if(DEVICE STREQUAL "WINDOWS") - add_definitions(-D_IRR_COMPILE_WITH_WGL_MANAGER_) + add_compile_definitions(_IRR_COMPILE_WITH_WGL_MANAGER_) elseif(DEVICE STREQUAL "X11") - add_definitions(-D_IRR_COMPILE_WITH_GLX_MANAGER_) + add_compile_definitions(_IRR_COMPILE_WITH_GLX_MANAGER_) elseif(DEVICE STREQUAL "OSX") - add_definitions(-D_IRR_COMPILE_WITH_NSOGL_MANAGER_) + add_compile_definitions(_IRR_COMPILE_WITH_NSOGL_MANAGER_) endif() endif() @@ -177,14 +175,14 @@ if(ENABLE_OPENGL3) endif() if(ENABLE_GLES2) - add_definitions(-D_IRR_COMPILE_WITH_OGLES2_) + add_compile_definitions(_IRR_COMPILE_WITH_OGLES2_) if(DEVICE MATCHES "^(WINDOWS|X11)$" OR EMSCRIPTEN) - add_definitions(-D_IRR_COMPILE_WITH_EGL_MANAGER_) + add_compile_definitions(_IRR_COMPILE_WITH_EGL_MANAGER_) endif() endif() if(ENABLE_WEBGL1) - add_definitions(-D_IRR_COMPILE_WITH_WEBGL1_) + add_compile_definitions(_IRR_COMPILE_WITH_WEBGL1_) endif() # Misc @@ -192,7 +190,7 @@ endif() include(TestBigEndian) TEST_BIG_ENDIAN(BIG_ENDIAN) if(BIG_ENDIAN) - add_definitions(-D__BIG_ENDIAN__) + add_compile_definitions(__BIG_ENDIAN__) endif() # Configuration report @@ -263,7 +261,7 @@ if(ENABLE_OPENGL AND DEVICE STREQUAL "SDL") #endif\n\ int main() {}" CHECK_GL_VERSION_4_5) if(CHECK_GL_VERSION_4_5) - add_definitions(-DIRR_PREFER_SDL_GL_HEADER) + add_compile_definitions(IRR_PREFER_SDL_GL_HEADER) endif() endif() @@ -275,7 +273,7 @@ elseif(APPLE) find_library(COCOA_LIB Cocoa REQUIRED) find_library(IOKIT_LIB IOKit REQUIRED) - add_definitions(-DGL_SILENCE_DEPRECATION) + add_compile_definitions(GL_SILENCE_DEPRECATION) elseif(NOT USE_SDL2) # Unix probably find_package(X11 REQUIRED) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 65e5ab6bc..e6e493184 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -260,12 +260,11 @@ endif() # Haiku endian support if(HAIKU) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_BSD_SOURCE") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_BSD_SOURCE") + add_compile_definitions(_BSD_SOURCE) endif() # Use cmake_config.h -add_definitions(-DUSE_CMAKE_CONFIG_H) +add_compile_definitions(USE_CMAKE_CONFIG_H) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -276,11 +275,15 @@ if(WIN32) if(MSVC) # MSVC Specifics set(PLATFORM_LIBS dbghelp.lib ${PLATFORM_LIBS}) # Surpress some useless warnings - add_definitions ( /D "_CRT_SECURE_NO_DEPRECATE" /W1 ) - # Get M_PI to work - add_definitions(/D "_USE_MATH_DEFINES") - # Don't define min/max macros in minwindef.h - add_definitions(/D "NOMINMAX") + add_compile_options(/W1) + add_compile_definitions( + # Suppress some useless warnings + _CRT_SECURE_NO_DEPRECATE + # Get M_PI to work + _USE_MATH_DEFINES + # Don't define min/max macros in minwindef.h + NOMINMAX + ) endif() set(PLATFORM_LIBS ws2_32.lib version.lib shlwapi.lib winmm.lib ${PLATFORM_LIBS}) @@ -871,7 +874,7 @@ endif() if(MSVC) # Visual Studio - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _WIN32_WINNT=0x0601 /D WIN32_LEAN_AND_MEAN /D _CRT_SECURE_NO_WARNINGS") + add_compile_definitions(_WIN32_WINNT=0x0601 WIN32_LEAN_AND_MEAN _CRT_SECURE_NO_WARNINGS) # EHa enables SEH exceptions (used for catching segfaults) set(CMAKE_CXX_FLAGS_RELEASE "/EHa /Ox /MD /GS- /Zi /fp:fast /D NDEBUG /D _HAS_ITERATOR_DEBUGGING=0") if(CMAKE_SIZEOF_VOID_P EQUAL 4) @@ -915,7 +918,7 @@ else() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(OTHER_FLAGS "${OTHER_FLAGS} -mthreads") endif() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_WIN32_WINNT=0x0601 -DWIN32_LEAN_AND_MEAN") + add_compile_definitions(_WIN32_WINNT=0x0601 WIN32_LEAN_AND_MEAN) endif() # Use a safe subset of flags to speed up math calculations: From 7cc5a6ec687d34338cd7608d18b656527883daf8 Mon Sep 17 00:00:00 2001 From: "Kevin @ Sesam Solutions" <56027840+KevinAtSesam@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:51:33 +0100 Subject: [PATCH 20/68] Add Minetest keyword for backwards compatibility (#15491) This helps people looking for `minetest` --- misc/net.minetest.minetest.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/net.minetest.minetest.desktop b/misc/net.minetest.minetest.desktop index c6c99c469..571eb58a9 100644 --- a/misc/net.minetest.minetest.desktop +++ b/misc/net.minetest.minetest.desktop @@ -11,4 +11,4 @@ PrefersNonDefaultGPU=true Type=Application Categories=Game;Simulation; StartupNotify=false -Keywords=sandbox;world;mining;crafting;blocks;nodes;multiplayer;roleplaying; +Keywords=sandbox;world;mining;crafting;blocks;nodes;multiplayer;roleplaying;minetest; From e9080f91f28af50a649c146ea34c371ef19affc3 Mon Sep 17 00:00:00 2001 From: wrrrzr <161970349+wrrrzr@users.noreply.github.com> Date: Sun, 1 Dec 2024 22:52:13 +0300 Subject: [PATCH 21/68] Cleanup ban.cpp/h (#15496) Make BanManager more const correctly Delete unused includes --- src/server/ban.cpp | 13 ++++++------- src/server/ban.h | 13 +++++-------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/server/ban.cpp b/src/server/ban.cpp index 737f5a57e..e63c4708f 100644 --- a/src/server/ban.cpp +++ b/src/server/ban.cpp @@ -7,11 +7,11 @@ #include #include "threading/mutex_auto_lock.h" #include -#include #include "util/strfnd.h" #include "util/string.h" #include "log.h" #include "filesys.h" +#include "exceptions.h" BanManager::BanManager(const std::string &banfilepath): m_banfilepath(banfilepath) @@ -68,13 +68,13 @@ void BanManager::save() m_modified = false; } -bool BanManager::isIpBanned(const std::string &ip) +bool BanManager::isIpBanned(const std::string &ip) const { MutexAutoLock lock(m_mutex); return m_ips.find(ip) != m_ips.end(); } -std::string BanManager::getBanDescription(const std::string &ip_or_name) +std::string BanManager::getBanDescription(const std::string &ip_or_name) const { MutexAutoLock lock(m_mutex); std::string s; @@ -88,10 +88,10 @@ std::string BanManager::getBanDescription(const std::string &ip_or_name) return s; } -std::string BanManager::getBanName(const std::string &ip) +std::string BanManager::getBanName(const std::string &ip) const { MutexAutoLock lock(m_mutex); - StringMap::iterator it = m_ips.find(ip); + StringMap::const_iterator it = m_ips.find(ip); if (it == m_ips.end()) return ""; return it->second; @@ -118,9 +118,8 @@ void BanManager::remove(const std::string &ip_or_name) } -bool BanManager::isModified() +bool BanManager::isModified() const { MutexAutoLock lock(m_mutex); return m_modified; } - diff --git a/src/server/ban.h b/src/server/ban.h index 146f3210a..4acd58fba 100644 --- a/src/server/ban.h +++ b/src/server/ban.h @@ -5,9 +5,6 @@ #pragma once #include "util/string.h" -#include "threading/thread.h" -#include "exceptions.h" -#include #include #include @@ -18,16 +15,16 @@ public: ~BanManager(); void load(); void save(); - bool isIpBanned(const std::string &ip); + bool isIpBanned(const std::string &ip) const; // Supplying ip_or_name = "" lists all bans. - std::string getBanDescription(const std::string &ip_or_name); - std::string getBanName(const std::string &ip); + std::string getBanDescription(const std::string &ip_or_name) const; + std::string getBanName(const std::string &ip) const; void add(const std::string &ip, const std::string &name); void remove(const std::string &ip_or_name); - bool isModified(); + bool isModified() const; private: - std::mutex m_mutex; + mutable std::mutex m_mutex; std::string m_banfilepath = ""; StringMap m_ips; bool m_modified = false; From a4d1b5b155566cb90961f6b75cf97b3ef21f20f8 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 3 Dec 2024 16:51:34 +0100 Subject: [PATCH 22/68] Fix script security path normalization in presence of links (#15481) --- src/filesys.cpp | 37 ++++++++++++++++++++++- src/filesys.h | 6 ++++ src/porting.cpp | 4 +++ src/porting.h | 3 +- src/script/cpp_api/s_security.cpp | 49 ++++++++++++------------------- src/script/scripting_mainmenu.cpp | 7 +++-- src/unittest/test_filesys.cpp | 44 ++++++++++++++++++++++++++- 7 files changed, 112 insertions(+), 38 deletions(-) diff --git a/src/filesys.cpp b/src/filesys.cpp index 8881eb2ca..a368bc697 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -833,16 +833,51 @@ std::string RemoveRelativePathComponents(std::string path) std::string AbsolutePath(const std::string &path) { #ifdef _WIN32 + // handle behavior differences on windows + if (path.empty()) + return ""; + else if (!PathExists(path)) + return ""; char *abs_path = _fullpath(NULL, path.c_str(), MAX_PATH); #else char *abs_path = realpath(path.c_str(), NULL); #endif - if (!abs_path) return ""; + if (!abs_path) + return ""; std::string abs_path_str(abs_path); free(abs_path); return abs_path_str; } +std::string AbsolutePathPartial(const std::string &path) +{ + if (path.empty()) + return ""; + // Try to determine absolute path + std::string abs_path = fs::AbsolutePath(path); + if (!abs_path.empty()) + return abs_path; + // Remove components until it works + std::string cur_path = path; + std::string removed; + while (abs_path.empty() && !cur_path.empty()) { + std::string component; + cur_path = RemoveLastPathComponent(cur_path, &component); + removed = component + (removed.empty() ? "" : DIR_DELIM + removed); + abs_path = AbsolutePath(cur_path); + } + // If we had a relative path that does not exist, it needs to be joined with cwd + if (cur_path.empty() && !IsPathAbsolute(path)) + abs_path = AbsolutePath("."); + // or there's an error + if (abs_path.empty()) + return ""; + // Put them back together and resolve the remaining relative components + if (!removed.empty()) + abs_path.append(DIR_DELIM).append(removed); + return RemoveRelativePathComponents(abs_path); +} + const char *GetFilenameFromPath(const char *path) { const char *filename = strrchr(path, DIR_DELIM_CHAR); diff --git a/src/filesys.h b/src/filesys.h index dededcdb3..0e974d822 100644 --- a/src/filesys.h +++ b/src/filesys.h @@ -131,6 +131,12 @@ std::string RemoveRelativePathComponents(std::string path); // components and symlinks removed. Returns "" on error. std::string AbsolutePath(const std::string &path); +// This is a combination of RemoveRelativePathComponents() and AbsolutePath() +// It will resolve symlinks for the leading path components that exist and +// still remove "." and ".." in the rest of the path. +// Returns "" on error. +std::string AbsolutePathPartial(const std::string &path); + // Returns the filename from a path or the entire path if no directory // delimiter is found. const char *GetFilenameFromPath(const char *path); diff --git a/src/porting.cpp b/src/porting.cpp index f0e2bee0b..faef75b7c 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -688,6 +688,10 @@ void initializePaths() #endif // RUN_IN_PLACE + assert(!path_share.empty()); + assert(!path_user.empty()); + assert(!path_cache.empty()); + infostream << "Detected share path: " << path_share << std::endl; infostream << "Detected user path: " << path_user << std::endl; infostream << "Detected cache path: " << path_cache << std::endl; diff --git a/src/porting.h b/src/porting.h index edbc236a8..d6cec6c1a 100644 --- a/src/porting.h +++ b/src/porting.h @@ -109,8 +109,7 @@ extern std::string path_cache; bool getCurrentExecPath(char *buf, size_t len); /* - Get full path of stuff in data directory. - Example: "stone.png" -> "../data/stone.png" + Concatenate subpath to path_share. */ std::string getDataPath(const char *subpath); diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 40a95ca1a..38094ab8c 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -566,38 +566,23 @@ bool ScriptApiSecurity::checkPath(lua_State *L, const char *path, if (write_allowed) *write_allowed = false; - std::string abs_path = fs::AbsolutePath(path); - - // If we couldn't find the absolute path (path doesn't exist) then - // try removing the last components until it works (to allow - // non-existent files/folders for mkdir). - std::string cur_path = path; - std::string removed; - while (abs_path.empty() && !cur_path.empty()) { - std::string component; - cur_path = fs::RemoveLastPathComponent(cur_path, &component); - if (component == "..") { - // Parent components can't be allowed or we could allow something like - // /home/user/minetest/worlds/foo/noexist/../../../../../../etc/passwd. - // If we have previous non-relative elements in the path we might be - // able to remove them so that things like worlds/foo/noexist/../auth.txt - // could be allowed, but those paths will be interpreted as nonexistent - // by the operating system anyways. - return false; - } - removed = component + (removed.empty() ? "" : DIR_DELIM + removed); - abs_path = fs::AbsolutePath(cur_path); - } - if (abs_path.empty()) - return false; - // Add the removed parts back so that you can e.g. create a - // directory in worldmods if worldmods doesn't exist. - if (!removed.empty()) - abs_path += DIR_DELIM + removed; - + // We can't use AbsolutePath() here since we want to allow creating paths that + // do not yet exist. But RemoveRelativePathComponents() would also be incorrect + // since that wouldn't normalize subpaths that *do* exist. + // This is required so that comparisons with other normalized paths work correctly. + std::string abs_path = fs::AbsolutePathPartial(path); tracestream << "ScriptApiSecurity: path \"" << path << "\" resolved to \"" << abs_path << "\"" << std::endl; + if (abs_path.empty()) + return false; + + // Note: abs_path can be a valid path while path isn't, e.g. + // abs_path = "/home/user/.luanti" + // path = "/home/user/.luanti/noexist/.." + // Letting this through the sandbox isn't a concern as any actual attempts to + // use the path would fail. + // Ask the environment-specific implementation auto *sec = ModApiBase::getScriptApi(L); return sec->checkPathInternal(abs_path, write_required, write_allowed); @@ -617,9 +602,11 @@ bool ScriptApiSecurity::checkPathWithGamedef(lua_State *L, if (!gamedef) return false; - if (!abs_path.empty()) { + assert(!abs_path.empty()); + + if (!g_settings_path.empty()) { // Don't allow accessing the settings file - str = fs::AbsolutePath(g_settings_path); + str = fs::AbsolutePathPartial(g_settings_path); if (str == abs_path) return false; } diff --git a/src/script/scripting_mainmenu.cpp b/src/script/scripting_mainmenu.cpp index d5434c325..88f80a001 100644 --- a/src/script/scripting_mainmenu.cpp +++ b/src/script/scripting_mainmenu.cpp @@ -76,10 +76,11 @@ void MainMenuScripting::registerLuaClasses(lua_State *L, int top) bool MainMenuScripting::mayModifyPath(const std::string &path) { - if (fs::PathStartsWith(path, fs::TempPath())) + std::string path_temp = fs::AbsolutePathPartial(fs::TempPath()); + if (fs::PathStartsWith(path, path_temp)) return true; - std::string path_user = fs::RemoveRelativePathComponents(porting::path_user); + std::string path_user = fs::AbsolutePathPartial(porting::path_user); if (fs::PathStartsWith(path, path_user + DIR_DELIM "client")) return true; @@ -92,7 +93,7 @@ bool MainMenuScripting::mayModifyPath(const std::string &path) if (fs::PathStartsWith(path, path_user + DIR_DELIM "worlds")) return true; - if (fs::PathStartsWith(path, fs::RemoveRelativePathComponents(porting::path_cache))) + if (fs::PathStartsWith(path, fs::AbsolutePathPartial(porting::path_cache))) return true; return false; diff --git a/src/unittest/test_filesys.cpp b/src/unittest/test_filesys.cpp index 9bab4fb5d..34666f611 100644 --- a/src/unittest/test_filesys.cpp +++ b/src/unittest/test_filesys.cpp @@ -24,6 +24,7 @@ public: void testRemoveLastPathComponent(); void testRemoveLastPathComponentWithTrailingDelimiter(); void testRemoveRelativePathComponent(); + void testAbsolutePath(); void testSafeWriteToFile(); void testCopyFileContents(); void testNonExist(); @@ -39,6 +40,7 @@ void TestFileSys::runTests(IGameDef *gamedef) TEST(testRemoveLastPathComponent); TEST(testRemoveLastPathComponentWithTrailingDelimiter); TEST(testRemoveRelativePathComponent); + TEST(testAbsolutePath); TEST(testSafeWriteToFile); TEST(testCopyFileContents); TEST(testNonExist); @@ -55,7 +57,7 @@ static std::string p(std::string path) for (size_t i = 0; i < path.size(); ++i) { if (path[i] == '/') { path.replace(i, 1, DIR_DELIM); - i += std::string(DIR_DELIM).size() - 1; // generally a no-op + i += strlen(DIR_DELIM) - 1; // generally a no-op } } @@ -259,6 +261,46 @@ void TestFileSys::testRemoveRelativePathComponent() } +void TestFileSys::testAbsolutePath() +{ + const auto dir_path = getTestTempDirectory(); + + /* AbsolutePath */ + UASSERTEQ(auto, fs::AbsolutePath(""), ""); // empty is a not valid path + const auto cwd = fs::AbsolutePath("."); + UASSERTCMP(auto, !=, cwd, ""); + { + const auto dir_path2 = getTestTempFile(); + UASSERTEQ(auto, fs::AbsolutePath(dir_path2), ""); // doesn't exist + fs::CreateDir(dir_path2); + UASSERTCMP(auto, !=, fs::AbsolutePath(dir_path2), ""); // now it does + UASSERTEQ(auto, fs::AbsolutePath(dir_path2 + DIR_DELIM ".."), fs::AbsolutePath(dir_path)); + } + + /* AbsolutePathPartial */ + // equivalent to AbsolutePath if it exists + UASSERTEQ(auto, fs::AbsolutePathPartial("."), cwd); + UASSERTEQ(auto, fs::AbsolutePathPartial(dir_path), fs::AbsolutePath(dir_path)); + // usual usage of the function with a partially existing path + auto expect = cwd + DIR_DELIM + p("does/not/exist"); + UASSERTEQ(auto, fs::AbsolutePathPartial("does/not/exist"), expect); + UASSERTEQ(auto, fs::AbsolutePathPartial(expect), expect); + + // a nonsense combination as you couldn't actually access it, but allowed by function + UASSERTEQ(auto, fs::AbsolutePathPartial("bla/blub/../.."), cwd); + UASSERTEQ(auto, fs::AbsolutePathPartial("./bla/blub/../.."), cwd); + +#ifdef __unix__ + // one way to produce the error case is to remove more components than there are + // but only if the path does not actually exist ("/.." does exist). + UASSERTEQ(auto, fs::AbsolutePathPartial("/.."), "/"); + UASSERTEQ(auto, fs::AbsolutePathPartial("/noexist/../.."), ""); +#endif + // or with an empty path + UASSERTEQ(auto, fs::AbsolutePathPartial(""), ""); +} + + void TestFileSys::testSafeWriteToFile() { const std::string dest_path = getTestTempFile(); From 818bca68d1633be775dc8c1a76a9325c03b9d977 Mon Sep 17 00:00:00 2001 From: JosiahWI <41302989+JosiahWI@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:51:53 -0600 Subject: [PATCH 23/68] Use `add_compile_options` where appropriate --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e6e493184..67f698b85 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -896,12 +896,12 @@ if(MSVC) # Flags that cannot be shared between cl and clang-cl # https://clang.llvm.org/docs/UsersManual.html#clang-cl if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld") + add_compile_options(-fuse-ld=lld) # Disable pragma-pack warning set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-pragma-pack") else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + add_compile_options(/MP) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /TP /FD /GL") set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") endif() From 03813a5b5e0b639ff9832148e3f9da34a65674e3 Mon Sep 17 00:00:00 2001 From: JosiahWI <41302989+JosiahWI@users.noreply.github.com> Date: Tue, 3 Dec 2024 09:52:15 -0600 Subject: [PATCH 24/68] Use CMake `list` directives where appropriate I think this communicates the intent a little better than using a `set` directive, and it makes the code a little less verbose, too. --- src/CMakeLists.txt | 35 +++++++++++++++++------------------ src/client/CMakeLists.txt | 2 +- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 67f698b85..d764e186e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -273,7 +273,7 @@ set(PLATFORM_LIBS Threads::Threads) if(WIN32) # Windows if(MSVC) # MSVC Specifics - set(PLATFORM_LIBS dbghelp.lib ${PLATFORM_LIBS}) + list(APPEND PLATFORM_LIBS dbghelp.lib) # Surpress some useless warnings add_compile_options(/W1) add_compile_definitions( @@ -285,7 +285,7 @@ if(WIN32) NOMINMAX ) endif() - set(PLATFORM_LIBS ws2_32.lib version.lib shlwapi.lib winmm.lib ${PLATFORM_LIBS}) + list(APPEND PLATFORM_LIBS ws2_32.lib version.lib shlwapi.lib winmm.lib) set(EXTRA_DLL "" CACHE FILEPATH "Optional paths to additional DLLs that should be packaged") @@ -312,28 +312,28 @@ if(WIN32) endif() else() # Unix probably - set(PLATFORM_LIBS ${PLATFORM_LIBS} ${CMAKE_DL_LIBS}) + list(APPEND PLATFORM_LIBS ${CMAKE_DL_LIBS}) if(APPLE) - set(PLATFORM_LIBS "-framework CoreFoundation" ${PLATFORM_LIBS}) + list(APPEND PLATFORM_LIBS "-framework CoreFoundation") else() check_library_exists(rt clock_gettime "" HAVE_LIBRT) if (HAVE_LIBRT) - set(PLATFORM_LIBS -lrt ${PLATFORM_LIBS}) + list(APPEND PLATFORM_LIBS -lrt) endif(HAVE_LIBRT) endif(APPLE) find_library(ICONV_LIBRARY iconv) mark_as_advanced(ICONV_LIBRARY) if (ICONV_LIBRARY) - set(PLATFORM_LIBS ${PLATFORM_LIBS} ${ICONV_LIBRARY}) + list(APPEND PLATFORM_LIBS ${ICONV_LIBRARY}) endif() if (HAIKU) - set(PLATFORM_LIBS ${PLATFORM_LIBS} network) + list(APPEND PLATFORM_LIBS network) endif() if (ANDROID) - set(PLATFORM_LIBS ${PLATFORM_LIBS} android log) + list(APPEND PLATFORM_LIBS android log) endif() endif() @@ -347,7 +347,7 @@ if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") check_c_source_compiles("int main(){}" HAVE_LINK_ATOMIC) set(CMAKE_REQUIRED_LIBRARIES "") if(HAVE_LINK_ATOMIC) - set(PLATFORM_LIBS ${PLATFORM_LIBS} "-latomic") + list(APPEND PLATFORM_LIBS "-latomic") endif() endif() @@ -471,21 +471,21 @@ set(common_SRCS ) if(ANDROID) - set(common_SRCS ${common_SRCS} porting_android.cpp) + list(APPEND common_SRCS porting_android.cpp) endif() if(BUILD_UNITTESTS) add_subdirectory(unittest) - set(common_SRCS ${common_SRCS} ${UNITTEST_SRCS}) + list(APPEND common_SRCS ${UNITTEST_SRCS}) endif() if(BUILD_BENCHMARKS) add_subdirectory(benchmark) - set(common_SRCS ${common_SRCS} ${BENCHMARK_SRCS}) + list(APPEND common_SRCS ${BENCHMARK_SRCS}) endif() if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) - set(common_SRCS ${common_SRCS} catch.cpp) + list(APPEND common_SRCS catch.cpp) endif() # This gives us the icon and file version information @@ -516,8 +516,7 @@ if (BUILD_CLIENT) add_subdirectory(irrlicht_changes) endif(BUILD_CLIENT) -set(client_SRCS - ${client_SRCS} +list(APPEND client_SRCS ${common_SRCS} ${gui_SRCS} ${client_network_SRCS} @@ -526,11 +525,11 @@ set(client_SRCS ) if(BUILD_UNITTESTS) - set(client_SRCS ${client_SRCS} ${UNITTEST_CLIENT_SRCS}) + list(APPEND client_SRCS ${UNITTEST_CLIENT_SRCS}) endif() if(BUILD_BENCHMARKS) - set(client_SRCS ${client_SRCS} ${BENCHMARK_CLIENT_SRCS}) + list(APPEND client_SRCS ${BENCHMARK_CLIENT_SRCS}) endif() # Server sources @@ -1106,7 +1105,7 @@ elseif (USE_GETTEXT) COMMENT "mo-update [${LOCALE}]: Creating mo file." ) - set(MO_FILES ${MO_FILES} ${MO_FILE_PATH}) + list(APPEND MO_FILES ${MO_FILE_PATH}) endforeach() add_custom_target(translations ALL COMMENT "mo update" DEPENDS ${MO_FILES}) diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index d451d0911..0bcb667bc 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -1,7 +1,7 @@ set(sound_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/sound.cpp) if(USE_SOUND) - set(sound_SRCS ${sound_SRCS} + list(APPEND sound_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/sound/al_extensions.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sound/al_helpers.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sound/ogg_file.cpp From a45b04ffb4c0583ef3c8727ea0f73d40e3662e9d Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Tue, 3 Dec 2024 16:52:48 +0100 Subject: [PATCH 25/68] Less explicit memory management in Irrlicht image writer classes (#15493) CImageWriterPNG::writeImage() and writeJPEGFile() explicitly allocate and deallocate memory with `new` and `delete`, which is prone to programming errors. The code also has non-functional error handling: When memory allocation fails, `new` throws an `std::bad_alloc` exception and never returns `nullptr`, so the check for `nullptr` is always false. Co-authored-by: DS --- irr/src/CImageWriterJPG.cpp | 33 ++++++++++++-------------- irr/src/CImageWriterPNG.cpp | 47 +++++++++++++++---------------------- 2 files changed, 34 insertions(+), 46 deletions(-) diff --git a/irr/src/CImageWriterJPG.cpp b/irr/src/CImageWriterJPG.cpp index fa8ad64bc..1220c47e6 100644 --- a/irr/src/CImageWriterJPG.cpp +++ b/irr/src/CImageWriterJPG.cpp @@ -10,6 +10,7 @@ #include "os.h" #include // IWYU pragma: keep (required for jpeglib.h) +#include #include #include @@ -130,32 +131,28 @@ static bool writeJPEGFile(io::IWriteFile *file, IImage *image, u32 quality) jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); - u8 *dest = new u8[dim.Width * 3]; + std::unique_ptr dest{new u8[dim.Width * 3]}; - if (dest) { - const u32 pitch = image->getPitch(); - JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ - row_pointer[0] = dest; + const u32 pitch = image->getPitch(); + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + row_pointer[0] = dest.get(); - u8 *src = (u8 *)image->getData(); + u8 *src = (u8 *)image->getData(); - while (cinfo.next_scanline < cinfo.image_height) { - // convert next line - format(src, dim.Width, dest); - src += pitch; - jpeg_write_scanlines(&cinfo, row_pointer, 1); - } - - delete[] dest; - - /* Step 6: Finish compression */ - jpeg_finish_compress(&cinfo); + while (cinfo.next_scanline < cinfo.image_height) { + // convert next line + format(src, dim.Width, dest.get()); + src += pitch; + jpeg_write_scanlines(&cinfo, row_pointer, 1); } + /* Step 6: Finish compression */ + jpeg_finish_compress(&cinfo); + /* Step 7: Destroy */ jpeg_destroy_compress(&cinfo); - return (dest != 0); + return true; } } // namespace video diff --git a/irr/src/CImageWriterPNG.cpp b/irr/src/CImageWriterPNG.cpp index 14c9f2d9c..35e33c3d5 100644 --- a/irr/src/CImageWriterPNG.cpp +++ b/irr/src/CImageWriterPNG.cpp @@ -10,6 +10,7 @@ #include "os.h" // for logging #include // use system lib png +#include namespace irr { @@ -94,22 +95,23 @@ bool CImageWriterPNG::writeImage(io::IWriteFile *file, IImage *image, u32 param) png_set_write_fn(png_ptr, file, user_write_data_fcn, NULL); // Set info + core::dimension2d img_dim = image->getDimension(); switch (image->getColorFormat()) { case ECF_A8R8G8B8: case ECF_A1R5G5B5: png_set_IHDR(png_ptr, info_ptr, - image->getDimension().Width, image->getDimension().Height, + img_dim.Width, img_dim.Height, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); break; default: png_set_IHDR(png_ptr, info_ptr, - image->getDimension().Width, image->getDimension().Height, + img_dim.Width, img_dim.Height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); } - s32 lineWidth = image->getDimension().Width; + s32 lineWidth = img_dim.Width; switch (image->getColorFormat()) { case ECF_R8G8B8: case ECF_R5G6B5: @@ -123,61 +125,52 @@ bool CImageWriterPNG::writeImage(io::IWriteFile *file, IImage *image, u32 param) default: break; } - u8 *tmpImage = new u8[image->getDimension().Height * lineWidth]; - if (!tmpImage) { - os::Printer::log("PNGWriter: Internal PNG create image failure", file->getFileName(), ELL_ERROR); - png_destroy_write_struct(&png_ptr, &info_ptr); - return false; - } + std::unique_ptr tmpImage{new u8[img_dim.Height * lineWidth]}; + auto num_pixels = img_dim.Height * img_dim.Width; u8 *data = (u8 *)image->getData(); switch (image->getColorFormat()) { case ECF_R8G8B8: - CColorConverter::convert_R8G8B8toR8G8B8(data, image->getDimension().Height * image->getDimension().Width, tmpImage); + CColorConverter::convert_R8G8B8toR8G8B8(data, num_pixels, + tmpImage.get()); break; case ECF_A8R8G8B8: - CColorConverter::convert_A8R8G8B8toA8R8G8B8(data, image->getDimension().Height * image->getDimension().Width, tmpImage); + CColorConverter::convert_A8R8G8B8toA8R8G8B8(data, num_pixels, + tmpImage.get()); break; case ECF_R5G6B5: - CColorConverter::convert_R5G6B5toR8G8B8(data, image->getDimension().Height * image->getDimension().Width, tmpImage); + CColorConverter::convert_R5G6B5toR8G8B8(data, num_pixels, + tmpImage.get()); break; case ECF_A1R5G5B5: - CColorConverter::convert_A1R5G5B5toA8R8G8B8(data, image->getDimension().Height * image->getDimension().Width, tmpImage); + CColorConverter::convert_A1R5G5B5toA8R8G8B8(data, num_pixels, + tmpImage.get()); break; // TODO: Error handling in case of unsupported color format default: os::Printer::log("CImageWriterPNG does not support image format", ColorFormatNames[image->getColorFormat()], ELL_WARNING); png_destroy_write_struct(&png_ptr, &info_ptr); - delete[] tmpImage; return false; } // Create array of pointers to rows in image data // Used to point to image rows - u8 **RowPointers = new png_bytep[image->getDimension().Height]; - if (!RowPointers) { - os::Printer::log("PNGWriter: Internal PNG create row pointers failure", file->getFileName(), ELL_ERROR); - png_destroy_write_struct(&png_ptr, &info_ptr); - delete[] tmpImage; - return false; - } + std::unique_ptr RowPointers{new u8*[img_dim.Height]}; - data = tmpImage; + data = tmpImage.get(); // Fill array of pointers to rows in image data - for (u32 i = 0; i < image->getDimension().Height; ++i) { + for (u32 i = 0; i < img_dim.Height; ++i) { RowPointers[i] = data; data += lineWidth; } // for proper error handling if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); - delete[] RowPointers; - delete[] tmpImage; return false; } - png_set_rows(png_ptr, info_ptr, RowPointers); + png_set_rows(png_ptr, info_ptr, RowPointers.get()); if (image->getColorFormat() == ECF_A8R8G8B8 || image->getColorFormat() == ECF_A1R5G5B5) png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL); @@ -185,8 +178,6 @@ bool CImageWriterPNG::writeImage(io::IWriteFile *file, IImage *image, u32 param) png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); } - delete[] RowPointers; - delete[] tmpImage; png_destroy_write_struct(&png_ptr, &info_ptr); return true; } From 18caf3a18dda95dbd9555cc16a5dcfc9f025cc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Wed, 4 Dec 2024 18:19:12 +0100 Subject: [PATCH 26/68] Fix false positive compiler warning --- irr/src/CGLTFMeshFileLoader.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp index fe67228a7..ce2c41f58 100644 --- a/irr/src/CGLTFMeshFileLoader.cpp +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -791,7 +791,8 @@ std::optional> SelfType::MeshExtractor::getIndices( index = std::get>(accessor).get(elemIdx); if (index == std::numeric_limits::max()) throw std::runtime_error("invalid index"); - } else if (std::holds_alternative>(accessor)) { + } else { + _IRR_DEBUG_BREAK_IF(!std::holds_alternative>(accessor)); u32 indexWide = std::get>(accessor).get(elemIdx); // Use >= here for consistency. if (indexWide >= std::numeric_limits::max()) From e545e96d2bc146765b3d663b24ab0fd7bf85abbd Mon Sep 17 00:00:00 2001 From: AFCMS Date: Wed, 4 Dec 2024 18:19:46 +0100 Subject: [PATCH 27/68] Make string to v3f parsing consistent, replace `core.setting_get_pos()` by `core.settings:get_pos()` (#15438) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: sfan5 Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com> --- builtin/game/misc_s.lua | 6 +--- builtin/game/static_spawn.lua | 2 +- doc/lua_api.md | 8 ++++- src/client/hud.cpp | 4 +-- src/database/database-files.cpp | 2 +- src/gui/guiChatConsole.cpp | 2 +- src/gui/guiFormSpecMenu.cpp | 2 +- src/inventory.cpp | 4 +-- src/mapgen/mapgen_fractal.cpp | 13 ++++++-- src/script/lua_api/l_settings.cpp | 34 +++++++++++++++++++++ src/script/lua_api/l_settings.h | 6 ++++ src/server.cpp | 11 +++++-- src/settings.cpp | 10 +++++-- src/settings.h | 4 +-- src/unittest/test_settings.cpp | 49 +++++++++++++++++++++++++------ src/util/string.cpp | 45 ++++++++++++++++++++++------ src/util/string.h | 5 ++-- 17 files changed, 162 insertions(+), 45 deletions(-) diff --git a/builtin/game/misc_s.lua b/builtin/game/misc_s.lua index 90092952d..07ab09b37 100644 --- a/builtin/game/misc_s.lua +++ b/builtin/game/misc_s.lua @@ -36,11 +36,7 @@ end function core.setting_get_pos(name) - local value = core.settings:get(name) - if not value then - return nil - end - return core.string_to_pos(value) + return core.settings:get_pos(name) end diff --git a/builtin/game/static_spawn.lua b/builtin/game/static_spawn.lua index 5b834310f..2d535251a 100644 --- a/builtin/game/static_spawn.lua +++ b/builtin/game/static_spawn.lua @@ -1,7 +1,7 @@ local static_spawnpoint_string = core.settings:get("static_spawnpoint") if static_spawnpoint_string and static_spawnpoint_string ~= "" and - not core.setting_get_pos("static_spawnpoint") then + not core.settings:get_pos("static_spawnpoint") then error('The static_spawnpoint setting is invalid: "' .. static_spawnpoint_string .. '"') end diff --git a/doc/lua_api.md b/doc/lua_api.md index 709bf2984..2d5405090 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -6180,7 +6180,7 @@ Setting-related * `core.settings`: Settings object containing all of the settings from the main config file (`minetest.conf`). See [`Settings`]. * `core.setting_get_pos(name)`: Loads a setting from the main settings and - parses it as a position (in the format `(1,2,3)`). Returns a position or nil. + parses it as a position (in the format `(1,2,3)`). Returns a position or nil. **Deprecated: use `core.settings:get_pos()` instead** Authentication -------------- @@ -9051,6 +9051,9 @@ means that no defaults will be returned for mod settings. * Is currently limited to mapgen flags `mg_flags` and mapgen-specific flags like `mgv5_spflags`. * Returns `nil` if `key` is not found. +* `get_pos(key)`: + * Returns a `vector` + * Returns `nil` if no value is found or parsing failed. * `set(key, value)` * Setting names can't contain whitespace or any of `="{}#`. * Setting values can't contain the sequence `\n"""`. @@ -9061,6 +9064,9 @@ means that no defaults will be returned for mod settings. * `set_np_group(key, value)` * `value` is a NoiseParams table. * Also, see documentation for `set()` above. +* `set_pos(key, value)` + * `value` is a `vector`. + * Also, see documentation for `set()` above. * `remove(key)`: returns a boolean (`true` for success) * `get_names()`: returns `{key1,...}` * `has(key)`: diff --git a/src/client/hud.cpp b/src/client/hud.cpp index e49327d30..4fbd8740f 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -55,14 +55,14 @@ Hud::Hud(Client *client, LocalPlayer *player, tsrc = client->getTextureSource(); - v3f crosshair_color = g_settings->getV3F("crosshair_color"); + v3f crosshair_color = g_settings->getV3F("crosshair_color").value_or(v3f()); u32 cross_r = rangelim(myround(crosshair_color.X), 0, 255); u32 cross_g = rangelim(myround(crosshair_color.Y), 0, 255); u32 cross_b = rangelim(myround(crosshair_color.Z), 0, 255); u32 cross_a = rangelim(g_settings->getS32("crosshair_alpha"), 0, 255); crosshair_argb = video::SColor(cross_a, cross_r, cross_g, cross_b); - v3f selectionbox_color = g_settings->getV3F("selectionbox_color"); + v3f selectionbox_color = g_settings->getV3F("selectionbox_color").value_or(v3f()); u32 sbox_r = rangelim(myround(selectionbox_color.X), 0, 255); u32 sbox_g = rangelim(myround(selectionbox_color.Y), 0, 255); u32 sbox_b = rangelim(myround(selectionbox_color.Z), 0, 255); diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp index e19d51108..5001a2810 100644 --- a/src/database/database-files.cpp +++ b/src/database/database-files.cpp @@ -43,7 +43,7 @@ void PlayerDatabaseFiles::deSerialize(RemotePlayer *p, std::istream &is, } try { - sao->setBasePosition(args.getV3F("position")); + sao->setBasePosition(args.getV3F("position").value_or(v3f())); } catch (SettingNotFoundException &e) {} try { diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 28aea66c1..689ad22e0 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -55,7 +55,7 @@ GUIChatConsole::GUIChatConsole( m_background_color.setGreen(255); m_background_color.setBlue(255); } else { - v3f console_color = g_settings->getV3F("console_color"); + v3f console_color = g_settings->getV3F("console_color").value_or(v3f()); m_background_color.setRed(clamp_u8(myround(console_color.X))); m_background_color.setGreen(clamp_u8(myround(console_color.Y))); m_background_color.setBlue(clamp_u8(myround(console_color.Z))); diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index ed1c3009d..2330f7c32 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3010,7 +3010,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_tabheader_upper_edge = 0; { - v3f formspec_bgcolor = g_settings->getV3F("formspec_fullscreen_bg_color"); + v3f formspec_bgcolor = g_settings->getV3F("formspec_fullscreen_bg_color").value_or(v3f()); m_fullscreen_bgcolor = video::SColor( (u8) clamp_u8(g_settings->getS32("formspec_fullscreen_bg_opacity")), clamp_u8(myround(formspec_bgcolor.X)), diff --git a/src/inventory.cpp b/src/inventory.cpp index 0ba59ceea..246086d95 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -295,10 +295,8 @@ std::string ItemStack::getWieldOverlay(const IItemDefManager *itemdef) const v3f ItemStack::getWieldScale(const IItemDefManager *itemdef) const { std::string scale = metadata.getString("wield_scale"); - if (scale.empty()) - return getDefinition(itemdef).wield_scale; - return str_to_v3f(scale); + return str_to_v3f(scale).value_or(getDefinition(itemdef).wield_scale); } ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef) diff --git a/src/mapgen/mapgen_fractal.cpp b/src/mapgen/mapgen_fractal.cpp index dcd62f63a..940fe0083 100644 --- a/src/mapgen/mapgen_fractal.cpp +++ b/src/mapgen/mapgen_fractal.cpp @@ -103,8 +103,17 @@ void MapgenFractalParams::readParams(const Settings *settings) settings->getS16NoEx("mgfractal_dungeon_ymax", dungeon_ymax); settings->getU16NoEx("mgfractal_fractal", fractal); settings->getU16NoEx("mgfractal_iterations", iterations); - settings->getV3FNoEx("mgfractal_scale", scale); - settings->getV3FNoEx("mgfractal_offset", offset); + + std::optional mgfractal_scale; + if (settings->getV3FNoEx("mgfractal_scale", mgfractal_scale) && mgfractal_scale.has_value()) { + scale = *mgfractal_scale; + } + + std::optional mgfractal_offset; + if (settings->getV3FNoEx("mgfractal_offset", mgfractal_offset) && mgfractal_offset.has_value()) { + offset = *mgfractal_offset; + } + settings->getFloatNoEx("mgfractal_slice_w", slice_w); settings->getFloatNoEx("mgfractal_julia_x", julia_x); settings->getFloatNoEx("mgfractal_julia_y", julia_y); diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp index c4395e390..241eb2542 100644 --- a/src/script/lua_api/l_settings.cpp +++ b/src/script/lua_api/l_settings.cpp @@ -10,6 +10,7 @@ #include "settings.h" #include "noise.h" #include "log.h" +#include "common/c_converter.h" /* @@ -177,6 +178,21 @@ int LuaSettings::l_get_flags(lua_State *L) return 1; } +// get_pos(self, key) -> vector or nil +int LuaSettings::l_get_pos(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings *o = checkObject(L, 1); + std::string key = luaL_checkstring(L, 2); + + std::optional pos; + if (o->m_settings->getV3FNoEx(key, pos) && pos.has_value()) + push_v3f(L, *pos); + else + lua_pushnil(L); + return 1; +} + // set(self, key, value) int LuaSettings::l_set(lua_State* L) { @@ -227,6 +243,22 @@ int LuaSettings::l_set_np_group(lua_State *L) return 0; } +// set_pos(self, key, value) +int LuaSettings::l_set_pos(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + LuaSettings *o = checkObject(L, 1); + + std::string key = luaL_checkstring(L, 2); + v3f value = check_v3f(L, 3); + + CHECK_SETTING_SECURITY(L, key); + + o->m_settings->setV3F(key, value); + + return 0; +} + // remove(self, key) -> success int LuaSettings::l_remove(lua_State* L) { @@ -356,9 +388,11 @@ const luaL_Reg LuaSettings::methods[] = { luamethod(LuaSettings, get_bool), luamethod(LuaSettings, get_np_group), luamethod(LuaSettings, get_flags), + luamethod(LuaSettings, get_pos), luamethod(LuaSettings, set), luamethod(LuaSettings, set_bool), luamethod(LuaSettings, set_np_group), + luamethod(LuaSettings, set_pos), luamethod(LuaSettings, remove), luamethod(LuaSettings, get_names), luamethod(LuaSettings, has), diff --git a/src/script/lua_api/l_settings.h b/src/script/lua_api/l_settings.h index ca609fc1f..63741477a 100644 --- a/src/script/lua_api/l_settings.h +++ b/src/script/lua_api/l_settings.h @@ -29,6 +29,9 @@ private: // get_flags(self, key) -> key/value table static int l_get_flags(lua_State *L); + // get_pos(self, key) -> vector or nil + static int l_get_pos(lua_State *L); + // set(self, key, value) static int l_set(lua_State *L); @@ -38,6 +41,9 @@ private: // set_np_group(self, key, value) static int l_set_np_group(lua_State *L); + // set_pos(self, key, value) + static int l_set_pos(lua_State *L); + // remove(self, key) -> success static int l_remove(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index ee9b0a859..b21955170 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -3863,9 +3863,14 @@ void Server::addShutdownError(const ModError &e) v3f Server::findSpawnPos() { ServerMap &map = m_env->getServerMap(); - v3f nodeposf; - if (g_settings->getV3FNoEx("static_spawnpoint", nodeposf)) - return nodeposf * BS; + + std::optional staticSpawnPoint; + if (g_settings->getV3FNoEx("static_spawnpoint", staticSpawnPoint) && staticSpawnPoint.has_value()) + { + return *staticSpawnPoint * BS; + } + + v3f nodeposf; bool is_good = false; // Limit spawn range to mapgen edges (determined by 'mapgen_limit') diff --git a/src/settings.cpp b/src/settings.cpp index 02c84437d..cec3f5239 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -541,7 +541,7 @@ v2f Settings::getV2F(const std::string &name) const } -v3f Settings::getV3F(const std::string &name) const +std::optional Settings::getV3F(const std::string &name) const { return str_to_v3f(get(name)); } @@ -626,7 +626,11 @@ bool Settings::getNoiseParamsFromGroup(const std::string &name, group->getFloatNoEx("offset", np.offset); group->getFloatNoEx("scale", np.scale); - group->getV3FNoEx("spread", np.spread); + + std::optional spread; + if (group->getV3FNoEx("spread", spread) && spread.has_value()) + np.spread = *spread; + group->getS32NoEx("seed", np.seed); group->getU16NoEx("octaves", np.octaves); group->getFloatNoEx("persistence", np.persist); @@ -783,7 +787,7 @@ bool Settings::getV2FNoEx(const std::string &name, v2f &val) const } -bool Settings::getV3FNoEx(const std::string &name, v3f &val) const +bool Settings::getV3FNoEx(const std::string &name, std::optional &val) const { try { val = getV3F(name); diff --git a/src/settings.h b/src/settings.h index 0638dad2d..9bc0e80d9 100644 --- a/src/settings.h +++ b/src/settings.h @@ -150,7 +150,7 @@ public: float getFloat(const std::string &name) const; float getFloat(const std::string &name, float min, float max) const; v2f getV2F(const std::string &name) const; - v3f getV3F(const std::string &name) const; + std::optional getV3F(const std::string &name) const; u32 getFlagStr(const std::string &name, const FlagDesc *flagdesc, u32 *flagmask) const; bool getNoiseParams(const std::string &name, NoiseParams &np) const; @@ -179,7 +179,7 @@ public: bool getU64NoEx(const std::string &name, u64 &val) const; bool getFloatNoEx(const std::string &name, float &val) const; bool getV2FNoEx(const std::string &name, v2f &val) const; - bool getV3FNoEx(const std::string &name, v3f &val) const; + bool getV3FNoEx(const std::string &name, std::optional &val) const; // Like other getters, but handling each flag individualy: // 1) Read default flags (or 0) diff --git a/src/unittest/test_settings.cpp b/src/unittest/test_settings.cpp index 94b222c1f..411ecb8de 100644 --- a/src/unittest/test_settings.cpp +++ b/src/unittest/test_settings.cpp @@ -42,6 +42,14 @@ const char *TestSettings::config_text_before = "floaty_thing = 1.1\n" "stringy_thing = asd /( ¤%&(/\" BLÖÄRP\n" "coord = (1, 2, 4.5)\n" + "coord_invalid = (1,2,3\n" + "coord_invalid_2 = 1, 2, 3 test\n" + "coord_invalid_3 = (test, something, stupid)\n" + "coord_invalid_4 = (1, test, 3)\n" + "coord_invalid_5 = ()\n" + "coord_invalid_6 = (1, 2)\n" + "coord_invalid_7 = (1)\n" + "coord_no_parenthesis = 1,2,3\n" " # this is just a comment\n" "this is an invalid line\n" "asdf = {\n" @@ -94,7 +102,15 @@ const char *TestSettings::config_text_after = " spread = (250,250,250)\n" "}\n" "zoop = true\n" - "coord2 = (1,2,3.3)\n" + "coord2 = (1,2,3.25)\n" + "coord_invalid = (1,2,3\n" + "coord_invalid_2 = 1, 2, 3 test\n" + "coord_invalid_3 = (test, something, stupid)\n" + "coord_invalid_4 = (1, test, 3)\n" + "coord_invalid_5 = ()\n" + "coord_invalid_6 = (1, 2)\n" + "coord_invalid_7 = (1)\n" + "coord_no_parenthesis = 1,2,3\n" "floaty_thing_2 = 1.25\n" "groupy_thing = {\n" " animals = cute\n" @@ -140,18 +156,33 @@ void TestSettings::testAllSettings() // Not sure if 1.1 is an exact value as a float, but doesn't matter UASSERT(fabs(s.getFloat("floaty_thing") - 1.1) < 0.001); UASSERT(s.get("stringy_thing") == u8"asd /( ¤%&(/\" BLÖÄRP"); - UASSERT(fabs(s.getV3F("coord").X - 1.0) < 0.001); - UASSERT(fabs(s.getV3F("coord").Y - 2.0) < 0.001); - UASSERT(fabs(s.getV3F("coord").Z - 4.5) < 0.001); + UASSERT(s.getV3F("coord").value().X == 1.0); + UASSERT(s.getV3F("coord").value().Y == 2.0); + UASSERT(s.getV3F("coord").value().Z == 4.5); // Test the setting of settings too s.setFloat("floaty_thing_2", 1.25); - s.setV3F("coord2", v3f(1, 2, 3.3)); + s.setV3F("coord2", v3f(1, 2, 3.25)); UASSERT(s.get("floaty_thing_2").substr(0,4) == "1.25"); - UASSERT(fabs(s.getFloat("floaty_thing_2") - 1.25) < 0.001); - UASSERT(fabs(s.getV3F("coord2").X - 1.0) < 0.001); - UASSERT(fabs(s.getV3F("coord2").Y - 2.0) < 0.001); - UASSERT(fabs(s.getV3F("coord2").Z - 3.3) < 0.001); + UASSERT(s.getFloat("floaty_thing_2") == 1.25); + UASSERT(s.getV3F("coord2").value().X == 1.0); + UASSERT(s.getV3F("coord2").value().Y == 2.0); + UASSERT(s.getV3F("coord2").value().Z == 3.25); + + std::optional testNotExist; + UASSERT(!s.getV3FNoEx("coord_not_exist", testNotExist)); + EXCEPTION_CHECK(SettingNotFoundException, s.getV3F("coord_not_exist")); + + UASSERT(!s.getV3F("coord_invalid").has_value()); + UASSERT(!s.getV3F("coord_invalid_2").has_value()); + UASSERT(!s.getV3F("coord_invalid_3").has_value()); + UASSERT(!s.getV3F("coord_invalid_4").has_value()); + UASSERT(!s.getV3F("coord_invalid_5").has_value()); + UASSERT(!s.getV3F("coord_invalid_6").has_value()); + UASSERT(!s.getV3F("coord_invalid_7").has_value()); + + std::optional testNoParenthesis = s.getV3F("coord_no_parenthesis"); + UASSERT(testNoParenthesis.value() == v3f(1, 2, 3)); // Test settings groups Settings *group = s.getGroup("asdf"); diff --git a/src/util/string.cpp b/src/util/string.cpp index 43e936788..a0eee85c1 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -1068,14 +1068,41 @@ void safe_print_string(std::ostream &os, std::string_view str) os.setf(flags); } - -v3f str_to_v3f(std::string_view str) +std::optional str_to_v3f(std::string_view str) { - v3f value; - Strfnd f(str); - f.next("("); - value.X = stof(f.next(",")); - value.Y = stof(f.next(",")); - value.Z = stof(f.next(")")); - return value; + str = trim(str); + + if (str.empty()) + return std::nullopt; + + // Strip parentheses if they exist + if (str.front() == '(' && str.back() == ')') { + str.remove_prefix(1); + str.remove_suffix(1); + str = trim(str); + } + + std::istringstream iss((std::string(str))); + + const auto expect_delimiter = [&]() { + const auto c = iss.get(); + return c == ' ' || c == ','; + }; + + v3f value; + if (!(iss >> value.X)) + return std::nullopt; + if (!expect_delimiter()) + return std::nullopt; + if (!(iss >> value.Y)) + return std::nullopt; + if (!expect_delimiter()) + return std::nullopt; + if (!(iss >> value.Z)) + return std::nullopt; + + if (!iss.eof()) + return std::nullopt; + + return value; } diff --git a/src/util/string.h b/src/util/string.h index 1874ccd2a..16c337b7f 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -20,6 +20,7 @@ #include #include #include +#include class Translations; @@ -789,9 +790,9 @@ std::string sanitize_untrusted(std::string_view str, bool keep_escapes = true); void safe_print_string(std::ostream &os, std::string_view str); /** - * Parses a string of form `(1, 2, 3)` to a v3f + * Parses a string of form `(1, 2, 3)` or `1, 2, 4` to a v3f * * @param str string * @return float vector */ -v3f str_to_v3f(std::string_view str); +std::optional str_to_v3f(std::string_view str); From 36edc3f161de03e3c5e8b507d2d6699f0d728ab9 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 28 Nov 2024 10:38:51 +0100 Subject: [PATCH 28/68] Add 10-bit texture format and setting to chose PP color depth (and move some settings to the advanced category) --- builtin/settingtypes.txt | 47 ++++++++++++++++++++++-------------- irr/include/IImage.h | 2 ++ irr/include/SColor.h | 9 ++++++- irr/src/OpenGL3/Driver.cpp | 1 + irr/src/OpenGLES2/Driver.cpp | 1 + src/client/render/plain.cpp | 5 +++- src/defaultsettings.cpp | 3 +++ 7 files changed, 48 insertions(+), 20 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index d23704694..b10f776ac 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -77,8 +77,11 @@ # Sections are marked by a single line in the format: [Section Name] # Sub-section are marked by adding * in front of the section name: [*Sub-section] # Sub-sub-sections have two * etc. -# There shouldn't be too much settings per category; settings that shouldn't be -# modified by the "average user" should be in (sub-)categories called "Advanced". +# There shouldn't be too many settings per category. +# +# The top-level categories "Advanced", "Client and Server" and "Mapgen" are +# handled specially and its contents only shown when a checkbox is checked. +# They contain settings not intended for the "average user". [Controls] @@ -471,6 +474,7 @@ performance_tradeoffs (Tradeoffs for performance) bool false # Adds particles when digging a node. enable_particles (Digging particles) bool true + [**Waving Nodes] # Set to true to enable waving leaves. @@ -534,12 +538,6 @@ shadow_map_texture_size (Shadow map texture size) int 2048 128 8192 # Requires: enable_dynamic_shadows, opengl shadow_map_texture_32bit (Shadow map texture in 32 bits) bool true -# Enable Poisson disk filtering. -# On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering. -# -# Requires: enable_dynamic_shadows, opengl -shadow_poisson_filter (Poisson filtering) bool true - # Define shadow filtering quality. # This simulates the soft shadows effect by applying a PCF or Poisson disk # but also uses more resources. @@ -553,14 +551,6 @@ shadow_filters (Shadow filter quality) enum 1 0,1,2 # Requires: enable_dynamic_shadows, opengl shadow_map_color (Colored shadows) bool false -# Spread a complete update of shadow map over given number of frames. -# Higher values might make shadows laggy, lower values -# will consume more resources. -# Minimum value: 1; maximum value: 16 -# -# Requires: enable_dynamic_shadows, opengl -shadow_update_frames (Map shadows update frames) int 8 1 16 - # Set the soft shadow radius size. # Lower values mean sharper shadows, bigger values mean softer shadows. # Minimum value: 1.0; maximum value: 15.0 @@ -1830,6 +1820,9 @@ ignore_world_load_errors (Ignore world errors) bool false [**Graphics] +# Enables debug and error-checking in the OpenGL driver. +opengl_debug (OpenGL debug) bool false + # Path to shader directory. If no path is defined, default location will be used. shader_path (Shader path) path @@ -1901,8 +1894,26 @@ texture_min_size (Base texture size) int 64 1 32768 # Systems with a low-end GPU (or no GPU) would benefit from smaller values. client_mesh_chunk (Client Mesh Chunksize) int 1 1 16 -# Enables debug and error-checking in the OpenGL driver. -opengl_debug (OpenGL debug) bool false +# Decide the color depth of the texture used for the post-processing pipeline. +# Reducing this can improve performance, but might cause some effects (e.g. bloom) +# to not work. +# +# Requires: enable_post_processing +post_processing_texture_bits (Color depth for post-processing texture) enum 16 8,10,16 + +# Enable Poisson disk filtering. +# On true uses Poisson disk to make "soft shadows". Otherwise uses PCF filtering. +# +# Requires: enable_dynamic_shadows, opengl +shadow_poisson_filter (Poisson filtering) bool true + +# Spread a complete update of shadow map over given number of frames. +# Higher values might make shadows laggy, lower values +# will consume more resources. +# Minimum value: 1; maximum value: 16 +# +# Requires: enable_dynamic_shadows, opengl +shadow_update_frames (Map shadows update frames) int 8 1 16 # Set to true to render debugging breakdown of the bloom effect. # In debug mode, the screen is split into 4 quadrants: diff --git a/irr/include/IImage.h b/irr/include/IImage.h index 47349ed1a..a303201a9 100644 --- a/irr/include/IImage.h +++ b/irr/include/IImage.h @@ -342,6 +342,8 @@ public: return 16; case ECF_R16G16: return 32; + case ECF_A2R10G10B10: + return 32; case ECF_R16F: return 16; case ECF_G16R16F: diff --git a/irr/include/SColor.h b/irr/include/SColor.h index 41bca1a8c..0845b31f7 100644 --- a/irr/include/SColor.h +++ b/irr/include/SColor.h @@ -72,6 +72,9 @@ enum ECOLOR_FORMAT //! 32 bit format using 16 bits for the red and green channels. ECF_R16G16, + //! 32 bit format using 10 bits for R, G, B and 2 for alpha. + ECF_A2R10G10B10, + /** Depth and stencil formats. */ //! 16 bit format using 16 bits for depth. @@ -91,7 +94,7 @@ enum ECOLOR_FORMAT }; //! Names for ECOLOR_FORMAT types -const c8 *const ColorFormatNames[ECF_UNKNOWN + 2] = { +const c8 *const ColorFormatNames[] = { "A1R5G5B5", "R5G6B5", "R8G8B8", @@ -106,6 +109,7 @@ const c8 *const ColorFormatNames[ECF_UNKNOWN + 2] = { "R8G8", "R16", "R16G16", + "A2R10G10B10", "D16", "D24", "D32", @@ -114,6 +118,9 @@ const c8 *const ColorFormatNames[ECF_UNKNOWN + 2] = { 0, }; +static_assert(sizeof(ColorFormatNames) / sizeof(ColorFormatNames[0]) + == ECF_UNKNOWN + 2, "name table size mismatch"); + //! Creates a 16 bit A1R5G5B5 color inline u16 RGBA16(u32 r, u32 g, u32 b, u32 a = 0xFF) { diff --git a/irr/src/OpenGL3/Driver.cpp b/irr/src/OpenGL3/Driver.cpp index b397846aa..5277c4dde 100644 --- a/irr/src/OpenGL3/Driver.cpp +++ b/irr/src/OpenGL3/Driver.cpp @@ -62,6 +62,7 @@ void COpenGL3Driver::initFeatures() TextureFormats[ECF_R8G8] = {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}; TextureFormats[ECF_R16] = {GL_R16, GL_RED, GL_UNSIGNED_SHORT}; TextureFormats[ECF_R16G16] = {GL_RG16, GL_RG, GL_UNSIGNED_SHORT}; + TextureFormats[ECF_A2R10G10B10] = {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}; TextureFormats[ECF_D16] = {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}; TextureFormats[ECF_D24] = {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}; TextureFormats[ECF_D32] = {GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}; // WARNING: may not be renderable (?!) diff --git a/irr/src/OpenGLES2/Driver.cpp b/irr/src/OpenGLES2/Driver.cpp index 3c034522c..32bf0aee0 100644 --- a/irr/src/OpenGLES2/Driver.cpp +++ b/irr/src/OpenGLES2/Driver.cpp @@ -59,6 +59,7 @@ void COpenGLES2Driver::initFeatures() TextureFormats[ECF_A32B32G32R32F] = {GL_RGBA32F, GL_RGBA, GL_FLOAT}; TextureFormats[ECF_R8] = {GL_R8, GL_RED, GL_UNSIGNED_BYTE}; TextureFormats[ECF_R8G8] = {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}; + TextureFormats[ECF_A2R10G10B10] = {GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV}; TextureFormats[ECF_D16] = {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}; TextureFormats[ECF_D24] = {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}; TextureFormats[ECF_D24S8] = {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}; diff --git a/src/client/render/plain.cpp b/src/client/render/plain.cpp index fd3e0b9ba..c24ba8837 100644 --- a/src/client/render/plain.cpp +++ b/src/client/render/plain.cpp @@ -155,8 +155,11 @@ void populatePlainPipeline(RenderPipeline *pipeline, Client *client) video::ECOLOR_FORMAT selectColorFormat(video::IVideoDriver *driver) { - if (driver->queryTextureFormat(video::ECF_A16B16G16R16F)) + u32 bits = g_settings->getU32("post_processing_texture_bits"); + if (bits >= 16 && driver->queryTextureFormat(video::ECF_A16B16G16R16F)) return video::ECF_A16B16G16R16F; + if (bits >= 10 && driver->queryTextureFormat(video::ECF_A2R10G10B10)) + return video::ECF_A2R10G10B10; return video::ECF_A8R8G8B8; } diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index e6b26bd4a..600058b5c 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -311,6 +311,7 @@ void set_default_settings() // Effects settings->setDefault("enable_post_processing", "true"); + settings->setDefault("post_processing_texture_bits", "16"); settings->setDefault("directional_colored_fog", "true"); settings->setDefault("inventory_items_animations", "false"); settings->setDefault("mip_map", "false"); @@ -567,6 +568,8 @@ void set_default_settings() settings->setDefault("active_block_range", "2"); settings->setDefault("viewing_range", "50"); settings->setDefault("leaves_style", "simple"); + // Note: OpenGL ES 2.0 is not guaranteed to provide depth textures, + // which we would need for PP. settings->setDefault("enable_post_processing", "false"); settings->setDefault("debanding", "false"); settings->setDefault("curl_verify_cert", "false"); From 1fb7202028ae67803f2c10c747cefacd18c35ee7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 28 Nov 2024 10:39:07 +0100 Subject: [PATCH 29/68] GL: fix and clean up some code --- irr/include/IRenderTarget.h | 5 --- irr/include/ISceneManager.h | 12 ------- irr/include/IVideoDriver.h | 23 ------------- irr/src/CNullDriver.cpp | 29 ----------------- irr/src/CNullDriver.h | 23 ------------- irr/src/COpenGLCoreTexture.h | 11 +++++++ irr/src/COpenGLDriver.cpp | 18 ----------- irr/src/COpenGLDriver.h | 5 --- irr/src/COpenGLExtensionHandler.cpp | 4 +-- irr/src/COpenGLExtensionHandler.h | 2 -- irr/src/CSceneManager.cpp | 17 +--------- irr/src/CSceneManager.h | 9 ------ irr/src/OpenGL/Common.h | 2 +- irr/src/OpenGL/Driver.cpp | 50 +++++++++++++++++------------ irr/src/OpenGL/Driver.h | 5 +++ irr/src/OpenGLES2/Driver.cpp | 13 +++----- src/client/render/anaglyph.cpp | 3 +- 17 files changed, 54 insertions(+), 177 deletions(-) diff --git a/irr/include/IRenderTarget.h b/irr/include/IRenderTarget.h index 85b9738dc..d4e5960a2 100644 --- a/irr/include/IRenderTarget.h +++ b/irr/include/IRenderTarget.h @@ -109,11 +109,6 @@ protected: //! Driver type of render target. E_DRIVER_TYPE DriverType; - -private: - // no copying (IReferenceCounted still allows that for reasons which take some time to work around) - IRenderTarget(const IRenderTarget &); - IRenderTarget &operator=(const IRenderTarget &); }; } diff --git a/irr/include/ISceneManager.h b/irr/include/ISceneManager.h index 18521dbe9..7fbc0670c 100644 --- a/irr/include/ISceneManager.h +++ b/irr/include/ISceneManager.h @@ -55,9 +55,6 @@ enum E_SCENE_NODE_RENDER_PASS //! Camera pass. The active view is set up here. The very first pass. ESNRP_CAMERA = 1, - //! In this pass, lights are transformed into camera space and added to the driver - ESNRP_LIGHT = 2, - //! This is used for sky boxes. ESNRP_SKY_BOX = 4, @@ -85,9 +82,6 @@ enum E_SCENE_NODE_RENDER_PASS //! Transparent effect scene nodes, drawn after Transparent nodes. They are sorted from back to front and drawn in that order. ESNRP_TRANSPARENT_EFFECT = 32, - //! Drawn after the solid nodes, before the transparent nodes, the time for drawing shadow volumes - ESNRP_SHADOW = 64, - //! Drawn after transparent effect nodes. For custom gui's. Unsorted (in order nodes registered themselves). ESNRP_GUI = 128 @@ -602,12 +596,6 @@ public: for details. */ virtual ISkinnedMesh *createSkinnedMesh() = 0; - //! Sets ambient color of the scene - virtual void setAmbientLight(const video::SColorf &ambientColor) = 0; - - //! Get ambient color of the scene - virtual const video::SColorf &getAmbientLight() const = 0; - //! Get current render pass. virtual E_SCENE_NODE_RENDER_PASS getCurrentRenderPass() const = 0; diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index 3d4deace5..ebb39dfd9 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -131,7 +131,6 @@ public: /** The following names can be queried for the given types: MaxTextures (int) The maximum number of simultaneous textures supported by the driver. This can be less than the supported number of textures of the driver. Use _IRR_MATERIAL_MAX_TEXTURES_ to adapt the number. MaxSupportedTextures (int) The maximum number of simultaneous textures supported by the fixed function pipeline of the (hw) driver. The actual supported number of textures supported by the engine can be lower. - MaxLights (int) Number of hardware lights supported in the fixed function pipeline of the driver, typically 6-8. Use light manager or deferred shading for more. MaxAnisotropy (int) Number of anisotropy levels supported for filtering. At least 1, max is typically at 16 or 32. MaxAuxBuffers (int) Special render buffers, which are currently not really usable inside Irrlicht. Only supported by OpenGL MaxMultipleRenderTargets (int) Number of render targets which can be bound simultaneously. Rendering to MRTs is done via shaders. @@ -1110,15 +1109,6 @@ public: //! Get the graphics card vendor name. virtual core::stringc getVendorInfo() = 0; - //! Only used by the engine internally. - /** The ambient color is set in the scene manager, see - scene::ISceneManager::setAmbientLight(). - \param color New color of the ambient light. */ - virtual void setAmbientLight(const SColorf &color) = 0; - - //! Get the global ambient light currently used by the driver - virtual const SColorf &getAmbientLight() const = 0; - //! Only used by the engine internally. /** Passes the global material flag AllowZWriteOnTransparent. Use the SceneManager attribute to set this value from your app. @@ -1128,19 +1118,6 @@ public: //! Get the maximum texture size supported. virtual core::dimension2du getMaxTextureSize() const = 0; - //! Color conversion convenience function - /** Convert an image (as array of pixels) from source to destination - array, thereby converting the color format. The pixel size is - determined by the color formats. - \param sP Pointer to source - \param sF Color format of source - \param sN Number of pixels to convert, both array must be large enough - \param dP Pointer to destination - \param dF Color format of destination - */ - virtual void convertColor(const void *sP, ECOLOR_FORMAT sF, s32 sN, - void *dP, ECOLOR_FORMAT dF) const = 0; - //! Check if the driver supports creating textures with the given color format /** \return True if the format is available, false if not. */ virtual bool queryTextureFormat(ECOLOR_FORMAT format) const = 0; diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index b5da7ef29..efbad7598 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -743,19 +743,6 @@ SFrameStats CNullDriver::getFrameStats() const return FrameStats; } -//! Sets the dynamic ambient light color. The default color is -//! (0,0,0,0) which means it is dark. -//! \param color: New color of the ambient light. -void CNullDriver::setAmbientLight(const SColorf &color) -{ - AmbientLight = color; -} - -const SColorf &CNullDriver::getAmbientLight() const -{ - return AmbientLight; -} - //! \return Returns the name of the video driver. Example: In case of the DIRECT3D8 //! driver, it would return "Direct3D8". @@ -1774,21 +1761,5 @@ bool CNullDriver::needsTransparentRenderPass(const irr::video::SMaterial &materi return false; } -//! Color conversion convenience function -/** Convert an image (as array of pixels) from source to destination -array, thereby converting the color format. The pixel size is -determined by the color formats. -\param sP Pointer to source -\param sF Color format of source -\param sN Number of pixels to convert, both array must be large enough -\param dP Pointer to destination -\param dF Color format of destination -*/ -void CNullDriver::convertColor(const void *sP, ECOLOR_FORMAT sF, s32 sN, - void *dP, ECOLOR_FORMAT dF) const -{ - video::CColorConverter::convert_viaFormat(sP, sF, sN, dP, dF); -} - } // end namespace } // end namespace diff --git a/irr/src/CNullDriver.h b/irr/src/CNullDriver.h index 5752b53cd..e772dd8e8 100644 --- a/irr/src/CNullDriver.h +++ b/irr/src/CNullDriver.h @@ -201,14 +201,6 @@ public: //! driver, it would return "Direct3D8.1". const char *getName() const override; - //! Sets the dynamic ambient light color. The default color is - //! (0,0,0,0) which means it is dark. - //! \param color: New color of the ambient light. - void setAmbientLight(const SColorf &color) override; - - //! Get the global ambient light currently used by the driver - const SColorf &getAmbientLight() const override; - //! Adds an external image loader to the engine. void addExternalImageLoader(IImageLoader *loader) override; @@ -559,19 +551,6 @@ public: //! Used by some SceneNodes to check if a material should be rendered in the transparent render pass bool needsTransparentRenderPass(const irr::video::SMaterial &material) const override; - //! Color conversion convenience function - /** Convert an image (as array of pixels) from source to destination - array, thereby converting the color format. The pixel size is - determined by the color formats. - \param sP Pointer to source - \param sF Color format of source - \param sN Number of pixels to convert, both array must be large enough - \param dP Pointer to destination - \param dF Color format of destination - */ - virtual void convertColor(const void *sP, ECOLOR_FORMAT sF, s32 sN, - void *dP, ECOLOR_FORMAT dF) const override; - protected: //! deletes all textures void deleteAllTextures(); @@ -759,8 +738,6 @@ protected: bool AllowZWriteOnTransparent; bool FeatureEnabled[video::EVDF_COUNT]; - - SColorf AmbientLight; }; } // end namespace video diff --git a/irr/src/COpenGLCoreTexture.h b/irr/src/COpenGLCoreTexture.h index a37562543..d8a813d5d 100644 --- a/irr/src/COpenGLCoreTexture.h +++ b/irr/src/COpenGLCoreTexture.h @@ -170,6 +170,17 @@ public: return; } +#ifndef IRR_COMPILE_GL_COMMON + // On GLES 3.0 we must use sized internal formats for textures in certain + // cases (e.g. with ETT_2D_MS). However ECF_A8R8G8B8 is mapped to GL_BGRA + // (an unsized format). + // Since we don't upload to RTT we can safely pick a different combo that works. + if (InternalFormat == GL_BGRA && Driver->Version.Major >= 3) { + InternalFormat = GL_RGBA8; + PixelFormat = GL_RGBA; + } +#endif + #ifdef _DEBUG char lbuf[100]; snprintf_irr(lbuf, sizeof(lbuf), diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index 07d7f7bb1..f7c0be7dd 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -122,7 +122,6 @@ bool COpenGLDriver::genericDriverInit() os::Printer::log("GLSL not available.", ELL_INFORMATION); DriverAttributes->setAttribute("MaxTextures", (s32)Feature.MaxTextureUnits); DriverAttributes->setAttribute("MaxSupportedTextures", (s32)Feature.MaxTextureUnits); - DriverAttributes->setAttribute("MaxLights", MaxLights); DriverAttributes->setAttribute("MaxAnisotropy", MaxAnisotropy); DriverAttributes->setAttribute("MaxAuxBuffers", MaxAuxBuffers); DriverAttributes->setAttribute("MaxMultipleRenderTargets", (s32)Feature.MultipleRenderTarget); @@ -139,13 +138,6 @@ bool COpenGLDriver::genericDriverInit() for (i = 0; i < ETS_COUNT; ++i) setTransform(static_cast(i), core::IdentityMatrix); - setAmbientLight(SColorf(0.0f, 0.0f, 0.0f, 0.0f)); -#ifdef GL_EXT_separate_specular_color - if (FeatureAvailable[IRR_EXT_separate_specular_color]) - glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); -#endif - glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1); - // This is a fast replacement for NORMALIZE_NORMALS // if ((Version>101) || FeatureAvailable[IRR_EXT_rescale_normal]) // glEnable(GL_RESCALE_NORMAL_EXT); @@ -2420,16 +2412,6 @@ const char *COpenGLDriver::getName() const return Name.c_str(); } -//! Sets the dynamic ambient light color. The default color is -//! (0,0,0,0) which means it is dark. -//! \param color: New color of the ambient light. -void COpenGLDriver::setAmbientLight(const SColorf &color) -{ - CNullDriver::setAmbientLight(color); - GLfloat data[4] = {color.r, color.g, color.b, color.a}; - glLightModelfv(GL_LIGHT_MODEL_AMBIENT, data); -} - // this code was sent in by Oliver Klems, thank you! (I modified the glViewport // method just a bit. void COpenGLDriver::setViewPort(const core::rect &area) diff --git a/irr/src/COpenGLDriver.h b/irr/src/COpenGLDriver.h index 126f7aadc..aa457b8ee 100644 --- a/irr/src/COpenGLDriver.h +++ b/irr/src/COpenGLDriver.h @@ -182,11 +182,6 @@ public: //! driver, it would return "Direct3D8.1". const char *getName() const override; - //! Sets the dynamic ambient light color. The default color is - //! (0,0,0,0) which means it is dark. - //! \param color: New color of the ambient light. - void setAmbientLight(const SColorf &color) override; - //! sets a viewport void setViewPort(const core::rect &area) override; diff --git a/irr/src/COpenGLExtensionHandler.cpp b/irr/src/COpenGLExtensionHandler.cpp index 4c7fe69b7..7eb4252a6 100644 --- a/irr/src/COpenGLExtensionHandler.cpp +++ b/irr/src/COpenGLExtensionHandler.cpp @@ -19,7 +19,7 @@ namespace video bool COpenGLExtensionHandler::needsDSAFramebufferHack = true; COpenGLExtensionHandler::COpenGLExtensionHandler() : - StencilBuffer(false), TextureCompressionExtension(false), MaxLights(1), + StencilBuffer(false), TextureCompressionExtension(false), MaxAnisotropy(1), MaxAuxBuffers(0), MaxIndices(65535), MaxTextureSize(1), MaxGeometryVerticesOut(0), MaxTextureLODBias(0.f), Version(0), ShaderLanguageVersion(0), @@ -399,8 +399,6 @@ void COpenGLExtensionHandler::initExtensions(video::IContextManager *cmgr, bool Feature.MaxTextureUnits = core::max_(Feature.MaxTextureUnits, static_cast(num)); } #endif - glGetIntegerv(GL_MAX_LIGHTS, &num); - MaxLights = static_cast(num); #ifdef GL_EXT_texture_filter_anisotropic if (FeatureAvailable[IRR_EXT_texture_filter_anisotropic]) { glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &num); diff --git a/irr/src/COpenGLExtensionHandler.h b/irr/src/COpenGLExtensionHandler.h index cdff911b9..a1754a328 100644 --- a/irr/src/COpenGLExtensionHandler.h +++ b/irr/src/COpenGLExtensionHandler.h @@ -1015,8 +1015,6 @@ public: bool TextureCompressionExtension; // Some non-boolean properties - //! Maximum hardware lights supported - u8 MaxLights; //! Maximal Anisotropy u8 MaxAnisotropy; //! Number of auxiliary buffers diff --git a/irr/src/CSceneManager.cpp b/irr/src/CSceneManager.cpp index d75abc284..04b3ec313 100644 --- a/irr/src/CSceneManager.cpp +++ b/irr/src/CSceneManager.cpp @@ -41,7 +41,7 @@ CSceneManager::CSceneManager(video::IVideoDriver *driver, ISceneNode(0, 0), Driver(driver), CursorControl(cursorControl), - ActiveCamera(0), ShadowColor(150, 0, 0, 0), AmbientLight(0, 0, 0, 0), Parameters(0), + ActiveCamera(0), Parameters(0), MeshCache(cache), CurrentRenderPass(ESNRP_NONE) { #ifdef _DEBUG @@ -445,9 +445,6 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode *node, E_SCENE_NODE_RENDE taken = 1; } - // as of yet unused - case ESNRP_LIGHT: - case ESNRP_SHADOW: case ESNRP_NONE: // ignore this one break; } @@ -775,18 +772,6 @@ ISceneManager *CSceneManager::createNewSceneManager(bool cloneContent) return manager; } -//! Sets ambient color of the scene -void CSceneManager::setAmbientLight(const video::SColorf &ambientColor) -{ - AmbientLight = ambientColor; -} - -//! Returns ambient color of the scene -const video::SColorf &CSceneManager::getAmbientLight() const -{ - return AmbientLight; -} - //! Get a skinned mesh, which is not available as header-only code ISkinnedMesh *CSceneManager::createSkinnedMesh() { diff --git a/irr/src/CSceneManager.h b/irr/src/CSceneManager.h index 4ef6d64b0..0f1b2c716 100644 --- a/irr/src/CSceneManager.h +++ b/irr/src/CSceneManager.h @@ -170,12 +170,6 @@ public: //! Get a skinned mesh, which is not available as header-only code ISkinnedMesh *createSkinnedMesh() override; - //! Sets ambient color of the scene - void setAmbientLight(const video::SColorf &ambientColor) override; - - //! Returns ambient color of the scene - const video::SColorf &getAmbientLight() const override; - //! Get current render time. E_SCENE_NODE_RENDER_PASS getCurrentRenderPass() const override { return CurrentRenderPass; } @@ -291,9 +285,6 @@ private: ICameraSceneNode *ActiveCamera; core::vector3df camWorldPos; // Position of camera for transparent nodes. - video::SColor ShadowColor; - video::SColorf AmbientLight; - //! String parameters // NOTE: Attributes are slow and should only be used for debug-info and not in release io::CAttributes *Parameters; diff --git a/irr/src/OpenGL/Common.h b/irr/src/OpenGL/Common.h index 84b2ec3e4..75c213bb0 100644 --- a/irr/src/OpenGL/Common.h +++ b/irr/src/OpenGL/Common.h @@ -40,7 +40,7 @@ typedef COpenGLCoreTexture COpenGL3Texture; typedef COpenGLCoreRenderTarget COpenGL3RenderTarget; typedef COpenGLCoreCacheHandler COpenGL3CacheHandler; -enum class OpenGLSpec : u8 +enum OpenGLSpec : u8 { Core, Compat, diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index ee272ceee..da19b0bae 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -27,37 +27,32 @@ namespace irr { namespace video { + struct VertexAttribute { - enum class Mode + enum Mode : u8 { Regular, Normalized, - Integral, + Integer, }; - int Index; - int ComponentCount; + u8 Index; + u8 ComponentCount; GLenum ComponentType; Mode mode; - int Offset; + u32 Offset; }; struct VertexType { - int VertexSize; + u32 VertexSize; std::vector Attributes; + + // allow ranged for loops + inline auto begin() const { return Attributes.begin(); } + inline auto end() const { return Attributes.end(); } }; -static const VertexAttribute *begin(const VertexType &type) -{ - return type.Attributes.data(); -} - -static const VertexAttribute *end(const VertexType &type) -{ - return type.Attributes.data() + type.Attributes.size(); -} - static const VertexType vtStandard = { sizeof(S3DVertex), { @@ -68,6 +63,9 @@ static const VertexType vtStandard = { }, }; +// FIXME: this is actually UB because these vertex classes are not "standard-layout" +// they violate the following requirement: +// - only one class in the hierarchy has non-static data members #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Winvalid-offsetof" @@ -170,11 +168,15 @@ COpenGL3DriverBase::COpenGL3DriverBase(const SIrrlichtCreationParameters ¶ms ExposedData = ContextManager->getContext(); ContextManager->activateContext(ExposedData, false); GL.LoadAllProcedures(ContextManager); - if (EnableErrorTest) { + if (EnableErrorTest && GL.IsExtensionPresent("GL_KHR_debug")) { GL.Enable(GL_DEBUG_OUTPUT); GL.DebugMessageCallback(debugCb, this); + } else if (EnableErrorTest) { + os::Printer::log("GL debug extension not available"); } initQuadsIndices(); + + TEST_GL_ERROR(this); } COpenGL3DriverBase::~COpenGL3DriverBase() @@ -267,7 +269,6 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d &screenS for (s32 i = 0; i < ETS_COUNT; ++i) setTransform(static_cast(i), core::IdentityMatrix); - setAmbientLight(SColorf(0.0f, 0.0f, 0.0f, 0.0f)); GL.ClearDepthf(1.0f); GL.Hint(GL_GENERATE_MIPMAP_HINT, GL_NICEST); @@ -760,6 +761,13 @@ void COpenGL3DriverBase::drawVertexPrimitiveList(const void *vertices, u32 verte endDraw(vTypeDesc); } +void COpenGL3DriverBase::draw2DVertexPrimitiveList(const void *vertices, u32 vertexCount, + const void *indexList, u32 primitiveCount, + E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType) +{ + os::Printer::log("draw2DVertexPrimitiveList unimplemented", ELL_ERROR); +} + void COpenGL3DriverBase::draw2DImage(const video::ITexture *texture, const core::position2d &destPos, const core::rect &sourceRect, const core::rect *clipRect, SColor color, bool useAlphaChannelOfTexture) @@ -1066,7 +1074,7 @@ void COpenGL3DriverBase::drawElements(GLenum primitiveType, const VertexType &ve void COpenGL3DriverBase::beginDraw(const VertexType &vertexType, uintptr_t verticesBase) { - for (auto attr : vertexType) { + for (auto &attr : vertexType) { GL.EnableVertexAttribArray(attr.Index); switch (attr.mode) { case VertexAttribute::Mode::Regular: @@ -1075,7 +1083,7 @@ void COpenGL3DriverBase::beginDraw(const VertexType &vertexType, uintptr_t verti case VertexAttribute::Mode::Normalized: GL.VertexAttribPointer(attr.Index, attr.ComponentCount, attr.ComponentType, GL_TRUE, vertexType.VertexSize, reinterpret_cast(verticesBase + attr.Offset)); break; - case VertexAttribute::Mode::Integral: + case VertexAttribute::Mode::Integer: GL.VertexAttribIPointer(attr.Index, attr.ComponentCount, attr.ComponentType, vertexType.VertexSize, reinterpret_cast(verticesBase + attr.Offset)); break; } @@ -1084,7 +1092,7 @@ void COpenGL3DriverBase::beginDraw(const VertexType &vertexType, uintptr_t verti void COpenGL3DriverBase::endDraw(const VertexType &vertexType) { - for (auto attr : vertexType) + for (auto &attr : vertexType) GL.DisableVertexAttribArray(attr.Index); } diff --git a/irr/src/OpenGL/Driver.h b/irr/src/OpenGL/Driver.h index f00ffa8af..e0787e560 100644 --- a/irr/src/OpenGL/Driver.h +++ b/irr/src/OpenGL/Driver.h @@ -81,6 +81,11 @@ public: const void *indexList, u32 primitiveCount, E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType) override; + //! draws a vertex primitive list in 2d + virtual void draw2DVertexPrimitiveList(const void *vertices, u32 vertexCount, + const void *indexList, u32 primitiveCount, + E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType) override; + //! queries the features of the driver, returns true if feature is available bool queryFeature(E_VIDEO_DRIVER_FEATURE feature) const override { diff --git a/irr/src/OpenGLES2/Driver.cpp b/irr/src/OpenGLES2/Driver.cpp index 32bf0aee0..7b84675aa 100644 --- a/irr/src/OpenGLES2/Driver.cpp +++ b/irr/src/OpenGLES2/Driver.cpp @@ -43,8 +43,6 @@ void COpenGLES2Driver::initFeatures() } initExtensions(); - static const GLenum BGRA8_EXT = 0x93A1; - if (Version.Major >= 3) { // NOTE floating-point formats may not be suitable for render targets. TextureFormats[ECF_A1R5G5B5] = {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, CColorConverter::convert_A1R5G5B5toR5G5B5A1}; @@ -64,10 +62,11 @@ void COpenGLES2Driver::initFeatures() TextureFormats[ECF_D24] = {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}; TextureFormats[ECF_D24S8] = {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}; - if (FeatureAvailable[IRR_GL_EXT_texture_format_BGRA8888]) + // NOTE a recent (2024) revision of EXT_texture_format_BGRA8888 also + // adds a sized format GL_BGRA8_EXT. We have a workaround in place to + // fix up the InternalFormat in case of render targets. + if (FeatureAvailable[IRR_GL_EXT_texture_format_BGRA8888] || FeatureAvailable[IRR_GL_APPLE_texture_format_BGRA8888]) TextureFormats[ECF_A8R8G8B8] = {GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE}; - else if (FeatureAvailable[IRR_GL_APPLE_texture_format_BGRA8888]) - TextureFormats[ECF_A8R8G8B8] = {BGRA8_EXT, GL_BGRA, GL_UNSIGNED_BYTE}; // OpenGL ES 3 doesn't include a GL_DEPTH_COMPONENT32, so still use // OES_depth_texture for 32-bit depth texture support. @@ -87,10 +86,8 @@ void COpenGLES2Driver::initFeatures() TextureFormats[ECF_R8G8B8] = {GL_RGB, GL_RGB, GL_UNSIGNED_BYTE}; TextureFormats[ECF_A8R8G8B8] = {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, CColorConverter::convert_A8R8G8B8toA8B8G8R8}; - if (FeatureAvailable[IRR_GL_EXT_texture_format_BGRA8888]) + if (FeatureAvailable[IRR_GL_EXT_texture_format_BGRA8888] || FeatureAvailable[IRR_GL_APPLE_texture_format_BGRA8888]) TextureFormats[ECF_A8R8G8B8] = {GL_BGRA, GL_BGRA, GL_UNSIGNED_BYTE}; - else if (FeatureAvailable[IRR_GL_APPLE_texture_format_BGRA8888]) - TextureFormats[ECF_A8R8G8B8] = {BGRA8_EXT, GL_BGRA, GL_UNSIGNED_BYTE}; if (FeatureAvailable[IRR_GL_OES_texture_half_float]) { TextureFormats[ECF_A16B16G16R16F] = {GL_RGBA, GL_RGBA, HALF_FLOAT_OES}; diff --git a/src/client/render/anaglyph.cpp b/src/client/render/anaglyph.cpp index fe3a5f0f8..7baf40322 100644 --- a/src/client/render/anaglyph.cpp +++ b/src/client/render/anaglyph.cpp @@ -21,8 +21,7 @@ void SetColorMaskStep::run(PipelineContext &context) mat.Material.ColorMask = color_mask; mat.EnableProps = video::EMP_COLOR_MASK; mat.EnablePasses = scene::ESNRP_SKY_BOX | scene::ESNRP_SOLID | - scene::ESNRP_TRANSPARENT | scene::ESNRP_TRANSPARENT_EFFECT | - scene::ESNRP_SHADOW; + scene::ESNRP_TRANSPARENT | scene::ESNRP_TRANSPARENT_EFFECT; } /// ClearDepthBufferTarget From a799a54894c5913a57d7004366044f8c640a8c8c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 24 Nov 2024 18:00:25 +0100 Subject: [PATCH 30/68] Fix some issues with mt_opengl --- irr/include/mt_opengl.h | 20 +++++++++++----- irr/scripts/BindingGenerator.lua | 39 +++++++++++++++++++++----------- irr/src/mt_opengl_loader.cpp | 11 +++++---- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/irr/include/mt_opengl.h b/irr/include/mt_opengl.h index f69ba8c69..1a92cde56 100755 --- a/irr/include/mt_opengl.h +++ b/irr/include/mt_opengl.h @@ -15,11 +15,18 @@ #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif -#ifndef GLAPI - #define GLAPI extern +// undefine a few names that can easily clash with system headers +#ifdef NO_ERROR +#undef NO_ERROR +#endif +#ifdef ZERO +#undef ZERO +#endif +#ifdef ONE +#undef ONE #endif -class OpenGLProcedures { +class OpenGLProcedures final { private: // ./glcorearb.h typedef void GLvoid; @@ -49,8 +56,6 @@ private: typedef khronos_int64_t GLint64EXT; typedef void *GLeglClientBufferEXT; - // The script will miss this particular typedef thinking it's a PFN, - // so we have to paste it in manually. It's the only such type in OpenGL. typedef void (APIENTRY *GLDEBUGPROC) (GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); @@ -775,10 +780,12 @@ private: typedef void (APIENTRYP PFNGLTEXPAGECOMMITMENTPROC_MT) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean commit); std::unordered_set extensions; + public: // Call this once after creating the context. void LoadAllProcedures(irr::video::IContextManager *cmgr); - // Check if an extension is supported. + /// Check if an extension is supported. + /// @param ext full extension name e.g. "GL_KHR_no_error" inline bool IsExtensionPresent(const std::string &ext) const { return extensions.count(ext) > 0; @@ -3185,6 +3192,7 @@ public: static constexpr const GLenum STATE_RESTORE = 0x8BDC; static constexpr const GLenum SHADER_BINARY_VIV = 0x8FC4; + static constexpr const GLenum NO_ERROR = 0; static constexpr const GLenum ZERO = 0; static constexpr const GLenum ONE = 1; static constexpr const GLenum NONE = 0; diff --git a/irr/scripts/BindingGenerator.lua b/irr/scripts/BindingGenerator.lua index d0be04293..1ca5b41d9 100755 --- a/irr/scripts/BindingGenerator.lua +++ b/irr/scripts/BindingGenerator.lua @@ -219,11 +219,11 @@ local function ParseHeader( path, into, apiRegex, defs, consts, nameSet, noNewNa }; end elseif ( line:find( "#" ) and not line:find( "#include" ) ) then - local rawName, value = line:match( "#define%s+GL_([_%w]+)%s+0x(%w+)" ); + local rawName, value = line:match( "#define%s+GL_([_%w]+)%s+(0x%w+)" ); if rawName and value then local name, vendor = StripVendorSuffix( rawName, true ); if not constBanned[vendor] then - consts:Add{ name = name, vendor = vendor, value = "0x"..value }; + consts:Add{ name = name, vendor = vendor, value = value }; end end ::skip:: @@ -359,21 +359,28 @@ f:write[[ #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif -#ifndef GLAPI - #define GLAPI extern +// undefine a few names that can easily clash with system headers +#ifdef NO_ERROR +#undef NO_ERROR +#endif +#ifdef ZERO +#undef ZERO +#endif +#ifdef ONE +#undef ONE #endif ]]; f:write[[ -class OpenGLProcedures { +class OpenGLProcedures final { private: ]]; f:write( definitions:Concat( "\n" ) ); f:write( "\n" ); +-- The script will miss this particular typedef thinking it's a PFN, +-- so we have to paste it in manually. It's the only such type in OpenGL. f:write[[ - // The script will miss this particular typedef thinking it's a PFN, - // so we have to paste it in manually. It's the only such type in OpenGL. typedef void (APIENTRY *GLDEBUGPROC) (GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); @@ -382,10 +389,12 @@ f:write( typedefs:Concat( "\n" ) ); f:write( "\n\n" ); f:write [[ std::unordered_set extensions; + public: // Call this once after creating the context. void LoadAllProcedures(irr::video::IContextManager *cmgr); - // Check if an extension is supported. + /// Check if an extension is supported. + /// @param ext full extension name e.g. "GL_KHR_no_error" inline bool IsExtensionPresent(const std::string &ext) const { return extensions.count(ext) > 0; @@ -396,7 +405,10 @@ f:write( pointers:Concat( "\n" ) ); f:write( "\n\n" ); f:write( cppConsts:Concat( "\n" ) ); f:write( "\n\n" ); +-- We filter constants not in hex format to avoid the VERSION_X_X and extension +-- defines, but that means we miss these. f:write[[ + static constexpr const GLenum NO_ERROR = 0; static constexpr const GLenum ZERO = 0; static constexpr const GLenum ONE = 1; static constexpr const GLenum NONE = 0; @@ -416,7 +428,7 @@ f:write[[ #include #include -OpenGLProcedures GL = OpenGLProcedures(); +OpenGLProcedures GL; void OpenGLProcedures::LoadAllProcedures(irr::video::IContextManager *cmgr) { @@ -425,9 +437,11 @@ void OpenGLProcedures::LoadAllProcedures(irr::video::IContextManager *cmgr) f:write( loader:Concat() ); f:write[[ - // OpenGL 3 way to enumerate extensions + /* OpenGL 3 & ES 3 way to enumerate extensions */ GLint ext_count = 0; GetIntegerv(NUM_EXTENSIONS, &ext_count); + // clear error which is raised if unsupported + while (GetError() != GL.NO_ERROR) {} extensions.reserve(ext_count); for (GLint k = 0; k < ext_count; k++) { auto tmp = GetStringi(EXTENSIONS, k); @@ -437,16 +451,15 @@ f:write[[ if (!extensions.empty()) return; - // OpenGL 2 / ES 2 way to enumerate extensions + /* OpenGL 2 / ES 2 way to enumerate extensions */ auto ext_str = GetString(EXTENSIONS); if (!ext_str) return; // get the extension string, chop it up - std::stringstream ext_ss((char*)ext_str); + std::istringstream ext_ss((char*)ext_str); std::string tmp; while (std::getline(ext_ss, tmp, ' ')) extensions.emplace(tmp); - } ]]; f:close(); diff --git a/irr/src/mt_opengl_loader.cpp b/irr/src/mt_opengl_loader.cpp index 084f83118..2b7184036 100755 --- a/irr/src/mt_opengl_loader.cpp +++ b/irr/src/mt_opengl_loader.cpp @@ -5,7 +5,7 @@ #include #include -OpenGLProcedures GL = OpenGLProcedures(); +OpenGLProcedures GL; void OpenGLProcedures::LoadAllProcedures(irr::video::IContextManager *cmgr) { @@ -758,9 +758,11 @@ void OpenGLProcedures::LoadAllProcedures(irr::video::IContextManager *cmgr) if (!NamedBufferPageCommitment) NamedBufferPageCommitment = (PFNGLNAMEDBUFFERPAGECOMMITMENTPROC_MT)cmgr->getProcAddress("glNamedBufferPageCommitmentARB"); if (!TexPageCommitment) TexPageCommitment = (PFNGLTEXPAGECOMMITMENTPROC_MT)cmgr->getProcAddress("glTexPageCommitmentARB"); - // OpenGL 3 way to enumerate extensions + /* OpenGL 3 & ES 3 way to enumerate extensions */ GLint ext_count = 0; GetIntegerv(NUM_EXTENSIONS, &ext_count); + // clear error which is raised if unsupported + while (GetError() != GL.NO_ERROR) {} extensions.reserve(ext_count); for (GLint k = 0; k < ext_count; k++) { auto tmp = GetStringi(EXTENSIONS, k); @@ -770,14 +772,13 @@ void OpenGLProcedures::LoadAllProcedures(irr::video::IContextManager *cmgr) if (!extensions.empty()) return; - // OpenGL 2 / ES 2 way to enumerate extensions + /* OpenGL 2 / ES 2 way to enumerate extensions */ auto ext_str = GetString(EXTENSIONS); if (!ext_str) return; // get the extension string, chop it up - std::stringstream ext_ss((char*)ext_str); + std::istringstream ext_ss((char*)ext_str); std::string tmp; while (std::getline(ext_ss, tmp, ' ')) extensions.emplace(tmp); - } From 810f39767cf4780cb050b409d660eed3ead17c81 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 25 Nov 2024 21:56:12 +0100 Subject: [PATCH 31/68] Move SMaterial std::hash impl to its header --- irr/include/SMaterial.h | 29 +++++++++++++++++++------ irr/src/CSceneManager.cpp | 4 +--- irr/src/CSceneManager.h | 45 ++++++++------------------------------- src/client/clientmap.cpp | 13 ++--------- 4 files changed, 35 insertions(+), 56 deletions(-) diff --git a/irr/include/SMaterial.h b/irr/include/SMaterial.h index 7a939317c..867557154 100644 --- a/irr/include/SMaterial.h +++ b/irr/include/SMaterial.h @@ -230,14 +230,14 @@ const c8 *const ZWriteNames[] = { /** SMaterial might ignore some textures in most function, like assignment and comparison, when SIrrlichtCreationParameters::MaxTextureUnits is set to a lower number. */ -const u32 MATERIAL_MAX_TEXTURES = 4; +constexpr static u32 MATERIAL_MAX_TEXTURES = 4; //! Struct for holding parameters for a material renderer // Note for implementors: Serialization is in CNullDriver class SMaterial { public: - //! Default constructor. Creates a solid, lit material with white colors + //! Default constructor. Creates a solid material SMaterial() : MaterialType(EMT_SOLID), ColorParam(0, 0, 0, 0), MaterialTypeParam(0.0f), Thickness(1.0f), ZBuffer(ECFN_LESSEQUAL), @@ -257,7 +257,7 @@ public: E_MATERIAL_TYPE MaterialType; //! Custom color parameter, can be used by custom shader materials. - // See MainShaderConstantSetter in Minetest. + // See MainShaderConstantSetter in Luanti. SColor ColorParam; //! Free parameter, dependent on the material type. @@ -427,10 +427,13 @@ public: PolygonOffsetDepthBias != b.PolygonOffsetDepthBias || PolygonOffsetSlopeScale != b.PolygonOffsetSlopeScale || UseMipMaps != b.UseMipMaps; - for (u32 i = 0; (i < MATERIAL_MAX_TEXTURES) && !different; ++i) { - different |= (TextureLayers[i] != b.TextureLayers[i]); + if (different) + return true; + for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; ++i) { + if (TextureLayers[i] != b.TextureLayers[i]) + return true; } - return different; + return false; } //! Equality operator @@ -477,5 +480,19 @@ public: //! global const identity Material IRRLICHT_API extern SMaterial IdentityMaterial; + } // end namespace video } // end namespace irr + +template<> +struct std::hash +{ + /// @brief std::hash specialization for video::SMaterial + std::size_t operator()(const irr::video::SMaterial &m) const noexcept + { + // basic implementation that hashes the two things most likely to differ + auto h1 = std::hash{}(m.getTexture(0)); + auto h2 = std::hash{}(m.MaterialType); + return (h1 << 1) ^ h2; + } +}; diff --git a/irr/src/CSceneManager.cpp b/irr/src/CSceneManager.cpp index 04b3ec313..71ae7f962 100644 --- a/irr/src/CSceneManager.cpp +++ b/irr/src/CSceneManager.cpp @@ -469,14 +469,12 @@ void CSceneManager::drawAll() if (!Driver) return; - u32 i; // new ISO for scoping problem in some compilers - // reset all transforms Driver->setMaterial(video::SMaterial()); Driver->setTransform(video::ETS_PROJECTION, core::IdentityMatrix); Driver->setTransform(video::ETS_VIEW, core::IdentityMatrix); Driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); - for (i = video::ETS_COUNT - 1; i >= video::ETS_TEXTURE_0; --i) + for (u32 i = video::ETS_COUNT - 1; i >= video::ETS_TEXTURE_0; --i) Driver->setTransform((video::E_TRANSFORMATION_STATE)i, core::IdentityMatrix); // TODO: This should not use an attribute here but a real parameter when necessary (too slow!) Driver->setAllowZWriteOnTransparent(Parameters->getAttributeAsBool(ALLOW_ZWRITE_ON_TRANSPARENT)); diff --git a/irr/src/CSceneManager.h b/irr/src/CSceneManager.h index 0f1b2c716..4362ba04d 100644 --- a/irr/src/CSceneManager.h +++ b/irr/src/CSceneManager.h @@ -193,21 +193,21 @@ private: } DefaultNodeEntry(ISceneNode *n) : - Node(n), TextureValue(0) + Node(n) { if (n->getMaterialCount()) - TextureValue = (n->getMaterial(0).getTexture(0)); + Hash = std::hash{}(n->getMaterial(0)); } - bool operator<(const DefaultNodeEntry &other) const + bool operator<(const DefaultNodeEntry &other) const noexcept { - return (TextureValue < other.TextureValue); + return Hash < other.Hash; } - ISceneNode *Node; + ISceneNode *Node = nullptr; private: - void *TextureValue; + size_t Hash = 0; }; //! sort on distance (center) to camera @@ -223,42 +223,15 @@ private: Distance = Node->getAbsoluteTransformation().getTranslation().getDistanceFromSQ(camera); } - bool operator<(const TransparentNodeEntry &other) const + bool operator<(const TransparentNodeEntry &other) const noexcept { return Distance > other.Distance; } - ISceneNode *Node; + ISceneNode *Node = nullptr; private: - f64 Distance; - }; - - //! sort on distance (sphere) to camera - struct DistanceNodeEntry - { - DistanceNodeEntry(ISceneNode *n, const core::vector3df &cameraPos) : - Node(n) - { - setNodeAndDistanceFromPosition(n, cameraPos); - } - - bool operator<(const DistanceNodeEntry &other) const - { - return Distance < other.Distance; - } - - void setNodeAndDistanceFromPosition(ISceneNode *n, const core::vector3df &fromPosition) - { - Node = n; - Distance = Node->getAbsoluteTransformation().getTranslation().getDistanceFromSQ(fromPosition); - Distance -= Node->getBoundingBox().getExtent().getLengthSQ() * 0.5; - } - - ISceneNode *Node; - - private: - f64 Distance; + f32 Distance = 0; }; //! video driver diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index c3207efd1..f57a04b60 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -25,19 +25,10 @@ namespace { // A helper struct struct MeshBufListMaps { - struct MaterialHash - { - size_t operator()(const video::SMaterial &m) const noexcept - { - // Only hash first texture. Simple and fast. - return std::hash{}(m.TextureLayers[0].Texture); - } - }; - using MeshBufListMap = std::unordered_map< video::SMaterial, - std::vector>, - MaterialHash>; + std::vector> + >; std::array maps; From 3e10d9ccf5c5dc20d1ceb0e696ce543f3618e56e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:03:44 +0100 Subject: [PATCH 32/68] Refactor: Merge `[IC]SkinnedMesh` into `SkinnedMesh` (#15511) --- irr/include/IBoneSceneNode.h | 2 +- irr/include/ISceneManager.h | 187 +------------- irr/include/ISkinnedMesh.h | 226 ---------------- .../CSkinnedMesh.h => include/SkinnedMesh.h} | 242 +++++++++++++----- irr/src/CAnimatedMeshSceneNode.cpp | 18 +- irr/src/CB3DMeshFileLoader.cpp | 24 +- irr/src/CB3DMeshFileLoader.h | 14 +- irr/src/CGLTFMeshFileLoader.cpp | 20 +- irr/src/CGLTFMeshFileLoader.h | 14 +- irr/src/CMakeLists.txt | 2 +- irr/src/CMeshManipulator.cpp | 6 +- irr/src/CMeshSceneNode.cpp | 4 +- irr/src/CSceneManager.cpp | 6 +- irr/src/CSceneManager.h | 7 +- irr/src/CXMeshFileLoader.cpp | 36 +-- irr/src/CXMeshFileLoader.h | 10 +- irr/src/{CSkinnedMesh.cpp => SkinnedMesh.cpp} | 144 +++++------ src/unittest/test_irr_gltf_mesh_loader.cpp | 10 +- 18 files changed, 326 insertions(+), 646 deletions(-) delete mode 100644 irr/include/ISkinnedMesh.h rename irr/{src/CSkinnedMesh.h => include/SkinnedMesh.h} (57%) rename irr/src/{CSkinnedMesh.cpp => SkinnedMesh.cpp} (89%) diff --git a/irr/include/IBoneSceneNode.h b/irr/include/IBoneSceneNode.h index 668289271..eef55f6e0 100644 --- a/irr/include/IBoneSceneNode.h +++ b/irr/include/IBoneSceneNode.h @@ -48,7 +48,7 @@ const c8 *const BoneAnimationModeNames[] = { }; //! Interface for bones used for skeletal animation. -/** Used with ISkinnedMesh and IAnimatedMeshSceneNode. */ +/** Used with SkinnedMesh and IAnimatedMeshSceneNode. */ class IBoneSceneNode : public ISceneNode { public: diff --git a/irr/include/ISceneManager.h b/irr/include/ISceneManager.h index 7fbc0670c..25e1e5fe4 100644 --- a/irr/include/ISceneManager.h +++ b/irr/include/ISceneManager.h @@ -11,7 +11,6 @@ #include "SColor.h" #include "ESceneNodeTypes.h" #include "SceneParameters.h" // IWYU pragma: export -#include "ISkinnedMesh.h" namespace irr { @@ -93,6 +92,7 @@ class IBillboardSceneNode; class ICameraSceneNode; class IDummyTransformationSceneNode; class IMesh; +class SkinnedMesh; class IMeshBuffer; class IMeshCache; class ISceneCollisionManager; @@ -121,189 +121,6 @@ public: //! Get pointer to an animatable mesh. Loads the file if not loaded already. /** * If you want to remove a loaded mesh from the cache again, use removeMesh(). - * Currently there are the following mesh formats supported: - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
FormatDescription
3D Studio (.3ds)Loader for 3D-Studio files which lots of 3D packages - * are able to export. Only static meshes are currently - * supported by this importer.
3D World Studio (.smf)Loader for Leadwerks SMF mesh files, a simple mesh format - * containing static geometry for games. The proprietary .STF texture format - * is not supported yet. This loader was originally written by Joseph Ellis.
Bliz Basic B3D (.b3d)Loader for blitz basic files, developed by Mark - * Sibly. This is the ideal animated mesh format for game - * characters as it is both rigidly defined and widely - * supported by modeling and animation software. - * As this format supports skeletal animations, an - * ISkinnedMesh will be returned by this importer.
Cartography shop 4 (.csm)Cartography Shop is a modeling program for creating - * architecture and calculating lighting. Irrlicht can - * directly import .csm files thanks to the IrrCSM library - * created by Saurav Mohapatra which is now integrated - * directly in Irrlicht. - *
Delgine DeleD (.dmf)DeleD (delgine.com) is a 3D editor and level-editor - * combined into one and is specifically designed for 3D - * game-development. With this loader, it is possible to - * directly load all geometry is as well as textures and - * lightmaps from .dmf files. To set texture and - * material paths, see scene::DMF_USE_MATERIALS_DIRS. - * It is also possible to flip the alpha texture by setting - * scene::DMF_FLIP_ALPHA_TEXTURES to true and to set the - * material transparent reference value by setting - * scene::DMF_ALPHA_CHANNEL_REF to a float between 0 and - * 1. The loader is based on Salvatore Russo's .dmf - * loader, I just changed some parts of it. Thanks to - * Salvatore for his work and for allowing me to use his - * code in Irrlicht and put it under Irrlicht's license. - * For newer and more enhanced versions of the loader, - * take a look at delgine.com. - *
DirectX (.x)Platform independent importer (so not D3D-only) for - * .x files. Most 3D packages can export these natively - * and there are several tools for them available, e.g. - * the Maya exporter included in the DX SDK. - * .x files can include skeletal animations and Irrlicht - * is able to play and display them, users can manipulate - * the joints via the ISkinnedMesh interface. Currently, - * Irrlicht only supports uncompressed .x files.
Half-Life model (.mdl)This loader opens Half-life 1 models, it was contributed - * by Fabio Concas and adapted by Thomas Alten.
LightWave (.lwo)Native to NewTek's LightWave 3D, the LWO format is well - * known and supported by many exporters. This loader will - * import LWO2 models including lightmaps, bumpmaps and - * reflection textures.
Maya (.obj)Most 3D software can create .obj files which contain - * static geometry without material data. The material - * files .mtl are also supported. This importer for - * Irrlicht can load them directly.
Milkshape (.ms3d).MS3D files contain models and sometimes skeletal - * animations from the Milkshape 3D modeling and animation - * software. Like the other skeletal mesh loaders, joints - * are exposed via the ISkinnedMesh animated mesh type.
My3D (.my3d).my3D is a flexible 3D file format. The My3DTools - * contains plug-ins to export .my3D files from several - * 3D packages. With this built-in importer, Irrlicht - * can read and display those files directly. This - * loader was written by Zhuck Dimitry who also created - * the whole My3DTools package. - *
OCT (.oct)The oct file format contains 3D geometry and - * lightmaps and can be loaded directly by Irrlicht. OCT - * files
can be created by FSRad, Paul Nette's - * radiosity processor or exported from Blender using - * OCTTools which can be found in the exporters/OCTTools - * directory of the SDK. Thanks to Murphy McCauley for - * creating all this.
OGRE Meshes (.mesh)Ogre .mesh files contain 3D data for the OGRE 3D - * engine. Irrlicht can read and display them directly - * with this importer. To define materials for the mesh, - * copy a .material file named like the corresponding - * .mesh file where the .mesh file is. (For example - * ogrehead.material for ogrehead.mesh). Thanks to - * Christian Stehno who wrote and contributed this - * loader.
Pulsar LMTools (.lmts)LMTools is a set of tools (Windows & Linux) for - * creating lightmaps. Irrlicht can directly read .lmts - * files thanks to
the importer created by Jonas - * Petersen. - * Notes for
this version of the loader:
- * - It does not recognize/support user data in the - * *.lmts files.
- * - The TGAs generated by LMTools don't work in - * Irrlicht for some reason (the textures are upside - * down). Opening and resaving them in a graphics app - * will solve the problem.
Quake 3 levels (.bsp)Quake 3 is a popular game by IDSoftware, and .pk3 - * files contain .bsp files and textures/lightmaps - * describing huge prelighted levels. Irrlicht can read - * .pk3 and .bsp files directly and thus render Quake 3 - * levels directly. Written by Nikolaus Gebhardt - * enhanced by Dean P. Macri with the curved surfaces - * feature.
Quake 2 models (.md2)Quake 2 models are characters with morph target - * animation. Irrlicht can read, display and animate - * them directly with this importer.
Quake 3 models (.md3)Quake 3 models are characters with morph target - * animation, they contain mount points for weapons and body - * parts and are typically made of several sections which are - * manually joined together.
Stanford Triangle (.ply)Invented by Stanford University and known as the native - * format of the infamous "Stanford Bunny" model, this is a - * popular static mesh format used by 3D scanning hardware - * and software. This loader supports extremely large models - * in both ASCII and binary format, but only has rudimentary - * material support in the form of vertex colors and texture - * coordinates.
Stereolithography (.stl)The STL format is used for rapid prototyping and - * computer-aided manufacturing, thus has no support for - * materials.
- * - * To load and display a mesh quickly, just do this: - * \code - * SceneManager->addAnimatedMeshSceneNode( - * SceneManager->getMesh("yourmesh.3ds")); - * \endcode * If you would like to implement and add your own file format loader to Irrlicht, * see addExternalMeshLoader(). * \param file File handle of the mesh to load. @@ -594,7 +411,7 @@ public: //! Get a skinned mesh, which is not available as header-only code /** Note: You need to drop() the pointer after use again, see IReferenceCounted::drop() for details. */ - virtual ISkinnedMesh *createSkinnedMesh() = 0; + virtual SkinnedMesh *createSkinnedMesh() = 0; //! Get current render pass. virtual E_SCENE_NODE_RENDER_PASS getCurrentRenderPass() const = 0; diff --git a/irr/include/ISkinnedMesh.h b/irr/include/ISkinnedMesh.h deleted file mode 100644 index af241f55d..000000000 --- a/irr/include/ISkinnedMesh.h +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#pragma once - -#include "irrArray.h" -#include "IAnimatedMesh.h" -#include "SSkinMeshBuffer.h" -#include "quaternion.h" - -#include -#include - -namespace irr -{ -namespace scene -{ - -enum E_INTERPOLATION_MODE -{ - // constant does use the current key-values without interpolation - EIM_CONSTANT = 0, - - // linear interpolation - EIM_LINEAR, - - //! count of all available interpolation modes - EIM_COUNT -}; - -//! Interface for using some special functions of Skinned meshes -class ISkinnedMesh : public IAnimatedMesh -{ -public: - //! Gets joint count. - /** \return Amount of joints in the skeletal animated mesh. */ - virtual u32 getJointCount() const = 0; - - //! Gets the name of a joint. - /** \param number: Zero based index of joint. The last joint - has the number getJointCount()-1; - \return Name of joint and null if an error happened. */ - virtual const std::optional &getJointName(u32 number) const = 0; - - //! Gets a joint number from its name - /** \param name: Name of the joint. - \return Number of the joint or std::nullopt if not found. */ - virtual std::optional getJointNumber(const std::string &name) const = 0; - - //! Use animation from another mesh - /** The animation is linked (not copied) based on joint names - so make sure they are unique. - \return True if all joints in this mesh were - matched up (empty names will not be matched, and it's case - sensitive). Unmatched joints will not be animated. */ - virtual bool useAnimationFrom(const ISkinnedMesh *mesh) = 0; - - //! Update Normals when Animating - /** \param on If false don't animate, which is faster. - Else update normals, which allows for proper lighting of - animated meshes. */ - virtual void updateNormalsWhenAnimating(bool on) = 0; - - //! Sets Interpolation Mode - virtual void setInterpolationMode(E_INTERPOLATION_MODE mode) = 0; - - //! Animates this mesh's joints based on frame input - virtual void animateMesh(f32 frame, f32 blend) = 0; - - //! Preforms a software skin on this mesh based of joint positions - virtual void skinMesh() = 0; - - //! converts the vertex type of all meshbuffers to tangents. - /** E.g. used for bump mapping. */ - virtual void convertMeshToTangents() = 0; - - //! Allows to enable hardware skinning. - /* This feature is not implemented in Irrlicht yet */ - virtual bool setHardwareSkinning(bool on) = 0; - - //! Refreshes vertex data cached in joints such as positions and normals - virtual void refreshJointCache() = 0; - - //! Moves the mesh into static position. - virtual void resetAnimation() = 0; - - //! A vertex weight - struct SWeight - { - //! Index of the mesh buffer - u16 buffer_id; // I doubt 32bits is needed - - //! Index of the vertex - u32 vertex_id; // Store global ID here - - //! Weight Strength/Percentage (0-1) - f32 strength; - - private: - //! Internal members used by CSkinnedMesh - friend class CSkinnedMesh; - char *Moved; - core::vector3df StaticPos; - core::vector3df StaticNormal; - }; - - //! Animation keyframe which describes a new position - struct SPositionKey - { - f32 frame; - core::vector3df position; - }; - - //! Animation keyframe which describes a new scale - struct SScaleKey - { - f32 frame; - core::vector3df scale; - }; - - //! Animation keyframe which describes a new rotation - struct SRotationKey - { - f32 frame; - core::quaternion rotation; - }; - - //! Joints - struct SJoint - { - SJoint() : - UseAnimationFrom(0), GlobalSkinningSpace(false), - positionHint(-1), scaleHint(-1), rotationHint(-1) - { - } - - //! The name of this joint - std::optional Name; - - //! Local matrix of this joint - core::matrix4 LocalMatrix; - - //! List of child joints - core::array Children; - - //! List of attached meshes - core::array AttachedMeshes; - - //! Animation keys causing translation change - core::array PositionKeys; - - //! Animation keys causing scale change - core::array ScaleKeys; - - //! Animation keys causing rotation change - core::array RotationKeys; - - //! Skin weights - core::array Weights; - - //! Unnecessary for loaders, will be overwritten on finalize - core::matrix4 GlobalMatrix; // loaders may still choose to set this (temporarily) to calculate absolute vertex data. - core::matrix4 GlobalAnimatedMatrix; - core::matrix4 LocalAnimatedMatrix; - - //! These should be set by loaders. - core::vector3df Animatedposition; - core::vector3df Animatedscale; - core::quaternion Animatedrotation; - - // The .x and .gltf formats pre-calculate this - std::optional GlobalInversedMatrix; - private: - //! Internal members used by CSkinnedMesh - friend class CSkinnedMesh; - - SJoint *UseAnimationFrom; - bool GlobalSkinningSpace; - - s32 positionHint; - s32 scaleHint; - s32 rotationHint; - }; - - // Interface for the mesh loaders (finalize should lock these functions, and they should have some prefix like loader_ - - // these functions will use the needed arrays, set values, etc to help the loaders - - //! exposed for loaders: to add mesh buffers - virtual core::array &getMeshBuffers() = 0; - - //! exposed for loaders: joints list - virtual core::array &getAllJoints() = 0; - - //! exposed for loaders: joints list - virtual const core::array &getAllJoints() const = 0; - - //! loaders should call this after populating the mesh - virtual void finalize() = 0; - - //! Adds a new meshbuffer to the mesh, access it as last one - virtual SSkinMeshBuffer *addMeshBuffer() = 0; - - //! Adds a new meshbuffer to the mesh, access it as last one - virtual void addMeshBuffer(SSkinMeshBuffer *meshbuf) = 0; - - //! Adds a new joint to the mesh, access it as last one - virtual SJoint *addJoint(SJoint *parent = 0) = 0; - - //! Adds a new weight to the mesh, access it as last one - virtual SWeight *addWeight(SJoint *joint) = 0; - - //! Adds a new position key to the mesh, access it as last one - virtual SPositionKey *addPositionKey(SJoint *joint) = 0; - //! Adds a new scale key to the mesh, access it as last one - virtual SScaleKey *addScaleKey(SJoint *joint) = 0; - //! Adds a new rotation key to the mesh, access it as last one - virtual SRotationKey *addRotationKey(SJoint *joint) = 0; - - //! Check if the mesh is non-animated - virtual bool isStatic() = 0; -}; - -} // end namespace scene -} // end namespace irr diff --git a/irr/src/CSkinnedMesh.h b/irr/include/SkinnedMesh.h similarity index 57% rename from irr/src/CSkinnedMesh.h rename to irr/include/SkinnedMesh.h index 576ae1217..54ce8ed2c 100644 --- a/irr/src/CSkinnedMesh.h +++ b/irr/include/SkinnedMesh.h @@ -2,32 +2,46 @@ // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h -// New skinned mesh - #pragma once +#include "IAnimatedMesh.h" #include "ISceneManager.h" -#include "ISkinnedMesh.h" #include "SMeshBuffer.h" +#include "SSkinMeshBuffer.h" #include "quaternion.h" +#include +#include + namespace irr { namespace scene { +enum E_INTERPOLATION_MODE +{ + // constant does use the current key-values without interpolation + EIM_CONSTANT = 0, + + // linear interpolation + EIM_LINEAR, + + //! count of all available interpolation modes + EIM_COUNT +}; + class IAnimatedMeshSceneNode; class IBoneSceneNode; class ISceneManager; -class CSkinnedMesh : public ISkinnedMesh +class SkinnedMesh : public IAnimatedMesh { public: //! constructor - CSkinnedMesh(); + SkinnedMesh(); //! destructor - virtual ~CSkinnedMesh(); + virtual ~SkinnedMesh(); //! If the duration is 0, it is a static (=non animated) mesh. f32 getMaxFrameNumber() const override; @@ -46,10 +60,10 @@ public: //! Animates this mesh's joints based on frame input //! blend: {0-old position, 1-New position} - void animateMesh(f32 frame, f32 blend) override; + void animateMesh(f32 frame, f32 blend); - //! Preforms a software skin on this mesh based of joint positions - void skinMesh() override; + //! Performs a software skin on this mesh based of joint positions + void skinMesh(); //! returns amount of mesh buffers. u32 getMeshBufferCount() const override; @@ -83,83 +97,53 @@ public: E_ANIMATED_MESH_TYPE getMeshType() const override; //! Gets joint count. - u32 getJointCount() const override; + u32 getJointCount() const; //! Gets the name of a joint. - const std::optional &getJointName(u32 number) const override; + /** \param number: Zero based index of joint. + \return Name of joint and null if an error happened. */ + const std::optional &getJointName(u32 number) const; //! Gets a joint number from its name - std::optional getJointNumber(const std::string &name) const override; - - //! uses animation from another mesh - bool useAnimationFrom(const ISkinnedMesh *mesh) override; + /** \param name: Name of the joint. + \return Number of the joint or std::nullopt if not found. */ + std::optional getJointNumber(const std::string &name) const; //! Update Normals when Animating - //! False= Don't (default) - //! True = Update normals, slower - void updateNormalsWhenAnimating(bool on) override; + /** \param on If false don't animate, which is faster. + Else update normals, which allows for proper lighting of + animated meshes. */ + void updateNormalsWhenAnimating(bool on); //! Sets Interpolation Mode - void setInterpolationMode(E_INTERPOLATION_MODE mode) override; + void setInterpolationMode(E_INTERPOLATION_MODE mode); - //! Convertes the mesh to contain tangent information - void convertMeshToTangents() override; + //! converts the vertex type of all meshbuffers to tangents. + /** E.g. used for bump mapping. */ + void convertMeshToTangents(); //! Does the mesh have no animation - bool isStatic() override; + bool isStatic() const; - //! (This feature is not implemented in irrlicht yet) - bool setHardwareSkinning(bool on) override; + //! Allows to enable hardware skinning. + /* This feature is not implemented in Irrlicht yet */ + bool setHardwareSkinning(bool on); //! Refreshes vertex data cached in joints such as positions and normals - void refreshJointCache() override; + void refreshJointCache(); //! Moves the mesh into static position. - void resetAnimation() override; + void resetAnimation(); - // Interface for the mesh loaders (finalize should lock these functions, and they should have some prefix like loader_ - // these functions will use the needed arrays, set values, etc to help the loaders - - //! exposed for loaders to add mesh buffers - core::array &getMeshBuffers() override; - - //! alternative method for adding joints - core::array &getAllJoints() override; - - //! alternative method for adding joints - const core::array &getAllJoints() const override; - - //! loaders should call this after populating the mesh - void finalize() override; - - //! Adds a new meshbuffer to the mesh, access it as last one - SSkinMeshBuffer *addMeshBuffer() override; - - //! Adds a new meshbuffer to the mesh, access it as last one - void addMeshBuffer(SSkinMeshBuffer *meshbuf) override; - - //! Adds a new joint to the mesh, access it as last one - SJoint *addJoint(SJoint *parent = 0) override; - - //! Adds a new position key to the mesh, access it as last one - SPositionKey *addPositionKey(SJoint *joint) override; - //! Adds a new rotation key to the mesh, access it as last one - SRotationKey *addRotationKey(SJoint *joint) override; - //! Adds a new scale key to the mesh, access it as last one - SScaleKey *addScaleKey(SJoint *joint) override; - - //! Adds a new weight to the mesh, access it as last one - SWeight *addWeight(SJoint *joint) override; - - virtual void updateBoundingBox(void); + virtual void updateBoundingBox(); //! Recovers the joints from the mesh void recoverJointsFromMesh(core::array &jointChildSceneNodes); - //! Tranfers the joint data to the mesh + //! Transfers the joint data to the mesh void transferJointsToMesh(const core::array &jointChildSceneNodes); - //! Tranfers the joint hints to the mesh + //! Transfers the joint hints to the mesh void transferOnlyJointsHintsToMesh(const core::array &jointChildSceneNodes); //! Creates an array of joints from this mesh as children of node @@ -167,6 +151,138 @@ public: IAnimatedMeshSceneNode *node, ISceneManager *smgr); + //! A vertex weight + struct SWeight + { + //! Index of the mesh buffer + u16 buffer_id; // I doubt 32bits is needed + + //! Index of the vertex + u32 vertex_id; // Store global ID here + + //! Weight Strength/Percentage (0-1) + f32 strength; + + private: + //! Internal members used by SkinnedMesh + friend class SkinnedMesh; + char *Moved; + core::vector3df StaticPos; + core::vector3df StaticNormal; + }; + + //! Animation keyframe which describes a new position + struct SPositionKey + { + f32 frame; + core::vector3df position; + }; + + //! Animation keyframe which describes a new scale + struct SScaleKey + { + f32 frame; + core::vector3df scale; + }; + + //! Animation keyframe which describes a new rotation + struct SRotationKey + { + f32 frame; + core::quaternion rotation; + }; + + //! Joints + struct SJoint + { + SJoint() : + UseAnimationFrom(0), GlobalSkinningSpace(false), + positionHint(-1), scaleHint(-1), rotationHint(-1) + { + } + + //! The name of this joint + std::optional Name; + + //! Local matrix of this joint + core::matrix4 LocalMatrix; + + //! List of child joints + core::array Children; + + //! List of attached meshes + core::array AttachedMeshes; + + //! Animation keys causing translation change + core::array PositionKeys; + + //! Animation keys causing scale change + core::array ScaleKeys; + + //! Animation keys causing rotation change + core::array RotationKeys; + + //! Skin weights + core::array Weights; + + //! Unnecessary for loaders, will be overwritten on finalize + core::matrix4 GlobalMatrix; // loaders may still choose to set this (temporarily) to calculate absolute vertex data. + core::matrix4 GlobalAnimatedMatrix; + core::matrix4 LocalAnimatedMatrix; + + //! These should be set by loaders. + core::vector3df Animatedposition; + core::vector3df Animatedscale; + core::quaternion Animatedrotation; + + // The .x and .gltf formats pre-calculate this + std::optional GlobalInversedMatrix; + private: + //! Internal members used by SkinnedMesh + friend class SkinnedMesh; + + SJoint *UseAnimationFrom; + bool GlobalSkinningSpace; + + s32 positionHint; + s32 scaleHint; + s32 rotationHint; + }; + + // Interface for the mesh loaders (finalize should lock these functions, and they should have some prefix like loader_ + // these functions will use the needed arrays, set values, etc to help the loaders + + //! exposed for loaders to add mesh buffers + core::array &getMeshBuffers(); + + //! alternative method for adding joints + core::array &getAllJoints(); + + //! alternative method for reading joints + const core::array &getAllJoints() const; + + //! loaders should call this after populating the mesh + void finalize(); + + //! Adds a new meshbuffer to the mesh, access it as last one + SSkinMeshBuffer *addMeshBuffer(); + + //! Adds a new meshbuffer to the mesh, access it as last one + void addMeshBuffer(SSkinMeshBuffer *meshbuf); + + //! Adds a new joint to the mesh, access it as last one + SJoint *addJoint(SJoint *parent = 0); + + //! Adds a new position key to the mesh, access it as last one + SPositionKey *addPositionKey(SJoint *joint); + //! Adds a new rotation key to the mesh, access it as last one + SRotationKey *addRotationKey(SJoint *joint); + //! Adds a new scale key to the mesh, access it as last one + SScaleKey *addScaleKey(SJoint *joint); + + //! Adds a new weight to the mesh, access it as last one + SWeight *addWeight(SJoint *joint); + private: void checkForAnimation(); diff --git a/irr/src/CAnimatedMeshSceneNode.cpp b/irr/src/CAnimatedMeshSceneNode.cpp index ba8bc3b78..5250fe889 100644 --- a/irr/src/CAnimatedMeshSceneNode.cpp +++ b/irr/src/CAnimatedMeshSceneNode.cpp @@ -7,7 +7,7 @@ #include "ISceneManager.h" #include "S3DVertex.h" #include "os.h" -#include "CSkinnedMesh.h" +#include "SkinnedMesh.h" #include "IDummyTransformationSceneNode.h" #include "IBoneSceneNode.h" #include "IMaterialRenderer.h" @@ -165,7 +165,7 @@ IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame() // As multiple scene nodes may be sharing the same skinned mesh, we have to // re-animate it every frame to ensure that this node gets the mesh that it needs. - CSkinnedMesh *skinnedMesh = static_cast(Mesh); + SkinnedMesh *skinnedMesh = static_cast(Mesh); if (JointMode == EJUOR_CONTROL) // write to mesh skinnedMesh->transferJointsToMesh(JointChildSceneNodes); @@ -299,8 +299,8 @@ void CAnimatedMeshSceneNode::render() if (Mesh->getMeshType() == EAMT_SKINNED) { // draw skeleton - for (u32 g = 0; g < ((ISkinnedMesh *)Mesh)->getAllJoints().size(); ++g) { - ISkinnedMesh::SJoint *joint = ((ISkinnedMesh *)Mesh)->getAllJoints()[g]; + for (u32 g = 0; g < ((SkinnedMesh *)Mesh)->getAllJoints().size(); ++g) { + auto *joint = ((SkinnedMesh *)Mesh)->getAllJoints()[g]; for (u32 n = 0; n < joint->Children.size(); ++n) { driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(), @@ -404,7 +404,7 @@ IBoneSceneNode *CAnimatedMeshSceneNode::getJointNode(const c8 *jointName) checkJoints(); - ISkinnedMesh *skinnedMesh = (ISkinnedMesh *)Mesh; + auto *skinnedMesh = (SkinnedMesh *)Mesh; const std::optional number = skinnedMesh->getJointNumber(jointName); @@ -446,7 +446,7 @@ u32 CAnimatedMeshSceneNode::getJointCount() const if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) return 0; - ISkinnedMesh *skinnedMesh = (ISkinnedMesh *)Mesh; + auto *skinnedMesh = (SkinnedMesh *)Mesh; return skinnedMesh->getJointCount(); } @@ -596,7 +596,7 @@ void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions) checkJoints(); const f32 frame = getFrameNr(); // old? - CSkinnedMesh *skinnedMesh = static_cast(Mesh); + SkinnedMesh *skinnedMesh = static_cast(Mesh); skinnedMesh->transferOnlyJointsHintsToMesh(JointChildSceneNodes); skinnedMesh->animateMesh(frame, 1.0f); @@ -671,8 +671,8 @@ void CAnimatedMeshSceneNode::checkJoints() JointChildSceneNodes.clear(); // Create joints for SkinnedMesh - ((CSkinnedMesh *)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager); - ((CSkinnedMesh *)Mesh)->recoverJointsFromMesh(JointChildSceneNodes); + ((SkinnedMesh *)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager); + ((SkinnedMesh *)Mesh)->recoverJointsFromMesh(JointChildSceneNodes); JointsUsed = true; JointMode = EJUOR_READ; diff --git a/irr/src/CB3DMeshFileLoader.cpp b/irr/src/CB3DMeshFileLoader.cpp index 6cb40cb95..6923cde04 100644 --- a/irr/src/CB3DMeshFileLoader.cpp +++ b/irr/src/CB3DMeshFileLoader.cpp @@ -51,7 +51,7 @@ IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file) return 0; B3DFile = file; - AnimatedMesh = new scene::CSkinnedMesh(); + AnimatedMesh = new scene::SkinnedMesh(); ShowWarning = true; // If true a warning is issued if too many textures are used VerticesStart = 0; @@ -111,7 +111,7 @@ bool CB3DMeshFileLoader::load() if (!readChunkBRUS()) return false; } else if (strncmp(B3dStack.getLast().name, "NODE", 4) == 0) { - if (!readChunkNODE((CSkinnedMesh::SJoint *)0)) + if (!readChunkNODE((SkinnedMesh::SJoint *)0)) return false; } else { os::Printer::log("Unknown chunk found in mesh base - skipping"); @@ -133,9 +133,9 @@ bool CB3DMeshFileLoader::load() return true; } -bool CB3DMeshFileLoader::readChunkNODE(CSkinnedMesh::SJoint *inJoint) +bool CB3DMeshFileLoader::readChunkNODE(SkinnedMesh::SJoint *inJoint) { - CSkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint); + SkinnedMesh::SJoint *joint = AnimatedMesh->addJoint(inJoint); joint->Name = readString(); #ifdef _B3D_READER_DEBUG @@ -211,7 +211,7 @@ bool CB3DMeshFileLoader::readChunkNODE(CSkinnedMesh::SJoint *inJoint) return true; } -bool CB3DMeshFileLoader::readChunkMESH(CSkinnedMesh::SJoint *inJoint) +bool CB3DMeshFileLoader::readChunkMESH(SkinnedMesh::SJoint *inJoint) { #ifdef _B3D_READER_DEBUG core::stringc logStr; @@ -302,7 +302,7 @@ VRTS: float tex_coords[tex_coord_sets][tex_coord_set_size] ;tex coords } */ -bool CB3DMeshFileLoader::readChunkVRTS(CSkinnedMesh::SJoint *inJoint) +bool CB3DMeshFileLoader::readChunkVRTS(SkinnedMesh::SJoint *inJoint) { #ifdef _B3D_READER_DEBUG core::stringc logStr; @@ -521,7 +521,7 @@ bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 m return true; } -bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint) +bool CB3DMeshFileLoader::readChunkBONE(SkinnedMesh::SJoint *inJoint) { #ifdef _B3D_READER_DEBUG core::stringc logStr; @@ -552,7 +552,7 @@ bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint) if (AnimatedVertices_VertexID[globalVertexID] == -1) { os::Printer::log("B3dMeshLoader: Weight has bad vertex id (no link to meshbuffer index found)"); } else if (strength > 0) { - CSkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(inJoint); + SkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(inJoint); weight->strength = strength; // Find the meshbuffer and Vertex index from the Global Vertex ID: weight->vertex_id = AnimatedVertices_VertexID[globalVertexID]; @@ -565,7 +565,7 @@ bool CB3DMeshFileLoader::readChunkBONE(CSkinnedMesh::SJoint *inJoint) return true; } -bool CB3DMeshFileLoader::readChunkKEYS(CSkinnedMesh::SJoint *inJoint) +bool CB3DMeshFileLoader::readChunkKEYS(SkinnedMesh::SJoint *inJoint) { #ifdef _B3D_READER_DEBUG // Only print first, that's just too much output otherwise @@ -584,11 +584,11 @@ bool CB3DMeshFileLoader::readChunkKEYS(CSkinnedMesh::SJoint *inJoint) flags = os::Byteswap::byteswap(flags); #endif - CSkinnedMesh::SPositionKey *oldPosKey = 0; + SkinnedMesh::SPositionKey *oldPosKey = 0; core::vector3df oldPos[2]; - CSkinnedMesh::SScaleKey *oldScaleKey = 0; + SkinnedMesh::SScaleKey *oldScaleKey = 0; core::vector3df oldScale[2]; - CSkinnedMesh::SRotationKey *oldRotKey = 0; + SkinnedMesh::SRotationKey *oldRotKey = 0; core::quaternion oldRot[2]; bool isFirst[3] = {true, true, true}; while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats diff --git a/irr/src/CB3DMeshFileLoader.h b/irr/src/CB3DMeshFileLoader.h index 808b68170..711f2737f 100644 --- a/irr/src/CB3DMeshFileLoader.h +++ b/irr/src/CB3DMeshFileLoader.h @@ -10,7 +10,7 @@ #include "IMeshLoader.h" #include "ISceneManager.h" -#include "CSkinnedMesh.h" +#include "SkinnedMesh.h" #include "SB3DStructs.h" #include "IReadFile.h" @@ -39,12 +39,12 @@ public: private: bool load(); - bool readChunkNODE(CSkinnedMesh::SJoint *InJoint); - bool readChunkMESH(CSkinnedMesh::SJoint *InJoint); - bool readChunkVRTS(CSkinnedMesh::SJoint *InJoint); + bool readChunkNODE(SkinnedMesh::SJoint *InJoint); + bool readChunkMESH(SkinnedMesh::SJoint *InJoint); + bool readChunkVRTS(SkinnedMesh::SJoint *InJoint); bool readChunkTRIS(scene::SSkinMeshBuffer *MeshBuffer, u32 MeshBufferID, s32 Vertices_Start); - bool readChunkBONE(CSkinnedMesh::SJoint *InJoint); - bool readChunkKEYS(CSkinnedMesh::SJoint *InJoint); + bool readChunkBONE(SkinnedMesh::SJoint *InJoint); + bool readChunkKEYS(SkinnedMesh::SJoint *InJoint); bool readChunkANIM(); bool readChunkTEXS(); bool readChunkBRUS(); @@ -63,7 +63,7 @@ private: core::array BaseVertices; - CSkinnedMesh *AnimatedMesh; + SkinnedMesh *AnimatedMesh; io::IReadFile *B3DFile; // B3Ds have Vertex ID's local within the mesh I don't want this diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp index ce2c41f58..9045569a6 100644 --- a/irr/src/CGLTFMeshFileLoader.cpp +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -5,7 +5,7 @@ #include "SMaterialLayer.h" #include "coreutil.h" -#include "CSkinnedMesh.h" +#include "SkinnedMesh.h" #include "IAnimatedMesh.h" #include "IReadFile.h" #include "irrTypes.h" @@ -344,7 +344,7 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file) const char *filename = file->getFileName().c_str(); try { tiniergltf::GlTF model = parseGLTF(file); - irr_ptr mesh(new CSkinnedMesh()); + irr_ptr mesh(new SkinnedMesh()); MeshExtractor extractor(std::move(model), mesh.get()); try { extractor.load(); @@ -410,7 +410,7 @@ static video::E_TEXTURE_CLAMP convertTextureWrap(const Wrap wrap) { void SelfType::MeshExtractor::addPrimitive( const tiniergltf::MeshPrimitive &primitive, const std::optional skinIdx, - CSkinnedMesh::SJoint *parent) + SkinnedMesh::SJoint *parent) { auto vertices = getVertices(primitive); if (!vertices.has_value()) @@ -510,7 +510,7 @@ void SelfType::MeshExtractor::addPrimitive( if (strength == 0) continue; - CSkinnedMesh::SWeight *weight = m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx))); + SkinnedMesh::SWeight *weight = m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx))); weight->buffer_id = meshbufNr; weight->vertex_id = v; weight->strength = strength; @@ -527,7 +527,7 @@ void SelfType::MeshExtractor::addPrimitive( void SelfType::MeshExtractor::deferAddMesh( const std::size_t meshIdx, const std::optional skinIdx, - CSkinnedMesh::SJoint *parent) + SkinnedMesh::SJoint *parent) { m_mesh_loaders.emplace_back([=] { for (std::size_t pi = 0; pi < getPrimitiveCount(meshIdx); ++pi) { @@ -547,7 +547,7 @@ static const core::matrix4 leftToRight = core::matrix4( ); static const core::matrix4 rightToLeft = leftToRight; -static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, CSkinnedMesh::SJoint *joint) +static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, SkinnedMesh::SJoint *joint) { // Note: Under the hood, this casts these doubles to floats. core::matrix4 mat = convertHandedness(core::matrix4( @@ -576,7 +576,7 @@ static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, CSkinnedMe return mat; } -static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, CSkinnedMesh::SJoint *joint) +static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, SkinnedMesh::SJoint *joint) { const auto &trans = trs.translation; const auto &rot = trs.rotation; @@ -594,7 +594,7 @@ static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, CSkinnedMes } static core::matrix4 loadTransform(std::optional> transform, - CSkinnedMesh::SJoint *joint) { + SkinnedMesh::SJoint *joint) { if (!transform.has_value()) { return core::matrix4(); } @@ -603,7 +603,7 @@ static core::matrix4 loadTransform(std::optionalat(nodeIdx); auto *joint = m_irr_model->addJoint(parent); @@ -626,7 +626,7 @@ void SelfType::MeshExtractor::loadNode( void SelfType::MeshExtractor::loadNodes() { - m_loaded_nodes = std::vector(m_gltf_model.nodes->size()); + m_loaded_nodes = std::vector(m_gltf_model.nodes->size()); std::vector isChild(m_gltf_model.nodes->size()); for (const auto &node : *m_gltf_model.nodes) { diff --git a/irr/src/CGLTFMeshFileLoader.h b/irr/src/CGLTFMeshFileLoader.h index ae178565d..35ee85726 100644 --- a/irr/src/CGLTFMeshFileLoader.h +++ b/irr/src/CGLTFMeshFileLoader.h @@ -3,7 +3,7 @@ #pragma once -#include "CSkinnedMesh.h" +#include "SkinnedMesh.h" #include "IMeshLoader.h" #include "IReadFile.h" #include "irrTypes.h" @@ -100,7 +100,7 @@ private: { public: MeshExtractor(tiniergltf::GlTF &&model, - CSkinnedMesh *mesh) noexcept + SkinnedMesh *mesh) noexcept : m_gltf_model(std::move(model)), m_irr_model(mesh) {}; /* Gets indices for the given mesh/primitive. @@ -124,10 +124,10 @@ private: private: const tiniergltf::GlTF m_gltf_model; - CSkinnedMesh *m_irr_model; + SkinnedMesh *m_irr_model; std::vector> m_mesh_loaders; - std::vector m_loaded_nodes; + std::vector m_loaded_nodes; std::vector warnings; void warn(const std::string &warning) { @@ -145,13 +145,13 @@ private: void addPrimitive(const tiniergltf::MeshPrimitive &primitive, const std::optional skinIdx, - CSkinnedMesh::SJoint *parent); + SkinnedMesh::SJoint *parent); void deferAddMesh(const std::size_t meshIdx, const std::optional skinIdx, - CSkinnedMesh::SJoint *parentJoint); + SkinnedMesh::SJoint *parentJoint); - void loadNode(const std::size_t nodeIdx, CSkinnedMesh::SJoint *parentJoint); + void loadNode(const std::size_t nodeIdx, SkinnedMesh::SJoint *parentJoint); void loadNodes(); diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index a05607830..7e0a3a3fa 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -308,7 +308,7 @@ set(IRRMESHLOADER ) add_library(IRRMESHOBJ OBJECT - CSkinnedMesh.cpp + SkinnedMesh.cpp CBoneSceneNode.cpp CMeshSceneNode.cpp CAnimatedMeshSceneNode.cpp diff --git a/irr/src/CMeshManipulator.cpp b/irr/src/CMeshManipulator.cpp index 67b22a07e..63157403b 100644 --- a/irr/src/CMeshManipulator.cpp +++ b/irr/src/CMeshManipulator.cpp @@ -3,7 +3,7 @@ // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CMeshManipulator.h" -#include "ISkinnedMesh.h" +#include "SkinnedMesh.h" #include "SMesh.h" #include "CMeshBuffer.h" #include "SAnimatedMesh.h" @@ -101,7 +101,7 @@ void CMeshManipulator::recalculateNormals(scene::IMesh *mesh, bool smooth, bool return; if (mesh->getMeshType() == EAMT_SKINNED) { - ISkinnedMesh *smesh = (ISkinnedMesh *)mesh; + auto *smesh = (SkinnedMesh *)mesh; smesh->resetAnimation(); } @@ -110,7 +110,7 @@ void CMeshManipulator::recalculateNormals(scene::IMesh *mesh, bool smooth, bool recalculateNormals(mesh->getMeshBuffer(b), smooth, angleWeighted); if (mesh->getMeshType() == EAMT_SKINNED) { - ISkinnedMesh *smesh = (ISkinnedMesh *)mesh; + auto *smesh = (SkinnedMesh *)mesh; smesh->refreshJointCache(); } } diff --git a/irr/src/CMeshSceneNode.cpp b/irr/src/CMeshSceneNode.cpp index 030e1fd15..6d02eada5 100644 --- a/irr/src/CMeshSceneNode.cpp +++ b/irr/src/CMeshSceneNode.cpp @@ -5,10 +5,8 @@ #include "CMeshSceneNode.h" #include "IVideoDriver.h" #include "ISceneManager.h" -#include "S3DVertex.h" -#include "ICameraSceneNode.h" #include "IMeshCache.h" -#include "IAnimatedMesh.h" +#include "IMeshBuffer.h" #include "IMaterialRenderer.h" #include "IFileSystem.h" diff --git a/irr/src/CSceneManager.cpp b/irr/src/CSceneManager.cpp index 71ae7f962..a6716525a 100644 --- a/irr/src/CSceneManager.cpp +++ b/irr/src/CSceneManager.cpp @@ -16,7 +16,7 @@ #include "os.h" -#include "CSkinnedMesh.h" +#include "SkinnedMesh.h" #include "CXMeshFileLoader.h" #include "COBJMeshFileLoader.h" #include "CB3DMeshFileLoader.h" @@ -771,9 +771,9 @@ ISceneManager *CSceneManager::createNewSceneManager(bool cloneContent) } //! Get a skinned mesh, which is not available as header-only code -ISkinnedMesh *CSceneManager::createSkinnedMesh() +SkinnedMesh *CSceneManager::createSkinnedMesh() { - return new CSkinnedMesh(); + return new SkinnedMesh(); } // creates a scenemanager diff --git a/irr/src/CSceneManager.h b/irr/src/CSceneManager.h index 4362ba04d..2a4fb7f7b 100644 --- a/irr/src/CSceneManager.h +++ b/irr/src/CSceneManager.h @@ -4,6 +4,7 @@ #pragma once +#include "SkinnedMesh.h" #include "ISceneManager.h" #include "ISceneNode.h" #include "ICursorControl.h" @@ -22,6 +23,8 @@ namespace scene { class IMeshCache; +class SkinnedMesh; + /*! The Scene Manager manages scene nodes, mesh resources, cameras and all the other stuff. */ @@ -167,8 +170,8 @@ public: //! Returns type of the scene node ESCENE_NODE_TYPE getType() const override { return ESNT_SCENE_MANAGER; } - //! Get a skinned mesh, which is not available as header-only code - ISkinnedMesh *createSkinnedMesh() override; + //! Get a skinned mesh + SkinnedMesh *createSkinnedMesh() override; //! Get current render time. E_SCENE_NODE_RENDER_PASS getCurrentRenderPass() const override { return CurrentRenderPass; } diff --git a/irr/src/CXMeshFileLoader.cpp b/irr/src/CXMeshFileLoader.cpp index 967fc367c..19d4e5b01 100644 --- a/irr/src/CXMeshFileLoader.cpp +++ b/irr/src/CXMeshFileLoader.cpp @@ -57,7 +57,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file) u32 time = os::Timer::getRealTime(); #endif - AnimatedMesh = new CSkinnedMesh(); + AnimatedMesh = new SkinnedMesh(); if (load(file)) { AnimatedMesh->finalize(); @@ -203,8 +203,8 @@ bool CXMeshFileLoader::load(io::IReadFile *file) } for (u32 j = 0; j < mesh->WeightJoint.size(); ++j) { - ISkinnedMesh::SJoint *joint = AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]; - ISkinnedMesh::SWeight &weight = joint->Weights[mesh->WeightNum[j]]; + SkinnedMesh::SJoint *joint = AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]; + SkinnedMesh::SWeight &weight = joint->Weights[mesh->WeightNum[j]]; u32 id = weight.vertex_id; @@ -219,7 +219,7 @@ bool CXMeshFileLoader::load(io::IReadFile *file) weight.buffer_id = verticesLinkBuffer[id][0]; } else if (verticesLinkBuffer[id].size() != 0) { for (u32 k = 1; k < verticesLinkBuffer[id].size(); ++k) { - ISkinnedMesh::SWeight *WeightClone = AnimatedMesh->addWeight(joint); + SkinnedMesh::SWeight *WeightClone = AnimatedMesh->addWeight(joint); WeightClone->strength = weight.strength; WeightClone->vertex_id = verticesLinkIndex[id][k]; WeightClone->buffer_id = verticesLinkBuffer[id][k]; @@ -314,7 +314,7 @@ bool CXMeshFileLoader::load(io::IReadFile *file) } for (u32 j = 0; j < mesh->WeightJoint.size(); ++j) { - ISkinnedMesh::SWeight &weight = (AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]->Weights[mesh->WeightNum[j]]); + SkinnedMesh::SWeight &weight = (AnimatedMesh->getAllJoints()[mesh->WeightJoint[j]]->Weights[mesh->WeightNum[j]]); u32 id = weight.vertex_id; @@ -486,7 +486,7 @@ bool CXMeshFileLoader::parseDataObjectTemplate() return true; } -bool CXMeshFileLoader::parseDataObjectFrame(CSkinnedMesh::SJoint *Parent) +bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent) { #ifdef _XREADER_DEBUG os::Printer::log("CXFileReader: Reading frame", ELL_DEBUG); @@ -508,7 +508,7 @@ bool CXMeshFileLoader::parseDataObjectFrame(CSkinnedMesh::SJoint *Parent) SET_ERR_AND_RETURN(); } - CSkinnedMesh::SJoint *joint = 0; + SkinnedMesh::SJoint *joint = 0; if (name.size()) { auto n = AnimatedMesh->getJointNumber(name.c_str()); @@ -947,7 +947,7 @@ bool CXMeshFileLoader::parseDataObjectSkinWeights(SXMesh &mesh) mesh.HasSkinning = true; auto n = AnimatedMesh->getJointNumber(TransformNodeName.c_str()); - CSkinnedMesh::SJoint *joint = n.has_value() ? AnimatedMesh->getAllJoints()[*n] : nullptr; + SkinnedMesh::SJoint *joint = n.has_value() ? AnimatedMesh->getAllJoints()[*n] : nullptr; if (!joint) { #ifdef _XREADER_DEBUG @@ -974,7 +974,7 @@ bool CXMeshFileLoader::parseDataObjectSkinWeights(SXMesh &mesh) mesh.WeightJoint.push_back(*n); mesh.WeightNum.push_back(joint->Weights.size()); - CSkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(joint); + SkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(joint); weight->buffer_id = 0; weight->vertex_id = readInt(); @@ -1358,7 +1358,7 @@ bool CXMeshFileLoader::parseDataObjectAnimation() // anim.closed = true; // anim.linearPositionQuality = true; - CSkinnedMesh::SJoint animationDump; + SkinnedMesh::SJoint animationDump; core::stringc FrameName; @@ -1400,7 +1400,7 @@ bool CXMeshFileLoader::parseDataObjectAnimation() #endif auto n = AnimatedMesh->getJointNumber(FrameName.c_str()); - CSkinnedMesh::SJoint *joint; + SkinnedMesh::SJoint *joint; if (n.has_value()) { joint = AnimatedMesh->getAllJoints()[*n]; } else { @@ -1431,7 +1431,7 @@ bool CXMeshFileLoader::parseDataObjectAnimation() return true; } -bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint) +bool CXMeshFileLoader::parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint) { #ifdef _XREADER_DEBUG os::Printer::log("CXFileReader: reading animation key", ELL_DEBUG); @@ -1488,7 +1488,7 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint) os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); } - ISkinnedMesh::SRotationKey *key = AnimatedMesh->addRotationKey(joint); + SkinnedMesh::SRotationKey *key = AnimatedMesh->addRotationKey(joint); key->frame = time; key->rotation.set(X, Y, Z, W); key->rotation.normalize(); @@ -1514,11 +1514,11 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint) } if (keyType == 2) { - ISkinnedMesh::SPositionKey *key = AnimatedMesh->addPositionKey(joint); + SkinnedMesh::SPositionKey *key = AnimatedMesh->addPositionKey(joint); key->frame = time; key->position = vector; } else { - ISkinnedMesh::SScaleKey *key = AnimatedMesh->addScaleKey(joint); + SkinnedMesh::SScaleKey *key = AnimatedMesh->addScaleKey(joint); key->frame = time; key->scale = vector; } @@ -1547,14 +1547,14 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint) // core::vector3df rotation = mat.getRotationDegrees(); - ISkinnedMesh::SRotationKey *keyR = AnimatedMesh->addRotationKey(joint); + SkinnedMesh::SRotationKey *keyR = AnimatedMesh->addRotationKey(joint); keyR->frame = time; // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from mat to mat.getTransposed() for downward compatibility. // Not tested so far if this was correct or wrong before quaternion fix! keyR->rotation = core::quaternion(mat.getTransposed()); - ISkinnedMesh::SPositionKey *keyP = AnimatedMesh->addPositionKey(joint); + SkinnedMesh::SPositionKey *keyP = AnimatedMesh->addPositionKey(joint); keyP->frame = time; keyP->position = mat.getTranslation(); @@ -1567,7 +1567,7 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint) scale.Y=1; if (scale.Z==0) scale.Z=1; - ISkinnedMesh::SScaleKey *keyS=AnimatedMesh->addScaleKey(joint); + SkinnedMesh::SScaleKey *keyS=AnimatedMesh->addScaleKey(joint); keyS->frame=time; keyS->scale=scale; */ diff --git a/irr/src/CXMeshFileLoader.h b/irr/src/CXMeshFileLoader.h index 711806270..0902651a9 100644 --- a/irr/src/CXMeshFileLoader.h +++ b/irr/src/CXMeshFileLoader.h @@ -6,7 +6,7 @@ #include "IMeshLoader.h" #include "irrString.h" -#include "CSkinnedMesh.h" +#include "SkinnedMesh.h" namespace irr { @@ -86,7 +86,7 @@ private: bool parseDataObjectTemplate(); - bool parseDataObjectFrame(CSkinnedMesh::SJoint *parent); + bool parseDataObjectFrame(SkinnedMesh::SJoint *parent); bool parseDataObjectTransformationMatrix(core::matrix4 &mat); @@ -110,7 +110,7 @@ private: bool parseDataObjectAnimation(); - bool parseDataObjectAnimationKey(ISkinnedMesh::SJoint *joint); + bool parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint); bool parseDataObjectTextureFilename(core::stringc &texturename); @@ -155,7 +155,7 @@ private: bool readRGB(video::SColor &color); bool readRGBA(video::SColor &color); - CSkinnedMesh *AnimatedMesh; + SkinnedMesh *AnimatedMesh; c8 *Buffer; const c8 *P; @@ -167,7 +167,7 @@ private: bool ErrorState; - CSkinnedMesh::SJoint *CurFrame; + SkinnedMesh::SJoint *CurFrame; core::array Meshes; diff --git a/irr/src/CSkinnedMesh.cpp b/irr/src/SkinnedMesh.cpp similarity index 89% rename from irr/src/CSkinnedMesh.cpp rename to irr/src/SkinnedMesh.cpp index 875fd8e7e..76bce0b87 100644 --- a/irr/src/CSkinnedMesh.cpp +++ b/irr/src/SkinnedMesh.cpp @@ -2,8 +2,8 @@ // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h -#include "CSkinnedMesh.h" -#include +#include "SkinnedMesh.h" +#include "IBoneSceneNode.h" #include "CBoneSceneNode.h" #include "IAnimatedMeshSceneNode.h" #include "SSkinMeshBuffer.h" @@ -63,17 +63,17 @@ irr::u32 dropMiddleKeys(irr::core::array &array, Cmp &cmp) return d; } -bool identicalPos(const irr::scene::ISkinnedMesh::SPositionKey &a, const irr::scene::ISkinnedMesh::SPositionKey &b) +bool identicalPos(const irr::scene::SkinnedMesh::SPositionKey &a, const irr::scene::SkinnedMesh::SPositionKey &b) { return a.position == b.position; } -bool identicalScale(const irr::scene::ISkinnedMesh::SScaleKey &a, const irr::scene::ISkinnedMesh::SScaleKey &b) +bool identicalScale(const irr::scene::SkinnedMesh::SScaleKey &a, const irr::scene::SkinnedMesh::SScaleKey &b) { return a.scale == b.scale; } -bool identicalRotation(const irr::scene::ISkinnedMesh::SRotationKey &a, const irr::scene::ISkinnedMesh::SRotationKey &b) +bool identicalRotation(const irr::scene::SkinnedMesh::SRotationKey &a, const irr::scene::SkinnedMesh::SRotationKey &b) { return a.rotation == b.rotation; } @@ -85,7 +85,7 @@ namespace scene { //! constructor -CSkinnedMesh::CSkinnedMesh() : +SkinnedMesh::SkinnedMesh() : SkinningBuffers(0), EndFrame(0.f), FramesPerSecond(25.f), LastAnimatedFrame(-1), SkinnedLastFrame(false), InterpolationMode(EIM_LINEAR), @@ -93,14 +93,14 @@ CSkinnedMesh::CSkinnedMesh() : AnimateNormals(true), HardwareSkinning(false) { #ifdef _DEBUG - setDebugName("CSkinnedMesh"); + setDebugName("SkinnedMesh"); #endif SkinningBuffers = &LocalBuffers; } //! destructor -CSkinnedMesh::~CSkinnedMesh() +SkinnedMesh::~SkinnedMesh() { for (u32 i = 0; i < AllJoints.size(); ++i) delete AllJoints[i]; @@ -111,14 +111,14 @@ CSkinnedMesh::~CSkinnedMesh() } } -f32 CSkinnedMesh::getMaxFrameNumber() const +f32 SkinnedMesh::getMaxFrameNumber() const { return EndFrame; } //! Gets the default animation speed of the animated mesh. /** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */ -f32 CSkinnedMesh::getAnimationSpeed() const +f32 SkinnedMesh::getAnimationSpeed() const { return FramesPerSecond; } @@ -126,13 +126,13 @@ f32 CSkinnedMesh::getAnimationSpeed() const //! Gets the frame count of the animated mesh. /** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated. The actual speed is set in the scene node the mesh is instantiated in.*/ -void CSkinnedMesh::setAnimationSpeed(f32 fps) +void SkinnedMesh::setAnimationSpeed(f32 fps) { FramesPerSecond = fps; } //! returns the animated mesh based -IMesh *CSkinnedMesh::getMesh(f32 frame) +IMesh *SkinnedMesh::getMesh(f32 frame) { // animate(frame,startFrameLoop, endFrameLoop); if (frame == -1) @@ -149,7 +149,7 @@ IMesh *CSkinnedMesh::getMesh(f32 frame) //! Animates this mesh's joints based on frame input //! blend: {0-old position, 1-New position} -void CSkinnedMesh::animateMesh(f32 frame, f32 blend) +void SkinnedMesh::animateMesh(f32 frame, f32 blend) { if (!HasAnimation || LastAnimatedFrame == frame) return; @@ -205,7 +205,7 @@ void CSkinnedMesh::animateMesh(f32 frame, f32 blend) updateBoundingBox(); } -void CSkinnedMesh::buildAllLocalAnimatedMatrices() +void SkinnedMesh::buildAllLocalAnimatedMatrices() { for (u32 i = 0; i < AllJoints.size(); ++i) { SJoint *joint = AllJoints[i]; @@ -270,7 +270,7 @@ void CSkinnedMesh::buildAllLocalAnimatedMatrices() SkinnedLastFrame = false; } -void CSkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint) +void SkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint) { if (!joint) { for (u32 i = 0; i < RootJoints.size(); ++i) @@ -288,7 +288,7 @@ void CSkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJ buildAllGlobalAnimatedMatrices(joint->Children[j], joint); } -void CSkinnedMesh::getFrameData(f32 frame, SJoint *joint, +void SkinnedMesh::getFrameData(f32 frame, SJoint *joint, core::vector3df &position, s32 &positionHint, core::vector3df &scale, s32 &scaleHint, core::quaternion &rotation, s32 &rotationHint) @@ -453,7 +453,7 @@ void CSkinnedMesh::getFrameData(f32 frame, SJoint *joint, //-------------------------------------------------------------------------- //! Preforms a software skin on this mesh based of joint positions -void CSkinnedMesh::skinMesh() +void SkinnedMesh::skinMesh() { if (!HasAnimation || SkinnedLastFrame) return; @@ -491,7 +491,7 @@ void CSkinnedMesh::skinMesh() updateBoundingBox(); } -void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) +void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) { if (joint->Weights.size()) { // Find this joints pull on vertices... @@ -541,19 +541,19 @@ void CSkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) skinJoint(joint->Children[j], joint); } -E_ANIMATED_MESH_TYPE CSkinnedMesh::getMeshType() const +E_ANIMATED_MESH_TYPE SkinnedMesh::getMeshType() const { return EAMT_SKINNED; } //! Gets joint count. -u32 CSkinnedMesh::getJointCount() const +u32 SkinnedMesh::getJointCount() const { return AllJoints.size(); } //! Gets the name of a joint. -const std::optional &CSkinnedMesh::getJointName(u32 number) const +const std::optional &SkinnedMesh::getJointName(u32 number) const { if (number >= getJointCount()) { static const std::optional nullopt; @@ -563,7 +563,7 @@ const std::optional &CSkinnedMesh::getJointName(u32 number) const } //! Gets a joint number from its name -std::optional CSkinnedMesh::getJointNumber(const std::string &name) const +std::optional SkinnedMesh::getJointNumber(const std::string &name) const { for (u32 i = 0; i < AllJoints.size(); ++i) { if (AllJoints[i]->Name == name) @@ -574,13 +574,13 @@ std::optional CSkinnedMesh::getJointNumber(const std::string &name) const } //! returns amount of mesh buffers. -u32 CSkinnedMesh::getMeshBufferCount() const +u32 SkinnedMesh::getMeshBufferCount() const { return LocalBuffers.size(); } //! returns pointer to a mesh buffer -IMeshBuffer *CSkinnedMesh::getMeshBuffer(u32 nr) const +IMeshBuffer *SkinnedMesh::getMeshBuffer(u32 nr) const { if (nr < LocalBuffers.size()) return LocalBuffers[nr]; @@ -589,7 +589,7 @@ IMeshBuffer *CSkinnedMesh::getMeshBuffer(u32 nr) const } //! Returns pointer to a mesh buffer which fits a material -IMeshBuffer *CSkinnedMesh::getMeshBuffer(const video::SMaterial &material) const +IMeshBuffer *SkinnedMesh::getMeshBuffer(const video::SMaterial &material) const { for (u32 i = 0; i < LocalBuffers.size(); ++i) { if (LocalBuffers[i]->getMaterial() == material) @@ -598,29 +598,29 @@ IMeshBuffer *CSkinnedMesh::getMeshBuffer(const video::SMaterial &material) const return 0; } -u32 CSkinnedMesh::getTextureSlot(u32 meshbufNr) const +u32 SkinnedMesh::getTextureSlot(u32 meshbufNr) const { return TextureSlots.at(meshbufNr); } -void CSkinnedMesh::setTextureSlot(u32 meshbufNr, u32 textureSlot) { +void SkinnedMesh::setTextureSlot(u32 meshbufNr, u32 textureSlot) { TextureSlots.at(meshbufNr) = textureSlot; } //! returns an axis aligned bounding box -const core::aabbox3d &CSkinnedMesh::getBoundingBox() const +const core::aabbox3d &SkinnedMesh::getBoundingBox() const { return BoundingBox; } //! set user axis aligned bounding box -void CSkinnedMesh::setBoundingBox(const core::aabbox3df &box) +void SkinnedMesh::setBoundingBox(const core::aabbox3df &box) { BoundingBox = box; } //! set the hardware mapping hint, for driver -void CSkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, +void SkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer) { for (u32 i = 0; i < LocalBuffers.size(); ++i) @@ -628,71 +628,43 @@ void CSkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, } //! flags the meshbuffer as changed, reloads hardware buffers -void CSkinnedMesh::setDirty(E_BUFFER_TYPE buffer) +void SkinnedMesh::setDirty(E_BUFFER_TYPE buffer) { for (u32 i = 0; i < LocalBuffers.size(); ++i) LocalBuffers[i]->setDirty(buffer); } -//! uses animation from another mesh -bool CSkinnedMesh::useAnimationFrom(const ISkinnedMesh *mesh) -{ - bool unmatched = false; - - for (u32 i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - joint->UseAnimationFrom = 0; - - if (joint->Name == "") - unmatched = true; - else { - for (u32 j = 0; j < mesh->getAllJoints().size(); ++j) { - SJoint *otherJoint = mesh->getAllJoints()[j]; - if (joint->Name == otherJoint->Name) { - joint->UseAnimationFrom = otherJoint; - } - } - if (!joint->UseAnimationFrom) - unmatched = true; - } - } - - checkForAnimation(); - - return !unmatched; -} - //! Update Normals when Animating //! False= Don't animate them, faster //! True= Update normals (default) -void CSkinnedMesh::updateNormalsWhenAnimating(bool on) +void SkinnedMesh::updateNormalsWhenAnimating(bool on) { AnimateNormals = on; } //! Sets Interpolation Mode -void CSkinnedMesh::setInterpolationMode(E_INTERPOLATION_MODE mode) +void SkinnedMesh::setInterpolationMode(E_INTERPOLATION_MODE mode) { InterpolationMode = mode; } -core::array &CSkinnedMesh::getMeshBuffers() +core::array &SkinnedMesh::getMeshBuffers() { return LocalBuffers; } -core::array &CSkinnedMesh::getAllJoints() +core::array &SkinnedMesh::getAllJoints() { return AllJoints; } -const core::array &CSkinnedMesh::getAllJoints() const +const core::array &SkinnedMesh::getAllJoints() const { return AllJoints; } //! (This feature is not implemented in irrlicht yet) -bool CSkinnedMesh::setHardwareSkinning(bool on) +bool SkinnedMesh::setHardwareSkinning(bool on) { if (HardwareSkinning != on) { if (on) { @@ -715,7 +687,7 @@ bool CSkinnedMesh::setHardwareSkinning(bool on) return HardwareSkinning; } -void CSkinnedMesh::refreshJointCache() +void SkinnedMesh::refreshJointCache() { // copy cache from the mesh... for (u32 i = 0; i < AllJoints.size(); ++i) { @@ -729,7 +701,7 @@ void CSkinnedMesh::refreshJointCache() } } -void CSkinnedMesh::resetAnimation() +void SkinnedMesh::resetAnimation() { // copy from the cache to the mesh... for (u32 i = 0; i < AllJoints.size(); ++i) { @@ -745,7 +717,7 @@ void CSkinnedMesh::resetAnimation() LastAnimatedFrame = -1; } -void CSkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint) +void SkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint) { if (!joint && parentJoint) // bit of protection from endless loops return; @@ -775,7 +747,7 @@ void CSkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint) SkinnedLastFrame = false; } -void CSkinnedMesh::checkForAnimation() +void SkinnedMesh::checkForAnimation() { u32 i, j; // Check for animation... @@ -868,7 +840,7 @@ void CSkinnedMesh::checkForAnimation() } //! called by loader after populating with mesh and bone data -void CSkinnedMesh::finalize() +void SkinnedMesh::finalize() { os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG); u32 i; @@ -1046,7 +1018,7 @@ void CSkinnedMesh::finalize() } } -void CSkinnedMesh::updateBoundingBox(void) +void SkinnedMesh::updateBoundingBox(void) { if (!SkinningBuffers) return; @@ -1065,7 +1037,7 @@ void CSkinnedMesh::updateBoundingBox(void) } } -scene::SSkinMeshBuffer *CSkinnedMesh::addMeshBuffer() +scene::SSkinMeshBuffer *SkinnedMesh::addMeshBuffer() { scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer(); TextureSlots.push_back(LocalBuffers.size()); @@ -1073,13 +1045,13 @@ scene::SSkinMeshBuffer *CSkinnedMesh::addMeshBuffer() return buffer; } -void CSkinnedMesh::addMeshBuffer(SSkinMeshBuffer *meshbuf) +void SkinnedMesh::addMeshBuffer(SSkinMeshBuffer *meshbuf) { TextureSlots.push_back(LocalBuffers.size()); LocalBuffers.push_back(meshbuf); } -CSkinnedMesh::SJoint *CSkinnedMesh::addJoint(SJoint *parent) +SkinnedMesh::SJoint *SkinnedMesh::addJoint(SJoint *parent) { SJoint *joint = new SJoint; @@ -1094,7 +1066,7 @@ CSkinnedMesh::SJoint *CSkinnedMesh::addJoint(SJoint *parent) return joint; } -CSkinnedMesh::SPositionKey *CSkinnedMesh::addPositionKey(SJoint *joint) +SkinnedMesh::SPositionKey *SkinnedMesh::addPositionKey(SJoint *joint) { if (!joint) return 0; @@ -1103,7 +1075,7 @@ CSkinnedMesh::SPositionKey *CSkinnedMesh::addPositionKey(SJoint *joint) return &joint->PositionKeys.getLast(); } -CSkinnedMesh::SScaleKey *CSkinnedMesh::addScaleKey(SJoint *joint) +SkinnedMesh::SScaleKey *SkinnedMesh::addScaleKey(SJoint *joint) { if (!joint) return 0; @@ -1112,7 +1084,7 @@ CSkinnedMesh::SScaleKey *CSkinnedMesh::addScaleKey(SJoint *joint) return &joint->ScaleKeys.getLast(); } -CSkinnedMesh::SRotationKey *CSkinnedMesh::addRotationKey(SJoint *joint) +SkinnedMesh::SRotationKey *SkinnedMesh::addRotationKey(SJoint *joint) { if (!joint) return 0; @@ -1121,7 +1093,7 @@ CSkinnedMesh::SRotationKey *CSkinnedMesh::addRotationKey(SJoint *joint) return &joint->RotationKeys.getLast(); } -CSkinnedMesh::SWeight *CSkinnedMesh::addWeight(SJoint *joint) +SkinnedMesh::SWeight *SkinnedMesh::addWeight(SJoint *joint) { if (!joint) return 0; @@ -1130,12 +1102,12 @@ CSkinnedMesh::SWeight *CSkinnedMesh::addWeight(SJoint *joint) return &joint->Weights.getLast(); } -bool CSkinnedMesh::isStatic() +bool SkinnedMesh::isStatic() const { return !HasAnimation; } -void CSkinnedMesh::normalizeWeights() +void SkinnedMesh::normalizeWeights() { // note: unsure if weights ids are going to be used. @@ -1176,7 +1148,7 @@ void CSkinnedMesh::normalizeWeights() } } -void CSkinnedMesh::recoverJointsFromMesh(core::array &jointChildSceneNodes) +void SkinnedMesh::recoverJointsFromMesh(core::array &jointChildSceneNodes) { for (u32 i = 0; i < AllJoints.size(); ++i) { IBoneSceneNode *node = jointChildSceneNodes[i]; @@ -1193,7 +1165,7 @@ void CSkinnedMesh::recoverJointsFromMesh(core::array &jointChi } } -void CSkinnedMesh::transferJointsToMesh(const core::array &jointChildSceneNodes) +void SkinnedMesh::transferJointsToMesh(const core::array &jointChildSceneNodes) { for (u32 i = 0; i < AllJoints.size(); ++i) { const IBoneSceneNode *const node = jointChildSceneNodes[i]; @@ -1214,7 +1186,7 @@ void CSkinnedMesh::transferJointsToMesh(const core::array &joi SkinnedLastFrame = false; } -void CSkinnedMesh::transferOnlyJointsHintsToMesh(const core::array &jointChildSceneNodes) +void SkinnedMesh::transferOnlyJointsHintsToMesh(const core::array &jointChildSceneNodes) { for (u32 i = 0; i < AllJoints.size(); ++i) { const IBoneSceneNode *const node = jointChildSceneNodes[i]; @@ -1227,7 +1199,7 @@ void CSkinnedMesh::transferOnlyJointsHintsToMesh(const core::array &jointChildSceneNodes, +void SkinnedMesh::addJoints(core::array &jointChildSceneNodes, IAnimatedMeshSceneNode *node, ISceneManager *smgr) { // Create new joints @@ -1264,7 +1236,7 @@ void CSkinnedMesh::addJoints(core::array &jointChildSceneNodes SkinnedLastFrame = false; } -void CSkinnedMesh::convertMeshToTangents() +void SkinnedMesh::convertMeshToTangents() { // now calculate tangents for (u32 b = 0; b < LocalBuffers.size(); ++b) { @@ -1315,7 +1287,7 @@ void CSkinnedMesh::convertMeshToTangents() } } -void CSkinnedMesh::calculateTangents( +void SkinnedMesh::calculateTangents( core::vector3df &normal, core::vector3df &tangent, core::vector3df &binormal, diff --git a/src/unittest/test_irr_gltf_mesh_loader.cpp b/src/unittest/test_irr_gltf_mesh_loader.cpp index 2d87db4e3..b1b0d99a1 100644 --- a/src/unittest/test_irr_gltf_mesh_loader.cpp +++ b/src/unittest/test_irr_gltf_mesh_loader.cpp @@ -12,7 +12,7 @@ #include "IFileSystem.h" #include "IReadFile.h" #include "ISceneManager.h" -#include "ISkinnedMesh.h" +#include "SkinnedMesh.h" #include "irrlicht.h" #include "catch.h" @@ -379,14 +379,14 @@ SECTION("simple sparse accessor") // https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/SimpleSkin SECTION("simple skin") { - using ISkinnedMesh = irr::scene::ISkinnedMesh; + using SkinnedMesh = irr::scene::SkinnedMesh; const auto mesh = loadMesh(model_stem + "simple_skin.gltf"); REQUIRE(mesh != nullptr); - auto csm = dynamic_cast(mesh); + auto csm = dynamic_cast(mesh); const auto joints = csm->getAllJoints(); REQUIRE(joints.size() == 3); - const auto findJoint = [&](const std::function &predicate) { + const auto findJoint = [&](const std::function &predicate) { for (std::size_t i = 0; i < joints.size(); ++i) { if (predicate(joints[i])) { return joints[i]; @@ -420,7 +420,7 @@ SECTION("simple skin") SECTION("weights are correct") { - const auto weights = [&](const ISkinnedMesh::SJoint *joint) { + const auto weights = [&](const SkinnedMesh::SJoint *joint) { std::unordered_map weights; for (std::size_t i = 0; i < joint->Weights.size(); ++i) { const auto weight = joint->Weights[i]; From 05d31222f713cd152b86b589ecb7dec1bc986ad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:05:03 +0100 Subject: [PATCH 33/68] Allow non-normalized weights in glTF models (#15310) We are being lax here, but the glTF specification just requires that "when the weights are stored using float component type, their linear sum SHOULD be as close as reasonably possible to 1.0 for a given vertex" In particular weights > 1 and weight sums well below or above 1 can be observed in models exported by Blender if they aren't manually normalized. These fail the glTF validator but Irrlicht normalizes weights itself so we can support them just fine. The docs have been updated to recommend normalizing weights (as well as documenting the status of interpolation support). Weights < 0, most of them close to 0, also occur. Consistent with Irrlicht, we ignore them, but we also raise a warning. --- doc/lua_api.md | 11 +++++++++-- irr/src/CGLTFMeshFileLoader.cpp | 31 ++++++++++++++++++++----------- irr/src/CGLTFMeshFileLoader.h | 8 ++++---- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 2d5405090..449309493 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -309,17 +309,24 @@ it unlocks no special rendering features. Binary glTF (`.glb`) files are supported and recommended over `.gltf` files due to their space savings. -This means that many glTF features are not supported *yet*, including: +Bone weights should be normalized, e.g. using ["normalize all" in Blender](https://docs.blender.org/manual/en/4.2/grease_pencil/modes/weight_paint/weights_menu.html#normalize-all). + +You can use the [Khronos glTF validator](https://github.com/KhronosGroup/glTF-Validator) +to check whether a model is a valid glTF file. + +Many glTF features are not supported *yet*, including: * Animations * Only a single animation is supported, use frame ranges within this animation. + * Only linear interpolation is supported. * Cameras * Materials * Only base color textures are supported * Backface culling is overridden * Double-sided materials don't work * Alternative means of supplying data - * Embedded images + * Embedded images. You can use `gltfutil.py` from the + [modding tools](https://github.com/minetest/modtools) to strip or extract embedded images. * References to files via URIs Textures are supplied solely via the same means as for the other model file formats: diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp index 9045569a6..139b4f670 100644 --- a/irr/src/CGLTFMeshFileLoader.cpp +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -305,7 +305,7 @@ SelfType::createNormalizedValuesAccessor( } } -template +template std::array SelfType::getNormalizedValues( const NormalizedValuesAccessor &accessor, const std::size_t i) @@ -313,17 +313,19 @@ std::array SelfType::getNormalizedValues( std::array values; if (std::holds_alternative>>(accessor)) { const auto u8s = std::get>>(accessor).get(i); - for (u8 i = 0; i < N; ++i) - values[i] = static_cast(u8s[i]) / std::numeric_limits::max(); + for (std::size_t j = 0; j < N; ++j) + values[j] = static_cast(u8s[j]) / std::numeric_limits::max(); } else if (std::holds_alternative>>(accessor)) { const auto u16s = std::get>>(accessor).get(i); - for (u8 i = 0; i < N; ++i) - values[i] = static_cast(u16s[i]) / std::numeric_limits::max(); + for (std::size_t j = 0; j < N; ++j) + values[j] = static_cast(u16s[j]) / std::numeric_limits::max(); } else { values = std::get>>(accessor).get(i); - for (u8 i = 0; i < N; ++i) { - if (values[i] < 0 || values[i] > 1) - throw std::runtime_error("invalid normalized value"); + if constexpr (validate) { + for (std::size_t j = 0; j < N; ++j) { + if (values[j] < 0 || values[j] > 1) + throw std::runtime_error("invalid normalized value"); + } } } return values; @@ -493,6 +495,7 @@ void SelfType::MeshExtractor::addPrimitive( const auto weightAccessor = createNormalizedValuesAccessor<4>(m_gltf_model, weights->at(set)); + bool negative_weights = false; for (std::size_t v = 0; v < n_vertices; ++v) { std::array jointIdxs; if (std::holds_alternative>>(jointAccessor)) { @@ -501,14 +504,18 @@ void SelfType::MeshExtractor::addPrimitive( } else if (std::holds_alternative>>(jointAccessor)) { jointIdxs = std::get>>(jointAccessor).get(v); } - std::array strengths = getNormalizedValues(weightAccessor, v); + + // Be lax: We can allow weights that aren't normalized. Irrlicht already normalizes them. + // The glTF spec only requires that these be "as close to 1 as reasonably possible". + auto strengths = getNormalizedValues<4, false>(weightAccessor, v); // 4 joints per set for (std::size_t in_set = 0; in_set < 4; ++in_set) { u16 jointIdx = jointIdxs[in_set]; f32 strength = strengths[in_set]; - if (strength == 0) - continue; + negative_weights = negative_weights || (strength < 0); + if (strength <= 0) + continue; // note: also ignores negative weights SkinnedMesh::SWeight *weight = m_irr_model->addWeight(m_loaded_nodes.at(skin.joints.at(jointIdx))); weight->buffer_id = meshbufNr; @@ -516,6 +523,8 @@ void SelfType::MeshExtractor::addPrimitive( weight->strength = strength; } } + if (negative_weights) + warn("negative weights"); } } diff --git a/irr/src/CGLTFMeshFileLoader.h b/irr/src/CGLTFMeshFileLoader.h index 35ee85726..1671d2903 100644 --- a/irr/src/CGLTFMeshFileLoader.h +++ b/irr/src/CGLTFMeshFileLoader.h @@ -91,7 +91,7 @@ private: const tiniergltf::GlTF &model, const std::size_t accessorIdx); - template + template static std::array getNormalizedValues( const NormalizedValuesAccessor &accessor, const std::size_t i); @@ -118,7 +118,7 @@ private: std::size_t getPrimitiveCount(const std::size_t meshIdx) const; void load(); - const std::vector &getWarnings() { + const std::unordered_set &getWarnings() { return warnings; } @@ -129,9 +129,9 @@ private: std::vector> m_mesh_loaders; std::vector m_loaded_nodes; - std::vector warnings; + std::unordered_set warnings; void warn(const std::string &warning) { - warnings.push_back(warning); + warnings.insert(warning); } void copyPositions(const std::size_t accessorIdx, From 88c845166cc364bdc34341620071db9cecaf7f16 Mon Sep 17 00:00:00 2001 From: cx384 Date: Fri, 6 Dec 2024 18:06:27 +0100 Subject: [PATCH 34/68] Fix L-System trees fruit node regression (#15513) --- src/mapgen/treegen.cpp | 3 +-- src/script/common/c_content.cpp | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp index 483ec4797..e03f41482 100644 --- a/src/mapgen/treegen.cpp +++ b/src/mapgen/treegen.cpp @@ -23,8 +23,7 @@ void TreeDef::resolveNodeNames() getIdFromNrBacklog(&leavesnode.param0, "", CONTENT_IGNORE); if (leaves2_chance) getIdFromNrBacklog(&leaves2node.param0, "", CONTENT_IGNORE); - if (fruit_chance) - getIdFromNrBacklog(&fruitnode.param0, "", CONTENT_IGNORE); + getIdFromNrBacklog(&fruitnode.param0, "", CONTENT_IGNORE); } /* diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 6367e53f3..5f000acd8 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -2032,12 +2032,11 @@ bool read_tree_def(lua_State *L, int idx, const NodeDefManager *ndef, getstringfield(L, idx, "trunk_type", tree_def.trunk_type); getboolfield(L, idx, "thin_branches", tree_def.thin_branches); tree_def.fruit_chance = 0; + fruit = "air"; getstringfield(L, idx, "fruit", fruit); - if (!fruit.empty()) { + if (!fruit.empty()) getintfield(L, idx, "fruit_chance", tree_def.fruit_chance); - if (tree_def.fruit_chance) - tree_def.m_nodenames.push_back(fruit); - } + tree_def.m_nodenames.push_back(fruit); tree_def.explicit_seed = getintfield(L, idx, "seed", tree_def.seed); // Resolves the node IDs for trunk, leaves, leaves2 and fruit at runtime, From 5a8412dd23d1632b4ea7ff90635f3f2ec84f30b0 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Fri, 6 Dec 2024 21:59:51 +0100 Subject: [PATCH 35/68] Formspec: Move tooltip above cursor when lacking space (#15470) --- src/gui/guiFormSpecMenu.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 2330f7c32..8a69f0429 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3610,10 +3610,25 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text, // Calculate and set the tooltip position s32 tooltip_x = m_pointer.X + tooltip_offset_x; s32 tooltip_y = m_pointer.Y + tooltip_offset_y; - if (tooltip_x + tooltip_width > (s32)screenSize.X) - tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height; - if (tooltip_y + tooltip_height > (s32)screenSize.Y) - tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height; + // Bottom/Left limited positions (if the tooltip is too far out) + s32 tooltip_x_alt = (s32)screenSize.X - tooltip_width - m_btn_height; + s32 tooltip_y_alt = (s32)screenSize.Y - tooltip_height - m_btn_height; + + int collision = (tooltip_x_alt < tooltip_x) + 2 * (tooltip_y_alt < tooltip_y); + switch (collision) { + case 1: // x + tooltip_x = tooltip_x_alt; + break; + case 2: // y + tooltip_y = tooltip_y_alt; + break; + case 3: // both + tooltip_x = tooltip_x_alt; + tooltip_y = (s32)screenSize.Y - 2 * tooltip_height - m_btn_height; + break; + default: // OK + break; + } m_tooltip_element->setRelativePosition( core::rect( From b8577988484649c2a9f73dedd4a85677343db463 Mon Sep 17 00:00:00 2001 From: veprogames <75524847+veprogames@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:00:06 +0100 Subject: [PATCH 36/68] Fix distorted Sun, Moon and Star visuals based on Orbit Tilt (#15459) --- src/client/sky.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 8da7fc68e..f0cb560d7 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -661,7 +661,9 @@ void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day) return; m_materials[0].ColorParam = color.toSColor(); - auto sky_rotation = core::matrix4().setRotationAxisRadians(2.0f * M_PI * (wicked_time_of_day - 0.25f), v3f(0.0f, 0.0f, 1.0f)); + auto day_rotation = core::matrix4().setRotationAxisRadians(2.0f * M_PI * (wicked_time_of_day - 0.25f), v3f(0.0f, 0.0f, 1.0f)); + auto orbit_rotation = core::matrix4().setRotationAxisRadians(m_sky_params.body_orbit_tilt * M_PI / 180.0, v3f(1.0f, 0.0f, 0.0f)); + auto sky_rotation = orbit_rotation * day_rotation; auto world_matrix = driver->getTransform(video::ETS_WORLD); driver->setTransform(video::ETS_WORLD, world_matrix * sky_rotation); driver->setMaterial(m_materials[0]); @@ -695,13 +697,11 @@ void Sky::place_sky_body( * day_position: turn the body around the Z axis, to place it depending of the time of the day */ { - v3f centrum = getSkyBodyPosition(horizon_position, day_position, m_sky_params.body_orbit_tilt); - v3f untilted_centrum = getSkyBodyPosition(horizon_position, day_position, 0.f); for (video::S3DVertex &vertex : vertices) { // Body is directed to -Z (south) by default vertex.Pos.rotateXZBy(horizon_position); vertex.Pos.rotateXYBy(day_position); - vertex.Pos += centrum - untilted_centrum; + vertex.Pos.rotateYZBy(m_sky_params.body_orbit_tilt); } } From ae96a8d4fa9ade70672fe8d2152013c547edfb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:01:31 +0100 Subject: [PATCH 37/68] Fix mainmenu crash if no servers match search fixes a regression caused by 6c324cb --- builtin/mainmenu/tab_online.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index 6545569b2..422e9ba23 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -57,6 +57,11 @@ end -- Persists the selected server in the "address" and "remote_port" settings local function set_selected_server(server) + if server == nil then -- reset selection + core.settings:remove("address") + core.settings:remove("remote_port") + return + end local address = server.address local port = server.port gamedata.serverdescription = server.description @@ -360,6 +365,7 @@ local function main_button_handler(tabview, fields, name, tabdata) tabdata.search_for = fields.te_search search_server_list(fields.te_search:lower()) if menudata.search_result then + -- Note: This clears the selection if there are no results set_selected_server(menudata.search_result[1]) end From 8d43ad252275999d66a47ba40219c8e014fe4917 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sun, 8 Dec 2024 19:48:52 +0100 Subject: [PATCH 38/68] Migrate rendered Lua API docs CNAME to `api.luanti.org` --- CNAME | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CNAME b/CNAME index c8f58d469..cd4ccafcc 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -api.minetest.net +api.luanti.org From c7fe2ee5c9c84284d54c8dda20c57c759f63f821 Mon Sep 17 00:00:00 2001 From: cx384 Date: Sun, 8 Dec 2024 20:27:22 +0100 Subject: [PATCH 39/68] Add `core.spawn_tree_on_vmanip` (#15415) This function works like `core.spawn_tree`, but spawns an L-system tree onto a VoxelManip object instead on the map. --- doc/lua_api.md | 3 + games/devtest/mods/testitems/init.lua | 65 ++++++++++++++++++ .../textures/testitems_tree_spawner.png | Bin 0 -> 120 bytes .../testitems_tree_spawner_vmanip.png | Bin 0 -> 150 bytes src/mapgen/treegen.cpp | 11 +++ src/mapgen/treegen.h | 3 + src/script/lua_api/l_env.cpp | 12 +--- src/script/lua_api/l_mapgen.cpp | 23 +++++++ src/script/lua_api/l_mapgen.h | 3 + 9 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 games/devtest/mods/testitems/textures/testitems_tree_spawner.png create mode 100644 games/devtest/mods/testitems/textures/testitems_tree_spawner_vmanip.png diff --git a/doc/lua_api.md b/doc/lua_api.md index 449309493..c4c74d884 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -6588,6 +6588,9 @@ Environment access on-the-fly * `core.spawn_tree (pos, {treedef})` * spawns L-system tree at given `pos` with definition in `treedef` table +* `core.spawn_tree_on_vmanip(vmanip, pos, treedef)` + * analogous to `core.spawn_tree`, but spawns a L-system tree onto the specified + VoxelManip object `vmanip` instead of the map. * `core.transforming_liquid_add(pos)` * add node to liquid flow update queue * `core.get_node_max_level(pos)` diff --git a/games/devtest/mods/testitems/init.lua b/games/devtest/mods/testitems/init.lua index 12da2ad1c..71a85ef6d 100644 --- a/games/devtest/mods/testitems/init.lua +++ b/games/devtest/mods/testitems/init.lua @@ -105,3 +105,68 @@ core.register_craftitem("testitems:telescope_stick", { return itemstack end, }) + + +-- Tree spawners + +local tree_def={ + axiom="Af", + rules_a="TT[&GB][&+GB][&++GB][&+++GB]A", + rules_b="[+GB]fB", + trunk="basenodes:tree", + leaves="basenodes:leaves", + angle=90, + iterations=4, + trunk_type="single", + thin_branches=true, +} + +core.register_craftitem("testitems:tree_spawner", { + description = S("Tree Spawner"), + inventory_image = "testitems_tree_spawner.png", + on_place = function(itemstack, placer, pointed_thing) + if (not pointed_thing or pointed_thing.type ~= "node") then + return + end + core.spawn_tree(pointed_thing.above, tree_def) + end, +}) + +local vmanip_for_trees = {} -- per player +core.register_craftitem("testitems:tree_spawner_vmanip", { + description = S("Tree Spawner using VoxelManip"), + inventory_image = "testitems_tree_spawner_vmanip.png", + on_place = function(itemstack, placer, pointed_thing) + if (not pointed_thing or pointed_thing.type ~= "node" or + not core.is_player(placer)) then + return + end + local name = placer:get_player_name() + local vm = vmanip_for_trees[name] + if not vm then + vm = VoxelManip(vector.add(pointed_thing.above, 20), + vector.subtract(pointed_thing.above, 20)) + vmanip_for_trees[name] = vm + core.chat_send_player(name, + "Tree in new VoxelManip spawned, left click to apply to map, ".. + "or right click to add more trees.") + end + core.spawn_tree_on_vmanip(vm, pointed_thing.above, tree_def) + end, + on_use = function(itemstack, user, pointed_thing) + if not core.is_player(user) then + return + end + local name = user:get_player_name() + local vm = vmanip_for_trees[name] + if vm then + vm:write_to_map() + vmanip_for_trees[name] = nil + core.chat_send_player(name, "VoxelManip written to map.") + end + end, +}) + +core.register_on_leaveplayer(function(player, timed_out) + vmanip_for_trees[player:get_player_name()] = nil +end) diff --git a/games/devtest/mods/testitems/textures/testitems_tree_spawner.png b/games/devtest/mods/testitems/textures/testitems_tree_spawner.png new file mode 100644 index 0000000000000000000000000000000000000000..aa743067735eb222486e5ca17a3d1b7a13bd1dec GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`_MR?|Ar`&K2@+Qhm`s0b&oMc- zVez&j^#aTGGa4*E%%gByoB1*8jN*-zjVbvTu5+I_cjP(iq&-FjoSj*00?Z7&wjPU5 Tw9R}8G>XB~)z4*}Q$iB}CYvYN literal 0 HcmV?d00001 diff --git a/games/devtest/mods/testitems/textures/testitems_tree_spawner_vmanip.png b/games/devtest/mods/testitems/textures/testitems_tree_spawner_vmanip.png new file mode 100644 index 0000000000000000000000000000000000000000..2d8e0311fa9bf3557f77da69f34bd92fe1d128cd GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`v7RoDAr`&K2@+Qhm`s0b&oMc- zVez&j^#aTGGa4*E%%gByoB1*8jN*-zjVbvPo;EFiAM;SkVOC;|KyPpF!;%Rso5T}p ya!ee<8!~2yPdIuZ)~l9zg6U%E2HiCpObn^k?WY#Zyy6G6h{4m<&t;ucLK6TG4K+ak literal 0 HcmV?d00001 diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp index e03f41482..688833fa2 100644 --- a/src/mapgen/treegen.cpp +++ b/src/mapgen/treegen.cpp @@ -875,4 +875,15 @@ void make_pine_tree(MMVManip &vmanip, v3s16 p0, const NodeDefManager *ndef, } } +std::string error_to_string(error e) +{ + switch (e) { + case SUCCESS: + return "success"; + case UNBALANCED_BRACKETS: + return "closing ']' has no matching opening bracket"; + } + return "unknown error"; +} + }; // namespace treegen diff --git a/src/mapgen/treegen.h b/src/mapgen/treegen.h index 159063eb3..dba7ea047 100644 --- a/src/mapgen/treegen.h +++ b/src/mapgen/treegen.h @@ -68,4 +68,7 @@ namespace treegen { treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, const TreeDef &def); // Helper to spawn it directly on map treegen::error spawn_ltree(ServerMap *map, v3s16 p0, const TreeDef &def); + + // Helper to get a string from the error message + std::string error_to_string(error e); }; // namespace treegen diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 666dee712..5ebb2c3e4 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -1307,11 +1307,7 @@ int ModApiEnv::l_spawn_tree(lua_State *L) ServerMap *map = &env->getServerMap(); treegen::error e; if ((e = treegen::spawn_ltree (map, p0, tree_def)) != treegen::SUCCESS) { - if (e == treegen::UNBALANCED_BRACKETS) { - luaL_error(L, "spawn_tree(): closing ']' has no matching opening bracket"); - } else { - luaL_error(L, "spawn_tree(): unknown error"); - } + throw LuaError("spawn_tree(): " + treegen::error_to_string(e)); } lua_pushboolean(L, true); @@ -1614,11 +1610,7 @@ int ModApiEnvVM::l_spawn_tree(lua_State *L) treegen::error e; if ((e = treegen::make_ltree(*vm, p0, tree_def)) != treegen::SUCCESS) { - if (e == treegen::UNBALANCED_BRACKETS) { - throw LuaError("spawn_tree(): closing ']' has no matching opening bracket"); - } else { - throw LuaError("spawn_tree(): unknown error"); - } + throw LuaError("spawn_tree(): " + treegen::error_to_string(e)); } lua_pushboolean(L, true); diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index 89323b2f6..d102b357f 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -1773,6 +1773,27 @@ int ModApiMapgen::l_place_schematic_on_vmanip(lua_State *L) return 1; } +// spawn_tree_on_vmanip(vmanip, pos, treedef) +int ModApiMapgen::l_spawn_tree_on_vmanip(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + MMVManip *vm = checkObject(L, 1)->vm; + v3s16 p0 = read_v3s16(L, 2); + treegen::TreeDef tree_def; + const NodeDefManager *ndef = getGameDef(L)->ndef(); + if (!read_tree_def(L, 3, ndef, tree_def)) + return 0; + + treegen::error e = treegen::make_ltree(*vm, p0, tree_def); + if (e != treegen::SUCCESS) { + throw LuaError("spawn_tree_on_vmanip(): " + treegen::error_to_string(e)); + } + + lua_pushboolean(L, true); + return 1; +} + // serialize_schematic(schematic, format, options={...}) int ModApiMapgen::l_serialize_schematic(lua_State *L) @@ -2023,6 +2044,7 @@ void ModApiMapgen::Initialize(lua_State *L, int top) API_FCT(create_schematic); API_FCT(place_schematic); API_FCT(place_schematic_on_vmanip); + API_FCT(spawn_tree_on_vmanip); API_FCT(serialize_schematic); API_FCT(read_schematic); } @@ -2049,6 +2071,7 @@ void ModApiMapgen::InitializeEmerge(lua_State *L, int top) API_FCT(generate_ores); API_FCT(generate_decorations); API_FCT(place_schematic_on_vmanip); + API_FCT(spawn_tree_on_vmanip); API_FCT(serialize_schematic); API_FCT(read_schematic); } diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h index d72cbcbad..860daa7e8 100644 --- a/src/script/lua_api/l_mapgen.h +++ b/src/script/lua_api/l_mapgen.h @@ -131,6 +131,9 @@ private: // replacements, force_placement, flagstring) static int l_place_schematic_on_vmanip(lua_State *L); + // spawn_tree_on_vmanip(vmanip, pos, treedef) + static int l_spawn_tree_on_vmanip(lua_State *L); + // serialize_schematic(schematic, format, options={...}) static int l_serialize_schematic(lua_State *L); From 50928b97593ba6381ec776e0408aad92761c0840 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 28 Nov 2024 19:16:07 +0100 Subject: [PATCH 40/68] Mapgen: Add rudimentary unittests --- src/mapgen/mg_biome.cpp | 28 ++------ src/mapgen/mg_biome.h | 52 ++++++++------- src/script/lua_api/l_mapgen.cpp | 25 +++---- src/unittest/CMakeLists.txt | 1 + src/unittest/test_mapgen.cpp | 114 ++++++++++++++++++++++++++++++++ 5 files changed, 162 insertions(+), 58 deletions(-) create mode 100644 src/unittest/test_mapgen.cpp diff --git a/src/mapgen/mg_biome.cpp b/src/mapgen/mg_biome.cpp index a1c4f5810..788bacede 100644 --- a/src/mapgen/mg_biome.cpp +++ b/src/mapgen/mg_biome.cpp @@ -25,21 +25,7 @@ BiomeManager::BiomeManager(Server *server) : // Create default biome to be used in case none exist Biome *b = new Biome; - b->name = "default"; - b->flags = 0; - b->depth_top = 0; - b->depth_filler = -MAX_MAP_GENERATION_LIMIT; - b->depth_water_top = 0; - b->depth_riverbed = 0; - b->min_pos = v3s16(-MAX_MAP_GENERATION_LIMIT, - -MAX_MAP_GENERATION_LIMIT, -MAX_MAP_GENERATION_LIMIT); - b->max_pos = v3s16(MAX_MAP_GENERATION_LIMIT, - MAX_MAP_GENERATION_LIMIT, MAX_MAP_GENERATION_LIMIT); - b->heat_point = 0.0; - b->humidity_point = 0.0; - b->vertical_blend = 0; - b->weight = 1.0f; b->m_nodenames.emplace_back("mapgen_stone"); b->m_nodenames.emplace_back("mapgen_stone"); @@ -64,11 +50,13 @@ void BiomeManager::clear() { EmergeManager *emerge = m_server->getEmergeManager(); - // Remove all dangling references in Decorations - DecorationManager *decomgr = emerge->getWritableDecorationManager(); - for (size_t i = 0; i != decomgr->getNumObjects(); i++) { - Decoration *deco = (Decoration *)decomgr->getRaw(i); - deco->biomes.clear(); + if (emerge) { + // Remove all dangling references in Decorations + DecorationManager *decomgr = emerge->getWritableDecorationManager(); + for (size_t i = 0; i != decomgr->getNumObjects(); i++) { + Decoration *deco = (Decoration *)decomgr->getRaw(i); + deco->biomes.clear(); + } } // Don't delete the first biome @@ -299,8 +287,6 @@ ObjDef *Biome::clone() const ObjDef::cloneTo(obj); NodeResolver::cloneTo(obj); - obj->flags = flags; - obj->c_top = c_top; obj->c_filler = c_filler; obj->c_stone = c_stone; diff --git a/src/mapgen/mg_biome.h b/src/mapgen/mg_biome.h index 3e9de89d9..637ec14b1 100644 --- a/src/mapgen/mg_biome.h +++ b/src/mapgen/mg_biome.h @@ -19,6 +19,12 @@ class BiomeManager; typedef u16 biome_t; +constexpr v3s16 MAX_MAP_GENERATION_LIMIT_V3( + MAX_MAP_GENERATION_LIMIT, + MAX_MAP_GENERATION_LIMIT, + MAX_MAP_GENERATION_LIMIT +); + #define BIOME_NONE ((biome_t)0) enum BiomeType { @@ -29,32 +35,32 @@ class Biome : public ObjDef, public NodeResolver { public: ObjDef *clone() const; - u32 flags; - - content_t c_top; - content_t c_filler; - content_t c_stone; - content_t c_water_top; - content_t c_water; - content_t c_river_water; - content_t c_riverbed; - content_t c_dust; + content_t + c_top = CONTENT_IGNORE, + c_filler = CONTENT_IGNORE, + c_stone = CONTENT_IGNORE, + c_water_top = CONTENT_IGNORE, + c_water = CONTENT_IGNORE, + c_river_water = CONTENT_IGNORE, + c_riverbed = CONTENT_IGNORE, + c_dust = CONTENT_IGNORE; std::vector c_cave_liquid; - content_t c_dungeon; - content_t c_dungeon_alt; - content_t c_dungeon_stair; + content_t + c_dungeon = CONTENT_IGNORE, + c_dungeon_alt = CONTENT_IGNORE, + c_dungeon_stair = CONTENT_IGNORE; - s16 depth_top; - s16 depth_filler; - s16 depth_water_top; - s16 depth_riverbed; + s16 depth_top = 0; + s16 depth_filler = -MAX_MAP_GENERATION_LIMIT; + s16 depth_water_top = 0; + s16 depth_riverbed = 0; - v3s16 min_pos; - v3s16 max_pos; - float heat_point; - float humidity_point; - s16 vertical_blend; - float weight; + v3s16 min_pos = -MAX_MAP_GENERATION_LIMIT_V3; + v3s16 max_pos = MAX_MAP_GENERATION_LIMIT_V3; + float heat_point = 0.0f; + float humidity_point = 0.0f; + s16 vertical_blend = 0; + float weight = 1.0f; virtual void resolveNodeNames(); }; diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index d102b357f..a7101ee92 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -366,22 +366,19 @@ Biome *read_biome_def(lua_State *L, int index, const NodeDefManager *ndef) ModApiMapgen::es_BiomeTerrainType, BIOMETYPE_NORMAL); Biome *b = BiomeManager::create(biometype); - b->name = getstringfield_default(L, index, "name", ""); - b->depth_top = getintfield_default(L, index, "depth_top", 0); - b->depth_filler = getintfield_default(L, index, "depth_filler", -31000); - b->depth_water_top = getintfield_default(L, index, "depth_water_top", 0); - b->depth_riverbed = getintfield_default(L, index, "depth_riverbed", 0); - b->heat_point = getfloatfield_default(L, index, "heat_point", 0.f); - b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.f); - b->vertical_blend = getintfield_default(L, index, "vertical_blend", 0); - b->weight = getfloatfield_default(L, index, "weight", 1.f); - b->flags = 0; // reserved + getstringfield(L, index, "name", b->name); + getintfield(L, index, "depth_top", b->depth_top); + getintfield(L, index, "depth_filler", b->depth_filler); + getintfield(L, index, "depth_water_top", b->depth_water_top); + getintfield(L, index, "depth_riverbed", b->depth_riverbed); + getfloatfield(L, index, "heat_point", b->heat_point); + getfloatfield(L, index, "humidity_point", b->humidity_point); + getintfield(L, index, "vertical_blend", b->vertical_blend); + getfloatfield(L, index, "weight", b->weight); - b->min_pos = getv3s16field_default( - L, index, "min_pos", v3s16(-31000, -31000, -31000)); + b->min_pos = getv3s16field_default(L, index, "min_pos", b->min_pos); getintfield(L, index, "y_min", b->min_pos.Y); - b->max_pos = getv3s16field_default( - L, index, "max_pos", v3s16(31000, 31000, 31000)); + b->max_pos = getv3s16field_default(L, index, "max_pos", b->max_pos); getintfield(L, index, "y_max", b->max_pos.Y); std::vector &nn = b->m_nodenames; diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt index 46e4f9a18..2b70a6918 100644 --- a/src/unittest/CMakeLists.txt +++ b/src/unittest/CMakeLists.txt @@ -17,6 +17,7 @@ set (UNITTEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_lua.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_map.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mapblock.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_mapgen.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_map_settings_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mapnode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_modchannels.cpp diff --git a/src/unittest/test_mapgen.cpp b/src/unittest/test_mapgen.cpp new file mode 100644 index 000000000..f72e85ba3 --- /dev/null +++ b/src/unittest/test_mapgen.cpp @@ -0,0 +1,114 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 Minetest core developers & community + +#include "test.h" + +#include "emerge.h" +#include "mapgen/mapgen.h" +#include "mapgen/mg_biome.h" +#include "mock_server.h" + +class TestMapgen : public TestBase +{ +public: + TestMapgen() { TestManager::registerTestModule(this); } + const char *getName() { return "TestMapgen"; } + + void runTests(IGameDef *gamedef); + + void testBiomeGen(IGameDef *gamedef); +}; + +static TestMapgen g_test_instance; + +namespace { + class MockBiomeManager : public BiomeManager { + public: + MockBiomeManager(Server *server) : BiomeManager(server) {} + + void setNodeDefManager(const NodeDefManager *ndef) + { + m_ndef = ndef; + } + }; +} + +void TestMapgen::runTests(IGameDef *gamedef) +{ + TEST(testBiomeGen, gamedef); +} + +void TestMapgen::testBiomeGen(IGameDef *gamedef) +{ + MockServer server(getTestTempDirectory()); + MockBiomeManager bmgr(&server); + bmgr.setNodeDefManager(gamedef->getNodeDefManager()); + + { + // Add some biomes (equivalent to l_register_biome) + // Taken from minetest_game/mods/default/mapgen.lua + size_t bmgr_count = bmgr.getNumObjects(); // this is 1 ? + + Biome *b = BiomeManager::create(BIOMETYPE_NORMAL); + b->name = "deciduous_forest"; + b->c_top = t_CONTENT_GRASS; + b->depth_top = 1; + b->c_filler = t_CONTENT_BRICK; // dirt + b->depth_filler = 3; + b->c_stone = t_CONTENT_STONE; + b->min_pos.Y = 1; + b->heat_point = 60.0f; + b->humidity_point = 68.0f; + UASSERT(bmgr.add(b) != OBJDEF_INVALID_HANDLE); + + b = BiomeManager::create(BIOMETYPE_NORMAL); + b->name = "deciduous_forest_shore"; + b->c_top = t_CONTENT_BRICK; // dirt + b->depth_top = 1; + b->c_filler = t_CONTENT_BRICK; // dirt + b->depth_filler = 3; + b->c_stone = t_CONTENT_STONE; + b->max_pos.Y = 0; + b->heat_point = 60.0f; + b->humidity_point = 68.0f; + UASSERT(bmgr.add(b) != OBJDEF_INVALID_HANDLE); + UASSERT(bmgr.getNumObjects() - bmgr_count == 2); + } + + + std::unique_ptr params(BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL)); + + constexpr v3s16 CSIZE(16, 16, 16); // misleading name. measured in nodes. + std::unique_ptr biomegen( + bmgr.createBiomeGen(BIOMEGEN_ORIGINAL, params.get(), CSIZE) + ); + + { + // Test biome transitions + // getBiomeAtIndex (Y only) + // getNextTransitionY + const struct { + s16 check_y; + const char *name; + s16 next_y; + } expected_biomes[] = { + { MAX_MAP_GENERATION_LIMIT, "deciduous_forest", 1 }, + // ^ FIXME: next_y should be 0 (min_pos.Y - 1) + { 1, "deciduous_forest", 0 }, + { 0, "deciduous_forest_shore", -MAX_MAP_GENERATION_LIMIT }, + { -100, "deciduous_forest_shore", -MAX_MAP_GENERATION_LIMIT }, + }; + for (const auto expected : expected_biomes) { + Biome *biome = biomegen->getBiomeAtIndex( + (1 * CSIZE.X) + 1, // index in CSIZE 2D noise map + v3s16(2000, expected.check_y, -1000) // absolute coordinates + ); + s16 next_y = biomegen->getNextTransitionY(expected.check_y); + + UASSERTEQ(auto, biome->name, expected.name); + UASSERTEQ(auto, next_y, expected.next_y); + } + } +} + From 480eb7d816db5b1a8073d188f46e4d1e0b36bc53 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 28 Nov 2024 22:12:24 +0100 Subject: [PATCH 41/68] Mapgen: Fix biome Y calculation regression BiomeGen::getNextTransitionY(y) did not guarantee the condition (y < biome_y_min) of the next loop because the function may return the value (biome_y_min - 1). Hence, the biome was not updated until one Y coordinate after. --- src/mapgen/cavegen.cpp | 8 ++++---- src/mapgen/mapgen.cpp | 16 +++++++++++----- src/mapgen/mg_biome.cpp | 5 ++++- src/mapgen/mg_biome.h | 3 ++- src/unittest/test_mapgen.cpp | 7 +++---- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/mapgen/cavegen.cpp b/src/mapgen/cavegen.cpp index f422db67d..6bb841ab5 100644 --- a/src/mapgen/cavegen.cpp +++ b/src/mapgen/cavegen.cpp @@ -84,7 +84,7 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, u16 depth_riverbed = biome->depth_riverbed; u16 nplaced = 0; - s16 biome_y_min = m_bmgn->getNextTransitionY(nmax.Y); + s16 biome_y_next = m_bmgn->getNextTransitionY(nmax.Y); // Don't excavate the overgenerated stone at nmax.Y + 1, // this creates a 'roof' over the tunnel, preventing light in @@ -94,13 +94,13 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, index3d -= m_ystride, VoxelArea::add_y(em, vi, -1)) { // We need this check to make sure that biomes don't generate too far down - if (y < biome_y_min) { + if (y <= biome_y_next) { biome = m_bmgn->getBiomeAtIndex(index2d, v3s16(x, y, z)); - biome_y_min = m_bmgn->getNextTransitionY(y); + biome_y_next = m_bmgn->getNextTransitionY(y); if (x == nmin.X && z == nmin.Z && false) { dstream << "cavegen: biome at " << y << " is " << biome->name - << ", next at " << biome_y_min << std::endl; + << ", next at " << biome_y_next << std::endl; } } diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index dd416a3e4..d236132df 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -644,7 +644,7 @@ void MapgenBasic::generateBiomes() u16 depth_riverbed = 0; u32 vi = vm->m_area.index(x, node_max.Y, z); - s16 biome_y_min = biomegen->getNextTransitionY(node_max.Y); + s16 biome_y_next = biomegen->getNextTransitionY(node_max.Y); // Check node at base of mapchunk above, either a node of a previously // generated mapchunk or if not, a node of overgenerated base terrain. @@ -661,23 +661,29 @@ void MapgenBasic::generateBiomes() for (s16 y = node_max.Y; y >= node_min.Y; y--) { content_t c = vm->m_data[vi].getContent(); + const bool biome_outdated = !biome || y <= biome_y_next; // Biome is (re)calculated: // 1. At the surface of stone below air or water. // 2. At the surface of water below air. // 3. When stone or water is detected but biome has not yet been calculated. // 4. When stone or water is detected just below a biome's lower limit. bool is_stone_surface = (c == c_stone) && - (air_above || water_above || !biome || y < biome_y_min); // 1, 3, 4 + (air_above || water_above || biome_outdated); // 1, 3, 4 bool is_water_surface = (c == c_water_source || c == c_river_water_source) && - (air_above || !biome || y < biome_y_min); // 2, 3, 4 + (air_above || biome_outdated); // 2, 3, 4 if (is_stone_surface || is_water_surface) { - if (!biome || y < biome_y_min) { + if (biome_outdated) { // (Re)calculate biome biome = biomegen->getBiomeAtIndex(index, v3s16(x, y, z)); - biome_y_min = biomegen->getNextTransitionY(y); + biome_y_next = biomegen->getNextTransitionY(y); + + if (x == node_min.X && z == node_min.Z && false) { + dstream << "biomegen: biome at " << y << " is " << biome->name + << ", next at " << biome_y_next << std::endl; + } } // Add biome to biomemap at first stone surface detected diff --git a/src/mapgen/mg_biome.cpp b/src/mapgen/mg_biome.cpp index 788bacede..a0bb6dee2 100644 --- a/src/mapgen/mg_biome.cpp +++ b/src/mapgen/mg_biome.cpp @@ -129,7 +129,10 @@ BiomeGenOriginal::BiomeGenOriginal(BiomeManager *biomemgr, for (size_t i = 0; i < m_bmgr->getNumObjects(); i++) { Biome *b = (Biome *)m_bmgr->getRaw(i); values.push_back(b->max_pos.Y); - values.push_back(b->min_pos.Y); + // We scan for biomes from high Y to low Y (top to bottom). Hence, + // biomes effectively transition at (min_pos.Y - 1). + if (b->min_pos.Y > -MAX_MAP_GENERATION_LIMIT) + values.push_back(b->min_pos.Y - 1); } std::sort(values.begin(), values.end(), std::greater<>()); diff --git a/src/mapgen/mg_biome.h b/src/mapgen/mg_biome.h index 637ec14b1..8adce5db6 100644 --- a/src/mapgen/mg_biome.h +++ b/src/mapgen/mg_biome.h @@ -197,7 +197,8 @@ private: Noise *noise_heat_blend; Noise *noise_humidity_blend; - // ordered descending + /// Y values at which biomes may transition. + /// This array may only be used for downwards scanning! std::vector m_transitions_y; }; diff --git a/src/unittest/test_mapgen.cpp b/src/unittest/test_mapgen.cpp index f72e85ba3..62bd7e54f 100644 --- a/src/unittest/test_mapgen.cpp +++ b/src/unittest/test_mapgen.cpp @@ -93,11 +93,10 @@ void TestMapgen::testBiomeGen(IGameDef *gamedef) const char *name; s16 next_y; } expected_biomes[] = { - { MAX_MAP_GENERATION_LIMIT, "deciduous_forest", 1 }, - // ^ FIXME: next_y should be 0 (min_pos.Y - 1) + { MAX_MAP_GENERATION_LIMIT, "deciduous_forest", 0 }, { 1, "deciduous_forest", 0 }, - { 0, "deciduous_forest_shore", -MAX_MAP_GENERATION_LIMIT }, - { -100, "deciduous_forest_shore", -MAX_MAP_GENERATION_LIMIT }, + { 0, "deciduous_forest_shore", S16_MIN }, + { -100, "deciduous_forest_shore", S16_MIN }, }; for (const auto expected : expected_biomes) { Biome *biome = biomegen->getBiomeAtIndex( From eb6731bdc698fcef9f97061ed9d90d92324393a5 Mon Sep 17 00:00:00 2001 From: siliconsniffer <97843108+siliconsniffer@users.noreply.github.com> Date: Sun, 8 Dec 2024 20:28:37 +0100 Subject: [PATCH 42/68] Main menu: Add server favorite button (#15486) This adopts PR 13446. --- builtin/mainmenu/tab_online.lua | 37 +++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index 422e9ba23..112c17457 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -55,7 +55,20 @@ local function get_sorted_servers() return servers end +local function is_selected_fav(server) + local address = core.settings:get("address") + local port = tonumber(core.settings:get("remote_port")) + + for _, fav in ipairs(serverlistmgr.get_favorites()) do + if address == fav.address and port == fav.port then + return true + end + end + return false +end + -- Persists the selected server in the "address" and "remote_port" settings + local function set_selected_server(server) if server == nil then -- reset selection core.settings:remove("address") @@ -66,14 +79,6 @@ local function set_selected_server(server) local port = server.port gamedata.serverdescription = server.description - gamedata.fav = false - for _, fav in ipairs(serverlistmgr.get_favorites()) do - if address == fav.address and port == fav.port then - gamedata.fav = true - break - end - end - if address and port then core.settings:set("address", address) core.settings:set("remote_port", port) @@ -165,15 +170,20 @@ local function get_formspec(tabview, name, tabdata) fgettext("Clients:\n$1", table.concat(clients_list, "\n")) .. "]" end retval = retval .. "style[btn_view_clients;padding=6]" - retval = retval .. "image_button[5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. + retval = retval .. "image_button[4.5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. "server_view_clients.png") .. ";btn_view_clients;]" end - if gamedata.fav then + if is_selected_fav() then retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]" retval = retval .. "style[btn_delete_favorite;padding=6]" - retval = retval .. "image_button[" .. (can_view_clients_list and "4.5" or "5") .. ",1.3;0.5,0.5;" .. + retval = retval .. "image_button[5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. "server_favorite_delete.png") .. ";btn_delete_favorite;]" + else + retval = retval .. "tooltip[btn_add_favorite;" .. fgettext("Add favorite") .. "]" + retval = retval .. "style[btn_add_favorite;padding=6]" + retval = retval .. "image_button[5,1.3;0.5,0.5;" .. + core.formspec_escape(defaulttexturedir .. "server_favorite.png") .. ";btn_add_favorite;]" end end @@ -336,6 +346,11 @@ local function main_button_handler(tabview, fields, name, tabdata) end end + if fields.btn_add_favorite then + serverlistmgr.add_favorite(find_selected_server()) + return true + end + if fields.btn_delete_favorite then local idx = core.get_table_index("servers") if not idx then return end From 21437090b80275f7967b7f5927ae0ead7f49d2e1 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 7 Dec 2024 12:59:40 +0100 Subject: [PATCH 43/68] Don't recalculate meshnode normals unnecessarily --- src/client/content_mapblock.cpp | 28 +++++++++++++++------------- src/client/mesh.cpp | 4 ++-- src/client/mesh.h | 2 +- src/nodedef.cpp | 7 +++++-- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 2d294953a..25d303156 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -1644,7 +1644,6 @@ void MapblockMeshGenerator::drawMeshNode() { u8 facedir = 0; scene::IMesh* mesh; - bool private_mesh; // as a grab/drop pair is not thread-safe int degrotate = 0; if (cur_node.f->param_type_2 == CPT2_FACEDIR || @@ -1664,32 +1663,37 @@ void MapblockMeshGenerator::drawMeshNode() if (cur_node.f->mesh_ptr) { // clone and rotate mesh - private_mesh = true; mesh = cloneMesh(cur_node.f->mesh_ptr); + bool modified = true; if (facedir) rotateMeshBy6dFacedir(mesh, facedir); else if (degrotate) rotateMeshXZby(mesh, 1.5f * degrotate); - recalculateBoundingBox(mesh); - meshmanip->recalculateNormals(mesh, true, false); + else + modified = false; + if (modified) { + recalculateBoundingBox(mesh); + // FIXME: we should rotate the normals too, instead of recalculating + meshmanip->recalculateNormals(mesh, true, false); + } } else { warningstream << "drawMeshNode(): missing mesh" << std::endl; return; } - int mesh_buffer_count = mesh->getMeshBufferCount(); - for (int j = 0; j < mesh_buffer_count; j++) { + for (u32 j = 0; j < mesh->getMeshBufferCount(); j++) { // Only up to 6 tiles are supported - const auto tile = mesh->getTextureSlot(j); + const u32 tile = mesh->getTextureSlot(j); useTile(MYMIN(tile, 5)); + scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices(); - int vertex_count = buf->getVertexCount(); + u32 vertex_count = buf->getVertexCount(); if (data->m_smooth_lighting) { // Mesh is always private here. So the lighting is applied to each // vertex right here. - for (int k = 0; k < vertex_count; k++) { + for (u32 k = 0; k < vertex_count; k++) { video::S3DVertex &vertex = vertices[k]; vertex.Color = blendLightColor(vertex.Pos, vertex.Normal); vertex.Pos += cur_node.origin; @@ -1697,15 +1701,13 @@ void MapblockMeshGenerator::drawMeshNode() collector->append(cur_node.tile, vertices, vertex_count, buf->getIndices(), buf->getIndexCount()); } else { - // Don't modify the mesh, it may not be private here. - // Instead, let the collector process colors, etc. + // Let the collector process colors, etc. collector->append(cur_node.tile, vertices, vertex_count, buf->getIndices(), buf->getIndexCount(), cur_node.origin, cur_node.color, cur_node.f->light_source); } } - if (private_mesh) - mesh->drop(); + mesh->drop(); } // also called when the drawtype is known but should have been pre-converted diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 3f947ae50..012b9a45a 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -272,9 +272,9 @@ void rotateMeshYZby(scene::IMesh *mesh, f64 degrees) rotateMesh<&v3f::Y, &v3f::Z>(mesh, degrees); } -void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir) +void rotateMeshBy6dFacedir(scene::IMesh *mesh, u8 facedir) { - int axisdir = facedir >> 2; + u8 axisdir = facedir >> 2; facedir &= 0x03; switch (facedir) { case 1: rotateMeshXZby(mesh, -90); break; diff --git a/src/client/mesh.h b/src/client/mesh.h index 5fd0ebb0e..3345e24f7 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -68,7 +68,7 @@ void setMeshColorByNormal(scene::IMesh *mesh, const v3f &normal, Rotate the mesh by 6d facedir value. Method only for meshnodes, not suitable for entities. */ -void rotateMeshBy6dFacedir(scene::IMesh *mesh, int facedir); +void rotateMeshBy6dFacedir(scene::IMesh *mesh, u8 facedir); /* Rotate the mesh around the axis and given angle in degrees. diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 2301b179f..81348cf23 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -943,14 +943,17 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc palette = tsrc->getPalette(palette_name); if (drawtype == NDT_MESH && !mesh.empty()) { - // Meshnode drawtype // Read the mesh and apply scale mesh_ptr = client->getMesh(mesh); if (mesh_ptr) { v3f scale = v3f(BS) * visual_scale; scaleMesh(mesh_ptr, scale); recalculateBoundingBox(mesh_ptr); - meshmanip->recalculateNormals(mesh_ptr, true, false); + if (!checkMeshNormals(mesh_ptr)) { + infostream << "ContentFeatures: recalculating normals for mesh " + << mesh << std::endl; + meshmanip->recalculateNormals(mesh_ptr, true, false); + } } } } From 3c5e0d10fc169ecdd062f7afa20a2b0faeaf3a7f Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Mon, 9 Dec 2024 00:45:37 +0100 Subject: [PATCH 44/68] Rotate meshnode normals correctly instead of recalculating --- src/client/content_mapblock.cpp | 2 -- src/client/mesh.cpp | 12 ++++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 25d303156..49abbd4fa 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -1673,8 +1673,6 @@ void MapblockMeshGenerator::drawMeshNode() modified = false; if (modified) { recalculateBoundingBox(mesh); - // FIXME: we should rotate the normals too, instead of recalculating - meshmanip->recalculateNormals(mesh, true, false); } } else { warningstream << "drawMeshNode(): missing mesh" << std::endl; diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 012b9a45a..eb56d3580 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -249,10 +249,14 @@ static void rotateMesh(scene::IMesh *mesh, float degrees) float c = std::cos(degrees); float s = std::sin(degrees); auto rotator = [c, s] (video::S3DVertex *vertex) { - float u = vertex->Pos.*U; - float v = vertex->Pos.*V; - vertex->Pos.*U = c * u - s * v; - vertex->Pos.*V = s * u + c * v; + auto rotate_vec = [c, s] (v3f &vec) { + float u = vec.*U; + float v = vec.*V; + vec.*U = c * u - s * v; + vec.*V = s * u + c * v; + }; + rotate_vec(vertex->Pos); + rotate_vec(vertex->Normal); }; applyToMesh(mesh, rotator); } From 8957739cdfa7010795bf8344c114702ff51b9857 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 6 Dec 2024 21:00:47 +0100 Subject: [PATCH 45/68] Use appropriate sized type for VoxelArea extent --- src/map.h | 2 +- src/mapgen/cavegen.cpp | 4 ++-- src/mapgen/dungeongen.cpp | 2 +- src/mapgen/mapgen.cpp | 16 ++++++++-------- src/mapgen/mapgen.h | 2 +- src/mapgen/mapgen_carpathian.cpp | 2 +- src/mapgen/mapgen_flat.cpp | 2 +- src/mapgen/mapgen_v6.cpp | 14 +++++++------- src/mapgen/mapgen_v6.h | 2 +- src/mapgen/mapgen_v7.cpp | 2 +- src/mapgen/mapgen_valleys.cpp | 2 +- src/mapgen/mg_decoration.cpp | 2 +- src/unittest/test_voxelarea.cpp | 20 +++++++++++++------ src/voxel.cpp | 2 +- src/voxel.h | 33 +++++++++++++++++++------------- src/voxelalgorithms.cpp | 2 +- 16 files changed, 62 insertions(+), 47 deletions(-) diff --git a/src/map.h b/src/map.h index bbe91f5d6..37d1a713d 100644 --- a/src/map.h +++ b/src/map.h @@ -79,7 +79,7 @@ struct MapEditEvent VoxelArea a; for (v3s16 p : modified_blocks) { v3s16 np1 = p*MAP_BLOCKSIZE; - v3s16 np2 = np1 + v3s16(1,1,1)*MAP_BLOCKSIZE - v3s16(1,1,1); + v3s16 np2 = np1 + v3s16(MAP_BLOCKSIZE-1); a.addPoint(np1); a.addPoint(np2); } diff --git a/src/mapgen/cavegen.cpp b/src/mapgen/cavegen.cpp index 6bb841ab5..0c3ec1f81 100644 --- a/src/mapgen/cavegen.cpp +++ b/src/mapgen/cavegen.cpp @@ -64,7 +64,7 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z); - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 index2d = 0; // Biomemap index for (s16 z = nmin.Z; z <= nmax.Z; z++) @@ -230,7 +230,7 @@ bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax) //// Place nodes bool near_cavern = false; - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 index2d = 0; for (s16 z = nmin.Z; z <= nmax.Z; z++) diff --git a/src/mapgen/dungeongen.cpp b/src/mapgen/dungeongen.cpp index 948cc62b1..249c462ba 100644 --- a/src/mapgen/dungeongen.cpp +++ b/src/mapgen/dungeongen.cpp @@ -127,7 +127,7 @@ void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax) void DungeonGen::makeDungeon(v3s16 start_padding) { - const v3s16 &areasize = vm->m_area.getExtent(); + const v3s32 &areasize = vm->m_area.getExtent(); v3s16 roomsize; v3s16 roomplace; diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index d236132df..3778c3807 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -240,7 +240,7 @@ u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed) // Returns -MAX_MAP_GENERATION_LIMIT if not found s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax) { - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y); s16 y; @@ -258,7 +258,7 @@ s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax) // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax) { - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y); s16 y; @@ -296,7 +296,7 @@ void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax) void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax, std::vector &floors, std::vector &ceilings) { - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); bool is_walkable = false; u32 vi = vm->m_area.index(p2d.X, ymax, p2d.Y); @@ -320,7 +320,7 @@ void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax, } -inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em) +inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s32 em) { u32 vi_neg_x = vi; VoxelArea::add_x(em, vi_neg_x, -1); @@ -357,7 +357,7 @@ void Mapgen::updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nm { bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed; content_t was_n; - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); isignored = true; isliquid = false; @@ -481,7 +481,7 @@ void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow) //TimeTaker t("propagateSunlight"); VoxelArea a(nmin, nmax); bool block_is_underground = (water_level >= nmax.Y); - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); // NOTE: Direct access to the low 4 bits of param1 is okay here because, // by definition, sunlight will never be in the night lightbank. @@ -629,7 +629,7 @@ void MapgenBasic::generateBiomes() assert(biomegen); assert(biomemap); - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 index = 0; noise_filler_depth->perlinMap2D(node_min.X, node_min.Z); @@ -774,7 +774,7 @@ void MapgenBasic::dustTopNodes() if (node_max.Y < water_level) return; - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 index = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) diff --git a/src/mapgen/mapgen.h b/src/mapgen/mapgen.h index 5e5ff9f3c..a81b9a361 100644 --- a/src/mapgen/mapgen.h +++ b/src/mapgen/mapgen.h @@ -261,7 +261,7 @@ private: // isLiquidHorizontallyFlowable() is a helper function for updateLiquid() // that checks whether there are floodable nodes without liquid beneath // the node at index vi. - inline bool isLiquidHorizontallyFlowable(u32 vi, v3s16 em); + inline bool isLiquidHorizontallyFlowable(u32 vi, v3s32 em); }; /* diff --git a/src/mapgen/mapgen_carpathian.cpp b/src/mapgen/mapgen_carpathian.cpp index ba54a3672..46048b51f 100644 --- a/src/mapgen/mapgen_carpathian.cpp +++ b/src/mapgen/mapgen_carpathian.cpp @@ -445,7 +445,7 @@ int MapgenCarpathian::generateTerrain() noise_rivers->perlinMap2D(node_min.X, node_min.Z); //// Place nodes - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index2d = 0; diff --git a/src/mapgen/mapgen_flat.cpp b/src/mapgen/mapgen_flat.cpp index f0e7984d5..e96e4e45a 100644 --- a/src/mapgen/mapgen_flat.cpp +++ b/src/mapgen/mapgen_flat.cpp @@ -278,7 +278,7 @@ s16 MapgenFlat::generateTerrain() MapNode n_stone(c_stone); MapNode n_water(c_water_source); - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 ni2d = 0; diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp index 2f5a3cf7a..44243618e 100644 --- a/src/mapgen/mapgen_v6.cpp +++ b/src/mapgen/mapgen_v6.cpp @@ -222,7 +222,7 @@ void MapgenV6Params::setDefaultSettings(Settings *settings) // Returns Y one under area minimum if not found s16 MapgenV6::find_stone_level(v2s16 p2d) { - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); s16 y_nodes_max = vm->m_area.MaxEdge.Y; s16 y_nodes_min = vm->m_area.MinEdge.Y; u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y); @@ -670,7 +670,7 @@ int MapgenV6::generateGround() BiomeV6Type bt = getBiome(v2s16(x, z)); // Fill ground with stone - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 i = vm->m_area.index(x, node_min.Y, z); for (s16 y = node_min.Y; y <= node_max.Y; y++) { if (vm->m_data[i].getContent() == CONTENT_IGNORE) { @@ -739,7 +739,7 @@ void MapgenV6::addMud() // Add mud on ground s16 mudcount = 0; - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); s16 y_start = surface_y + 1; u32 i = vm->m_area.index(x, y_start, z); for (s16 y = y_start; y <= node_max.Y; y++) { @@ -757,7 +757,7 @@ void MapgenV6::addMud() void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos) { - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); static const v3s16 dirs4[4] = { v3s16(0, 0, 1), // Back v3s16(1, 0, 0), // Right @@ -870,7 +870,7 @@ void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos) void MapgenV6::moveMud(u32 remove_index, u32 place_index, - u32 above_remove_index, v2s16 pos, v3s16 em) + u32 above_remove_index, v2s16 pos, v3s32 em) { MapNode n_air(CONTENT_AIR); // Copy mud from old place to new place @@ -920,7 +920,7 @@ void MapgenV6::placeTreesAndJungleGrass() if (c_junglegrass == CONTENT_IGNORE) c_junglegrass = CONTENT_AIR; MapNode n_junglegrass(c_junglegrass); - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); // Divide area into parts s16 div = 8; @@ -1027,7 +1027,7 @@ void MapgenV6::growGrass() // Add surface nodes MapNode n_dirt_with_grass(c_dirt_with_grass); MapNode n_dirt_with_snow(c_dirt_with_snow); MapNode n_snowblock(c_snowblock); - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 index = 0; for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++) diff --git a/src/mapgen/mapgen_v6.h b/src/mapgen/mapgen_v6.h index 30b31f84a..6d776665a 100644 --- a/src/mapgen/mapgen_v6.h +++ b/src/mapgen/mapgen_v6.h @@ -150,7 +150,7 @@ public: void addMud(); void flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos); void moveMud(u32 remove_index, u32 place_index, - u32 above_remove_index, v2s16 pos, v3s16 em); + u32 above_remove_index, v2s16 pos, v3s32 em); void growGrass(); void placeTreesAndJungleGrass(); virtual void generateCaves(int max_stone_y); diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp index 491a1514a..fe052f3b7 100644 --- a/src/mapgen/mapgen_v7.cpp +++ b/src/mapgen/mapgen_v7.cpp @@ -523,7 +523,7 @@ int MapgenV7::generateTerrain() } //// Place nodes - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index2d = 0; diff --git a/src/mapgen/mapgen_valleys.cpp b/src/mapgen/mapgen_valleys.cpp index 196454642..55185c445 100644 --- a/src/mapgen/mapgen_valleys.cpp +++ b/src/mapgen/mapgen_valleys.cpp @@ -344,7 +344,7 @@ int MapgenValleys::generateTerrain() noise_inter_valley_fill->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z); - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index_2d = 0; diff --git a/src/mapgen/mg_decoration.cpp b/src/mapgen/mg_decoration.cpp index 60183d4e4..e8f381ec6 100644 --- a/src/mapgen/mg_decoration.cpp +++ b/src/mapgen/mg_decoration.cpp @@ -346,7 +346,7 @@ size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling) pr->range(deco_param2, deco_param2_max) : deco_param2; bool force_placement = (flags & DECO_FORCE_PLACEMENT); - const v3s16 &em = vm->m_area.getExtent(); + const v3s32 &em = vm->m_area.getExtent(); u32 vi = vm->m_area.index(p); if (ceiling) { diff --git a/src/unittest/test_voxelarea.cpp b/src/unittest/test_voxelarea.cpp index 384fda0d1..4bdd9af5b 100644 --- a/src/unittest/test_voxelarea.cpp +++ b/src/unittest/test_voxelarea.cpp @@ -107,11 +107,16 @@ void TestVoxelArea::test_pad() void TestVoxelArea::test_extent() { VoxelArea v1(v3s16(-1337, -547, -789), v3s16(-147, 447, 669)); - UASSERT(v1.getExtent() == v3s16(1191, 995, 1459)); + UASSERT(v1.getExtent() == v3s32(1191, 995, 1459)); VoxelArea v2(v3s16(32493, -32507, 32752), v3s16(32508, -32492, 32767)); - UASSERT(v2.getExtent() == v3s16(16, 16, 16)); + UASSERT(v2.getExtent() == v3s32(16, 16, 16)); + // side length bigger than S16_MAX + VoxelArea v3({-20000, 12, 34}, {20000, 12, 34}); + UASSERT(v3.getExtent() == v3s32(40001, 1, 1)); + + UASSERT(VoxelArea().hasEmptyExtent()); UASSERT(VoxelArea({2,3,4}, {1,2,3}).hasEmptyExtent()); UASSERT(VoxelArea({2,3,4}, {2,2,3}).hasEmptyExtent() == false); } @@ -124,6 +129,9 @@ void TestVoxelArea::test_volume() VoxelArea v2(v3s16(32493, -32507, 32752), v3s16(32508, -32492, 32767)); UASSERTEQ(s32, v2.getVolume(), 4096); + VoxelArea v3({-20000, 12, 34}, {20000, 12, 34}); + UASSERTEQ(s32, v3.getVolume(), 40000); + UASSERTEQ(s32, VoxelArea({2,3,4}, {1,2,3}).getVolume(), 0); UASSERTEQ(s32, VoxelArea({2,3,4}, {2,2,3}).getVolume(), 0); } @@ -388,7 +396,7 @@ void TestVoxelArea::test_index_v3s16_all_neg() void TestVoxelArea::test_add_x() { - v3s16 extent; + v3s32 extent; u32 i = 4; VoxelArea::add_x(extent, i, 8); UASSERTEQ(u32, i, 12) @@ -396,7 +404,7 @@ void TestVoxelArea::test_add_x() void TestVoxelArea::test_add_y() { - v3s16 extent(740, 16, 87); + v3s32 extent(740, 16, 87); u32 i = 8; VoxelArea::add_y(extent, i, 88); UASSERTEQ(u32, i, 65128) @@ -404,7 +412,7 @@ void TestVoxelArea::test_add_y() void TestVoxelArea::test_add_z() { - v3s16 extent(114, 80, 256); + v3s32 extent(114, 80, 256); u32 i = 4; VoxelArea::add_z(extent, i, 8); UASSERTEQ(u32, i, 72964) @@ -412,7 +420,7 @@ void TestVoxelArea::test_add_z() void TestVoxelArea::test_add_p() { - v3s16 extent(33, 14, 742); + v3s32 extent(33, 14, 742); v3s16 a(15, 12, 369); u32 i = 4; VoxelArea::add_p(extent, i, a); diff --git a/src/voxel.cpp b/src/voxel.cpp index b0a63b4f0..d9fceb2b9 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -38,7 +38,7 @@ void VoxelManipulator::clear() void VoxelManipulator::print(std::ostream &o, const NodeDefManager *ndef, VoxelPrintMode mode) const { - const v3s16 &em = m_area.getExtent(); + auto &em = m_area.getExtent(); v3s16 of = m_area.MinEdge; o<<"size: "<= 0); - assert(m_cache_extent.Y >= 0); - assert(m_cache_extent.Z >= 0); + assert(m_cache_extent.X >= 0 && m_cache_extent.X <= MAX_EXTENT); + assert(m_cache_extent.Y >= 0 && m_cache_extent.Y <= MAX_EXTENT); + assert(m_cache_extent.Z >= 0 && m_cache_extent.Z <= MAX_EXTENT); } - v3s16 m_cache_extent = v3s16(0,0,0); + static constexpr s32 MAX_EXTENT = S16_MAX - S16_MIN + 1; + v3s32 m_cache_extent; }; enum : u8 { diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index cd2664938..5da39d6ed 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -767,7 +767,7 @@ void fill_with_sunlight(MMVManip *vm, const NodeDefManager *ndef, v2s16 offset, bool light[MAP_BLOCKSIZE][MAP_BLOCKSIZE]) { // Distance in array between two nodes on top of each other. - s16 ystride = vm->m_area.getExtent().X; + s32 ystride = vm->m_area.getExtent().X; // Cache the ignore node. MapNode ignore = MapNode(CONTENT_IGNORE); // For each column of nodes: From 67126cbd1bab16e5860de6510b8af15fb9f7c9e7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 6 Dec 2024 21:03:52 +0100 Subject: [PATCH 46/68] Fix meaning of VoxelArea::hasEmptyExtent() --- src/emerge_internal.h | 4 ++-- src/unittest/test_voxelarea.cpp | 2 +- src/voxel.h | 8 +++----- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/emerge_internal.h b/src/emerge_internal.h index 6dd438603..6a71c7523 100644 --- a/src/emerge_internal.h +++ b/src/emerge_internal.h @@ -93,7 +93,7 @@ public: MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a): m_ignorevariable(ignorevariable) { - if (m_ignorevariable->getVolume() == 0) + if (m_ignorevariable->hasEmptyExtent()) *m_ignorevariable = a; else m_ignorevariable = nullptr; @@ -102,7 +102,7 @@ public: ~MapEditEventAreaIgnorer() { if (m_ignorevariable) { - assert(m_ignorevariable->getVolume() != 0); + assert(!m_ignorevariable->hasEmptyExtent()); *m_ignorevariable = VoxelArea(); } } diff --git a/src/unittest/test_voxelarea.cpp b/src/unittest/test_voxelarea.cpp index 4bdd9af5b..321420a52 100644 --- a/src/unittest/test_voxelarea.cpp +++ b/src/unittest/test_voxelarea.cpp @@ -118,7 +118,7 @@ void TestVoxelArea::test_extent() UASSERT(VoxelArea().hasEmptyExtent()); UASSERT(VoxelArea({2,3,4}, {1,2,3}).hasEmptyExtent()); - UASSERT(VoxelArea({2,3,4}, {2,2,3}).hasEmptyExtent() == false); + UASSERT(VoxelArea({2,3,4}, {2,2,3}).hasEmptyExtent()); } void TestVoxelArea::test_volume() diff --git a/src/voxel.h b/src/voxel.h index 2a0970aad..a5ffc7ce6 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -114,11 +114,9 @@ public: return m_cache_extent; } - /// @note `getVolume() == 0` and `getEmptyExtent()` are not equivalent. bool hasEmptyExtent() const { - // FIXME: shouldn't this actually be a component-wise check? - return m_cache_extent == v3s32(0,0,0); + return !m_cache_extent.X || !m_cache_extent.Y || !m_cache_extent.Z; } s32 getVolume() const @@ -207,7 +205,7 @@ public: if(a.hasEmptyExtent()) { VoxelArea b = *this; - if (b.getVolume() != 0) + if (!b.hasEmptyExtent()) result.push_back(b); return; } @@ -216,7 +214,7 @@ public: const auto &take = [&result] (v3s16 min, v3s16 max) { VoxelArea b(min, max); - if (b.getVolume() != 0) + if (!b.hasEmptyExtent()) result.push_back(b); }; From 4f800dd2b4bd61bb248f93f0a3146847350e4318 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 6 Dec 2024 21:17:59 +0100 Subject: [PATCH 47/68] Change VoxelArea volume to be u32 --- src/map.cpp | 4 ++-- src/mapgen/treegen.cpp | 7 +++---- src/unittest/test_voxelalgorithms.cpp | 4 ++-- src/unittest/test_voxelarea.cpp | 13 +++++++------ src/voxel.cpp | 4 ++-- src/voxel.h | 7 ++++--- 6 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 1af8684e1..240788944 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -766,7 +766,7 @@ void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, VoxelArea block_area_nodes (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1)); - u32 size_MB = block_area_nodes.getVolume()*4/1000000; + u32 size_MB = block_area_nodes.getVolume() * sizeof(MapNode) / 1000000U; if(size_MB >= 1) { infostream<<"initialEmerge: area: "; @@ -855,7 +855,7 @@ MMVManip *MMVManip::clone() const { MMVManip *ret = new MMVManip(); - const s32 size = m_area.getVolume(); + const u32 size = m_area.getVolume(); ret->m_area = m_area; if (m_data) { ret->m_data = new MapNode[size]; diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp index 688833fa2..8198cf266 100644 --- a/src/mapgen/treegen.cpp +++ b/src/mapgen/treegen.cpp @@ -77,7 +77,7 @@ void make_tree(MMVManip &vmanip, v3s16 p0, bool is_apple_tree, VoxelArea leaves_a(v3s16(-2, -1, -2), v3s16(2, 2, 2)); Buffer leaves_d(leaves_a.getVolume()); - for (s32 i = 0; i < leaves_a.getVolume(); i++) + for (u32 i = 0; i < leaves_d.getSize(); i++) leaves_d[i] = 0; // Force leaves at near the end of the trunk @@ -697,9 +697,8 @@ void make_jungletree(MMVManip &vmanip, v3s16 p0, const NodeDefManager *ndef, p1.Y -= 1; VoxelArea leaves_a(v3s16(-3, -2, -3), v3s16(3, 2, 3)); - //SharedPtr leaves_d(new u8[leaves_a.getVolume()]); Buffer leaves_d(leaves_a.getVolume()); - for (s32 i = 0; i < leaves_a.getVolume(); i++) + for (u32 i = 0; i < leaves_d.getSize(); i++) leaves_d[i] = 0; // Force leaves at near the end of the trunk @@ -788,7 +787,7 @@ void make_pine_tree(MMVManip &vmanip, v3s16 p0, const NodeDefManager *ndef, VoxelArea leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3)); Buffer leaves_d(leaves_a.getVolume()); - for (s32 i = 0; i < leaves_a.getVolume(); i++) + for (u32 i = 0; i < leaves_d.getSize(); i++) leaves_d[i] = 0; // Upper branches diff --git a/src/unittest/test_voxelalgorithms.cpp b/src/unittest/test_voxelalgorithms.cpp index 2a98412b4..b8ba11248 100644 --- a/src/unittest/test_voxelalgorithms.cpp +++ b/src/unittest/test_voxelalgorithms.cpp @@ -98,8 +98,8 @@ void TestVoxelAlgorithms::testLighting(IGameDef *gamedef) std::map modified_blocks; MMVManip vm(&map); vm.initialEmerge(bpmin, bpmax, false); - s32 volume = vm.m_area.getVolume(); - for (s32 i = 0; i < volume; i++) + u32 volume = vm.m_area.getVolume(); + for (u32 i = 0; i < volume; i++) vm.m_data[i] = MapNode(CONTENT_AIR); for (s16 z = -10; z <= 10; z++) for (s16 y = -10; y <= 10; y++) diff --git a/src/unittest/test_voxelarea.cpp b/src/unittest/test_voxelarea.cpp index 321420a52..f594a9be7 100644 --- a/src/unittest/test_voxelarea.cpp +++ b/src/unittest/test_voxelarea.cpp @@ -124,16 +124,17 @@ void TestVoxelArea::test_extent() void TestVoxelArea::test_volume() { VoxelArea v1(v3s16(-1337, -547, -789), v3s16(-147, 447, 669)); - UASSERTEQ(s32, v1.getVolume(), 1728980655); + UASSERTEQ(u32, v1.getVolume(), 1728980655); VoxelArea v2(v3s16(32493, -32507, 32752), v3s16(32508, -32492, 32767)); - UASSERTEQ(s32, v2.getVolume(), 4096); + UASSERTEQ(u32, v2.getVolume(), 4096); - VoxelArea v3({-20000, 12, 34}, {20000, 12, 34}); - UASSERTEQ(s32, v3.getVolume(), 40000); + // volume bigger than S32_MAX + VoxelArea v3({1, 1, 1}, {1337, 1337, 1337}); + UASSERTEQ(u32, v3.getVolume(), 2389979753U); - UASSERTEQ(s32, VoxelArea({2,3,4}, {1,2,3}).getVolume(), 0); - UASSERTEQ(s32, VoxelArea({2,3,4}, {2,2,3}).getVolume(), 0); + UASSERTEQ(u32, VoxelArea({2,3,4}, {1,2,3}).getVolume(), 0); + UASSERTEQ(u32, VoxelArea({2,3,4}, {2,2,3}).getVolume(), 0); } void TestVoxelArea::test_contains_voxelarea() diff --git a/src/voxel.cpp b/src/voxel.cpp index d9fceb2b9..8f3858a1f 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -137,7 +137,7 @@ void VoxelManipulator::addArea(const VoxelArea &area) new_area.addArea(area); } - s32 new_size = new_area.getVolume(); + u32 new_size = new_area.getVolume(); // Allocate new data and clear flags MapNode *new_data = new MapNode[new_size]; @@ -147,7 +147,7 @@ void VoxelManipulator::addArea(const VoxelArea &area) memset(new_flags, VOXELFLAG_NO_DATA, new_size); // Copy old data - s32 old_x_width = m_area.MaxEdge.X - m_area.MinEdge.X + 1; + u32 old_x_width = m_area.getExtent().X; for(s32 z=m_area.MinEdge.Z; z<=m_area.MaxEdge.Z; z++) for(s32 y=m_area.MinEdge.Y; y<=m_area.MaxEdge.Y; y++) { diff --git a/src/voxel.h b/src/voxel.h index a5ffc7ce6..a35be3e19 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -119,10 +119,10 @@ public: return !m_cache_extent.X || !m_cache_extent.Y || !m_cache_extent.Z; } - s32 getVolume() const + u32 getVolume() const { // FIXME: possible integer overflow here - return m_cache_extent.X * m_cache_extent.Y * m_cache_extent.Z; + return (u32)m_cache_extent.X * (u32)m_cache_extent.Y * (u32)m_cache_extent.Z; } bool contains(const VoxelArea &a) const @@ -148,8 +148,9 @@ public: } bool contains(s32 i) const { - return (i >= 0 && i < getVolume()); + return i >= 0 && static_cast(i) < getVolume(); } + bool operator==(const VoxelArea &other) const { return (MinEdge == other.MinEdge From bcbee873e8e9a44f558aba61bb2a54dfc6bab8ed Mon Sep 17 00:00:00 2001 From: DS Date: Tue, 10 Dec 2024 22:00:43 +0100 Subject: [PATCH 48/68] Use openssl's sha1 and sha256, optionally (#15472) --- .github/workflows/linux.yml | 2 + doc/compiling/README.md | 1 + doc/compiling/linux.md | 1 + src/CMakeLists.txt | 22 ++++++++++ src/benchmark/CMakeLists.txt | 1 + src/benchmark/benchmark_sha.cpp | 23 ++++++++++ src/client/clientmedia.cpp | 9 +--- src/cmake_config.h.in | 1 + src/network/clientpackethandler.cpp | 9 +--- src/script/lua_api/l_util.cpp | 15 ++----- src/server.cpp | 13 +++--- src/unittest/test_map_settings_manager.cpp | 6 +-- src/util/CMakeLists.txt | 3 +- src/util/auth.cpp | 6 +-- src/util/hashing.cpp | 50 ++++++++++++++++++++++ src/util/hashing.h | 21 +++++++++ util/ci/common.sh | 1 + 17 files changed, 141 insertions(+), 43 deletions(-) create mode 100644 src/benchmark/benchmark_sha.cpp create mode 100644 src/util/hashing.cpp create mode 100644 src/util/hashing.h diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fe0c97324..0fb780464 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -49,6 +49,8 @@ jobs: env: CC: gcc-7 CXX: g++-7 + # Test fallback SHA implementations + CMAKE_FLAGS: '-DENABLE_OPENSSL=0' - name: Test run: | diff --git a/doc/compiling/README.md b/doc/compiling/README.md index 55357adf6..16167977b 100644 --- a/doc/compiling/README.md +++ b/doc/compiling/README.md @@ -30,6 +30,7 @@ General options and their default values: ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended) ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores + ENABLE_OPENSSL=ON - Build with OpenSSL; Speeds up SHA1 and SHA2 hashing ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds ENABLE_LTO= - Build with IPO/LTO optimizations (smaller and more efficient than regular build) ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua) diff --git a/doc/compiling/linux.md b/doc/compiling/linux.md index 54f7de2a0..54a44d501 100644 --- a/doc/compiling/linux.md +++ b/doc/compiling/linux.md @@ -18,6 +18,7 @@ | JsonCPP | 1.0.0+ | Bundled JsonCPP is used if not present | | Curl | 7.56.0+ | Optional | | gettext | - | Optional | +| OpenSSL | 3.0+ | Optional (only libcrypto used) | For Debian/Ubuntu users: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d764e186e..692651049 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -249,6 +249,19 @@ if(ENABLE_SPATIAL) endif(SPATIAL_LIBRARY AND SPATIAL_INCLUDE_DIR) endif(ENABLE_SPATIAL) +option(ENABLE_OPENSSL "Use OpenSSL's libcrypto for faster SHA implementations" TRUE) +set(USE_OPENSSL FALSE) + +if(ENABLE_OPENSSL) + find_package(OpenSSL 3.0) + if(OPENSSL_FOUND) + set(USE_OPENSSL TRUE) + message(STATUS "OpenSSL's libcrypto SHA enabled.") + else() + message(STATUS "OpenSSL not found!") + endif() +endif(ENABLE_OPENSSL) + find_package(ZLIB REQUIRED) find_package(Zstd REQUIRED) @@ -593,6 +606,9 @@ add_dependencies(EngineCommon GenerateVersion) target_link_libraries(EngineCommon sha256 ) +if(USE_OPENSSL) + target_link_libraries(EngineCommon OpenSSL::Crypto) +endif() get_target_property( IRRLICHT_INCLUDES IrrlichtMt::IrrlichtMt INTERFACE_INCLUDE_DIRECTORIES) target_include_directories(EngineCommon PRIVATE ${IRRLICHT_INCLUDES}) @@ -726,6 +742,9 @@ if(BUILD_CLIENT) if (USE_SPATIAL) target_link_libraries(${PROJECT_NAME} ${SPATIAL_LIBRARY}) endif() + if (USE_OPENSSL) + target_link_libraries(${PROJECT_NAME} OpenSSL::Crypto) + endif() if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) target_link_libraries(${PROJECT_NAME} Catch2::Catch2) endif() @@ -795,6 +814,9 @@ if(BUILD_SERVER) if (USE_SPATIAL) target_link_libraries(${PROJECT_NAME}server ${SPATIAL_LIBRARY}) endif() + if (USE_OPENSSL) + target_link_libraries(${PROJECT_NAME}server OpenSSL::Crypto) + endif() if(USE_CURL) target_link_libraries( ${PROJECT_NAME}server diff --git a/src/benchmark/CMakeLists.txt b/src/benchmark/CMakeLists.txt index f79fcf1ef..e8150848a 100644 --- a/src/benchmark/CMakeLists.txt +++ b/src/benchmark/CMakeLists.txt @@ -5,6 +5,7 @@ set (BENCHMARK_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapblock.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapmodify.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_sha.cpp PARENT_SCOPE) set (BENCHMARK_CLIENT_SRCS diff --git a/src/benchmark/benchmark_sha.cpp b/src/benchmark/benchmark_sha.cpp new file mode 100644 index 000000000..616606f55 --- /dev/null +++ b/src/benchmark/benchmark_sha.cpp @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 Luanti Contributors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "catch.h" +#include "util/hashing.h" +#include + +TEST_CASE("benchmark_sha") +{ + std::string input; + input.resize(100000); + + BENCHMARK("sha1_input100000", i) { + input[0] = (char)i; + return hashing::sha1(input); + }; + + BENCHMARK("sha256_input100000", i) { + input[0] = (char)i; + return hashing::sha256(input); + }; +} diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 2c6cb69b3..2ee0d747b 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -13,7 +13,7 @@ #include "settings.h" #include "util/hex.h" #include "util/serialize.h" -#include "util/sha1.h" +#include "util/hashing.h" #include "util/string.h" #include @@ -537,12 +537,7 @@ bool IClientMediaDownloader::checkAndLoad( std::string sha1_hex = hex_encode(sha1); // Compute actual checksum of data - std::string data_sha1; - { - SHA1 ctx; - ctx.addBytes(data); - data_sha1 = ctx.getDigest(); - } + std::string data_sha1 = hashing::sha1(data); // Check that received file matches announced checksum if (data_sha1 != sha1) { diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index 5dc6e4b74..2ec91dfd1 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -29,6 +29,7 @@ #cmakedefine01 USE_SYSTEM_GMP #cmakedefine01 USE_SYSTEM_JSONCPP #cmakedefine01 USE_REDIS +#cmakedefine01 USE_OPENSSL #cmakedefine01 HAVE_ENDIAN_H #cmakedefine01 HAVE_STRLCPY #cmakedefine01 HAVE_MALLOC_TRIM diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 4b899e598..1024988bd 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -28,7 +28,7 @@ #include "script/scripting_client.h" #include "util/serialize.h" #include "util/srp.h" -#include "util/sha1.h" +#include "util/hashing.h" #include "tileanimation.h" #include "gettext.h" #include "skyparams.h" @@ -1645,12 +1645,7 @@ void Client::handleCommand_MediaPush(NetworkPacket *pkt) if (!filedata.empty()) { // LEGACY CODEPATH // Compute and check checksum of data - std::string computed_hash; - { - SHA1 ctx; - ctx.addBytes(filedata); - computed_hash = ctx.getDigest(); - } + std::string computed_hash = hashing::sha1(filedata); if (raw_hash != computed_hash) { verbosestream << "Hash of file data mismatches, ignoring." << std::endl; return; diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index cfea974b3..f84835e8b 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -27,8 +27,7 @@ #include "config.h" #include "version.h" #include "util/hex.h" -#include "util/sha1.h" -#include "my_sha256.h" +#include "util/hashing.h" #include "util/png.h" #include "player.h" #include "daynightratio.h" @@ -540,12 +539,7 @@ int ModApiUtil::l_sha1(lua_State *L) bool hex = !lua_isboolean(L, 2) || !readParam(L, 2); // Compute actual checksum of data - std::string data_sha1; - { - SHA1 ctx; - ctx.addBytes(data); - data_sha1 = ctx.getDigest(); - } + std::string data_sha1 = hashing::sha1(data); if (hex) { std::string sha1_hex = hex_encode(data_sha1); @@ -564,10 +558,7 @@ int ModApiUtil::l_sha256(lua_State *L) auto data = readParam(L, 1); bool hex = !lua_isboolean(L, 2) || !readParam(L, 2); - std::string data_sha256; - data_sha256.resize(SHA256_DIGEST_LENGTH); - SHA256(reinterpret_cast(data.data()), data.size(), - reinterpret_cast(data_sha256.data())); + std::string data_sha256 = hashing::sha256(data); if (hex) { lua_pushstring(L, hex_encode(data_sha256).c_str()); diff --git a/src/server.cpp b/src/server.cpp index b21955170..0a5300f5f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -44,7 +44,7 @@ #include "defaultsettings.h" #include "server/mods.h" #include "util/base64.h" -#include "util/sha1.h" +#include "util/hashing.h" #include "util/hex.h" #include "database/database.h" #include "chatmessage.h" @@ -2548,14 +2548,11 @@ bool Server::addMediaFile(const std::string &filename, return false; } - SHA1 sha1; - sha1.addBytes(filedata); - - std::string digest = sha1.getDigest(); - std::string sha1_base64 = base64_encode(digest); - std::string sha1_hex = hex_encode(digest); + std::string sha1 = hashing::sha1(filedata); + std::string sha1_base64 = base64_encode(sha1); + std::string sha1_hex = hex_encode(sha1); if (digest_to) - *digest_to = digest; + *digest_to = sha1; // Put in list m_media[filename] = MediaInfo(filepath, sha1_base64); diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp index 234b40cda..8fb074e17 100644 --- a/src/unittest/test_map_settings_manager.cpp +++ b/src/unittest/test_map_settings_manager.cpp @@ -7,7 +7,7 @@ #include "noise.h" #include "settings.h" #include "mapgen/mapgen_v5.h" -#include "util/sha1.h" +#include "util/hashing.h" #include "map_settings_manager.h" class TestMapSettingsManager : public TestBase { @@ -171,11 +171,9 @@ void TestMapSettingsManager::testMapSettingsManager() 0x78, 0x56, 0x95, 0x2d, 0xdc, 0x6a, 0xf7, 0x61, 0x36, 0x5f }; - SHA1 ctx; std::string metafile_contents; UASSERT(fs::ReadFile(test_mapmeta_path, metafile_contents)); - ctx.addBytes(metafile_contents); - std::string sha1_result = ctx.getDigest(); + std::string sha1_result = hashing::sha1(metafile_contents); int resultdiff = memcmp(sha1_result.data(), expected_contents_hash, 20); UASSERT(!resultdiff); diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 761e51a4a..ec88a33c2 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,10 +1,11 @@ set(util_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/colorize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/colorize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/hashing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ieee_float.cpp ${CMAKE_CURRENT_SOURCE_DIR}/metricsbackend.cpp ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp diff --git a/src/util/auth.cpp b/src/util/auth.cpp index 3c8e5763a..040d15bf9 100644 --- a/src/util/auth.cpp +++ b/src/util/auth.cpp @@ -6,7 +6,7 @@ #include #include "auth.h" #include "base64.h" -#include "sha1.h" +#include "util/hashing.h" #include "srp.h" #include "util/string.h" #include "debug.h" @@ -23,9 +23,7 @@ std::string translate_password(const std::string &name, return ""; std::string slt = name + password; - SHA1 sha1; - sha1.addBytes(slt); - std::string digest = sha1.getDigest(); + std::string digest = hashing::sha1(slt); std::string pwd = base64_encode(digest); return pwd; } diff --git a/src/util/hashing.cpp b/src/util/hashing.cpp new file mode 100644 index 000000000..452cd6818 --- /dev/null +++ b/src/util/hashing.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2024 Luanti Contributors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "hashing.h" + +#include "debug.h" +#include "config.h" +#if USE_OPENSSL +#include +#include +#else +#include "util/sha1.h" +#include "my_sha256.h" +#endif + +namespace hashing +{ + +std::string sha1(std::string_view data) +{ +#if USE_OPENSSL + std::string digest(SHA1_DIGEST_SIZE, '\000'); + auto src = reinterpret_cast(data.data()); + auto dst = reinterpret_cast(digest.data()); + SHA1(src, data.size(), dst); + return digest; +#else + SHA1 sha1; + sha1.addBytes(data); + return sha1.getDigest(); +#endif +} + +std::string sha256(std::string_view data) +{ + std::string digest(SHA256_DIGEST_SIZE, '\000'); + auto src = reinterpret_cast(data.data()); + auto dst = reinterpret_cast(digest.data()); +#if USE_OPENSSL + // can't call SHA256(), because it's defined by our sha256.c fallback + auto success = EVP_Digest(src, data.size(), dst, nullptr, EVP_sha256(), nullptr) == 1; + FATAL_ERROR_IF(!success, "sha256 failed"); +#else + SHA256(src, data.size(), dst); +#endif + return digest; +} + +} diff --git a/src/util/hashing.h b/src/util/hashing.h new file mode 100644 index 000000000..3e9a0fee7 --- /dev/null +++ b/src/util/hashing.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 Luanti Contributors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include + +namespace hashing +{ + +// Size of raw digest in bytes +constexpr size_t SHA1_DIGEST_SIZE = 20; +constexpr size_t SHA256_DIGEST_SIZE = 32; + +// Returns the digest of data +std::string sha1(std::string_view data); +std::string sha256(std::string_view data); + +} diff --git a/util/ci/common.sh b/util/ci/common.sh index 201b182f2..4d4fe1195 100644 --- a/util/ci/common.sh +++ b/util/ci/common.sh @@ -7,6 +7,7 @@ install_linux_deps() { libpng-dev libjpeg-dev libgl1-mesa-dev libsdl2-dev libfreetype-dev libsqlite3-dev libhiredis-dev libogg-dev libgmp-dev libvorbis-dev libopenal-dev libpq-dev libleveldb-dev libcurl4-openssl-dev libzstd-dev + libssl-dev ) sudo apt-get update From 9f71e741580e5340d400561c146b39d26dfe06a4 Mon Sep 17 00:00:00 2001 From: Timur1324 <46261539+Timur13240@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:31:12 +0000 Subject: [PATCH 49/68] Get server list over https (#15538) --- builtin/settingtypes.txt | 4 ++-- src/defaultsettings.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index b10f776ac..e4a7c120a 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -772,7 +772,7 @@ contentdb_max_concurrent_downloads (ContentDB Max Concurrent Downloads) int 3 1 enable_local_map_saving (Saving map received from server) bool false # URL to the server list displayed in the Multiplayer Tab. -serverlist_url (Serverlist URL) string servers.luanti.org +serverlist_url (Serverlist URL) string https://servers.luanti.org # If enabled, account registration is separate from login in the UI. # If disabled, new accounts will be registered automatically when logging in. @@ -810,7 +810,7 @@ server_announce (Announce server) bool false server_announce_send_players (Send player names to the server list) bool true # Announce to this serverlist. -serverlist_url (Serverlist URL) string servers.luanti.org +serverlist_url (Serverlist URL) string https://servers.luanti.org # Message of the day displayed to players connecting. motd (Message of the day) string diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 600058b5c..674442469 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -78,7 +78,7 @@ void set_default_settings() settings->setDefault("language", ""); settings->setDefault("name", ""); settings->setDefault("bind_address", ""); - settings->setDefault("serverlist_url", "servers.luanti.org"); + settings->setDefault("serverlist_url", "https://servers.luanti.org"); // Client settings->setDefault("address", ""); From d123bc095134f1cb00ccb80f820be3a4c4fddf88 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Thu, 12 Dec 2024 15:31:48 +0100 Subject: [PATCH 50/68] Main menu: show favorite removal button for offline servers (#15530) This fixes a regression from 6c324cb871d. --- builtin/mainmenu/tab_online.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index 112c17457..b8257b90a 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -93,6 +93,11 @@ local function find_selected_server() return server end end + for _, server in ipairs(serverlistmgr.get_favorites()) do + if server.address == address and server.port == port then + return server + end + end end local function get_formspec(tabview, name, tabdata) From 1e59b9a75651130aa7061c2c67dd588d74bb6cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:33:08 +0100 Subject: [PATCH 51/68] Refactor `SkinnedMesh` (#15522) --- irr/include/SkinnedMesh.h | 308 +++++++----- irr/src/CAnimatedMeshSceneNode.cpp | 13 +- irr/src/CAnimatedMeshSceneNode.h | 2 +- irr/src/CB3DMeshFileLoader.cpp | 99 +--- irr/src/CB3DMeshFileLoader.h | 2 +- irr/src/CGLTFMeshFileLoader.cpp | 24 +- irr/src/CGLTFMeshFileLoader.h | 4 +- irr/src/CXMeshFileLoader.cpp | 51 +- irr/src/CXMeshFileLoader.h | 2 +- irr/src/SkinnedMesh.cpp | 778 ++++++----------------------- 10 files changed, 387 insertions(+), 896 deletions(-) diff --git a/irr/include/SkinnedMesh.h b/irr/include/SkinnedMesh.h index 54ce8ed2c..4cf5905c9 100644 --- a/irr/include/SkinnedMesh.h +++ b/irr/include/SkinnedMesh.h @@ -9,6 +9,7 @@ #include "SMeshBuffer.h" #include "SSkinMeshBuffer.h" #include "quaternion.h" +#include "vector3d.h" #include #include @@ -18,18 +19,6 @@ namespace irr namespace scene { -enum E_INTERPOLATION_MODE -{ - // constant does use the current key-values without interpolation - EIM_CONSTANT = 0, - - // linear interpolation - EIM_LINEAR, - - //! count of all available interpolation modes - EIM_COUNT -}; - class IAnimatedMeshSceneNode; class IBoneSceneNode; class ISceneManager; @@ -38,7 +27,14 @@ class SkinnedMesh : public IAnimatedMesh { public: //! constructor - SkinnedMesh(); + SkinnedMesh() : + EndFrame(0.f), FramesPerSecond(25.f), + LastAnimatedFrame(-1), SkinnedLastFrame(false), + HasAnimation(false), PreparedForSkinning(false), + AnimateNormals(true), HardwareSkinning(false) + { + SkinningBuffers = &LocalBuffers; + } //! destructor virtual ~SkinnedMesh(); @@ -58,9 +54,8 @@ public: //! returns the animated mesh for the given frame IMesh *getMesh(f32) override; - //! Animates this mesh's joints based on frame input - //! blend: {0-old position, 1-New position} - void animateMesh(f32 frame, f32 blend); + //! Animates joints based on frame input + void animateMesh(f32 frame); //! Performs a software skin on this mesh based of joint positions void skinMesh(); @@ -82,10 +77,14 @@ public: void setTextureSlot(u32 meshbufNr, u32 textureSlot); //! returns an axis aligned bounding box - const core::aabbox3d &getBoundingBox() const override; + const core::aabbox3d &getBoundingBox() const override { + return BoundingBox; + } //! set user axis aligned bounding box - void setBoundingBox(const core::aabbox3df &box) override; + void setBoundingBox(const core::aabbox3df &box) override { + BoundingBox = box; + } //! set the hardware mapping hint, for driver void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) override; @@ -94,7 +93,9 @@ public: void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) override; //! Returns the type of the animated mesh. - E_ANIMATED_MESH_TYPE getMeshType() const override; + E_ANIMATED_MESH_TYPE getMeshType() const override { + return EAMT_SKINNED; + } //! Gets joint count. u32 getJointCount() const; @@ -112,18 +113,19 @@ public: //! Update Normals when Animating /** \param on If false don't animate, which is faster. Else update normals, which allows for proper lighting of - animated meshes. */ - void updateNormalsWhenAnimating(bool on); - - //! Sets Interpolation Mode - void setInterpolationMode(E_INTERPOLATION_MODE mode); + animated meshes (default). */ + void updateNormalsWhenAnimating(bool on) { + AnimateNormals = on; + } //! converts the vertex type of all meshbuffers to tangents. /** E.g. used for bump mapping. */ void convertMeshToTangents(); //! Does the mesh have no animation - bool isStatic() const; + bool isStatic() const { + return !HasAnimation; + } //! Allows to enable hardware skinning. /* This feature is not implemented in Irrlicht yet */ @@ -138,16 +140,13 @@ public: virtual void updateBoundingBox(); //! Recovers the joints from the mesh - void recoverJointsFromMesh(core::array &jointChildSceneNodes); + void recoverJointsFromMesh(std::vector &jointChildSceneNodes); //! Transfers the joint data to the mesh - void transferJointsToMesh(const core::array &jointChildSceneNodes); - - //! Transfers the joint hints to the mesh - void transferOnlyJointsHintsToMesh(const core::array &jointChildSceneNodes); + void transferJointsToMesh(const std::vector &jointChildSceneNodes); //! Creates an array of joints from this mesh as children of node - void addJoints(core::array &jointChildSceneNodes, + void addJoints(std::vector &jointChildSceneNodes, IAnimatedMeshSceneNode *node, ISceneManager *smgr); @@ -171,35 +170,133 @@ public: core::vector3df StaticNormal; }; - //! Animation keyframe which describes a new position - struct SPositionKey - { - f32 frame; - core::vector3df position; + template + struct Channel { + struct Frame { + f32 time; + T value; + }; + std::vector frames; + bool interpolate = true; + + bool empty() const { + return frames.empty(); + } + + f32 getEndFrame() const { + return frames.empty() ? 0 : frames.back().time; + } + + void pushBack(f32 time, const T &value) { + frames.push_back({time, value}); + } + + void append(const Channel &other) { + frames.insert(frames.end(), other.frames.begin(), other.frames.end()); + } + + void cleanup() { + if (frames.empty()) + return; + + std::vector ordered; + ordered.push_back(frames.front()); + // Drop out-of-order frames + for (auto it = frames.begin() + 1; it != frames.end(); ++it) { + if (it->time > ordered.back().time) { + ordered.push_back(*it); + } + } + frames.clear(); + // Drop redundant middle keys + frames.push_back(ordered.front()); + for (u32 i = 1; i < ordered.size() - 1; ++i) { + if (ordered[i - 1].value != ordered[i].value + || ordered[i + 1].value != ordered[i].value) { + frames.push_back(ordered[i]); + } + } + if (ordered.size() > 1) + frames.push_back(ordered.back()); + frames.shrink_to_fit(); + } + + static core::quaternion interpolateValue(core::quaternion from, core::quaternion to, f32 time) { + core::quaternion result; + result.slerp(from, to, time, 0.001f); + return result; + } + + static core::vector3df interpolateValue(core::vector3df from, core::vector3df to, f32 time) { + // Note: `from` and `to` are swapped here compared to quaternion slerp + return to.getInterpolated(from, time); + } + + std::optional get(f32 time) const { + if (frames.empty()) + return std::nullopt; + + const auto next = std::lower_bound(frames.begin(), frames.end(), time, [](const auto& frame, f32 time) { + return frame.time < time; + }); + if (next == frames.begin()) + return next->value; + if (next == frames.end()) + return frames.back().value; + + const auto prev = next - 1; + if (!interpolate) + return prev->value; + + return interpolateValue(prev->value, next->value, (time - prev->time) / (next->time - prev->time)); + } }; - //! Animation keyframe which describes a new scale - struct SScaleKey - { - f32 frame; - core::vector3df scale; - }; + struct Keys { + Channel position; + Channel rotation; + Channel scale; - //! Animation keyframe which describes a new rotation - struct SRotationKey - { - f32 frame; - core::quaternion rotation; + bool empty() const { + return position.empty() && rotation.empty() && scale.empty(); + } + + void append(const Keys &other) { + position.append(other.position); + rotation.append(other.rotation); + scale.append(other.scale); + } + + f32 getEndFrame() const { + return std::max({ + position.getEndFrame(), + rotation.getEndFrame(), + scale.getEndFrame() + }); + } + + void updateTransform(f32 frame, + core::vector3df &t, core::quaternion &r, core::vector3df &s) const + { + if (auto pos = position.get(frame)) + t = *pos; + if (auto rot = rotation.get(frame)) + r = *rot; + if (auto scl = scale.get(frame)) + s = *scl; + } + + void cleanup() { + position.cleanup(); + rotation.cleanup(); + scale.cleanup(); + } }; //! Joints struct SJoint { - SJoint() : - UseAnimationFrom(0), GlobalSkinningSpace(false), - positionHint(-1), scaleHint(-1), rotationHint(-1) - { - } + SJoint() : GlobalSkinningSpace(false) {} //! The name of this joint std::optional Name; @@ -208,22 +305,16 @@ public: core::matrix4 LocalMatrix; //! List of child joints - core::array Children; + std::vector Children; //! List of attached meshes - core::array AttachedMeshes; + std::vector AttachedMeshes; - //! Animation keys causing translation change - core::array PositionKeys; - - //! Animation keys causing scale change - core::array ScaleKeys; - - //! Animation keys causing rotation change - core::array RotationKeys; + // Animation keyframes for translation, rotation, scale + Keys keys; //! Skin weights - core::array Weights; + std::vector Weights; //! Unnecessary for loaders, will be overwritten on finalize core::matrix4 GlobalMatrix; // loaders may still choose to set this (temporarily) to calculate absolute vertex data. @@ -241,49 +332,14 @@ public: //! Internal members used by SkinnedMesh friend class SkinnedMesh; - SJoint *UseAnimationFrom; bool GlobalSkinningSpace; - - s32 positionHint; - s32 scaleHint; - s32 rotationHint; }; - // Interface for the mesh loaders (finalize should lock these functions, and they should have some prefix like loader_ - // these functions will use the needed arrays, set values, etc to help the loaders + const std::vector &getAllJoints() const { + return AllJoints; + } - //! exposed for loaders to add mesh buffers - core::array &getMeshBuffers(); - - //! alternative method for adding joints - core::array &getAllJoints(); - - //! alternative method for reading joints - const core::array &getAllJoints() const; - - //! loaders should call this after populating the mesh - void finalize(); - - //! Adds a new meshbuffer to the mesh, access it as last one - SSkinMeshBuffer *addMeshBuffer(); - - //! Adds a new meshbuffer to the mesh, access it as last one - void addMeshBuffer(SSkinMeshBuffer *meshbuf); - - //! Adds a new joint to the mesh, access it as last one - SJoint *addJoint(SJoint *parent = 0); - - //! Adds a new position key to the mesh, access it as last one - SPositionKey *addPositionKey(SJoint *joint); - //! Adds a new rotation key to the mesh, access it as last one - SRotationKey *addRotationKey(SJoint *joint); - //! Adds a new scale key to the mesh, access it as last one - SScaleKey *addScaleKey(SJoint *joint); - - //! Adds a new weight to the mesh, access it as last one - SWeight *addWeight(SJoint *joint); - -private: +protected: void checkForAnimation(); void normalizeWeights(); @@ -292,11 +348,6 @@ private: void buildAllGlobalAnimatedMatrices(SJoint *Joint = 0, SJoint *ParentJoint = 0); - void getFrameData(f32 frame, SJoint *Node, - core::vector3df &position, s32 &positionHint, - core::vector3df &scale, s32 &scaleHint, - core::quaternion &rotation, s32 &rotationHint); - void calculateGlobalMatrices(SJoint *Joint, SJoint *ParentJoint); void skinJoint(SJoint *Joint, SJoint *ParentJoint); @@ -306,18 +357,18 @@ private: const core::vector3df &vt1, const core::vector3df &vt2, const core::vector3df &vt3, const core::vector2df &tc1, const core::vector2df &tc2, const core::vector2df &tc3); - core::array *SkinningBuffers; // Meshbuffer to skin, default is to skin localBuffers + std::vector *SkinningBuffers; // Meshbuffer to skin, default is to skin localBuffers - core::array LocalBuffers; + std::vector LocalBuffers; //! Mapping from meshbuffer number to bindable texture slot std::vector TextureSlots; - core::array AllJoints; - core::array RootJoints; + std::vector AllJoints; + std::vector RootJoints; // bool can't be used here because std::vector // doesn't allow taking a reference to individual elements. - core::array> Vertices_Moved; + std::vector> Vertices_Moved; core::aabbox3d BoundingBox; @@ -327,13 +378,42 @@ private: f32 LastAnimatedFrame; bool SkinnedLastFrame; - E_INTERPOLATION_MODE InterpolationMode : 8; - bool HasAnimation; bool PreparedForSkinning; bool AnimateNormals; bool HardwareSkinning; }; +// Interface for mesh loaders +class SkinnedMeshBuilder : public SkinnedMesh { +public: + SkinnedMeshBuilder() : SkinnedMesh() {} + + //! loaders should call this after populating the mesh + // returns *this, so do not try to drop the mesh builder instance + SkinnedMesh *finalize(); + + //! alternative method for adding joints + std::vector &getAllJoints() { + return AllJoints; + } + + //! Adds a new meshbuffer to the mesh, access it as last one + SSkinMeshBuffer *addMeshBuffer(); + + //! Adds a new meshbuffer to the mesh, access it as last one + void addMeshBuffer(SSkinMeshBuffer *meshbuf); + + //! Adds a new joint to the mesh, access it as last one + SJoint *addJoint(SJoint *parent = nullptr); + + void addPositionKey(SJoint *joint, f32 frame, core::vector3df pos); + void addRotationKey(SJoint *joint, f32 frame, core::quaternion rotation); + void addScaleKey(SJoint *joint, f32 frame, core::vector3df scale); + + //! Adds a new weight to the mesh, access it as last one + SWeight *addWeight(SJoint *joint); +}; + } // end namespace scene } // end namespace irr diff --git a/irr/src/CAnimatedMeshSceneNode.cpp b/irr/src/CAnimatedMeshSceneNode.cpp index 5250fe889..d249e37c5 100644 --- a/irr/src/CAnimatedMeshSceneNode.cpp +++ b/irr/src/CAnimatedMeshSceneNode.cpp @@ -170,7 +170,7 @@ IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame() if (JointMode == EJUOR_CONTROL) // write to mesh skinnedMesh->transferJointsToMesh(JointChildSceneNodes); else - skinnedMesh->animateMesh(getFrameNr(), 1.0f); + skinnedMesh->animateMesh(getFrameNr()); // Update the skinned mesh for the current joint transforms. skinnedMesh->skinMesh(); @@ -299,12 +299,10 @@ void CAnimatedMeshSceneNode::render() if (Mesh->getMeshType() == EAMT_SKINNED) { // draw skeleton - for (u32 g = 0; g < ((SkinnedMesh *)Mesh)->getAllJoints().size(); ++g) { - auto *joint = ((SkinnedMesh *)Mesh)->getAllJoints()[g]; - - for (u32 n = 0; n < joint->Children.size(); ++n) { + for (auto *joint : ((SkinnedMesh *)Mesh)->getAllJoints()) { + for (const auto *childJoint : joint->Children) { driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(), - joint->Children[n]->GlobalAnimatedMatrix.getTranslation(), + childJoint->GlobalAnimatedMatrix.getTranslation(), video::SColor(255, 51, 66, 255)); } } @@ -598,8 +596,7 @@ void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions) SkinnedMesh *skinnedMesh = static_cast(Mesh); - skinnedMesh->transferOnlyJointsHintsToMesh(JointChildSceneNodes); - skinnedMesh->animateMesh(frame, 1.0f); + skinnedMesh->animateMesh(frame); skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes); //----------------------------------------- diff --git a/irr/src/CAnimatedMeshSceneNode.h b/irr/src/CAnimatedMeshSceneNode.h index e45edca86..047a4030f 100644 --- a/irr/src/CAnimatedMeshSceneNode.h +++ b/irr/src/CAnimatedMeshSceneNode.h @@ -169,7 +169,7 @@ private: IAnimationEndCallBack *LoopCallBack; s32 PassCount; - core::array JointChildSceneNodes; + std::vector JointChildSceneNodes; core::array PretransitingSave; }; diff --git a/irr/src/CB3DMeshFileLoader.cpp b/irr/src/CB3DMeshFileLoader.cpp index 6923cde04..3bcc4b85f 100644 --- a/irr/src/CB3DMeshFileLoader.cpp +++ b/irr/src/CB3DMeshFileLoader.cpp @@ -10,6 +10,7 @@ #include "IVideoDriver.h" #include "IFileSystem.h" +#include "SkinnedMesh.h" #include "coreutil.h" #include "os.h" @@ -51,12 +52,12 @@ IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file) return 0; B3DFile = file; - AnimatedMesh = new scene::SkinnedMesh(); + AnimatedMesh = new scene::SkinnedMeshBuilder(); ShowWarning = true; // If true a warning is issued if too many textures are used VerticesStart = 0; if (load()) { - AnimatedMesh->finalize(); + return AnimatedMesh->finalize(); } else { AnimatedMesh->drop(); AnimatedMesh = 0; @@ -254,7 +255,7 @@ bool CB3DMeshFileLoader::readChunkMESH(SkinnedMesh::SJoint *inJoint) meshBuffer->Material = Materials[brushID].Material; } - if (readChunkTRIS(meshBuffer, AnimatedMesh->getMeshBuffers().size() - 1, VerticesStart) == false) + if (readChunkTRIS(meshBuffer, AnimatedMesh->getMeshBufferCount() - 1, VerticesStart) == false) return false; if (!NormalsInFile) { @@ -569,7 +570,7 @@ bool CB3DMeshFileLoader::readChunkKEYS(SkinnedMesh::SJoint *inJoint) { #ifdef _B3D_READER_DEBUG // Only print first, that's just too much output otherwise - if (!inJoint || (inJoint->PositionKeys.empty() && inJoint->ScaleKeys.empty() && inJoint->RotationKeys.empty())) { + if (!inJoint || inJoint->keys.empty()) { core::stringc logStr; for (u32 i = 1; i < B3dStack.size(); ++i) logStr += "-"; @@ -584,13 +585,6 @@ bool CB3DMeshFileLoader::readChunkKEYS(SkinnedMesh::SJoint *inJoint) flags = os::Byteswap::byteswap(flags); #endif - SkinnedMesh::SPositionKey *oldPosKey = 0; - core::vector3df oldPos[2]; - SkinnedMesh::SScaleKey *oldScaleKey = 0; - core::vector3df oldScale[2]; - SkinnedMesh::SRotationKey *oldRotKey = 0; - core::quaternion oldRot[2]; - bool isFirst[3] = {true, true, true}; while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats { s32 frame; @@ -600,91 +594,24 @@ bool CB3DMeshFileLoader::readChunkKEYS(SkinnedMesh::SJoint *inJoint) frame = os::Byteswap::byteswap(frame); #endif + if (frame < 1) { + os::Printer::log("Illegal frame number found", B3DFile->getFileName(), ELL_ERROR); + frame = 1; + } + // Add key frames, frames in Irrlicht are zero-based f32 data[4]; if (flags & 1) { readFloats(data, 3); - if ((oldPosKey != 0) && (oldPos[0] == oldPos[1])) { - const core::vector3df pos(data[0], data[1], data[2]); - if (oldPos[1] == pos) - oldPosKey->frame = (f32)frame - 1; - else { - oldPos[0] = oldPos[1]; - oldPosKey = AnimatedMesh->addPositionKey(inJoint); - oldPosKey->frame = (f32)frame - 1; - oldPos[1].set(oldPosKey->position.set(pos)); - } - } else if (oldPosKey == 0 && isFirst[0]) { - oldPosKey = AnimatedMesh->addPositionKey(inJoint); - oldPosKey->frame = (f32)frame - 1; - oldPos[0].set(oldPosKey->position.set(data[0], data[1], data[2])); - oldPosKey = 0; - isFirst[0] = false; - } else { - if (oldPosKey != 0) - oldPos[0] = oldPos[1]; - oldPosKey = AnimatedMesh->addPositionKey(inJoint); - oldPosKey->frame = (f32)frame - 1; - oldPos[1].set(oldPosKey->position.set(data[0], data[1], data[2])); - } + AnimatedMesh->addPositionKey(inJoint, frame - 1, {data[0], data[1], data[2]}); } if (flags & 2) { readFloats(data, 3); - if ((oldScaleKey != 0) && (oldScale[0] == oldScale[1])) { - const core::vector3df scale(data[0], data[1], data[2]); - if (oldScale[1] == scale) - oldScaleKey->frame = (f32)frame - 1; - else { - oldScale[0] = oldScale[1]; - oldScaleKey = AnimatedMesh->addScaleKey(inJoint); - oldScaleKey->frame = (f32)frame - 1; - oldScale[1].set(oldScaleKey->scale.set(scale)); - } - } else if (oldScaleKey == 0 && isFirst[1]) { - oldScaleKey = AnimatedMesh->addScaleKey(inJoint); - oldScaleKey->frame = (f32)frame - 1; - oldScale[0].set(oldScaleKey->scale.set(data[0], data[1], data[2])); - oldScaleKey = 0; - isFirst[1] = false; - } else { - if (oldScaleKey != 0) - oldScale[0] = oldScale[1]; - oldScaleKey = AnimatedMesh->addScaleKey(inJoint); - oldScaleKey->frame = (f32)frame - 1; - oldScale[1].set(oldScaleKey->scale.set(data[0], data[1], data[2])); - } + AnimatedMesh->addScaleKey(inJoint, frame - 1, {data[0], data[1], data[2]}); } if (flags & 4) { readFloats(data, 4); - if ((oldRotKey != 0) && (oldRot[0] == oldRot[1])) { - // meant to be in this order since b3d stores W first - const core::quaternion rot(data[1], data[2], data[3], data[0]); - if (oldRot[1] == rot) - oldRotKey->frame = (f32)frame - 1; - else { - oldRot[0] = oldRot[1]; - oldRotKey = AnimatedMesh->addRotationKey(inJoint); - oldRotKey->frame = (f32)frame - 1; - oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0])); - oldRot[1].normalize(); - } - } else if (oldRotKey == 0 && isFirst[2]) { - oldRotKey = AnimatedMesh->addRotationKey(inJoint); - oldRotKey->frame = (f32)frame - 1; - // meant to be in this order since b3d stores W first - oldRot[0].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0])); - oldRot[0].normalize(); - oldRotKey = 0; - isFirst[2] = false; - } else { - if (oldRotKey != 0) - oldRot[0] = oldRot[1]; - oldRotKey = AnimatedMesh->addRotationKey(inJoint); - oldRotKey->frame = (f32)frame - 1; - // meant to be in this order since b3d stores W first - oldRot[1].set(oldRotKey->rotation.set(data[1], data[2], data[3], data[0])); - oldRot[1].normalize(); - } + AnimatedMesh->addRotationKey(inJoint, frame - 1, core::quaternion(data[1], data[2], data[3], data[0])); } } diff --git a/irr/src/CB3DMeshFileLoader.h b/irr/src/CB3DMeshFileLoader.h index 711f2737f..55978d743 100644 --- a/irr/src/CB3DMeshFileLoader.h +++ b/irr/src/CB3DMeshFileLoader.h @@ -63,7 +63,7 @@ private: core::array BaseVertices; - SkinnedMesh *AnimatedMesh; + SkinnedMeshBuilder *AnimatedMesh; io::IReadFile *B3DFile; // B3Ds have Vertex ID's local within the mesh I don't want this diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp index 139b4f670..79c68355b 100644 --- a/irr/src/CGLTFMeshFileLoader.cpp +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -346,14 +346,14 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file) const char *filename = file->getFileName().c_str(); try { tiniergltf::GlTF model = parseGLTF(file); - irr_ptr mesh(new SkinnedMesh()); + irr_ptr mesh(new SkinnedMeshBuilder()); MeshExtractor extractor(std::move(model), mesh.get()); try { extractor.load(); for (const auto &warning : extractor.getWarnings()) { os::Printer::log(filename, warning.c_str(), ELL_WARNING); } - return mesh.release(); + return mesh.release()->finalize(); } catch (const std::runtime_error &e) { os::Printer::log("error converting gltf to irrlicht mesh", e.what(), ELL_ERROR); } @@ -691,27 +691,27 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx) case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: { const auto outputAccessor = Accessor::make(m_gltf_model, sampler.output); for (std::size_t i = 0; i < n_frames; ++i) { - auto *key = m_irr_model->addPositionKey(joint); - key->frame = inputAccessor.get(i); - key->position = convertHandedness(outputAccessor.get(i)); + f32 frame = inputAccessor.get(i); + core::vector3df position = outputAccessor.get(i); + m_irr_model->addPositionKey(joint, frame, convertHandedness(position)); } break; } case tiniergltf::AnimationChannelTarget::Path::ROTATION: { const auto outputAccessor = Accessor::make(m_gltf_model, sampler.output); for (std::size_t i = 0; i < n_frames; ++i) { - auto *key = m_irr_model->addRotationKey(joint); - key->frame = inputAccessor.get(i); - key->rotation = convertHandedness(outputAccessor.get(i)); + f32 frame = inputAccessor.get(i); + core::quaternion rotation = outputAccessor.get(i); + m_irr_model->addRotationKey(joint, frame, convertHandedness(rotation)); } break; } case tiniergltf::AnimationChannelTarget::Path::SCALE: { const auto outputAccessor = Accessor::make(m_gltf_model, sampler.output); for (std::size_t i = 0; i < n_frames; ++i) { - auto *key = m_irr_model->addScaleKey(joint); - key->frame = inputAccessor.get(i); - key->scale = outputAccessor.get(i); + f32 frame = inputAccessor.get(i); + core::vector3df scale = outputAccessor.get(i); + m_irr_model->addScaleKey(joint, frame, scale); } break; } @@ -756,8 +756,6 @@ void SelfType::MeshExtractor::load() } catch (const std::bad_optional_access &e) { throw std::runtime_error(e.what()); } - - m_irr_model->finalize(); } /** diff --git a/irr/src/CGLTFMeshFileLoader.h b/irr/src/CGLTFMeshFileLoader.h index 1671d2903..a4eac8baa 100644 --- a/irr/src/CGLTFMeshFileLoader.h +++ b/irr/src/CGLTFMeshFileLoader.h @@ -100,7 +100,7 @@ private: { public: MeshExtractor(tiniergltf::GlTF &&model, - SkinnedMesh *mesh) noexcept + SkinnedMeshBuilder *mesh) noexcept : m_gltf_model(std::move(model)), m_irr_model(mesh) {}; /* Gets indices for the given mesh/primitive. @@ -124,7 +124,7 @@ private: private: const tiniergltf::GlTF m_gltf_model; - SkinnedMesh *m_irr_model; + SkinnedMeshBuilder *m_irr_model; std::vector> m_mesh_loaders; std::vector m_loaded_nodes; diff --git a/irr/src/CXMeshFileLoader.cpp b/irr/src/CXMeshFileLoader.cpp index 19d4e5b01..cb02298eb 100644 --- a/irr/src/CXMeshFileLoader.cpp +++ b/irr/src/CXMeshFileLoader.cpp @@ -3,6 +3,7 @@ // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CXMeshFileLoader.h" +#include "SkinnedMesh.h" #include "os.h" #include "fast_atof.h" @@ -57,7 +58,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file) u32 time = os::Timer::getRealTime(); #endif - AnimatedMesh = new SkinnedMesh(); + AnimatedMesh = new SkinnedMeshBuilder(); if (load(file)) { AnimatedMesh->finalize(); @@ -92,7 +93,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file) delete Meshes[i]; Meshes.clear(); - return AnimatedMesh; + return AnimatedMesh->finalize(); } bool CXMeshFileLoader::load(io::IReadFile *file) @@ -124,7 +125,7 @@ bool CXMeshFileLoader::load(io::IReadFile *file) if (!mesh->HasSkinning) { // Set up rigid animation if (mesh->AttachedJointID != -1) { - AnimatedMesh->getAllJoints()[mesh->AttachedJointID]->AttachedMeshes.push_back(AnimatedMesh->getMeshBuffers().size() - 1); + AnimatedMesh->getAllJoints()[mesh->AttachedJointID]->AttachedMeshes.push_back(AnimatedMesh->getMeshBufferCount() - 1); } } } @@ -965,7 +966,7 @@ bool CXMeshFileLoader::parseDataObjectSkinWeights(SXMesh &mesh) u32 i; const u32 jointStart = joint->Weights.size(); - joint->Weights.reallocate(jointStart + nWeights); + joint->Weights.resize(jointStart + nWeights); mesh.WeightJoint.reallocate(mesh.WeightJoint.size() + nWeights); mesh.WeightNum.reallocate(mesh.WeightNum.size() + nWeights); @@ -1411,20 +1412,7 @@ bool CXMeshFileLoader::parseDataObjectAnimation() joint->Name = FrameName.c_str(); } - joint->PositionKeys.reallocate(joint->PositionKeys.size() + animationDump.PositionKeys.size()); - for (u32 n = 0; n < animationDump.PositionKeys.size(); ++n) { - joint->PositionKeys.push_back(animationDump.PositionKeys[n]); - } - - joint->ScaleKeys.reallocate(joint->ScaleKeys.size() + animationDump.ScaleKeys.size()); - for (u32 n = 0; n < animationDump.ScaleKeys.size(); ++n) { - joint->ScaleKeys.push_back(animationDump.ScaleKeys[n]); - } - - joint->RotationKeys.reallocate(joint->RotationKeys.size() + animationDump.RotationKeys.size()); - for (u32 n = 0; n < animationDump.RotationKeys.size(); ++n) { - joint->RotationKeys.push_back(animationDump.RotationKeys[n]); - } + joint->keys.append(animationDump.keys); } else os::Printer::log("joint name was never given", ELL_WARNING); @@ -1488,10 +1476,9 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint) os::Printer::log("Line", core::stringc(Line).c_str(), ELL_WARNING); } - SkinnedMesh::SRotationKey *key = AnimatedMesh->addRotationKey(joint); - key->frame = time; - key->rotation.set(X, Y, Z, W); - key->rotation.normalize(); + core::quaternion rotation(X, Y, Z, W); + rotation.normalize(); + AnimatedMesh->addRotationKey(joint, time, rotation); } break; case 1: // scale case 2: // position @@ -1514,13 +1501,9 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint) } if (keyType == 2) { - SkinnedMesh::SPositionKey *key = AnimatedMesh->addPositionKey(joint); - key->frame = time; - key->position = vector; + AnimatedMesh->addPositionKey(joint, time, vector); } else { - SkinnedMesh::SScaleKey *key = AnimatedMesh->addScaleKey(joint); - key->frame = time; - key->scale = vector; + AnimatedMesh->addScaleKey(joint, time, vector); } } break; case 3: @@ -1547,16 +1530,8 @@ bool CXMeshFileLoader::parseDataObjectAnimationKey(SkinnedMesh::SJoint *joint) // core::vector3df rotation = mat.getRotationDegrees(); - SkinnedMesh::SRotationKey *keyR = AnimatedMesh->addRotationKey(joint); - keyR->frame = time; - - // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched from mat to mat.getTransposed() for downward compatibility. - // Not tested so far if this was correct or wrong before quaternion fix! - keyR->rotation = core::quaternion(mat.getTransposed()); - - SkinnedMesh::SPositionKey *keyP = AnimatedMesh->addPositionKey(joint); - keyP->frame = time; - keyP->position = mat.getTranslation(); + AnimatedMesh->addRotationKey(joint, time, core::quaternion(mat.getTransposed())); + AnimatedMesh->addPositionKey(joint, time, mat.getTranslation()); /* core::vector3df scale=mat.getScale(); diff --git a/irr/src/CXMeshFileLoader.h b/irr/src/CXMeshFileLoader.h index 0902651a9..36610d3b3 100644 --- a/irr/src/CXMeshFileLoader.h +++ b/irr/src/CXMeshFileLoader.h @@ -155,7 +155,7 @@ private: bool readRGB(video::SColor &color); bool readRGBA(video::SColor &color); - SkinnedMesh *AnimatedMesh; + SkinnedMeshBuilder *AnimatedMesh; c8 *Buffer; const c8 *P; diff --git a/irr/src/SkinnedMesh.cpp b/irr/src/SkinnedMesh.cpp index 76bce0b87..64b25205e 100644 --- a/irr/src/SkinnedMesh.cpp +++ b/irr/src/SkinnedMesh.cpp @@ -8,106 +8,22 @@ #include "IAnimatedMeshSceneNode.h" #include "SSkinMeshBuffer.h" #include "os.h" - -namespace -{ -// Frames must always be increasing, so we remove objects where this isn't the case -// return number of kicked keys -template // T = objects containing a "frame" variable -irr::u32 dropBadKeys(irr::core::array &array) -{ - if (array.size() < 2) - return 0; - - irr::u32 n = 1; // new index - for (irr::u32 j = 1; j < array.size(); ++j) { - if (array[j].frame < array[n - 1].frame) - continue; // bad frame, unneeded and may cause problems - if (n != j) - array[n] = array[j]; - ++n; - } - irr::u32 d = array.size() - n; // remove already copied keys - if (d > 0) { - array.erase(n, d); - } - return d; -} - -// drop identical middle keys - we only need the first and last -// return number of kicked keys -template // Cmp = comparison for keys of type T -irr::u32 dropMiddleKeys(irr::core::array &array, Cmp &cmp) -{ - if (array.size() < 3) - return 0; - - irr::u32 s = 0; // old index for current key - irr::u32 n = 1; // new index for next key - for (irr::u32 j = 1; j < array.size(); ++j) { - if (cmp(array[j], array[s])) - continue; // same key, handle later - - if (j > s + 1) // had there been identical keys? - array[n++] = array[j - 1]; // keep the last - array[n++] = array[j]; // keep the new one - s = j; - } - if (array.size() > s + 1) // identical keys at the array end? - array[n++] = array[array.size() - 1]; // keep the last - - irr::u32 d = array.size() - n; // remove already copied keys - if (d > 0) { - array.erase(n, d); - } - return d; -} - -bool identicalPos(const irr::scene::SkinnedMesh::SPositionKey &a, const irr::scene::SkinnedMesh::SPositionKey &b) -{ - return a.position == b.position; -} - -bool identicalScale(const irr::scene::SkinnedMesh::SScaleKey &a, const irr::scene::SkinnedMesh::SScaleKey &b) -{ - return a.scale == b.scale; -} - -bool identicalRotation(const irr::scene::SkinnedMesh::SRotationKey &a, const irr::scene::SkinnedMesh::SRotationKey &b) -{ - return a.rotation == b.rotation; -} -} +#include namespace irr { namespace scene { -//! constructor -SkinnedMesh::SkinnedMesh() : - SkinningBuffers(0), EndFrame(0.f), FramesPerSecond(25.f), - LastAnimatedFrame(-1), SkinnedLastFrame(false), - InterpolationMode(EIM_LINEAR), - HasAnimation(false), PreparedForSkinning(false), - AnimateNormals(true), HardwareSkinning(false) -{ -#ifdef _DEBUG - setDebugName("SkinnedMesh"); -#endif - - SkinningBuffers = &LocalBuffers; -} - //! destructor SkinnedMesh::~SkinnedMesh() { - for (u32 i = 0; i < AllJoints.size(); ++i) - delete AllJoints[i]; + for (auto *joint : AllJoints) + delete joint; - for (u32 j = 0; j < LocalBuffers.size(); ++j) { - if (LocalBuffers[j]) - LocalBuffers[j]->drop(); + for (auto *buffer : LocalBuffers) { + if (buffer) + buffer->drop(); } } @@ -138,7 +54,7 @@ IMesh *SkinnedMesh::getMesh(f32 frame) if (frame == -1) return this; - animateMesh(frame, 1.0f); + animateMesh(frame); skinMesh(); return this; } @@ -147,9 +63,8 @@ IMesh *SkinnedMesh::getMesh(f32 frame) // Keyframe Animation //-------------------------------------------------------------------------- -//! Animates this mesh's joints based on frame input -//! blend: {0-old position, 1-New position} -void SkinnedMesh::animateMesh(f32 frame, f32 blend) +//! Animates joints based on frame input +void SkinnedMesh::animateMesh(f32 frame) { if (!HasAnimation || LastAnimatedFrame == frame) return; @@ -157,39 +72,14 @@ void SkinnedMesh::animateMesh(f32 frame, f32 blend) LastAnimatedFrame = frame; SkinnedLastFrame = false; - if (blend <= 0.f) - return; // No need to animate - - for (u32 i = 0; i < AllJoints.size(); ++i) { + for (auto *joint : AllJoints) { // The joints can be animated here with no input from their // parents, but for setAnimationMode extra checks are needed // to their parents - SJoint *joint = AllJoints[i]; - - const core::vector3df oldPosition = joint->Animatedposition; - const core::vector3df oldScale = joint->Animatedscale; - const core::quaternion oldRotation = joint->Animatedrotation; - - core::vector3df position = oldPosition; - core::vector3df scale = oldScale; - core::quaternion rotation = oldRotation; - - getFrameData(frame, joint, - position, joint->positionHint, - scale, joint->scaleHint, - rotation, joint->rotationHint); - - if (blend == 1.0f) { - // No blending needed - joint->Animatedposition = position; - joint->Animatedscale = scale; - joint->Animatedrotation = rotation; - } else { - // Blend animation - joint->Animatedposition = core::lerp(oldPosition, position, blend); - joint->Animatedscale = core::lerp(oldScale, scale, blend); - joint->Animatedrotation.slerp(oldRotation, rotation, blend); - } + joint->keys.updateTransform(frame, + joint->Animatedposition, + joint->Animatedrotation, + joint->Animatedscale); } // Note: @@ -207,15 +97,10 @@ void SkinnedMesh::animateMesh(f32 frame, f32 blend) void SkinnedMesh::buildAllLocalAnimatedMatrices() { - for (u32 i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - + for (auto *joint : AllJoints) { // Could be faster: - if (joint->UseAnimationFrom && - (joint->UseAnimationFrom->PositionKeys.size() || - joint->UseAnimationFrom->ScaleKeys.size() || - joint->UseAnimationFrom->RotationKeys.size())) { + if (!joint->keys.empty()) { joint->GlobalSkinningSpace = false; // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched to getMatrix_transposed instead of getMatrix for downward compatibility. @@ -240,7 +125,7 @@ void SkinnedMesh::buildAllLocalAnimatedMatrices() m1[14] += Pos.Z * m1[15]; // ----------------------------------- - if (joint->ScaleKeys.size()) { + if (!joint->keys.scale.empty()) { /* core::matrix4 scaleMatrix; scaleMatrix.setScale(joint->Animatedscale); @@ -273,8 +158,8 @@ void SkinnedMesh::buildAllLocalAnimatedMatrices() void SkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint) { if (!joint) { - for (u32 i = 0; i < RootJoints.size(); ++i) - buildAllGlobalAnimatedMatrices(RootJoints[i], 0); + for (auto *rootJoint : RootJoints) + buildAllGlobalAnimatedMatrices(rootJoint, 0); return; } else { // Find global matrix... @@ -284,168 +169,8 @@ void SkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJo joint->GlobalAnimatedMatrix = parentJoint->GlobalAnimatedMatrix * joint->LocalAnimatedMatrix; } - for (u32 j = 0; j < joint->Children.size(); ++j) - buildAllGlobalAnimatedMatrices(joint->Children[j], joint); -} - -void SkinnedMesh::getFrameData(f32 frame, SJoint *joint, - core::vector3df &position, s32 &positionHint, - core::vector3df &scale, s32 &scaleHint, - core::quaternion &rotation, s32 &rotationHint) -{ - s32 foundPositionIndex = -1; - s32 foundScaleIndex = -1; - s32 foundRotationIndex = -1; - - if (joint->UseAnimationFrom) { - const core::array &PositionKeys = joint->UseAnimationFrom->PositionKeys; - const core::array &ScaleKeys = joint->UseAnimationFrom->ScaleKeys; - const core::array &RotationKeys = joint->UseAnimationFrom->RotationKeys; - - if (PositionKeys.size()) { - foundPositionIndex = -1; - - // Test the Hints... - if (positionHint >= 0 && (u32)positionHint < PositionKeys.size()) { - // check this hint - if (positionHint > 0 && PositionKeys[positionHint].frame >= frame && PositionKeys[positionHint - 1].frame < frame) - foundPositionIndex = positionHint; - else if (positionHint + 1 < (s32)PositionKeys.size()) { - // check the next index - if (PositionKeys[positionHint + 1].frame >= frame && - PositionKeys[positionHint + 0].frame < frame) { - positionHint++; - foundPositionIndex = positionHint; - } - } - } - - // The hint test failed, do a full scan... - if (foundPositionIndex == -1) { - for (u32 i = 0; i < PositionKeys.size(); ++i) { - if (PositionKeys[i].frame >= frame) { // Keys should to be sorted by frame - foundPositionIndex = i; - positionHint = i; - break; - } - } - } - - // Do interpolation... - if (foundPositionIndex != -1) { - if (InterpolationMode == EIM_CONSTANT || foundPositionIndex == 0) { - position = PositionKeys[foundPositionIndex].position; - } else if (InterpolationMode == EIM_LINEAR) { - const SPositionKey &KeyA = PositionKeys[foundPositionIndex]; - const SPositionKey &KeyB = PositionKeys[foundPositionIndex - 1]; - - const f32 fd1 = frame - KeyA.frame; - const f32 fd2 = KeyB.frame - frame; - position = ((KeyB.position - KeyA.position) / (fd1 + fd2)) * fd1 + KeyA.position; - } - } - } - - //------------------------------------------------------------ - - if (ScaleKeys.size()) { - foundScaleIndex = -1; - - // Test the Hints... - if (scaleHint >= 0 && (u32)scaleHint < ScaleKeys.size()) { - // check this hint - if (scaleHint > 0 && ScaleKeys[scaleHint].frame >= frame && ScaleKeys[scaleHint - 1].frame < frame) - foundScaleIndex = scaleHint; - else if (scaleHint + 1 < (s32)ScaleKeys.size()) { - // check the next index - if (ScaleKeys[scaleHint + 1].frame >= frame && - ScaleKeys[scaleHint + 0].frame < frame) { - scaleHint++; - foundScaleIndex = scaleHint; - } - } - } - - // The hint test failed, do a full scan... - if (foundScaleIndex == -1) { - for (u32 i = 0; i < ScaleKeys.size(); ++i) { - if (ScaleKeys[i].frame >= frame) { // Keys should to be sorted by frame - foundScaleIndex = i; - scaleHint = i; - break; - } - } - } - - // Do interpolation... - if (foundScaleIndex != -1) { - if (InterpolationMode == EIM_CONSTANT || foundScaleIndex == 0) { - scale = ScaleKeys[foundScaleIndex].scale; - } else if (InterpolationMode == EIM_LINEAR) { - const SScaleKey &KeyA = ScaleKeys[foundScaleIndex]; - const SScaleKey &KeyB = ScaleKeys[foundScaleIndex - 1]; - - const f32 fd1 = frame - KeyA.frame; - const f32 fd2 = KeyB.frame - frame; - scale = ((KeyB.scale - KeyA.scale) / (fd1 + fd2)) * fd1 + KeyA.scale; - } - } - } - - //------------------------------------------------------------- - - if (RotationKeys.size()) { - foundRotationIndex = -1; - - // Test the Hints... - if (rotationHint >= 0 && (u32)rotationHint < RotationKeys.size()) { - // check this hint - if (rotationHint > 0 && RotationKeys[rotationHint].frame >= frame && RotationKeys[rotationHint - 1].frame < frame) - foundRotationIndex = rotationHint; - else if (rotationHint + 1 < (s32)RotationKeys.size()) { - // check the next index - if (RotationKeys[rotationHint + 1].frame >= frame && - RotationKeys[rotationHint + 0].frame < frame) { - rotationHint++; - foundRotationIndex = rotationHint; - } - } - } - - // The hint test failed, do a full scan... - if (foundRotationIndex == -1) { - for (u32 i = 0; i < RotationKeys.size(); ++i) { - if (RotationKeys[i].frame >= frame) { // Keys should be sorted by frame - foundRotationIndex = i; - rotationHint = i; - break; - } - } - } - - // Do interpolation... - if (foundRotationIndex != -1) { - if (InterpolationMode == EIM_CONSTANT || foundRotationIndex == 0) { - rotation = RotationKeys[foundRotationIndex].rotation; - } else if (InterpolationMode == EIM_LINEAR) { - const SRotationKey &KeyA = RotationKeys[foundRotationIndex]; - const SRotationKey &KeyB = RotationKeys[foundRotationIndex - 1]; - - const f32 fd1 = frame - KeyA.frame; - const f32 fd2 = KeyB.frame - frame; - const f32 t = fd1 / (fd1 + fd2); - - /* - f32 t = 0; - if (KeyA.frame!=KeyB.frame) - t = (frame-KeyA.frame) / (KeyB.frame - KeyA.frame); - */ - - rotation.slerp(KeyA.rotation, KeyB.rotation, t); - } - } - } - } + for (auto *childJoint : joint->Children) + buildAllGlobalAnimatedMatrices(childJoint, joint); } //-------------------------------------------------------------------------- @@ -465,28 +190,24 @@ void SkinnedMesh::skinMesh() SkinnedLastFrame = true; if (!HardwareSkinning) { - // Software skin.... - u32 i; - // rigid animation - for (i = 0; i < AllJoints.size(); ++i) { - for (u32 j = 0; j < AllJoints[i]->AttachedMeshes.size(); ++j) { - SSkinMeshBuffer *Buffer = (*SkinningBuffers)[AllJoints[i]->AttachedMeshes[j]]; - Buffer->Transformation = AllJoints[i]->GlobalAnimatedMatrix; + for (auto *joint : AllJoints) { + for (u32 attachedMeshIdx : joint->AttachedMeshes) { + SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx]; + Buffer->Transformation = joint->GlobalAnimatedMatrix; } } // clear skinning helper array - for (i = 0; i < Vertices_Moved.size(); ++i) - for (u32 j = 0; j < Vertices_Moved[i].size(); ++j) - Vertices_Moved[i][j] = false; + for (std::vector &buf : Vertices_Moved) + std::fill(buf.begin(), buf.end(), false); // skin starting with the root joints - for (i = 0; i < RootJoints.size(); ++i) - skinJoint(RootJoints[i], 0); + for (auto *rootJoint : RootJoints) + skinJoint(rootJoint, 0); - for (i = 0; i < SkinningBuffers->size(); ++i) - (*SkinningBuffers)[i]->setDirty(EBT_VERTEX); + for (auto *buffer : *SkinningBuffers) + buffer->setDirty(EBT_VERTEX); } updateBoundingBox(); } @@ -500,12 +221,10 @@ void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) core::vector3df thisVertexMove, thisNormalMove; - core::array &buffersUsed = *SkinningBuffers; + auto &buffersUsed = *SkinningBuffers; // Skin Vertices Positions and Normals... - for (u32 i = 0; i < joint->Weights.size(); ++i) { - SWeight &weight = joint->Weights[i]; - + for (const auto &weight : joint->Weights) { // Pull this vertex... jointVertexPull.transformVect(thisVertexMove, weight.StaticPos); @@ -537,13 +256,8 @@ void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) } // Skin all children - for (u32 j = 0; j < joint->Children.size(); ++j) - skinJoint(joint->Children[j], joint); -} - -E_ANIMATED_MESH_TYPE SkinnedMesh::getMeshType() const -{ - return EAMT_SKINNED; + for (auto *childJoint : joint->Children) + skinJoint(childJoint, joint); } //! Gets joint count. @@ -607,18 +321,6 @@ void SkinnedMesh::setTextureSlot(u32 meshbufNr, u32 textureSlot) { TextureSlots.at(meshbufNr) = textureSlot; } -//! returns an axis aligned bounding box -const core::aabbox3d &SkinnedMesh::getBoundingBox() const -{ - return BoundingBox; -} - -//! set user axis aligned bounding box -void SkinnedMesh::setBoundingBox(const core::aabbox3df &box) -{ - BoundingBox = box; -} - //! set the hardware mapping hint, for driver void SkinnedMesh::setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer) @@ -634,35 +336,6 @@ void SkinnedMesh::setDirty(E_BUFFER_TYPE buffer) LocalBuffers[i]->setDirty(buffer); } -//! Update Normals when Animating -//! False= Don't animate them, faster -//! True= Update normals (default) -void SkinnedMesh::updateNormalsWhenAnimating(bool on) -{ - AnimateNormals = on; -} - -//! Sets Interpolation Mode -void SkinnedMesh::setInterpolationMode(E_INTERPOLATION_MODE mode) -{ - InterpolationMode = mode; -} - -core::array &SkinnedMesh::getMeshBuffers() -{ - return LocalBuffers; -} - -core::array &SkinnedMesh::getAllJoints() -{ - return AllJoints; -} - -const core::array &SkinnedMesh::getAllJoints() const -{ - return AllJoints; -} - //! (This feature is not implemented in irrlicht yet) bool SkinnedMesh::setHardwareSkinning(bool on) { @@ -670,13 +343,12 @@ bool SkinnedMesh::setHardwareSkinning(bool on) if (on) { // set mesh to static pose... - for (u32 i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - for (u32 j = 0; j < joint->Weights.size(); ++j) { - const u16 buffer_id = joint->Weights[j].buffer_id; - const u32 vertex_id = joint->Weights[j].vertex_id; - LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = joint->Weights[j].StaticPos; - LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = joint->Weights[j].StaticNormal; + for (auto *joint : AllJoints) { + for (const auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; + LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = weight.StaticPos; + LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = weight.StaticNormal; LocalBuffers[buffer_id]->boundingBoxNeedsRecalculated(); } } @@ -690,13 +362,12 @@ bool SkinnedMesh::setHardwareSkinning(bool on) void SkinnedMesh::refreshJointCache() { // copy cache from the mesh... - for (u32 i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - for (u32 j = 0; j < joint->Weights.size(); ++j) { - const u16 buffer_id = joint->Weights[j].buffer_id; - const u32 vertex_id = joint->Weights[j].vertex_id; - joint->Weights[j].StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos; - joint->Weights[j].StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal; + for (auto *joint : AllJoints) { + for (auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; + weight.StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos; + weight.StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal; } } } @@ -704,13 +375,12 @@ void SkinnedMesh::refreshJointCache() void SkinnedMesh::resetAnimation() { // copy from the cache to the mesh... - for (u32 i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - for (u32 j = 0; j < joint->Weights.size(); ++j) { - const u16 buffer_id = joint->Weights[j].buffer_id; - const u32 vertex_id = joint->Weights[j].vertex_id; - LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = joint->Weights[j].StaticPos; - LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = joint->Weights[j].StaticNormal; + for (auto *joint : AllJoints) { + for (const auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; + LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = weight.StaticPos; + LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = weight.StaticNormal; } } SkinnedLastFrame = false; @@ -724,8 +394,8 @@ void SkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint) // Go through the root bones if (!joint) { - for (u32 i = 0; i < RootJoints.size(); ++i) - calculateGlobalMatrices(RootJoints[i], 0); + for (auto *rootJoint : RootJoints) + calculateGlobalMatrices(rootJoint, nullptr); return; } @@ -742,51 +412,36 @@ void SkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint) joint->GlobalInversedMatrix->makeInverse(); // slow } - for (u32 j = 0; j < joint->Children.size(); ++j) - calculateGlobalMatrices(joint->Children[j], joint); + for (auto *childJoint : joint->Children) + calculateGlobalMatrices(childJoint, joint); SkinnedLastFrame = false; } void SkinnedMesh::checkForAnimation() { - u32 i, j; // Check for animation... HasAnimation = false; - for (i = 0; i < AllJoints.size(); ++i) { - if (AllJoints[i]->UseAnimationFrom) { - if (AllJoints[i]->UseAnimationFrom->PositionKeys.size() || - AllJoints[i]->UseAnimationFrom->ScaleKeys.size() || - AllJoints[i]->UseAnimationFrom->RotationKeys.size()) { - HasAnimation = true; - } + for (auto *joint : AllJoints) { + if (!joint->keys.empty()) { + HasAnimation = true; + break; } } // meshes with weights, are still counted as animated for ragdolls, etc if (!HasAnimation) { - for (i = 0; i < AllJoints.size(); ++i) { - if (AllJoints[i]->Weights.size()) + for (auto *joint : AllJoints) { + if (joint->Weights.size()) { HasAnimation = true; + break; + } } } if (HasAnimation) { - //--- Find the length of the animation --- - EndFrame = 0; - for (i = 0; i < AllJoints.size(); ++i) { - if (AllJoints[i]->UseAnimationFrom) { - if (AllJoints[i]->UseAnimationFrom->PositionKeys.size()) - if (AllJoints[i]->UseAnimationFrom->PositionKeys.getLast().frame > EndFrame) - EndFrame = AllJoints[i]->UseAnimationFrom->PositionKeys.getLast().frame; - - if (AllJoints[i]->UseAnimationFrom->ScaleKeys.size()) - if (AllJoints[i]->UseAnimationFrom->ScaleKeys.getLast().frame > EndFrame) - EndFrame = AllJoints[i]->UseAnimationFrom->ScaleKeys.getLast().frame; - - if (AllJoints[i]->UseAnimationFrom->RotationKeys.size()) - if (AllJoints[i]->UseAnimationFrom->RotationKeys.getLast().frame > EndFrame) - EndFrame = AllJoints[i]->UseAnimationFrom->RotationKeys.getLast().frame; - } + EndFrame = 0.0f; + for (const auto *joint : AllJoints) { + EndFrame = std::max(EndFrame, joint->keys.getEndFrame()); } } @@ -794,42 +449,40 @@ void SkinnedMesh::checkForAnimation() PreparedForSkinning = true; // check for bugs: - for (i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - for (j = 0; j < joint->Weights.size(); ++j) { - const u16 buffer_id = joint->Weights[j].buffer_id; - const u32 vertex_id = joint->Weights[j].vertex_id; + for (auto *joint : AllJoints) { + for (auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; // check for invalid ids if (buffer_id >= LocalBuffers.size()) { os::Printer::log("Skinned Mesh: Weight buffer id too large", ELL_WARNING); - joint->Weights[j].buffer_id = joint->Weights[j].vertex_id = 0; + weight.buffer_id = weight.vertex_id = 0; } else if (vertex_id >= LocalBuffers[buffer_id]->getVertexCount()) { os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING); - joint->Weights[j].buffer_id = joint->Weights[j].vertex_id = 0; + weight.buffer_id = weight.vertex_id = 0; } } } // An array used in skinning - for (i = 0; i < Vertices_Moved.size(); ++i) - for (j = 0; j < Vertices_Moved[i].size(); ++j) + for (u32 i = 0; i < Vertices_Moved.size(); ++i) + for (u32 j = 0; j < Vertices_Moved[i].size(); ++j) Vertices_Moved[i][j] = false; // For skinning: cache weight values for speed - for (i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - for (j = 0; j < joint->Weights.size(); ++j) { - const u16 buffer_id = joint->Weights[j].buffer_id; - const u32 vertex_id = joint->Weights[j].vertex_id; + for (auto *joint : AllJoints) { + for (auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; - joint->Weights[j].Moved = &Vertices_Moved[buffer_id][vertex_id]; - joint->Weights[j].StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos; - joint->Weights[j].StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal; + weight.Moved = &Vertices_Moved[buffer_id][vertex_id]; + weight.StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos; + weight.StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal; - // joint->Weights[j]._Pos=&Buffers[buffer_id]->getVertex(vertex_id)->Pos; + // weight._Pos=&Buffers[buffer_id]->getVertex(vertex_id)->Pos; } } @@ -840,148 +493,52 @@ void SkinnedMesh::checkForAnimation() } //! called by loader after populating with mesh and bone data -void SkinnedMesh::finalize() +SkinnedMesh *SkinnedMeshBuilder::finalize() { os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG); - u32 i; // Make sure we recalc the next frame LastAnimatedFrame = -1; SkinnedLastFrame = false; // calculate bounding box - for (i = 0; i < LocalBuffers.size(); ++i) { - LocalBuffers[i]->recalculateBoundingBox(); + for (auto *buffer : LocalBuffers) { + buffer->recalculateBoundingBox(); } if (AllJoints.size() || RootJoints.size()) { // populate AllJoints or RootJoints, depending on which is empty - if (!RootJoints.size()) { + if (RootJoints.empty()) { - for (u32 CheckingIdx = 0; CheckingIdx < AllJoints.size(); ++CheckingIdx) { + for (auto *joint : AllJoints) { bool foundParent = false; - for (i = 0; i < AllJoints.size(); ++i) { - for (u32 n = 0; n < AllJoints[i]->Children.size(); ++n) { - if (AllJoints[i]->Children[n] == AllJoints[CheckingIdx]) + for (const auto *parentJoint : AllJoints) { + for (const auto *childJoint : parentJoint->Children) { + if (childJoint == joint) foundParent = true; } } if (!foundParent) - RootJoints.push_back(AllJoints[CheckingIdx]); + RootJoints.push_back(joint); } } else { AllJoints = RootJoints; } } - for (i = 0; i < AllJoints.size(); ++i) { - AllJoints[i]->UseAnimationFrom = AllJoints[i]; - } - // Set array sizes... - for (i = 0; i < LocalBuffers.size(); ++i) { - Vertices_Moved.push_back(core::array()); - Vertices_Moved[i].set_used(LocalBuffers[i]->getVertexCount()); + for (u32 i = 0; i < LocalBuffers.size(); ++i) { + Vertices_Moved.emplace_back(LocalBuffers[i]->getVertexCount()); } checkForAnimation(); if (HasAnimation) { - irr::u32 redundantPosKeys = 0; - irr::u32 unorderedPosKeys = 0; - irr::u32 redundantScaleKeys = 0; - irr::u32 unorderedScaleKeys = 0; - irr::u32 redundantRotationKeys = 0; - irr::u32 unorderedRotationKeys = 0; - - //--- optimize and check keyframes --- - for (i = 0; i < AllJoints.size(); ++i) { - core::array &PositionKeys = AllJoints[i]->PositionKeys; - core::array &ScaleKeys = AllJoints[i]->ScaleKeys; - core::array &RotationKeys = AllJoints[i]->RotationKeys; - - // redundant = identical middle keys - we only need the first and last frame - // unordered = frames which are out of order - we can't handle those - redundantPosKeys += dropMiddleKeys(PositionKeys, identicalPos); - unorderedPosKeys += dropBadKeys(PositionKeys); - redundantScaleKeys += dropMiddleKeys(ScaleKeys, identicalScale); - unorderedScaleKeys += dropBadKeys(ScaleKeys); - redundantRotationKeys += dropMiddleKeys(RotationKeys, identicalRotation); - unorderedRotationKeys += dropBadKeys(RotationKeys); - - // Fill empty keyframe areas - if (PositionKeys.size()) { - SPositionKey *Key; - Key = &PositionKeys[0]; // getFirst - if (Key->frame != 0) { - PositionKeys.push_front(*Key); - Key = &PositionKeys[0]; // getFirst - Key->frame = 0; - } - - Key = &PositionKeys.getLast(); - if (Key->frame != EndFrame) { - PositionKeys.push_back(*Key); - Key = &PositionKeys.getLast(); - Key->frame = EndFrame; - } - } - - if (ScaleKeys.size()) { - SScaleKey *Key; - Key = &ScaleKeys[0]; // getFirst - if (Key->frame != 0) { - ScaleKeys.push_front(*Key); - Key = &ScaleKeys[0]; // getFirst - Key->frame = 0; - } - - Key = &ScaleKeys.getLast(); - if (Key->frame != EndFrame) { - ScaleKeys.push_back(*Key); - Key = &ScaleKeys.getLast(); - Key->frame = EndFrame; - } - } - - if (RotationKeys.size()) { - SRotationKey *Key; - Key = &RotationKeys[0]; // getFirst - if (Key->frame != 0) { - RotationKeys.push_front(*Key); - Key = &RotationKeys[0]; // getFirst - Key->frame = 0; - } - - Key = &RotationKeys.getLast(); - if (Key->frame != EndFrame) { - RotationKeys.push_back(*Key); - Key = &RotationKeys.getLast(); - Key->frame = EndFrame; - } - } - } - - if (redundantPosKeys > 0) { - os::Printer::log("Skinned Mesh - redundant position frames kicked", core::stringc(redundantPosKeys).c_str(), ELL_DEBUG); - } - if (unorderedPosKeys > 0) { - irr::os::Printer::log("Skinned Mesh - unsorted position frames kicked", irr::core::stringc(unorderedPosKeys).c_str(), irr::ELL_DEBUG); - } - if (redundantScaleKeys > 0) { - os::Printer::log("Skinned Mesh - redundant scale frames kicked", core::stringc(redundantScaleKeys).c_str(), ELL_DEBUG); - } - if (unorderedScaleKeys > 0) { - irr::os::Printer::log("Skinned Mesh - unsorted scale frames kicked", irr::core::stringc(unorderedScaleKeys).c_str(), irr::ELL_DEBUG); - } - if (redundantRotationKeys > 0) { - os::Printer::log("Skinned Mesh - redundant rotation frames kicked", core::stringc(redundantRotationKeys).c_str(), ELL_DEBUG); - } - if (unorderedRotationKeys > 0) { - irr::os::Printer::log("Skinned Mesh - unsorted rotation frames kicked", irr::core::stringc(unorderedRotationKeys).c_str(), irr::ELL_DEBUG); + for (auto *joint : AllJoints) { + joint->keys.cleanup(); } } @@ -989,15 +546,11 @@ void SkinnedMesh::finalize() calculateGlobalMatrices(0, 0); - // animateMesh(0, 1); - // buildAllLocalAnimatedMatrices(); - // buildAllGlobalAnimatedMatrices(); - // rigid animation for non animated meshes - for (i = 0; i < AllJoints.size(); ++i) { - for (u32 j = 0; j < AllJoints[i]->AttachedMeshes.size(); ++j) { - SSkinMeshBuffer *Buffer = (*SkinningBuffers)[AllJoints[i]->AttachedMeshes[j]]; - Buffer->Transformation = AllJoints[i]->GlobalAnimatedMatrix; + for (auto *joint : AllJoints) { + for (u32 attachedMeshIdx : joint->AttachedMeshes) { + SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx]; + Buffer->Transformation = joint->GlobalAnimatedMatrix; } } @@ -1016,6 +569,8 @@ void SkinnedMesh::finalize() BoundingBox.addInternalBox(bb); } } + + return this; } void SkinnedMesh::updateBoundingBox(void) @@ -1023,21 +578,18 @@ void SkinnedMesh::updateBoundingBox(void) if (!SkinningBuffers) return; - core::array &buffer = *SkinningBuffers; BoundingBox.reset(0, 0, 0); - if (!buffer.empty()) { - for (u32 j = 0; j < buffer.size(); ++j) { - buffer[j]->recalculateBoundingBox(); - core::aabbox3df bb = buffer[j]->BoundingBox; - buffer[j]->Transformation.transformBoxEx(bb); + for (auto *buffer : *SkinningBuffers) { + buffer->recalculateBoundingBox(); + core::aabbox3df bb = buffer->BoundingBox; + buffer->Transformation.transformBoxEx(bb); - BoundingBox.addInternalBox(bb); - } + BoundingBox.addInternalBox(bb); } } -scene::SSkinMeshBuffer *SkinnedMesh::addMeshBuffer() +scene::SSkinMeshBuffer *SkinnedMeshBuilder::addMeshBuffer() { scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer(); TextureSlots.push_back(LocalBuffers.size()); @@ -1045,13 +597,13 @@ scene::SSkinMeshBuffer *SkinnedMesh::addMeshBuffer() return buffer; } -void SkinnedMesh::addMeshBuffer(SSkinMeshBuffer *meshbuf) +void SkinnedMeshBuilder::addMeshBuffer(SSkinMeshBuffer *meshbuf) { TextureSlots.push_back(LocalBuffers.size()); LocalBuffers.push_back(meshbuf); } -SkinnedMesh::SJoint *SkinnedMesh::addJoint(SJoint *parent) +SkinnedMesh::SJoint *SkinnedMeshBuilder::addJoint(SJoint *parent) { SJoint *joint = new SJoint; @@ -1066,45 +618,31 @@ SkinnedMesh::SJoint *SkinnedMesh::addJoint(SJoint *parent) return joint; } -SkinnedMesh::SPositionKey *SkinnedMesh::addPositionKey(SJoint *joint) +void SkinnedMeshBuilder::addPositionKey(SJoint *joint, f32 frame, core::vector3df pos) { - if (!joint) - return 0; - - joint->PositionKeys.push_back(SPositionKey()); - return &joint->PositionKeys.getLast(); + _IRR_DEBUG_BREAK_IF(!joint); + joint->keys.position.pushBack(frame, pos); } -SkinnedMesh::SScaleKey *SkinnedMesh::addScaleKey(SJoint *joint) +void SkinnedMeshBuilder::addScaleKey(SJoint *joint, f32 frame, core::vector3df scale) { - if (!joint) - return 0; - - joint->ScaleKeys.push_back(SScaleKey()); - return &joint->ScaleKeys.getLast(); + _IRR_DEBUG_BREAK_IF(!joint); + joint->keys.scale.pushBack(frame, scale); } -SkinnedMesh::SRotationKey *SkinnedMesh::addRotationKey(SJoint *joint) +void SkinnedMeshBuilder::addRotationKey(SJoint *joint, f32 frame, core::quaternion rot) { - if (!joint) - return 0; - - joint->RotationKeys.push_back(SRotationKey()); - return &joint->RotationKeys.getLast(); + _IRR_DEBUG_BREAK_IF(!joint); + joint->keys.rotation.pushBack(frame, rot); } -SkinnedMesh::SWeight *SkinnedMesh::addWeight(SJoint *joint) +SkinnedMesh::SWeight *SkinnedMeshBuilder::addWeight(SJoint *joint) { if (!joint) - return 0; + return nullptr; - joint->Weights.push_back(SWeight()); - return &joint->Weights.getLast(); -} - -bool SkinnedMesh::isStatic() const -{ - return !HasAnimation; + joint->Weights.emplace_back(); + return &joint->Weights.back(); } void SkinnedMesh::normalizeWeights() @@ -1113,42 +651,39 @@ void SkinnedMesh::normalizeWeights() // Normalise the weights on bones.... - u32 i, j; - core::array> verticesTotalWeight; + std::vector> verticesTotalWeight; - verticesTotalWeight.reallocate(LocalBuffers.size()); - for (i = 0; i < LocalBuffers.size(); ++i) { - verticesTotalWeight.push_back(core::array()); - verticesTotalWeight[i].set_used(LocalBuffers[i]->getVertexCount()); + verticesTotalWeight.reserve(LocalBuffers.size()); + for (u32 i = 0; i < LocalBuffers.size(); ++i) { + verticesTotalWeight.emplace_back(LocalBuffers[i]->getVertexCount()); } - for (i = 0; i < verticesTotalWeight.size(); ++i) - for (j = 0; j < verticesTotalWeight[i].size(); ++j) + for (u32 i = 0; i < verticesTotalWeight.size(); ++i) + for (u32 j = 0; j < verticesTotalWeight[i].size(); ++j) verticesTotalWeight[i][j] = 0; - for (i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - for (j = 0; j < joint->Weights.size(); ++j) { - if (joint->Weights[j].strength <= 0) { // Check for invalid weights - joint->Weights.erase(j); - --j; - } else { - verticesTotalWeight[joint->Weights[j].buffer_id][joint->Weights[j].vertex_id] += joint->Weights[j].strength; - } + for (auto *joint : AllJoints) { + auto &weights = joint->Weights; + + weights.erase(std::remove_if(weights.begin(), weights.end(), [](const auto &weight) { + return weight.strength <= 0; + }), weights.end()); + + for (const auto &weight : weights) { + verticesTotalWeight[weight.buffer_id][weight.vertex_id] += weight.strength; } } - for (i = 0; i < AllJoints.size(); ++i) { - SJoint *joint = AllJoints[i]; - for (j = 0; j < joint->Weights.size(); ++j) { - const f32 total = verticesTotalWeight[joint->Weights[j].buffer_id][joint->Weights[j].vertex_id]; + for (auto *joint : AllJoints) { + for (auto &weight : joint->Weights) { + const f32 total = verticesTotalWeight[weight.buffer_id][weight.vertex_id]; if (total != 0 && total != 1) - joint->Weights[j].strength /= total; + weight.strength /= total; } } } -void SkinnedMesh::recoverJointsFromMesh(core::array &jointChildSceneNodes) +void SkinnedMesh::recoverJointsFromMesh(std::vector &jointChildSceneNodes) { for (u32 i = 0; i < AllJoints.size(); ++i) { IBoneSceneNode *node = jointChildSceneNodes[i]; @@ -1157,15 +692,11 @@ void SkinnedMesh::recoverJointsFromMesh(core::array &jointChil node->setRotation(joint->LocalAnimatedMatrix.getRotationDegrees()); node->setScale(joint->LocalAnimatedMatrix.getScale()); - node->positionHint = joint->positionHint; - node->scaleHint = joint->scaleHint; - node->rotationHint = joint->rotationHint; - node->updateAbsolutePosition(); } } -void SkinnedMesh::transferJointsToMesh(const core::array &jointChildSceneNodes) +void SkinnedMesh::transferJointsToMesh(const std::vector &jointChildSceneNodes) { for (u32 i = 0; i < AllJoints.size(); ++i) { const IBoneSceneNode *const node = jointChildSceneNodes[i]; @@ -1175,10 +706,6 @@ void SkinnedMesh::transferJointsToMesh(const core::array &join joint->LocalAnimatedMatrix.setTranslation(node->getPosition()); joint->LocalAnimatedMatrix *= core::matrix4().setScale(node->getScale()); - joint->positionHint = node->positionHint; - joint->scaleHint = node->scaleHint; - joint->rotationHint = node->rotationHint; - joint->GlobalSkinningSpace = (node->getSkinningSpace() == EBSS_GLOBAL); } // Make sure we recalc the next frame @@ -1186,20 +713,7 @@ void SkinnedMesh::transferJointsToMesh(const core::array &join SkinnedLastFrame = false; } -void SkinnedMesh::transferOnlyJointsHintsToMesh(const core::array &jointChildSceneNodes) -{ - for (u32 i = 0; i < AllJoints.size(); ++i) { - const IBoneSceneNode *const node = jointChildSceneNodes[i]; - SJoint *joint = AllJoints[i]; - - joint->positionHint = node->positionHint; - joint->scaleHint = node->scaleHint; - joint->rotationHint = node->rotationHint; - } - SkinnedLastFrame = false; -} - -void SkinnedMesh::addJoints(core::array &jointChildSceneNodes, +void SkinnedMesh::addJoints(std::vector &jointChildSceneNodes, IAnimatedMeshSceneNode *node, ISceneManager *smgr) { // Create new joints From ac7406c8a1013ea8bfc94c48dbc5dc97532c0cbd Mon Sep 17 00:00:00 2001 From: y5nw <37980625+y5nw@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:33:34 +0100 Subject: [PATCH 52/68] Fixup parsing for `Plural-Forms` (#15519) --- src/gettext_plural_form.cpp | 34 ++++++++++++++--------------- src/gettext_plural_form.h | 4 ++-- src/unittest/test_translations.cpp | 35 +++++++++++++++++++++++++++--- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/src/gettext_plural_form.cpp b/src/gettext_plural_form.cpp index 6a5322421..89dd721a4 100644 --- a/src/gettext_plural_form.cpp +++ b/src/gettext_plural_form.cpp @@ -90,16 +90,16 @@ class TernaryOperation: public GettextPluralForm }; typedef std::pair ParserResult; -typedef ParserResult (*Parser)(const size_t, const std::wstring_view &); +typedef ParserResult (*Parser)(const size_t, std::wstring_view); -static ParserResult parse_expr(const size_t nplurals, const std::wstring_view &str); +static ParserResult parse_expr(const size_t nplurals, std::wstring_view str); template typename Operator> static ParserResult reduce_ltr(const size_t nplurals, const ParserResult &res, const wchar_t* pattern) { if (!str_starts_with(res.second, pattern)) return ParserResult(nullptr, res.second); - auto next = Parser(nplurals, res.second.substr(std::char_traits::length(pattern))); + auto next = Parser(nplurals, trim(res.second.substr(std::char_traits::length(pattern)))); if (!next.first) return next; next.first = GettextPluralForm::Ptr(new BinaryOperation(res.first, next.first)); @@ -123,7 +123,7 @@ static ParserResult reduce_ltr(const size_t nplurals, const ParserResult &res, c } template typename Operator, template typename... Operators> -static ParserResult parse_ltr(const size_t nplurals, const std::wstring_view &str, const wchar_t** patterns) +static ParserResult parse_ltr(const size_t nplurals, std::wstring_view str, const wchar_t** patterns) { auto &&pres = Parser(nplurals, str); if (!pres.first) @@ -139,7 +139,7 @@ static ParserResult parse_ltr(const size_t nplurals, const std::wstring_view &st return pres; } -static ParserResult parse_atomic(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_atomic(const size_t nplurals, std::wstring_view str) { if (str.empty()) return ParserResult(nullptr, str); @@ -151,7 +151,7 @@ static ParserResult parse_atomic(const size_t nplurals, const std::wstring_view return ParserResult(new ConstValue(nplurals, val), trim(str.substr(endp-str.data()))); } -static ParserResult parse_parenthesized(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_parenthesized(const size_t nplurals, std::wstring_view str) { if (str.empty()) return ParserResult(nullptr, str); @@ -167,7 +167,7 @@ static ParserResult parse_parenthesized(const size_t nplurals, const std::wstrin return result; } -static ParserResult parse_negation(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_negation(const size_t nplurals, std::wstring_view str) { if (str.empty()) return ParserResult(nullptr, str); @@ -179,43 +179,43 @@ static ParserResult parse_negation(const size_t nplurals, const std::wstring_vie return result; } -static ParserResult parse_multiplicative(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_multiplicative(const size_t nplurals, std::wstring_view str) { static const wchar_t *patterns[] = { L"*", L"/", L"%" }; return parse_ltr(nplurals, str, patterns); } -static ParserResult parse_additive(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_additive(const size_t nplurals, std::wstring_view str) { static const wchar_t *patterns[] = { L"+", L"-" }; return parse_ltr(nplurals, str, patterns); } -static ParserResult parse_comparison(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_comparison(const size_t nplurals, std::wstring_view str) { static const wchar_t *patterns[] = { L"<=", L">=", L"<", L">" }; return parse_ltr(nplurals, str, patterns); } -static ParserResult parse_equality(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_equality(const size_t nplurals, std::wstring_view str) { static const wchar_t *patterns[] = { L"==", L"!=" }; return parse_ltr(nplurals, str, patterns); } -static ParserResult parse_conjunction(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_conjunction(const size_t nplurals, std::wstring_view str) { static const wchar_t *and_pattern[] = { L"&&" }; return parse_ltr(nplurals, str, and_pattern); } -static ParserResult parse_disjunction(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_disjunction(const size_t nplurals, std::wstring_view str) { static const wchar_t *or_pattern[] = { L"||" }; return parse_ltr(nplurals, str, or_pattern); } -static ParserResult parse_ternary(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_ternary(const size_t nplurals, std::wstring_view str) { auto pres = parse_disjunction(nplurals, str); if (pres.second.empty() || pres.second[0] != '?') // no ? : @@ -229,12 +229,12 @@ static ParserResult parse_ternary(const size_t nplurals, const std::wstring_view return ParserResult(new TernaryOperation(cond, val, pres.first), pres.second); } -static ParserResult parse_expr(const size_t nplurals, const std::wstring_view &str) +static ParserResult parse_expr(const size_t nplurals, std::wstring_view str) { return parse_ternary(nplurals, trim(str)); } -GettextPluralForm::Ptr GettextPluralForm::parse(const size_t nplurals, const std::wstring_view &str) +GettextPluralForm::Ptr GettextPluralForm::parse(const size_t nplurals, std::wstring_view str) { if (nplurals == 0) return nullptr; @@ -244,7 +244,7 @@ GettextPluralForm::Ptr GettextPluralForm::parse(const size_t nplurals, const std return result.first; } -GettextPluralForm::Ptr GettextPluralForm::parseHeaderLine(const std::wstring_view &str) +GettextPluralForm::Ptr GettextPluralForm::parseHeaderLine(std::wstring_view str) { if (!str_starts_with(str, L"Plural-Forms: nplurals=") || !str_ends_with(str, L";")) return nullptr; diff --git a/src/gettext_plural_form.h b/src/gettext_plural_form.h index d73718965..1d3195e9a 100644 --- a/src/gettext_plural_form.h +++ b/src/gettext_plural_form.h @@ -24,8 +24,8 @@ public: } virtual ~GettextPluralForm() {}; - static GettextPluralForm::Ptr parse(const size_t nplurals, const std::wstring_view &str); - static GettextPluralForm::Ptr parseHeaderLine(const std::wstring_view &str); + static GettextPluralForm::Ptr parse(const size_t nplurals, std::wstring_view str); + static GettextPluralForm::Ptr parseHeaderLine(std::wstring_view str); protected: GettextPluralForm(size_t nplurals): nplurals(nplurals) {}; private: diff --git a/src/unittest/test_translations.cpp b/src/unittest/test_translations.cpp index 37fc78ee4..5bab3e15c 100644 --- a/src/unittest/test_translations.cpp +++ b/src/unittest/test_translations.cpp @@ -25,12 +25,41 @@ TEST_CASE("test translations") { SECTION("Plural-Forms function for translations") { - auto form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=3; plural= (n-1+1)<=1 ? n||1?0:1 : 1?(!!2):2;"); - REQUIRE(form); - REQUIRE(form->size() == 3); +#define REQUIRE_FORM_SIZE(x) {REQUIRE(form); REQUIRE(form->size() == (x));} + // Test cases from https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html + auto form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=2; plural=n != 1;"); + REQUIRE_FORM_SIZE(2); + CHECK((*form)(0) == 1); + CHECK((*form)(1) == 0); + CHECK((*form)(2) == 1); + + form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2;"); + REQUIRE_FORM_SIZE(3); + CHECK((*form)(0) == 2); + CHECK((*form)(1) == 0); + CHECK((*form)(102) == 1); + CHECK((*form)(111) == 1); + + form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=3; " + "plural=n%10==1 && n%100!=11 ? 0 : " + "n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;"); + REQUIRE_FORM_SIZE(3); + CHECK((*form)(0) == 2); + CHECK((*form)(1) == 0); + CHECK((*form)(102) == 1); + CHECK((*form)(104) == 1); + CHECK((*form)(111) == 2); + CHECK((*form)(112) == 2); + CHECK((*form)(121) == 0); + CHECK((*form)(122) == 1); + + // Edge cases + form = GettextPluralForm::parseHeaderLine(L"Plural-Forms: nplurals=3; plural= (n-1+1)<=1 ? n||1?0:1 : 1?(!!2):2;"); + REQUIRE_FORM_SIZE(3); CHECK((*form)(0) == 0); CHECK((*form)(1) == 0); CHECK((*form)(2) == 1); +#undef REQUIRE_FORM_SIZE } SECTION("PO file parser") From ba63c1505ad4cda3d959ed0402c9c4c9771ba7f9 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Sat, 14 Dec 2024 16:59:29 +0100 Subject: [PATCH 53/68] Docs: Change translation example from `NS` to `PS` (#15476) The currently established convention uses `NS` for "translation no-ops", i.e., it will be collected by a string-collecting utility but not be translated by Luanti at this place. We don't want to mislead modders with this example into using `NS` for plural forms instead, breaking with the established convention and making use of automated tools harder. See also: https://github.com/minetest/modtools/pull/11 --- doc/lua_api.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index c4c74d884..7fe464a30 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -4190,14 +4190,14 @@ Two functions are provided to translate strings: `core.translate` and * `core.get_translator(textdomain)` is a simple wrapper around `core.translate` and `core.translate_n`. - After `local S, NS = core.get_translator(textdomain)`, we have + After `local S, PS = core.get_translator(textdomain)`, we have `S(str, ...)` equivalent to `core.translate(textdomain, str, ...)`, and - `NS(str, str_plural, n, ...)` to `core.translate_n(textdomain, str, str_plural, n, ...)`. + `PS(str, str_plural, n, ...)` to `core.translate_n(textdomain, str, str_plural, n, ...)`. It is intended to be used in the following way, so that it avoids verbose repetitions of `core.translate`: ```lua - local S, NS = core.get_translator(textdomain) + local S, PS = core.get_translator(textdomain) S(str, ...) ``` @@ -4231,7 +4231,7 @@ command that shows the amount of time since the player joined. We can do the following: ```lua -local S, NS = core.get_translator("hello") +local S, PS = core.get_translator("hello") core.register_on_joinplayer(function(player) local name = player:get_player_name() core.chat_send_player(name, S("Hello @1, how are you today?", name)) @@ -4240,7 +4240,7 @@ core.register_chatcommand("playtime", { func = function(name) local last_login = core.get_auth_handler().get_auth(name).last_login local playtime = math.floor((last_login-os.time())/60) - return true, NS( + return true, PS( "You have been playing for @1 minute.", "You have been playing for @1 minutes.", minutes, tostring(minutes)) @@ -4287,7 +4287,7 @@ After creating the `locale` directory, a translation template for the above example using the following command: ```sh -xgettext -L lua -kS -kNS:1,2 -kcore.translate:1c,2 -kcore.translate_n:1c,2,3 \ +xgettext -L lua -kS -kPS:1,2 -kcore.translate:1c,2 -kcore.translate_n:1c,2,3 \ -d hello -o locale/hello.pot *.lua ``` From f7a695c212bea96887b32b859645f621fd8f1b48 Mon Sep 17 00:00:00 2001 From: sfence Date: Sat, 14 Dec 2024 17:01:06 +0100 Subject: [PATCH 54/68] Optimize raycast performance (#15233) by skipping nodes not on the ray with selection boxes smaller than 1x1x1 early on --- doc/lua_api.md | 2 ++ src/environment.cpp | 6 ++++++ src/nodedef.cpp | 12 ++++++++++++ src/nodedef.h | 5 +++++ 4 files changed, 25 insertions(+) diff --git a/doc/lua_api.md b/doc/lua_api.md index 7fe464a30..d753b6b53 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -9948,6 +9948,8 @@ Used by `core.register_node`. selection_box = { -- see [Node boxes] for possibilities + -- Selection boxes that oversize node size can cause + -- significant performance drop of Raycasts. }, -- Custom selection box definition. Multiple boxes can be defined. -- If "nodebox" drawtype is used and selection_box is nil, then node_box diff --git a/src/environment.cpp b/src/environment.cpp index fe582afd4..e66c7e2de 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -163,6 +163,8 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) break; // About to go out of bounds } + const v3s16 pos_on_ray = state->m_iterator.m_current_node_pos; + // For each untested node for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) @@ -175,6 +177,10 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) if (!is_valid_position) continue; + // Optimization: Skip non-oversized selection boxes for other positions. + if ((pos_on_ray != np) && !nodedef->get(n).has_big_selection_box) + continue; + PointabilityType pointable = isPointableNode(n, nodedef, state->m_liquids_pointable, state->m_pointabilities); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 81348cf23..0fde1b68e 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1257,6 +1257,15 @@ inline void NodeDefManager::fixSelectionBoxIntUnion() m_selection_box_union.MaxEdge.Z / BS - 0.5f); } +inline void NodeDefManager::calcBigSelectionBox(content_t id, const ContentFeatures &def) +{ + aabb3f box_union; + getNodeBoxUnion(def.selection_box, def, &box_union); + m_content_features[id].has_big_selection_box = + (box_union.MinEdge.X < -BS/2) || (box_union.MaxEdge.X > BS/2) || + (box_union.MinEdge.Y < -BS/2) || (box_union.MaxEdge.Y > BS/2) || + (box_union.MinEdge.Z < -BS/2) || (box_union.MaxEdge.Z > BS/2); +} void NodeDefManager::eraseIdFromGroups(content_t id) { @@ -1312,6 +1321,7 @@ content_t NodeDefManager::set(const std::string &name, const ContentFeatures &de getNodeBoxUnion(def.selection_box, def, &m_selection_box_union); fixSelectionBoxIntUnion(); + calcBigSelectionBox(id, def); // Add this content to the list of all groups it belongs to for (const auto &group : def.groups) { const std::string &group_name = group.first; @@ -1525,6 +1535,8 @@ void NodeDefManager::deSerialize(std::istream &is, u16 protocol_version) getNodeBoxUnion(f.selection_box, f, &m_selection_box_union); fixSelectionBoxIntUnion(); + + calcBigSelectionBox(i, f); } // Since liquid_alternative_flowing_id and liquid_alternative_source_id diff --git a/src/nodedef.h b/src/nodedef.h index c3f88ce83..e33f42699 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -425,6 +425,8 @@ struct ContentFeatures NodeBox node_box; NodeBox selection_box; NodeBox collision_box; + //! Whether any selection box extent is > BS/2. + bool has_big_selection_box; // --- SOUND PROPERTIES --- @@ -774,6 +776,9 @@ private: */ void fixSelectionBoxIntUnion(); + //! Calculates ContentFeatures::&has_big_selection_box + void calcBigSelectionBox(content_t id, const ContentFeatures &def); + //! Features indexed by ID. std::vector m_content_features; From 23e502fa0efc4db4052ed26ed12e9666fbf51279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sat, 14 Dec 2024 17:02:16 +0100 Subject: [PATCH 55/68] Test & document conventions used by `matrix4::setRotation*` (#15542) Also includes a minor `matrix4::transformVect` refactor to make testing easier. --- irr/include/matrix4.h | 47 ++++++++++----------- src/unittest/CMakeLists.txt | 1 + src/unittest/test_irr_matrix4.cpp | 69 +++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 23 deletions(-) create mode 100644 src/unittest/test_irr_matrix4.cpp diff --git a/irr/include/matrix4.h b/irr/include/matrix4.h index 7897b9acb..ed74dc9d2 100644 --- a/irr/include/matrix4.h +++ b/irr/include/matrix4.h @@ -178,9 +178,13 @@ public: CMatrix4 &setInverseTranslation(const vector3d &translation); //! Make a rotation matrix from Euler angles. The 4th row and column are unmodified. + //! NOTE: Rotation order is ZYX. This means that vectors are + //! first rotated around the X, then the Y, and finally the Z axis. + //! NOTE: The rotation is done as per the right-hand rule. + //! See test_irr_matrix4.cpp if you're still unsure about the conventions used here. inline CMatrix4 &setRotationRadians(const vector3d &rotation); - //! Make a rotation matrix from Euler angles. The 4th row and column are unmodified. + //! Same as `setRotationRadians`, but uses degrees. CMatrix4 &setRotationDegrees(const vector3d &rotation); //! Get the rotation, as set by setRotation() when you already know the scale used to create the matrix @@ -236,12 +240,21 @@ public: [[nodiscard]] vector3d rotateAndScaleVect(const vector3d &vect) const; //! Transforms the vector by this matrix - /** This operation is performed as if the vector was 4d with the 4th component =1 */ - void transformVect(vector3df &vect) const; + /** This operation is performed as if the vector was 4d with the 4th component = 1 */ + [[nodiscard]] vector3d transformVect(const vector3d &v) const; + + //! Transforms the vector by this matrix + /** This operation is performed as if the vector was 4d with the 4th component = 1 */ + void transformVect(vector3d &vect) const { + const vector3d &v = vect; + vect = transformVect(v); + } //! Transforms input vector by this matrix and stores result in output vector - /** This operation is performed as if the vector was 4d with the 4th component =1 */ - void transformVect(vector3df &out, const vector3df &in) const; + /** This operation is performed as if the vector was 4d with the 4th component = 1 */ + void transformVect(vector3d &out, const vector3d &in) const { + out = transformVect(in); + } //! An alternate transform vector method, writing into an array of 4 floats /** This operation is performed as if the vector was 4d with the 4th component =1. @@ -1099,25 +1112,13 @@ inline vector3d CMatrix4::scaleThenInvRotVect(const vector3d &v) const } template -inline void CMatrix4::transformVect(vector3df &vect) const +inline vector3d CMatrix4::transformVect(const vector3d &v) const { - T vector[3]; - - vector[0] = vect.X * M[0] + vect.Y * M[4] + vect.Z * M[8] + M[12]; - vector[1] = vect.X * M[1] + vect.Y * M[5] + vect.Z * M[9] + M[13]; - vector[2] = vect.X * M[2] + vect.Y * M[6] + vect.Z * M[10] + M[14]; - - vect.X = static_cast(vector[0]); - vect.Y = static_cast(vector[1]); - vect.Z = static_cast(vector[2]); -} - -template -inline void CMatrix4::transformVect(vector3df &out, const vector3df &in) const -{ - out.X = in.X * M[0] + in.Y * M[4] + in.Z * M[8] + M[12]; - out.Y = in.X * M[1] + in.Y * M[5] + in.Z * M[9] + M[13]; - out.Z = in.X * M[2] + in.Y * M[6] + in.Z * M[10] + M[14]; + return { + v.X * M[0] + v.Y * M[4] + v.Z * M[8] + M[12], + v.X * M[1] + v.Y * M[5] + v.Z * M[9] + M[13], + v.X * M[2] + v.Y * M[6] + v.Z * M[10] + M[14], + }; } template diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt index 2b70a6918..b7ca17199 100644 --- a/src/unittest/CMakeLists.txt +++ b/src/unittest/CMakeLists.txt @@ -53,6 +53,7 @@ set (UNITTEST_CLIENT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_eventmanager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_gameui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_irr_gltf_mesh_loader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_irr_matrix4.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mesh_compare.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_keycode.cpp PARENT_SCOPE) diff --git a/src/unittest/test_irr_matrix4.cpp b/src/unittest/test_irr_matrix4.cpp new file mode 100644 index 000000000..6272c8c26 --- /dev/null +++ b/src/unittest/test_irr_matrix4.cpp @@ -0,0 +1,69 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "catch.h" +#include "irrMath.h" +#include "matrix4.h" +#include "irr_v3d.h" + +using matrix4 = core::matrix4; + +static bool matrix_equals(const matrix4 &a, const matrix4 &b) { + return a.equals(b, 0.00001f); +} + +constexpr v3f x{1, 0, 0}; +constexpr v3f y{0, 1, 0}; +constexpr v3f z{0, 0, 1}; + +TEST_CASE("matrix4") { + +SECTION("setRotationRadians") { + SECTION("rotation order is ZYX (matrix notation)") { + v3f rot{1, 2, 3}; + matrix4 X, Y, Z, ZYX; + X.setRotationRadians({rot.X, 0, 0}); + Y.setRotationRadians({0, rot.Y, 0}); + Z.setRotationRadians({0, 0, rot.Z}); + ZYX.setRotationRadians(rot); + CHECK(!matrix_equals(X * Y * Z, ZYX)); + CHECK(!matrix_equals(X * Z * Y, ZYX)); + CHECK(!matrix_equals(Y * X * Z, ZYX)); + CHECK(!matrix_equals(Y * Z * X, ZYX)); + CHECK(!matrix_equals(Z * X * Y, ZYX)); + CHECK(matrix_equals(Z * Y * X, ZYX)); + } + + const f32 quarter_turn = core::PI / 2; + + // See https://en.wikipedia.org/wiki/Right-hand_rule#/media/File:Cartesian_coordinate_system_handedness.svg + // for a visualization of what handedness means for rotations + + SECTION("rotation is right-handed") { + SECTION("rotation around the X-axis is Z-up, counter-clockwise") { + matrix4 X; + X.setRotationRadians({quarter_turn, 0, 0}); + CHECK(X.transformVect(x).equals(x)); + CHECK(X.transformVect(y).equals(z)); + CHECK(X.transformVect(z).equals(-y)); + } + + SECTION("rotation around the Y-axis is Z-up, clockwise") { + matrix4 Y; + Y.setRotationRadians({0, quarter_turn, 0}); + CHECK(Y.transformVect(y).equals(y)); + CHECK(Y.transformVect(x).equals(-z)); + CHECK(Y.transformVect(z).equals(x)); + } + + SECTION("rotation around the Z-axis is Y-up, counter-clockwise") { + matrix4 Z; + Z.setRotationRadians({0, 0, quarter_turn}); + CHECK(Z.transformVect(z).equals(z)); + CHECK(Z.transformVect(x).equals(y)); + CHECK(Z.transformVect(y).equals(-x)); + } + } +} + +} From fef28aced9236d80fc16ba928fb9661feac94207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sat, 14 Dec 2024 17:03:08 +0100 Subject: [PATCH 56/68] Irrlicht: Get rid of obsolete `setDebugName` (#15541) Co-authored-by: sfan5 --- irr/include/CIndexBuffer.h | 7 +------ irr/include/CMeshBuffer.h | 3 --- irr/include/CVertexBuffer.h | 7 +------ irr/include/IGUIElement.h | 4 ---- irr/include/IReferenceCounted.h | 25 ----------------------- irr/include/SAnimatedMesh.h | 3 --- irr/include/SMesh.h | 7 +------ irr/include/SSkinMeshBuffer.h | 3 --- irr/src/CAnimatedMeshSceneNode.cpp | 6 +----- irr/src/CAttributes.cpp | 7 +------ irr/src/CB3DMeshFileLoader.cpp | 6 +----- irr/src/CBillboardSceneNode.cpp | 4 ---- irr/src/CBoneSceneNode.cpp | 3 --- irr/src/CCameraSceneNode.cpp | 4 ---- irr/src/CDummyTransformationSceneNode.cpp | 4 ---- irr/src/CEGLManager.cpp | 6 +----- irr/src/CEmptySceneNode.cpp | 4 ---- irr/src/CFileList.cpp | 4 ---- irr/src/CFileSystem.cpp | 4 ---- irr/src/CGLXManager.cpp | 4 ---- irr/src/CGUIButton.cpp | 3 --- irr/src/CGUICheckBox.cpp | 4 ---- irr/src/CGUIComboBox.cpp | 4 ---- irr/src/CGUIEditBox.cpp | 4 ---- irr/src/CGUIEnvironment.cpp | 4 ---- irr/src/CGUIFileOpenDialog.cpp | 4 ---- irr/src/CGUIFont.cpp | 4 ---- irr/src/CGUIImage.cpp | 6 +----- irr/src/CGUIImageList.cpp | 4 ---- irr/src/CGUIListBox.cpp | 4 ---- irr/src/CGUIScrollBar.cpp | 4 ---- irr/src/CGUISkin.cpp | 4 ---- irr/src/CGUISpriteBank.cpp | 4 ---- irr/src/CGUIStaticText.cpp | 4 ---- irr/src/CGUITabControl.cpp | 8 -------- irr/src/CImageLoaderJPG.cpp | 3 --- irr/src/CImageWriterJPG.cpp | 6 +----- irr/src/CImageWriterPNG.cpp | 6 +----- irr/src/CIrrDeviceLinux.cpp | 4 ---- irr/src/CIrrDeviceOSX.mm | 4 ---- irr/src/CIrrDeviceSDL.cpp | 4 ---- irr/src/CIrrDeviceWin32.cpp | 4 ---- irr/src/CLimitReadFile.cpp | 4 ---- irr/src/CLogger.cpp | 6 +----- irr/src/CMemoryFile.cpp | 12 ++--------- irr/src/CMeshSceneNode.cpp | 4 ---- irr/src/CNSOGLManager.mm | 6 +----- irr/src/CNullDriver.cpp | 4 ---- irr/src/COBJMeshFileLoader.cpp | 6 +----- irr/src/COSOperator.cpp | 6 +----- irr/src/COpenGLCoreRenderTarget.h | 4 ---- irr/src/COpenGLDriver.cpp | 6 +----- irr/src/COpenGLSLMaterialRenderer.cpp | 4 ---- irr/src/CReadFile.cpp | 4 ---- irr/src/CSDLManager.cpp | 6 +----- irr/src/CSceneCollisionManager.cpp | 4 ---- irr/src/CSceneManager.cpp | 5 ----- irr/src/CWGLManager.cpp | 3 --- irr/src/CWriteFile.cpp | 4 ---- irr/src/CXMeshFileLoader.cpp | 6 +----- irr/src/CZipReader.cpp | 10 +-------- irr/src/OpenGL/Driver.cpp | 4 ---- irr/src/OpenGL/MaterialRenderer.cpp | 4 ---- irr/src/OpenGL/Renderer2D.cpp | 4 ---- src/gui/guiEditBoxWithScrollbar.cpp | 4 ---- src/gui/guiHyperText.cpp | 4 ---- src/irrlicht_changes/CGUITTFont.cpp | 3 --- src/irrlicht_changes/static_text.cpp | 4 ---- 68 files changed, 20 insertions(+), 322 deletions(-) diff --git a/irr/include/CIndexBuffer.h b/irr/include/CIndexBuffer.h index 904b0ab9a..ba85d49e6 100644 --- a/irr/include/CIndexBuffer.h +++ b/irr/include/CIndexBuffer.h @@ -24,12 +24,7 @@ class CIndexBuffer final : public IIndexBuffer { public: //! Default constructor for empty buffer - CIndexBuffer() - { -#ifdef _DEBUG - setDebugName("CIndexBuffer"); -#endif - } + CIndexBuffer() {} video::E_INDEX_TYPE getType() const override { diff --git a/irr/include/CMeshBuffer.h b/irr/include/CMeshBuffer.h index 9a6d4426f..d0c41ccfa 100644 --- a/irr/include/CMeshBuffer.h +++ b/irr/include/CMeshBuffer.h @@ -22,9 +22,6 @@ public: CMeshBuffer() : PrimitiveType(EPT_TRIANGLES) { -#ifdef _DEBUG - setDebugName("CMeshBuffer"); -#endif Vertices = new CVertexBuffer(); Indices = new SIndexBuffer(); } diff --git a/irr/include/CVertexBuffer.h b/irr/include/CVertexBuffer.h index 4b3f33688..1bface16c 100644 --- a/irr/include/CVertexBuffer.h +++ b/irr/include/CVertexBuffer.h @@ -24,12 +24,7 @@ class CVertexBuffer final : public IVertexBuffer { public: //! Default constructor for empty buffer - CVertexBuffer() - { -#ifdef _DEBUG - setDebugName("CVertexBuffer"); -#endif - } + CVertexBuffer() {} const void *getData() const override { diff --git a/irr/include/IGUIElement.h b/irr/include/IGUIElement.h index cffac10d1..429bc06b3 100644 --- a/irr/include/IGUIElement.h +++ b/irr/include/IGUIElement.h @@ -34,10 +34,6 @@ public: AlignLeft(EGUIA_UPPERLEFT), AlignRight(EGUIA_UPPERLEFT), AlignTop(EGUIA_UPPERLEFT), AlignBottom(EGUIA_UPPERLEFT), Environment(environment), Type(type) { -#ifdef _DEBUG - setDebugName("IGUIElement"); -#endif - // if we were given a parent to attach to if (parent) { parent->addChildToEnd(this); diff --git a/irr/include/IReferenceCounted.h b/irr/include/IReferenceCounted.h index 68aa20fb6..65c991db2 100644 --- a/irr/include/IReferenceCounted.h +++ b/irr/include/IReferenceCounted.h @@ -136,31 +136,6 @@ public: return ReferenceCounter; } -#ifdef _DEBUG - //! Returns the debug name of the object. - /** The Debugname may only be set and changed by the object - itself. This method should only be used in Debug mode. - \return Returns a string, previously set by setDebugName(); */ - const c8 *getDebugName() const - { - return DebugName; - } - -protected: - //! Sets the debug name of the object. - /** The Debugname may only be set and changed by the object - itself. This method should only be used in Debug mode. - \param newName: New debug name to set. */ - void setDebugName(const c8 *newName) - { - DebugName = newName; - } - -private: - //! The debug name. - const c8 *DebugName = nullptr; -#endif - private: //! The reference counter. Mutable to do reference counting on const objects. diff --git a/irr/include/SAnimatedMesh.h b/irr/include/SAnimatedMesh.h index dd7306633..8f7156236 100644 --- a/irr/include/SAnimatedMesh.h +++ b/irr/include/SAnimatedMesh.h @@ -21,9 +21,6 @@ struct SAnimatedMesh final : public IAnimatedMesh SAnimatedMesh(scene::IMesh *mesh = 0, scene::E_ANIMATED_MESH_TYPE type = scene::EAMT_UNKNOWN) : IAnimatedMesh(), FramesPerSecond(25.f), Type(type) { -#ifdef _DEBUG - setDebugName("SAnimatedMesh"); -#endif addMesh(mesh); recalculateBoundingBox(); } diff --git a/irr/include/SMesh.h b/irr/include/SMesh.h index 15fa65115..c9a76b051 100644 --- a/irr/include/SMesh.h +++ b/irr/include/SMesh.h @@ -17,12 +17,7 @@ namespace scene struct SMesh final : public IMesh { //! constructor - SMesh() - { -#ifdef _DEBUG - setDebugName("SMesh"); -#endif - } + SMesh() {} //! destructor virtual ~SMesh() diff --git a/irr/include/SSkinMeshBuffer.h b/irr/include/SSkinMeshBuffer.h index b0b6658b3..82c0d9f37 100644 --- a/irr/include/SSkinMeshBuffer.h +++ b/irr/include/SSkinMeshBuffer.h @@ -22,9 +22,6 @@ struct SSkinMeshBuffer final : public IMeshBuffer VertexType(vt), PrimitiveType(EPT_TRIANGLES), BoundingBoxNeedsRecalculated(true) { -#ifdef _DEBUG - setDebugName("SSkinMeshBuffer"); -#endif Vertices_Tangents = new SVertexBufferTangents(); Vertices_2TCoords = new SVertexBufferLightMap(); Vertices_Standard = new SVertexBuffer(); diff --git a/irr/src/CAnimatedMeshSceneNode.cpp b/irr/src/CAnimatedMeshSceneNode.cpp index d249e37c5..3871d52a3 100644 --- a/irr/src/CAnimatedMeshSceneNode.cpp +++ b/irr/src/CAnimatedMeshSceneNode.cpp @@ -38,10 +38,6 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh *mesh, Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false), LoopCallBack(0), PassCount(0) { -#ifdef _DEBUG - setDebugName("CAnimatedMeshSceneNode"); -#endif - setMesh(mesh); } @@ -227,7 +223,7 @@ void CAnimatedMeshSceneNode::render() Box = m->getBoundingBox(); } else { #ifdef _DEBUG - os::Printer::log("Animated Mesh returned no mesh to render.", Mesh->getDebugName(), ELL_WARNING); + os::Printer::log("Animated Mesh returned no mesh to render.", ELL_WARNING); #endif return; } diff --git a/irr/src/CAttributes.cpp b/irr/src/CAttributes.cpp index b1509e455..924bd1a45 100644 --- a/irr/src/CAttributes.cpp +++ b/irr/src/CAttributes.cpp @@ -12,12 +12,7 @@ namespace irr namespace io { -CAttributes::CAttributes() -{ -#ifdef _DEBUG - setDebugName("CAttributes"); -#endif -} +CAttributes::CAttributes() {} CAttributes::~CAttributes() { diff --git a/irr/src/CB3DMeshFileLoader.cpp b/irr/src/CB3DMeshFileLoader.cpp index 3bcc4b85f..e99bd2eed 100644 --- a/irr/src/CB3DMeshFileLoader.cpp +++ b/irr/src/CB3DMeshFileLoader.cpp @@ -29,11 +29,7 @@ namespace scene CB3DMeshFileLoader::CB3DMeshFileLoader(scene::ISceneManager *smgr) : AnimatedMesh(0), B3DFile(0), VerticesStart(0), NormalsInFile(false), HasVertexColors(false), ShowWarning(true) -{ -#ifdef _DEBUG - setDebugName("CB3DMeshFileLoader"); -#endif -} +{} //! returns true if the file maybe is able to be loaded by this class //! based on the file extension (e.g. ".bsp") diff --git a/irr/src/CBillboardSceneNode.cpp b/irr/src/CBillboardSceneNode.cpp index 1c6d88dae..6769e8f04 100644 --- a/irr/src/CBillboardSceneNode.cpp +++ b/irr/src/CBillboardSceneNode.cpp @@ -20,10 +20,6 @@ CBillboardSceneNode::CBillboardSceneNode(ISceneNode *parent, ISceneManager *mgr, IBillboardSceneNode(parent, mgr, id, position), Buffer(new SMeshBuffer()) { -#ifdef _DEBUG - setDebugName("CBillboardSceneNode"); -#endif - setSize(size); auto &Vertices = Buffer->Vertices->Data; diff --git a/irr/src/CBoneSceneNode.cpp b/irr/src/CBoneSceneNode.cpp index b9eb8892e..7aa637094 100644 --- a/irr/src/CBoneSceneNode.cpp +++ b/irr/src/CBoneSceneNode.cpp @@ -18,9 +18,6 @@ CBoneSceneNode::CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id, BoneIndex(boneIndex), AnimationMode(EBAM_AUTOMATIC), SkinningSpace(EBSS_LOCAL) { -#ifdef _DEBUG - setDebugName("CBoneSceneNode"); -#endif setName(boneName); } diff --git a/irr/src/CCameraSceneNode.cpp b/irr/src/CCameraSceneNode.cpp index f8899e5ff..e1c3c96f6 100644 --- a/irr/src/CCameraSceneNode.cpp +++ b/irr/src/CCameraSceneNode.cpp @@ -20,10 +20,6 @@ CCameraSceneNode::CCameraSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 i Target(lookat), UpVector(0.0f, 1.0f, 0.0f), ZNear(1.0f), ZFar(3000.0f), InputReceiverEnabled(true), TargetAndRotationAreBound(false) { -#ifdef _DEBUG - setDebugName("CCameraSceneNode"); -#endif - // set default projection Fovy = core::PI / 2.5f; // Field of view, in radians. Aspect = 4.0f / 3.0f; // Aspect ratio. diff --git a/irr/src/CDummyTransformationSceneNode.cpp b/irr/src/CDummyTransformationSceneNode.cpp index 73953dd38..e354038df 100644 --- a/irr/src/CDummyTransformationSceneNode.cpp +++ b/irr/src/CDummyTransformationSceneNode.cpp @@ -15,10 +15,6 @@ CDummyTransformationSceneNode::CDummyTransformationSceneNode( ISceneNode *parent, ISceneManager *mgr, s32 id) : IDummyTransformationSceneNode(parent, mgr, id) { -#ifdef _DEBUG - setDebugName("CDummyTransformationSceneNode"); -#endif - setAutomaticCulling(scene::EAC_OFF); } diff --git a/irr/src/CEGLManager.cpp b/irr/src/CEGLManager.cpp index 6c39c5c74..b70a0c091 100644 --- a/irr/src/CEGLManager.cpp +++ b/irr/src/CEGLManager.cpp @@ -18,11 +18,7 @@ namespace video CEGLManager::CEGLManager() : IContextManager(), EglWindow(0), EglDisplay(EGL_NO_DISPLAY), EglSurface(EGL_NO_SURFACE), EglContext(EGL_NO_CONTEXT), EglConfig(0), MajorVersion(0), MinorVersion(0) -{ -#ifdef _DEBUG - setDebugName("CEGLManager"); -#endif -} +{} CEGLManager::~CEGLManager() { diff --git a/irr/src/CEmptySceneNode.cpp b/irr/src/CEmptySceneNode.cpp index 2b965a198..70a2ca21d 100644 --- a/irr/src/CEmptySceneNode.cpp +++ b/irr/src/CEmptySceneNode.cpp @@ -14,10 +14,6 @@ namespace scene CEmptySceneNode::CEmptySceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id) : ISceneNode(parent, mgr, id) { -#ifdef _DEBUG - setDebugName("CEmptySceneNode"); -#endif - setAutomaticCulling(scene::EAC_OFF); } diff --git a/irr/src/CFileList.cpp b/irr/src/CFileList.cpp index cd6c85df4..9294f3f46 100644 --- a/irr/src/CFileList.cpp +++ b/irr/src/CFileList.cpp @@ -18,10 +18,6 @@ static const io::path emptyFileListEntry; CFileList::CFileList(const io::path &path, bool ignoreCase, bool ignorePaths) : IgnorePaths(ignorePaths), IgnoreCase(ignoreCase), Path(path) { -#ifdef _DEBUG - setDebugName("CFileList"); -#endif - Path.replace('\\', '/'); } diff --git a/irr/src/CFileSystem.cpp b/irr/src/CFileSystem.cpp index 4b938c4c5..d8f04eb1d 100644 --- a/irr/src/CFileSystem.cpp +++ b/irr/src/CFileSystem.cpp @@ -43,10 +43,6 @@ namespace io //! constructor CFileSystem::CFileSystem() { -#ifdef _DEBUG - setDebugName("CFileSystem"); -#endif - setFileListSystem(FILESYSTEM_NATIVE); //! reset current working directory getWorkingDirectory(); diff --git a/irr/src/CGLXManager.cpp b/irr/src/CGLXManager.cpp index 8593621b7..89a5cac20 100644 --- a/irr/src/CGLXManager.cpp +++ b/irr/src/CGLXManager.cpp @@ -23,10 +23,6 @@ namespace video CGLXManager::CGLXManager(const SIrrlichtCreationParameters ¶ms, const SExposedVideoData &videodata, int screennr) : Params(params), PrimaryContext(videodata), VisualInfo(0), glxFBConfig(0), GlxWin(0) { -#ifdef _DEBUG - setDebugName("CGLXManager"); -#endif - CurrentContext.OpenGLLinux.X11Display = PrimaryContext.OpenGLLinux.X11Display; int major, minor; diff --git a/irr/src/CGUIButton.cpp b/irr/src/CGUIButton.cpp index 60bab5f83..ea685be94 100644 --- a/irr/src/CGUIButton.cpp +++ b/irr/src/CGUIButton.cpp @@ -26,9 +26,6 @@ CGUIButton::CGUIButton(IGUIEnvironment *environment, IGUIElement *parent, IsPushButton(false), Pressed(false), UseAlphaChannel(false), DrawBorder(true), ScaleImage(false) { -#ifdef _DEBUG - setDebugName("CGUIButton"); -#endif setNotClipped(noclip); // This element can be tabbed. diff --git a/irr/src/CGUICheckBox.cpp b/irr/src/CGUICheckBox.cpp index 18dd6856a..4d407e676 100644 --- a/irr/src/CGUICheckBox.cpp +++ b/irr/src/CGUICheckBox.cpp @@ -19,10 +19,6 @@ namespace gui CGUICheckBox::CGUICheckBox(bool checked, IGUIEnvironment *environment, IGUIElement *parent, s32 id, core::rect rectangle) : IGUICheckBox(environment, parent, id, rectangle), CheckTime(0), Pressed(false), Checked(checked), Border(false), Background(false) { -#ifdef _DEBUG - setDebugName("CGUICheckBox"); -#endif - // this element can be tabbed into setTabStop(true); setTabOrder(-1); diff --git a/irr/src/CGUIComboBox.cpp b/irr/src/CGUIComboBox.cpp index 0088641b5..b55602b0c 100644 --- a/irr/src/CGUIComboBox.cpp +++ b/irr/src/CGUIComboBox.cpp @@ -26,10 +26,6 @@ CGUIComboBox::CGUIComboBox(IGUIEnvironment *environment, IGUIElement *parent, Selected(-1), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER), MaxSelectionRows(5), HasFocus(false), ActiveFont(nullptr) { -#ifdef _DEBUG - setDebugName("CGUIComboBox"); -#endif - IGUISkin *skin = Environment->getSkin(); ListButton = Environment->addButton(core::recti(0, 0, 1, 1), this, -1, L""); diff --git a/irr/src/CGUIEditBox.cpp b/irr/src/CGUIEditBox.cpp index 8d03caf4a..7d1571606 100644 --- a/irr/src/CGUIEditBox.cpp +++ b/irr/src/CGUIEditBox.cpp @@ -39,10 +39,6 @@ CGUIEditBox::CGUIEditBox(const wchar_t *text, bool border, PasswordChar(L'*'), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER), CurrentTextRect(0, 0, 1, 1), FrameRect(rectangle) { -#ifdef _DEBUG - setDebugName("CGUIEditBox"); -#endif - Text = text; if (Environment) diff --git a/irr/src/CGUIEnvironment.cpp b/irr/src/CGUIEnvironment.cpp index 80115ab11..b40896327 100644 --- a/irr/src/CGUIEnvironment.cpp +++ b/irr/src/CGUIEnvironment.cpp @@ -50,10 +50,6 @@ CGUIEnvironment::CGUIEnvironment(io::IFileSystem *fs, video::IVideoDriver *drive if (Operator) Operator->grab(); -#ifdef _DEBUG - IGUIEnvironment::setDebugName("CGUIEnvironment"); -#endif - loadBuiltInFont(); IGUISkin *skin = createSkin(gui::EGST_WINDOWS_METALLIC); diff --git a/irr/src/CGUIFileOpenDialog.cpp b/irr/src/CGUIFileOpenDialog.cpp index 1e669b255..7add9d8e1 100644 --- a/irr/src/CGUIFileOpenDialog.cpp +++ b/irr/src/CGUIFileOpenDialog.cpp @@ -33,10 +33,6 @@ CGUIFileOpenDialog::CGUIFileOpenDialog(const wchar_t *title, (parent->getAbsolutePosition().getHeight() - FOD_HEIGHT) / 2 + FOD_HEIGHT)), FileNameText(0), FileList(0), Dragging(false) { -#ifdef _DEBUG - IGUIElement::setDebugName("CGUIFileOpenDialog"); -#endif - Text = title; FileSystem = Environment ? Environment->getFileSystem() : 0; diff --git a/irr/src/CGUIFont.cpp b/irr/src/CGUIFont.cpp index 4d85702f5..c00b40395 100644 --- a/irr/src/CGUIFont.cpp +++ b/irr/src/CGUIFont.cpp @@ -21,10 +21,6 @@ CGUIFont::CGUIFont(IGUIEnvironment *env, const io::path &filename) : Driver(0), SpriteBank(0), Environment(env), WrongCharacter(0), MaxHeight(0), GlobalKerningWidth(0), GlobalKerningHeight(0) { -#ifdef _DEBUG - setDebugName("CGUIFont"); -#endif - if (Environment) { // don't grab environment, to avoid circular references Driver = Environment->getVideoDriver(); diff --git a/irr/src/CGUIImage.cpp b/irr/src/CGUIImage.cpp index 5f7bb8e26..a17e8f2d0 100644 --- a/irr/src/CGUIImage.cpp +++ b/irr/src/CGUIImage.cpp @@ -17,11 +17,7 @@ namespace gui CGUIImage::CGUIImage(IGUIEnvironment *environment, IGUIElement *parent, s32 id, core::rect rectangle) : IGUIImage(environment, parent, id, rectangle), Texture(0), Color(255, 255, 255, 255), UseAlphaChannel(false), ScaleImage(false), DrawBounds(0.f, 0.f, 1.f, 1.f), DrawBackground(true) -{ -#ifdef _DEBUG - setDebugName("CGUIImage"); -#endif -} +{} //! destructor CGUIImage::~CGUIImage() diff --git a/irr/src/CGUIImageList.cpp b/irr/src/CGUIImageList.cpp index 3bd30d913..b11f10b0c 100644 --- a/irr/src/CGUIImageList.cpp +++ b/irr/src/CGUIImageList.cpp @@ -18,10 +18,6 @@ CGUIImageList::CGUIImageList(video::IVideoDriver *driver) : ImagesPerRow(0), UseAlphaChannel(false) { -#ifdef _DEBUG - setDebugName("CGUIImageList"); -#endif - if (Driver) { Driver->grab(); } diff --git a/irr/src/CGUIListBox.cpp b/irr/src/CGUIListBox.cpp index 78cb96ecf..f8db2cf80 100644 --- a/irr/src/CGUIListBox.cpp +++ b/irr/src/CGUIListBox.cpp @@ -29,10 +29,6 @@ CGUIListBox::CGUIListBox(IGUIEnvironment *environment, IGUIElement *parent, ScrollBar(0), selectTime(0), LastKeyTime(0), Selecting(false), DrawBack(drawBack), MoveOverSelect(moveOverSelect), AutoScroll(true), HighlightWhenNotFocused(true) { -#ifdef _DEBUG - setDebugName("CGUIListBox"); -#endif - IGUISkin *skin = Environment->getSkin(); ScrollBar = new CGUIScrollBar(false, Environment, this, -1, diff --git a/irr/src/CGUIScrollBar.cpp b/irr/src/CGUIScrollBar.cpp index f9ebad09e..5e7737bc1 100644 --- a/irr/src/CGUIScrollBar.cpp +++ b/irr/src/CGUIScrollBar.cpp @@ -28,10 +28,6 @@ CGUIScrollBar::CGUIScrollBar(bool horizontal, IGUIEnvironment *environment, DrawHeight(0), Min(0), Max(100), SmallStep(10), LargeStep(50), DesiredPos(0), LastChange(0) { -#ifdef _DEBUG - setDebugName("CGUIScrollBar"); -#endif - refreshControls(); setNotClipped(noclip); diff --git a/irr/src/CGUISkin.cpp b/irr/src/CGUISkin.cpp index e9721a5fa..3c130f7a1 100644 --- a/irr/src/CGUISkin.cpp +++ b/irr/src/CGUISkin.cpp @@ -20,10 +20,6 @@ namespace gui CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver) : SpriteBank(0), Driver(driver), Type(type) { - #ifdef _DEBUG - setDebugName("CGUISkin"); - #endif - if ((Type == EGST_WINDOWS_CLASSIC) || (Type == EGST_WINDOWS_METALLIC)) { Colors[EGDC_3D_DARK_SHADOW] = video::SColor(101,50,50,50); diff --git a/irr/src/CGUISpriteBank.cpp b/irr/src/CGUISpriteBank.cpp index fe0c087b8..ea3fe8788 100644 --- a/irr/src/CGUISpriteBank.cpp +++ b/irr/src/CGUISpriteBank.cpp @@ -16,10 +16,6 @@ namespace gui CGUISpriteBank::CGUISpriteBank(IGUIEnvironment *env) : Environment(env), Driver(0) { -#ifdef _DEBUG - setDebugName("CGUISpriteBank"); -#endif - if (Environment) { Driver = Environment->getVideoDriver(); if (Driver) diff --git a/irr/src/CGUIStaticText.cpp b/irr/src/CGUIStaticText.cpp index 035847583..871589447 100644 --- a/irr/src/CGUIStaticText.cpp +++ b/irr/src/CGUIStaticText.cpp @@ -27,10 +27,6 @@ CGUIStaticText::CGUIStaticText(const wchar_t *text, bool border, OverrideColor(video::SColor(101, 255, 255, 255)), BGColor(video::SColor(101, 210, 210, 210)), OverrideFont(0), LastBreakFont(0) { -#ifdef _DEBUG - setDebugName("CGUIStaticText"); -#endif - Text = text; if (environment && environment->getSkin()) { BGColor = environment->getSkin()->getColor(gui::EGDC_3D_FACE); diff --git a/irr/src/CGUITabControl.cpp b/irr/src/CGUITabControl.cpp index 3e02773db..bca7f0f21 100644 --- a/irr/src/CGUITabControl.cpp +++ b/irr/src/CGUITabControl.cpp @@ -29,10 +29,6 @@ CGUITab::CGUITab(IGUIEnvironment *environment, BackColor(0, 0, 0, 0), OverrideTextColorEnabled(false), TextColor(255, 0, 0, 0), DrawBackground(false) { -#ifdef _DEBUG - setDebugName("CGUITab"); -#endif - const IGUISkin *const skin = environment->getSkin(); if (skin) TextColor = skin->getColor(EGDC_BUTTON_TEXT); @@ -104,10 +100,6 @@ CGUITabControl::CGUITabControl(IGUIEnvironment *environment, Border(border), FillBackground(fillbackground), ScrollControl(false), TabHeight(0), VerticalAlignment(EGUIA_UPPERLEFT), UpButton(0), DownButton(0), TabMaxWidth(0), CurrentScrollTabIndex(0), TabExtraWidth(20) { -#ifdef _DEBUG - setDebugName("CGUITabControl"); -#endif - IGUISkin *skin = Environment->getSkin(); IGUISpriteBank *sprites = 0; diff --git a/irr/src/CImageLoaderJPG.cpp b/irr/src/CImageLoaderJPG.cpp index 5b7978a4b..ec1a998b9 100644 --- a/irr/src/CImageLoaderJPG.cpp +++ b/irr/src/CImageLoaderJPG.cpp @@ -19,9 +19,6 @@ namespace video //! constructor CImageLoaderJPG::CImageLoaderJPG() { -#ifdef _DEBUG - setDebugName("CImageLoaderJPG"); -#endif } //! destructor diff --git a/irr/src/CImageWriterJPG.cpp b/irr/src/CImageWriterJPG.cpp index 1220c47e6..778d1fb74 100644 --- a/irr/src/CImageWriterJPG.cpp +++ b/irr/src/CImageWriterJPG.cpp @@ -169,11 +169,7 @@ IImageWriter *createImageWriterJPG() } CImageWriterJPG::CImageWriterJPG() -{ -#ifdef _DEBUG - setDebugName("CImageWriterJPG"); -#endif -} +{} bool CImageWriterJPG::isAWriteableFileExtension(const io::path &filename) const { diff --git a/irr/src/CImageWriterPNG.cpp b/irr/src/CImageWriterPNG.cpp index 35e33c3d5..51332d285 100644 --- a/irr/src/CImageWriterPNG.cpp +++ b/irr/src/CImageWriterPNG.cpp @@ -54,11 +54,7 @@ void PNGAPI user_write_data_fcn(png_structp png_ptr, png_bytep data, png_size_t } CImageWriterPNG::CImageWriterPNG() -{ -#ifdef _DEBUG - setDebugName("CImageWriterPNG"); -#endif -} +{} bool CImageWriterPNG::isAWriteableFileExtension(const io::path &filename) const { diff --git a/irr/src/CIrrDeviceLinux.cpp b/irr/src/CIrrDeviceLinux.cpp index 0b9ef5b71..7c5d9cf0b 100644 --- a/irr/src/CIrrDeviceLinux.cpp +++ b/irr/src/CIrrDeviceLinux.cpp @@ -105,10 +105,6 @@ CIrrDeviceLinux::CIrrDeviceLinux(const SIrrlichtCreationParameters ¶m) : WindowHasFocus(false), WindowMinimized(false), WindowMaximized(param.WindowMaximized), ExternalWindow(false), AutorepeatSupport(0) { -#ifdef _DEBUG - setDebugName("CIrrDeviceLinux"); -#endif - // print version, distribution etc. // thx to LynxLuna for pointing me to the uname function core::stringc linuxversion; diff --git a/irr/src/CIrrDeviceOSX.mm b/irr/src/CIrrDeviceOSX.mm index 4b46e5e29..859d9b752 100644 --- a/irr/src/CIrrDeviceOSX.mm +++ b/irr/src/CIrrDeviceOSX.mm @@ -524,10 +524,6 @@ CIrrDeviceMacOSX::CIrrDeviceMacOSX(const SIrrlichtCreationParameters ¶m) : { struct utsname name; -#ifdef _DEBUG - setDebugName("CIrrDeviceMacOSX"); -#endif - if (firstLaunch) { firstLaunch = false; diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index 543bea63e..f7974202f 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -253,10 +253,6 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m) : Resizable(param.WindowResizable == 1 ? true : false), CurrentTouchCount(0), IsInBackground(false) { -#ifdef _DEBUG - setDebugName("CIrrDeviceSDL"); -#endif - if (++SDLDeviceInstances == 1) { #ifdef __ANDROID__ // Blocking on pause causes problems with multiplayer. diff --git a/irr/src/CIrrDeviceWin32.cpp b/irr/src/CIrrDeviceWin32.cpp index 417694517..f0947163d 100644 --- a/irr/src/CIrrDeviceWin32.cpp +++ b/irr/src/CIrrDeviceWin32.cpp @@ -713,10 +713,6 @@ CIrrDeviceWin32::CIrrDeviceWin32(const SIrrlichtCreationParameters ¶ms) : ExternalWindow(false), Win32CursorControl(0), JoyControl(0), WindowMaximized(params.WindowMaximized) { -#ifdef _DEBUG - setDebugName("CIrrDeviceWin32"); -#endif - // get windows version and create OS operator core::stringc winversion; getWindowsVersion(winversion); diff --git a/irr/src/CLimitReadFile.cpp b/irr/src/CLimitReadFile.cpp index de9bf9ec3..b13120194 100644 --- a/irr/src/CLimitReadFile.cpp +++ b/irr/src/CLimitReadFile.cpp @@ -17,10 +17,6 @@ CLimitReadFile::CLimitReadFile(IReadFile *alreadyOpenedFile, long pos, AreaStart(0), AreaEnd(0), Pos(0), File(alreadyOpenedFile) { -#ifdef _DEBUG - setDebugName("CLimitReadFile"); -#endif - if (File) { File->grab(); AreaStart = pos; diff --git a/irr/src/CLogger.cpp b/irr/src/CLogger.cpp index 70d06b36a..2bb589a99 100644 --- a/irr/src/CLogger.cpp +++ b/irr/src/CLogger.cpp @@ -9,11 +9,7 @@ namespace irr CLogger::CLogger(IEventReceiver *r) : LogLevel(ELL_INFORMATION), Receiver(r) -{ -#ifdef _DEBUG - setDebugName("CLogger"); -#endif -} +{} //! Returns the current set log level. ELOG_LEVEL CLogger::getLogLevel() const diff --git a/irr/src/CMemoryFile.cpp b/irr/src/CMemoryFile.cpp index 6d7906b91..4e6baa99c 100644 --- a/irr/src/CMemoryFile.cpp +++ b/irr/src/CMemoryFile.cpp @@ -12,11 +12,7 @@ namespace io CMemoryReadFile::CMemoryReadFile(const void *memory, long len, const io::path &fileName, bool d) : Buffer(memory), Len(len), Pos(0), Filename(fileName), deleteMemoryWhenDropped(d) -{ -#ifdef _DEBUG - setDebugName("CMemoryReadFile"); -#endif -} +{} CMemoryReadFile::~CMemoryReadFile() { @@ -82,11 +78,7 @@ const io::path &CMemoryReadFile::getFileName() const CMemoryWriteFile::CMemoryWriteFile(void *memory, long len, const io::path &fileName, bool d) : Buffer(memory), Len(len), Pos(0), Filename(fileName), deleteMemoryWhenDropped(d) -{ -#ifdef _DEBUG - setDebugName("CMemoryWriteFile"); -#endif -} +{} CMemoryWriteFile::~CMemoryWriteFile() { diff --git a/irr/src/CMeshSceneNode.cpp b/irr/src/CMeshSceneNode.cpp index 6d02eada5..2d9e400e9 100644 --- a/irr/src/CMeshSceneNode.cpp +++ b/irr/src/CMeshSceneNode.cpp @@ -23,10 +23,6 @@ CMeshSceneNode::CMeshSceneNode(IMesh *mesh, ISceneNode *parent, ISceneManager *m Mesh(0), PassCount(0), ReadOnlyMaterials(false) { -#ifdef _DEBUG - setDebugName("CMeshSceneNode"); -#endif - setMesh(mesh); } diff --git a/irr/src/CNSOGLManager.mm b/irr/src/CNSOGLManager.mm index c1e543e53..b550df20e 100644 --- a/irr/src/CNSOGLManager.mm +++ b/irr/src/CNSOGLManager.mm @@ -16,11 +16,7 @@ namespace video CNSOGLManager::CNSOGLManager() : PrimaryContext(SExposedVideoData(0)), PixelFormat(nil) -{ -#ifdef _DEBUG - setDebugName("CNSOGLManager"); -#endif -} +{} CNSOGLManager::~CNSOGLManager() { diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index efbad7598..80aacb042 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -53,10 +53,6 @@ CNullDriver::CNullDriver(io::IFileSystem *io, const core::dimension2d &scre ViewPort(0, 0, 0, 0), ScreenSize(screenSize), MinVertexCountForVBO(500), TextureCreationFlags(0), OverrideMaterial2DEnabled(false), AllowZWriteOnTransparent(false) { -#ifdef _DEBUG - setDebugName("CNullDriver"); -#endif - DriverAttributes = new io::CAttributes(); DriverAttributes->addInt("MaxTextures", MATERIAL_MAX_TEXTURES); DriverAttributes->addInt("MaxSupportedTextures", MATERIAL_MAX_TEXTURES); diff --git a/irr/src/COBJMeshFileLoader.cpp b/irr/src/COBJMeshFileLoader.cpp index 97e90c322..fdbcbd1d0 100644 --- a/irr/src/COBJMeshFileLoader.cpp +++ b/irr/src/COBJMeshFileLoader.cpp @@ -26,11 +26,7 @@ namespace scene //! Constructor COBJMeshFileLoader::COBJMeshFileLoader(scene::ISceneManager *smgr) : SceneManager(smgr) -{ -#ifdef _DEBUG - setDebugName("COBJMeshFileLoader"); -#endif -} +{} //! destructor COBJMeshFileLoader::~COBJMeshFileLoader() diff --git a/irr/src/COSOperator.cpp b/irr/src/COSOperator.cpp index d53223916..518f26563 100644 --- a/irr/src/COSOperator.cpp +++ b/irr/src/COSOperator.cpp @@ -43,11 +43,7 @@ COSOperator::COSOperator(const core::stringc &osVersion, CIrrDeviceLinux *device // constructor COSOperator::COSOperator(const core::stringc &osVersion) : OperatingSystem(osVersion) -{ -#ifdef _DEBUG - setDebugName("COSOperator"); -#endif -} +{} COSOperator::~COSOperator() { diff --git a/irr/src/COpenGLCoreRenderTarget.h b/irr/src/COpenGLCoreRenderTarget.h index a174e926e..50656ce1f 100644 --- a/irr/src/COpenGLCoreRenderTarget.h +++ b/irr/src/COpenGLCoreRenderTarget.h @@ -28,10 +28,6 @@ public: AssignedDepth(false), AssignedStencil(false), RequestTextureUpdate(false), RequestDepthStencilUpdate(false), BufferID(0), ColorAttachment(0), MultipleRenderTarget(0), Driver(driver) { -#ifdef _DEBUG - setDebugName("COpenGLCoreRenderTarget"); -#endif - DriverType = Driver->getDriverType(); Size = Driver->getScreenSize(); diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index f7c0be7dd..7d98482e8 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -32,11 +32,7 @@ COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters ¶ms, io::IFil CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(), CacheHandler(0), CurrentRenderMode(ERM_NONE), ResetRenderStates(true), Transformation3DChanged(true), AntiAlias(params.AntiAlias), ColorFormat(ECF_R8G8B8), FixedPipelineState(EOFPS_ENABLE), Params(params), ContextManager(contextManager) -{ -#ifdef _DEBUG - setDebugName("COpenGLDriver"); -#endif -} +{} bool COpenGLDriver::initDriver() { diff --git a/irr/src/COpenGLSLMaterialRenderer.cpp b/irr/src/COpenGLSLMaterialRenderer.cpp index 27393adeb..de0f090c3 100644 --- a/irr/src/COpenGLSLMaterialRenderer.cpp +++ b/irr/src/COpenGLSLMaterialRenderer.cpp @@ -50,10 +50,6 @@ COpenGLSLMaterialRenderer::COpenGLSLMaterialRenderer(video::COpenGLDriver *drive Driver(driver), CallBack(callback), Alpha(false), Blending(false), AlphaTest(false), Program(0), Program2(0), UserData(userData) { -#ifdef _DEBUG - setDebugName("COpenGLSLMaterialRenderer"); -#endif - switch (baseMaterial) { case EMT_TRANSPARENT_VERTEX_ALPHA: case EMT_TRANSPARENT_ALPHA_CHANNEL: diff --git a/irr/src/CReadFile.cpp b/irr/src/CReadFile.cpp index 6c6e49d55..3ef90e715 100644 --- a/irr/src/CReadFile.cpp +++ b/irr/src/CReadFile.cpp @@ -12,10 +12,6 @@ namespace io CReadFile::CReadFile(const io::path &fileName) : File(0), FileSize(0), Filename(fileName) { -#ifdef _DEBUG - setDebugName("CReadFile"); -#endif - openFile(); } diff --git a/irr/src/CSDLManager.cpp b/irr/src/CSDLManager.cpp index 855c8c9e4..56fe9e0d7 100644 --- a/irr/src/CSDLManager.cpp +++ b/irr/src/CSDLManager.cpp @@ -15,11 +15,7 @@ namespace video CSDLManager::CSDLManager(CIrrDeviceSDL *device) : IContextManager(), SDLDevice(device) -{ -#ifdef _DEBUG - setDebugName("CSDLManager"); -#endif -} +{} bool CSDLManager::initialize(const SIrrlichtCreationParameters ¶ms, const SExposedVideoData &data) { diff --git a/irr/src/CSceneCollisionManager.cpp b/irr/src/CSceneCollisionManager.cpp index 77549a7dc..3d8f1091f 100644 --- a/irr/src/CSceneCollisionManager.cpp +++ b/irr/src/CSceneCollisionManager.cpp @@ -17,10 +17,6 @@ namespace scene CSceneCollisionManager::CSceneCollisionManager(ISceneManager *smanager, video::IVideoDriver *driver) : SceneManager(smanager), Driver(driver) { -#ifdef _DEBUG - setDebugName("CSceneCollisionManager"); -#endif - if (Driver) Driver->grab(); } diff --git a/irr/src/CSceneManager.cpp b/irr/src/CSceneManager.cpp index a6716525a..94b6c24a7 100644 --- a/irr/src/CSceneManager.cpp +++ b/irr/src/CSceneManager.cpp @@ -44,11 +44,6 @@ CSceneManager::CSceneManager(video::IVideoDriver *driver, ActiveCamera(0), Parameters(0), MeshCache(cache), CurrentRenderPass(ESNRP_NONE) { -#ifdef _DEBUG - ISceneManager::setDebugName("CSceneManager ISceneManager"); - ISceneNode::setDebugName("CSceneManager ISceneNode"); -#endif - // root node's scene manager SceneManager = this; diff --git a/irr/src/CWGLManager.cpp b/irr/src/CWGLManager.cpp index 785524fbc..d0dc9bf6a 100644 --- a/irr/src/CWGLManager.cpp +++ b/irr/src/CWGLManager.cpp @@ -19,9 +19,6 @@ namespace video CWGLManager::CWGLManager() : PrimaryContext(SExposedVideoData(0)), PixelFormat(0), libHandle(NULL) { -#ifdef _DEBUG - setDebugName("CWGLManager"); -#endif memset(FunctionPointers, 0, sizeof(FunctionPointers)); } diff --git a/irr/src/CWriteFile.cpp b/irr/src/CWriteFile.cpp index 362b284ee..f6a7efd2b 100644 --- a/irr/src/CWriteFile.cpp +++ b/irr/src/CWriteFile.cpp @@ -13,10 +13,6 @@ namespace io CWriteFile::CWriteFile(const io::path &fileName, bool append) : Filename(fileName), FileSize(0) { -#ifdef _DEBUG - setDebugName("CWriteFile"); -#endif - openFile(append); } diff --git a/irr/src/CXMeshFileLoader.cpp b/irr/src/CXMeshFileLoader.cpp index cb02298eb..508b632c0 100644 --- a/irr/src/CXMeshFileLoader.cpp +++ b/irr/src/CXMeshFileLoader.cpp @@ -32,11 +32,7 @@ namespace scene CXMeshFileLoader::CXMeshFileLoader(scene::ISceneManager *smgr) : AnimatedMesh(0), Buffer(0), P(0), End(0), BinaryNumCount(0), Line(0), ErrorState(false), CurFrame(0), MajorVersion(0), MinorVersion(0), BinaryFormat(false), FloatSize(0) -{ -#ifdef _DEBUG - setDebugName("CXMeshFileLoader"); -#endif -} +{} //! returns true if the file maybe is able to be loaded by this class //! based on the file extension (e.g. ".bsp") diff --git a/irr/src/CZipReader.cpp b/irr/src/CZipReader.cpp index 036f6302a..dc5c0a4f0 100644 --- a/irr/src/CZipReader.cpp +++ b/irr/src/CZipReader.cpp @@ -24,11 +24,7 @@ namespace io //! Constructor CArchiveLoaderZIP::CArchiveLoaderZIP(io::IFileSystem *fs) : FileSystem(fs) -{ -#ifdef _DEBUG - setDebugName("CArchiveLoaderZIP"); -#endif -} +{} //! returns true if the file maybe is able to be loaded by this class bool CArchiveLoaderZIP::isALoadableFileFormat(const io::path &filename) const @@ -107,10 +103,6 @@ bool CArchiveLoaderZIP::isALoadableFileFormat(io::IReadFile *file) const CZipReader::CZipReader(IFileSystem *fs, IReadFile *file, bool ignoreCase, bool ignorePaths, bool isGZip) : CFileList((file ? file->getFileName() : io::path("")), ignoreCase, ignorePaths), FileSystem(fs), File(file), IsGZip(isGZip) { -#ifdef _DEBUG - setDebugName("CZipReader"); -#endif - if (File) { File->grab(); diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index da19b0bae..5df87861d 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -155,10 +155,6 @@ COpenGL3DriverBase::COpenGL3DriverBase(const SIrrlichtCreationParameters ¶ms OGLES2ShaderPath(params.OGLES2ShaderPath), ColorFormat(ECF_R8G8B8), ContextManager(contextManager), EnableErrorTest(params.DriverDebug) { -#ifdef _DEBUG - setDebugName("Driver"); -#endif - if (!ContextManager) return; diff --git a/irr/src/OpenGL/MaterialRenderer.cpp b/irr/src/OpenGL/MaterialRenderer.cpp index d5bf9004a..7439dba61 100644 --- a/irr/src/OpenGL/MaterialRenderer.cpp +++ b/irr/src/OpenGL/MaterialRenderer.cpp @@ -30,10 +30,6 @@ COpenGL3MaterialRenderer::COpenGL3MaterialRenderer(COpenGL3DriverBase *driver, Driver(driver), CallBack(callback), Alpha(false), Blending(false), Program(0), UserData(userData) { -#ifdef _DEBUG - setDebugName("MaterialRenderer"); -#endif - switch (baseMaterial) { case EMT_TRANSPARENT_VERTEX_ALPHA: case EMT_TRANSPARENT_ALPHA_CHANNEL: diff --git a/irr/src/OpenGL/Renderer2D.cpp b/irr/src/OpenGL/Renderer2D.cpp index b625feda4..1dd717aa8 100644 --- a/irr/src/OpenGL/Renderer2D.cpp +++ b/irr/src/OpenGL/Renderer2D.cpp @@ -22,10 +22,6 @@ COpenGL3Renderer2D::COpenGL3Renderer2D(const c8 *vertexShaderProgram, const c8 * COpenGL3MaterialRenderer(driver, 0, EMT_SOLID), WithTexture(withTexture) { -#ifdef _DEBUG - setDebugName("Renderer2D"); -#endif - int Temp = 0; init(Temp, vertexShaderProgram, pixelShaderProgram, false); diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp index ed5db785b..bc594bba6 100644 --- a/src/gui/guiEditBoxWithScrollbar.cpp +++ b/src/gui/guiEditBoxWithScrollbar.cpp @@ -30,10 +30,6 @@ GUIEditBoxWithScrollBar::GUIEditBoxWithScrollBar(const wchar_t* text, bool borde : GUIEditBox(environment, parent, id, rectangle, border, writable), m_background(true), m_bg_color_used(false), m_tsrc(tsrc) { -#ifdef _DEBUG - setDebugName("GUIEditBoxWithScrollBar"); -#endif - Text = text; diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index e3900a1fd..8efd81d0f 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -1015,10 +1015,6 @@ GUIHyperText::GUIHyperText(const wchar_t *text, IGUIEnvironment *environment, m_drawer(text, client, environment, tsrc), m_text_scrollpos(0, 0) { -#ifdef _DEBUG - setDebugName("GUIHyperText"); -#endif - IGUISkin *skin = 0; if (Environment) skin = Environment->getSkin(); diff --git a/src/irrlicht_changes/CGUITTFont.cpp b/src/irrlicht_changes/CGUITTFont.cpp index 8337390c1..73962605d 100644 --- a/src/irrlicht_changes/CGUITTFont.cpp +++ b/src/irrlicht_changes/CGUITTFont.cpp @@ -254,9 +254,6 @@ CGUITTFont::CGUITTFont(IGUIEnvironment *env) batch_load_size(1), Device(0), Environment(env), Driver(0), GlobalKerningWidth(0), GlobalKerningHeight(0), shadow_offset(0), shadow_alpha(0), fallback(0) { - #ifdef _DEBUG - setDebugName("CGUITTFont"); - #endif if (Environment) { diff --git a/src/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp index eba397963..d06419e1f 100644 --- a/src/irrlicht_changes/static_text.cpp +++ b/src/irrlicht_changes/static_text.cpp @@ -30,10 +30,6 @@ StaticText::StaticText(const EnrichedString &text, bool border, RestrainTextInside(true), RightToLeft(false), OverrideFont(0), LastBreakFont(0) { - #ifdef _DEBUG - setDebugName("StaticText"); - #endif - setText(text); } From 52a6673dab08800df0ef8f6403d9c58489764722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sat, 14 Dec 2024 17:04:12 +0100 Subject: [PATCH 57/68] Get rid of obsolete workaround for collision detection bugs (#15540) --- src/client/content_cao.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index b73b3602c..d90d4e8b0 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1663,11 +1663,6 @@ void GenericCAO::processMessage(const std::string &data) bool is_end_position = readU8(is); float update_interval = readF32(is); - // Place us a bit higher if we're physical, to not sink into - // the ground due to sucky collision detection... - if(m_prop.physical) - m_position += v3f(0,0.002,0); - if(getParent() != NULL) // Just in case return; From f99a1a7c7c1a3d96332169978b63ffb10c56dbb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Sat, 14 Dec 2024 18:50:21 +0100 Subject: [PATCH 58/68] Revert "Optimize raycast performance (#15233)" This reverts commit f7a695c212bea96887b32b859645f621fd8f1b48. --- doc/lua_api.md | 2 -- src/environment.cpp | 6 ------ src/nodedef.cpp | 12 ------------ src/nodedef.h | 5 ----- 4 files changed, 25 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index d753b6b53..7fe464a30 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -9948,8 +9948,6 @@ Used by `core.register_node`. selection_box = { -- see [Node boxes] for possibilities - -- Selection boxes that oversize node size can cause - -- significant performance drop of Raycasts. }, -- Custom selection box definition. Multiple boxes can be defined. -- If "nodebox" drawtype is used and selection_box is nil, then node_box diff --git a/src/environment.cpp b/src/environment.cpp index e66c7e2de..fe582afd4 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -163,8 +163,6 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) break; // About to go out of bounds } - const v3s16 pos_on_ray = state->m_iterator.m_current_node_pos; - // For each untested node for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) @@ -177,10 +175,6 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) if (!is_valid_position) continue; - // Optimization: Skip non-oversized selection boxes for other positions. - if ((pos_on_ray != np) && !nodedef->get(n).has_big_selection_box) - continue; - PointabilityType pointable = isPointableNode(n, nodedef, state->m_liquids_pointable, state->m_pointabilities); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 0fde1b68e..81348cf23 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -1257,15 +1257,6 @@ inline void NodeDefManager::fixSelectionBoxIntUnion() m_selection_box_union.MaxEdge.Z / BS - 0.5f); } -inline void NodeDefManager::calcBigSelectionBox(content_t id, const ContentFeatures &def) -{ - aabb3f box_union; - getNodeBoxUnion(def.selection_box, def, &box_union); - m_content_features[id].has_big_selection_box = - (box_union.MinEdge.X < -BS/2) || (box_union.MaxEdge.X > BS/2) || - (box_union.MinEdge.Y < -BS/2) || (box_union.MaxEdge.Y > BS/2) || - (box_union.MinEdge.Z < -BS/2) || (box_union.MaxEdge.Z > BS/2); -} void NodeDefManager::eraseIdFromGroups(content_t id) { @@ -1321,7 +1312,6 @@ content_t NodeDefManager::set(const std::string &name, const ContentFeatures &de getNodeBoxUnion(def.selection_box, def, &m_selection_box_union); fixSelectionBoxIntUnion(); - calcBigSelectionBox(id, def); // Add this content to the list of all groups it belongs to for (const auto &group : def.groups) { const std::string &group_name = group.first; @@ -1535,8 +1525,6 @@ void NodeDefManager::deSerialize(std::istream &is, u16 protocol_version) getNodeBoxUnion(f.selection_box, f, &m_selection_box_union); fixSelectionBoxIntUnion(); - - calcBigSelectionBox(i, f); } // Since liquid_alternative_flowing_id and liquid_alternative_source_id diff --git a/src/nodedef.h b/src/nodedef.h index e33f42699..c3f88ce83 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -425,8 +425,6 @@ struct ContentFeatures NodeBox node_box; NodeBox selection_box; NodeBox collision_box; - //! Whether any selection box extent is > BS/2. - bool has_big_selection_box; // --- SOUND PROPERTIES --- @@ -776,9 +774,6 @@ private: */ void fixSelectionBoxIntUnion(); - //! Calculates ContentFeatures::&has_big_selection_box - void calcBigSelectionBox(content_t id, const ContentFeatures &def); - //! Features indexed by ID. std::vector m_content_features; From a37bdbf8b7778336c6740f994164097ebb4129e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Wed, 18 Dec 2024 20:29:35 +0100 Subject: [PATCH 59/68] Restore `.x` models to working state (#15550) * Add "lava flan" (.x model) smoke test * Fix double finalize in `.x` mesh loader * Use reserve instead of resize again The weights are added indirectly via `AnimatedMesh->addWeight` --- .gitattributes | 1 + .../mods/testentities/models/LICENSE.txt | 8 + .../models/testentities_lava_flan.png | Bin 0 -> 1294 bytes .../models/testentities_lava_flan.x | 3506 +++++++++++++++++ games/devtest/mods/testentities/visuals.lua | 14 + irr/src/CXMeshFileLoader.cpp | 10 +- 6 files changed, 3535 insertions(+), 4 deletions(-) create mode 100644 games/devtest/mods/testentities/models/testentities_lava_flan.png create mode 100644 games/devtest/mods/testentities/models/testentities_lava_flan.x diff --git a/.gitattributes b/.gitattributes index ecd9a7a29..41a6c0979 100644 --- a/.gitattributes +++ b/.gitattributes @@ -5,3 +5,4 @@ *.h diff=cpp *.gltf binary +*.x binary diff --git a/games/devtest/mods/testentities/models/LICENSE.txt b/games/devtest/mods/testentities/models/LICENSE.txt index 4317d68d3..19ffffc5c 100644 --- a/games/devtest/mods/testentities/models/LICENSE.txt +++ b/games/devtest/mods/testentities/models/LICENSE.txt @@ -1,3 +1,5 @@ +"Minetest Sam": + Original model by MirceaKitsune (CC BY-SA 3.0). Various alterations and fixes by kilbith, sofar, xunto, Rogier-5, TeTpaAka, Desour, stujones11, An0n3m0us (CC BY-SA 3.0): @@ -5,3 +7,9 @@ stujones11, An0n3m0us (CC BY-SA 3.0): Jordach (CC BY-SA 3.0): testentities_sam.png + +"Lava Flan": + +Zeg9 (CC BY-SA 3.0): + testentities_lava_flan.x + testentities_lava_flan.png \ No newline at end of file diff --git a/games/devtest/mods/testentities/models/testentities_lava_flan.png b/games/devtest/mods/testentities/models/testentities_lava_flan.png new file mode 100644 index 0000000000000000000000000000000000000000..8cf37238fe1fdd8e11ffab67b1c1d8d2663a7ac3 GIT binary patch literal 1294 zcmV+p1@ZccP) zW!V?AB?2>w*~ZW;EXF7wmiRJ_$&7Kym>UTgVnkzrB_qq@InPU5D08hh=fmxDfB$>V zJ?Gq8=3cW#?&R~mVy0ZYEreVp@TX=~!Xrs&ZIX1zWZD-KgBC8&WWoOO@bM1OD8!pW zbf9LZUw~g)B*|MK8q>J@3HECm@qr_l4_rl_{qACoajb@Ow24CQrxgLl=n~?n!+};8 z%ciZwlOR5D6?yhc4I8ttO3uH_=X1{m(24-9UWi)7M$K#?i+F7dCB|T$J}LlC zkY_gzupNvXe7)I^e(EyB7(2-4yt15rxZB9WI+lfOe4UU8D*+`E(oqHlLRECnvvdGGV`k%Tpb$ zJgo^RYk`7hHr2wv+DuFPl9F7{@Y>k0c{UGh7t;ij`jLARh@N$`>sz?GRL=QGF0Wo6 zHqR=U)}~h^es}w#qWzGnZL}9xU*n?=5`@dMV#anVf^YHyu1qtf;tM4e6`Wtq<44nY zb#iiXRMfJ(+V#uwtcOu<}6V}sdTV={R%ctn93{G!aOOA))7ha|m zse)@KVWLcvg~IC&1`Ts1X>{H*Fn$&mE}_7NA4xD6DOY5EzlZ1}0H5!MN!>vxyyoEK zQ>*@QX0|Iiy;Vx#$RinA7onZs`16Y-s0FCW&PKrJ;Gw*Jf^*SxPFtYhL{^qO5TOcM z6B5RoNPxT7=fjPXWgI++*B#g+F>$H@4d?O&6ZNPX(O~4AQW6}<&PIE;aFK)m=5+^B zv|LFyKyACZa*!`ts3lavQ2d`SQDfBQ}15oE|B~wQk-2 zdQCswK>Mn@<6JFQKbv(JTddZ;LJ9{b?zlL5-@_dL0#E%Ivk87af&c&j07*qoM6N<$ Ef+HAvNdN!< literal 0 HcmV?d00001 diff --git a/games/devtest/mods/testentities/models/testentities_lava_flan.x b/games/devtest/mods/testentities/models/testentities_lava_flan.x new file mode 100644 index 000000000..be78ff97e --- /dev/null +++ b/games/devtest/mods/testentities/models/testentities_lava_flan.x @@ -0,0 +1,3506 @@ +xof 0303txt 0032 + +template XSkinMeshHeader { + <3cf169ce-ff7c-44ab-93c0-f78f62d172e2> + WORD nMaxSkinWeightsPerVertex; + WORD nMaxSkinWeightsPerFace; + WORD nBones; +} + +template SkinWeights { + <6f0d123b-bad2-4167-a0d0-80224f25fabb> + STRING transformNodeName; + DWORD nWeights; + array DWORD vertexIndices[nWeights]; + array float weights[nWeights]; + Matrix4x4 matrixOffset; +} + +Frame Root { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000,-0.000000, 1.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 1.000000;; + } + Frame Armature { + FrameTransformMatrix { + -5.000000, 0.000001, 0.000000, 0.000000, + -0.000001,-5.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 5.000000, 0.000000, + 0.000000, 0.000000,-5.000000, 1.000000;; + } + Frame Armature_Bone { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 0.000000, + 0.000000,-1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 0.000000, 1.000000;; + } + Frame Armature_Bone_001 { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 0.000000, + 0.000000, 2.000000, 0.000000, 1.000000;; + } + Frame Armature_Bone_002 { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 0.000000, + 0.000000, 0.300000, 0.000000, 1.000000;; + } + Frame Armature_Bone_003 { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 0.000000, + 0.000000, 0.300000, 0.000000, 1.000000;; + } + Frame Armature_Bone_004 { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 0.000000, + 0.000000, 0.400000, 0.000000, 1.000000;; + } + } // End of Armature_Bone_004 + } // End of Armature_Bone_003 + } // End of Armature_Bone_002 + } // End of Armature_Bone_001 + } // End of Armature_Bone + Frame Cube { + FrameTransformMatrix { + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 0.000000, + 0.000000, 0.000000, 1.000000, 1.000000;; + } + Mesh { // Cube mesh + 264; + 1.000000; 1.000000;-1.000000;, + 1.000000;-1.000000;-1.000000;, + -1.000000;-1.000000;-1.000000;, + -1.000000; 1.000000;-1.000000;, + -1.000000; 1.000000; 1.000000;, + -1.000000;-1.000000; 1.000000;, + -0.800000;-0.800000; 1.000000;, + -0.800000; 0.800000; 1.000000;, + 1.000000; 1.000000;-1.000000;, + 1.000000; 0.999999; 1.000000;, + 0.999999;-1.000001; 1.000000;, + 1.000000;-1.000000;-1.000000;, + 1.000000;-1.000000;-1.000000;, + 0.999999;-1.000001; 1.000000;, + -1.000000;-1.000000; 1.000000;, + -1.000000;-1.000000;-1.000000;, + -1.000000;-1.000000;-1.000000;, + -1.000000;-1.000000; 1.000000;, + -1.000000; 1.000000; 1.000000;, + -1.000000; 1.000000;-1.000000;, + 1.000000; 0.999999; 1.000000;, + 1.000000; 1.000000;-1.000000;, + -1.000000; 1.000000;-1.000000;, + -1.000000; 1.000000; 1.000000;, + -0.800000; 0.800000; 1.666667;, + -0.800000;-0.800000; 1.666667;, + -0.800000;-0.800000; 2.000000;, + -0.800000; 0.800000; 2.000000;, + 0.999999;-1.000001; 1.000000;, + 1.000000; 0.999999; 1.000000;, + 0.800000; 0.800000; 1.000000;, + 0.799999;-0.800001; 1.000000;, + 1.000000; 0.999999; 1.000000;, + -1.000000; 1.000000; 1.000000;, + -0.800000; 0.800000; 1.000000;, + 0.800000; 0.800000; 1.000000;, + -1.000000;-1.000000; 1.000000;, + 0.999999;-1.000001; 1.000000;, + 0.799999;-0.800001; 1.000000;, + -0.800000;-0.800000; 1.000000;, + -0.800000;-0.800000; 2.000000;, + 0.799999;-0.800001; 2.000000;, + 0.640000;-0.640000; 2.000000;, + -0.640000;-0.640000; 2.000000;, + 0.799999;-0.800001; 1.666667;, + 0.800000; 0.800000; 1.666667;, + 0.800000; 0.800000; 2.000000;, + 0.799999;-0.800001; 2.000000;, + 0.800000; 0.800000; 1.666667;, + -0.800000; 0.800000; 1.666667;, + -0.800000; 0.800000; 2.000000;, + 0.800000; 0.800000; 2.000000;, + -0.800000;-0.800000; 1.666667;, + 0.799999;-0.800001; 1.666667;, + 0.799999;-0.800001; 2.000000;, + -0.800000;-0.800000; 2.000000;, + 0.640000; 0.640000; 2.000000;, + -0.640000; 0.640000; 2.000000;, + -0.640000; 0.640000; 2.500000;, + 0.640000; 0.640000; 2.500000;, + -0.800000; 0.800000; 2.000000;, + -0.800000;-0.800000; 2.000000;, + -0.640000;-0.640000; 2.000000;, + -0.640000; 0.640000; 2.000000;, + 0.799999;-0.800001; 2.000000;, + 0.800000; 0.800000; 2.000000;, + 0.640000; 0.640000; 2.000000;, + 0.640000;-0.640000; 2.000000;, + 0.800000; 0.800000; 2.000000;, + -0.800000; 0.800000; 2.000000;, + -0.640000; 0.640000; 2.000000;, + 0.640000; 0.640000; 2.000000;, + 0.640000; 0.640000; 2.500000;, + -0.640000; 0.640000; 2.500000;, + -0.640000;-0.640000; 2.500000;, + 0.640000;-0.640000; 2.500000;, + -0.640000;-0.640000; 2.000000;, + 0.640000;-0.640000; 2.000000;, + 0.640000;-0.640000; 2.500000;, + -0.640000;-0.640000; 2.500000;, + -0.640000; 0.640000; 2.000000;, + -0.640000;-0.640000; 2.000000;, + -0.640000;-0.640000; 2.500000;, + -0.640000; 0.640000; 2.500000;, + 0.640000;-0.640000; 2.000000;, + 0.640000; 0.640000; 2.000000;, + 0.640000; 0.640000; 2.500000;, + 0.640000;-0.640000; 2.500000;, + -0.800000; 0.800000; 1.000000;, + -0.800000;-0.800000; 1.000000;, + -0.800000;-0.800000; 1.333333;, + -0.800000; 0.800000; 1.333333;, + -0.800000; 0.800000; 1.333333;, + -0.800000;-0.800000; 1.333333;, + -0.800000;-0.800000; 1.666667;, + -0.800000; 0.800000; 1.666667;, + 0.799999;-0.800001; 1.000000;, + 0.800000; 0.800000; 1.000000;, + 0.800000; 0.800000; 1.333333;, + 0.799999;-0.800001; 1.333333;, + 0.799999;-0.800001; 1.333333;, + 0.800000; 0.800000; 1.333333;, + 0.800000; 0.800000; 1.666667;, + 0.799999;-0.800001; 1.666667;, + 0.800000; 0.800000; 1.000000;, + -0.800000; 0.800000; 1.000000;, + -0.800000; 0.800000; 1.333333;, + 0.800000; 0.800000; 1.333333;, + 0.800000; 0.800000; 1.333333;, + -0.800000; 0.800000; 1.333333;, + -0.800000; 0.800000; 1.666667;, + 0.800000; 0.800000; 1.666667;, + -0.800000;-0.800000; 1.000000;, + 0.799999;-0.800001; 1.000000;, + 0.799999;-0.800001; 1.333333;, + -0.800000;-0.800000; 1.333333;, + 0.799999;-0.800001; 1.333333;, + 0.799999;-0.800001; 1.666667;, + 0.560000;-0.000000; 1.616667;, + 0.560000;-0.000000; 1.383333;, + -0.560000; 0.000000; 1.383333;, + 0.560000;-0.000000; 1.383333;, + 0.560000;-0.000000; 1.616667;, + -0.560000; 0.000000; 1.616667;, + 0.799999;-0.800001; 1.666667;, + -0.800000;-0.800000; 1.666667;, + -0.560000; 0.000000; 1.616667;, + 0.560000;-0.000000; 1.616667;, + -0.800000;-0.800000; 1.666667;, + -0.800000;-0.800000; 1.333333;, + -0.560000; 0.000000; 1.383333;, + -0.560000; 0.000000; 1.616667;, + -0.800000;-0.800000; 1.333333;, + 0.799999;-0.800001; 1.333333;, + 0.560000;-0.000000; 1.383333;, + -0.560000; 0.000000; 1.383333;, + -0.000000;-0.790000; 1.340000;, + -0.000000;-0.690000; 1.540000;, + -0.100000;-0.690000; 1.340000;, + -0.000000;-0.590000; 1.340000;, + -0.000000;-0.690000; 1.540000;, + 0.100000;-0.690000; 1.340000;, + 0.400000;-0.590000; 1.340000;, + 0.500000;-0.690000; 1.340000;, + 0.400000;-0.790000; 1.340000;, + 0.300000;-0.690000; 1.340000;, + -0.100000;-0.690000; 1.340000;, + -0.000000;-0.690000; 1.540000;, + -0.000000;-0.590000; 1.340000;, + 0.100000;-0.690000; 1.340000;, + -0.000000;-0.690000; 1.540000;, + -0.000000;-0.790000; 1.340000;, + 0.200000;-0.590000; 1.340000;, + 0.300000;-0.690000; 1.340000;, + 0.200000;-0.790000; 1.340000;, + 0.100000;-0.690000; 1.340000;, + 0.300000;-0.690000; 1.340000;, + 0.200000;-0.690000; 1.540000;, + 0.200000;-0.790000; 1.340000;, + 0.100000;-0.690000; 1.340000;, + 0.200000;-0.690000; 1.540000;, + 0.200000;-0.590000; 1.340000;, + 0.200000;-0.590000; 1.340000;, + 0.200000;-0.690000; 1.540000;, + 0.300000;-0.690000; 1.340000;, + 0.500000;-0.690000; 1.340000;, + 0.400000;-0.690000; 1.540000;, + 0.400000;-0.790000; 1.340000;, + 0.200000;-0.790000; 1.340000;, + 0.200000;-0.690000; 1.540000;, + 0.100000;-0.690000; 1.340000;, + -0.000000;-0.590000; 1.340000;, + 0.100000;-0.690000; 1.340000;, + -0.000000;-0.790000; 1.340000;, + -0.100000;-0.690000; 1.340000;, + 0.300000;-0.690000; 1.340000;, + 0.400000;-0.690000; 1.540000;, + 0.400000;-0.590000; 1.340000;, + 0.400000;-0.590000; 1.340000;, + 0.400000;-0.690000; 1.540000;, + 0.500000;-0.690000; 1.340000;, + 0.400000;-0.790000; 1.340000;, + 0.400000;-0.690000; 1.540000;, + 0.300000;-0.690000; 1.340000;, + -0.200000;-0.590000; 1.340000;, + -0.100000;-0.690000; 1.340000;, + -0.200000;-0.790000; 1.340000;, + -0.300000;-0.690000; 1.340000;, + -0.100000;-0.690000; 1.340000;, + -0.200000;-0.690000; 1.540000;, + -0.200000;-0.790000; 1.340000;, + -0.300000;-0.690000; 1.340000;, + -0.200000;-0.690000; 1.540000;, + -0.200000;-0.590000; 1.340000;, + -0.200000;-0.590000; 1.340000;, + -0.200000;-0.690000; 1.540000;, + -0.100000;-0.690000; 1.340000;, + -0.200000;-0.790000; 1.340000;, + -0.200000;-0.690000; 1.540000;, + -0.300000;-0.690000; 1.340000;, + -0.400000;-0.590000; 1.340000;, + -0.300000;-0.690000; 1.340000;, + -0.400000;-0.790000; 1.340000;, + -0.500000;-0.690000; 1.340000;, + -0.300000;-0.690000; 1.340000;, + -0.400000;-0.690000; 1.540000;, + -0.400000;-0.790000; 1.340000;, + -0.500000;-0.690000; 1.340000;, + -0.400000;-0.690000; 1.540000;, + -0.400000;-0.590000; 1.340000;, + -0.400000;-0.590000; 1.340000;, + -0.400000;-0.690000; 1.540000;, + -0.300000;-0.690000; 1.340000;, + -0.400000;-0.790000; 1.340000;, + -0.400000;-0.690000; 1.540000;, + -0.500000;-0.690000; 1.340000;, + 0.200000;-0.700000; 2.400000;, + 0.200000;-0.500000; 2.400000;, + 0.200000;-0.500000; 2.200000;, + 0.200000;-0.700000; 2.200000;, + 0.200000;-0.500000; 2.400000;, + 0.400000;-0.500000; 2.400000;, + 0.400000;-0.500000; 2.200000;, + 0.200000;-0.500000; 2.200000;, + 0.400000;-0.500000; 2.400000;, + 0.400000;-0.700000; 2.400000;, + 0.400000;-0.700000; 2.200000;, + 0.400000;-0.500000; 2.200000;, + 0.400000;-0.700000; 2.400000;, + 0.200000;-0.700000; 2.400000;, + 0.200000;-0.700000; 2.200000;, + 0.400000;-0.700000; 2.200000;, + 0.200000;-0.700000; 2.200000;, + 0.200000;-0.500000; 2.200000;, + 0.400000;-0.500000; 2.200000;, + 0.400000;-0.700000; 2.200000;, + 0.400000;-0.700000; 2.400000;, + 0.400000;-0.500000; 2.400000;, + 0.200000;-0.500000; 2.400000;, + 0.200000;-0.700000; 2.400000;, + -0.400000;-0.700000; 2.400000;, + -0.400000;-0.500000; 2.400000;, + -0.400000;-0.500000; 2.200000;, + -0.400000;-0.700000; 2.200000;, + -0.400000;-0.500000; 2.400000;, + -0.200000;-0.500000; 2.400000;, + -0.200000;-0.500000; 2.200000;, + -0.400000;-0.500000; 2.200000;, + -0.200000;-0.500000; 2.400000;, + -0.200000;-0.700000; 2.400000;, + -0.200000;-0.700000; 2.200000;, + -0.200000;-0.500000; 2.200000;, + -0.200000;-0.700000; 2.400000;, + -0.400000;-0.700000; 2.400000;, + -0.400000;-0.700000; 2.200000;, + -0.200000;-0.700000; 2.200000;, + -0.400000;-0.700000; 2.200000;, + -0.400000;-0.500000; 2.200000;, + -0.200000;-0.500000; 2.200000;, + -0.200000;-0.700000; 2.200000;, + -0.200000;-0.700000; 2.400000;, + -0.200000;-0.500000; 2.400000;, + -0.400000;-0.500000; 2.400000;, + -0.400000;-0.700000; 2.400000;; + 71; + 4;3;2;1;0;, + 4;7;6;5;4;, + 4;11;10;9;8;, + 4;15;14;13;12;, + 4;19;18;17;16;, + 4;23;22;21;20;, + 4;27;26;25;24;, + 4;31;30;29;28;, + 4;35;34;33;32;, + 4;39;38;37;36;, + 4;43;42;41;40;, + 4;47;46;45;44;, + 4;51;50;49;48;, + 4;55;54;53;52;, + 4;59;58;57;56;, + 4;63;62;61;60;, + 4;67;66;65;64;, + 4;71;70;69;68;, + 4;75;74;73;72;, + 4;79;78;77;76;, + 4;83;82;81;80;, + 4;87;86;85;84;, + 4;91;90;89;88;, + 4;95;94;93;92;, + 4;99;98;97;96;, + 4;103;102;101;100;, + 4;107;106;105;104;, + 4;111;110;109;108;, + 4;115;114;113;112;, + 4;119;118;117;116;, + 4;123;122;121;120;, + 4;127;126;125;124;, + 4;131;130;129;128;, + 4;135;134;133;132;, + 3;138;137;136;, + 3;141;140;139;, + 4;145;144;143;142;, + 3;148;147;146;, + 3;151;150;149;, + 4;155;154;153;152;, + 3;158;157;156;, + 3;161;160;159;, + 3;164;163;162;, + 3;167;166;165;, + 3;170;169;168;, + 4;174;173;172;171;, + 3;177;176;175;, + 3;180;179;178;, + 3;183;182;181;, + 4;187;186;185;184;, + 3;190;189;188;, + 3;193;192;191;, + 3;196;195;194;, + 3;199;198;197;, + 4;203;202;201;200;, + 3;206;205;204;, + 3;209;208;207;, + 3;212;211;210;, + 3;215;214;213;, + 4;219;218;217;216;, + 4;223;222;221;220;, + 4;227;226;225;224;, + 4;231;230;229;228;, + 4;235;234;233;232;, + 4;239;238;237;236;, + 4;243;242;241;240;, + 4;247;246;245;244;, + 4;251;250;249;248;, + 4;255;254;253;252;, + 4;259;258;257;256;, + 4;263;262;261;260;; + MeshNormals { // Cube normals + 71; + 0.000000; 0.000000;-1.000000;, + 0.000000; 0.000000; 1.000000;, + 1.000000;-0.000000; 0.000000;, + -0.000000;-1.000000;-0.000000;, + -1.000000; 0.000000;-0.000000;, + 0.000000; 1.000000; 0.000000;, + -1.000000; 0.000000; 0.000000;, + -0.000000; 0.000000; 1.000000;, + 0.000000;-0.000000; 1.000000;, + 0.000000; 0.000000; 1.000000;, + 0.000000; 0.000000; 1.000000;, + 1.000000;-0.000001; 0.000000;, + 0.000000; 1.000000; 0.000000;, + -0.000000;-1.000000; 0.000000;, + 0.000000; 1.000000; 0.000000;, + 0.000000; 0.000000; 1.000000;, + -0.000000; 0.000000; 1.000000;, + 0.000000;-0.000000; 1.000000;, + 0.000000;-0.000000; 1.000000;, + -0.000000;-1.000000; 0.000000;, + -1.000000; 0.000000; 0.000000;, + 1.000000;-0.000001; 0.000000;, + -1.000000; 0.000000; 0.000000;, + -1.000000; 0.000000; 0.000000;, + 1.000000;-0.000001; 0.000000;, + 1.000000;-0.000001; 0.000000;, + 0.000000; 1.000000; 0.000000;, + 0.000000; 1.000000; 0.000000;, + -0.000000;-1.000000; 0.000000;, + -0.957826;-0.287348; 0.000000;, + -0.000000;-1.000000; 0.000000;, + -0.000000;-0.062378;-0.998053;, + 0.957826;-0.287348; 0.000000;, + 0.000000;-0.062378; 0.998053;, + -0.666667;-0.666667; 0.333333;, + 0.666667; 0.666666; 0.333333;, + 0.000000; 0.000000;-1.000000;, + -0.666667; 0.666667; 0.333333;, + 0.666667;-0.666667; 0.333333;, + 0.000000; 0.000000;-1.000000;, + 0.666667;-0.666667; 0.333333;, + -0.666667; 0.666667; 0.333333;, + 0.666667; 0.666667; 0.333333;, + 0.666667;-0.666667; 0.333333;, + -0.666667;-0.666667; 0.333333;, + 0.000000; 0.000000;-1.000000;, + -0.666667; 0.666667; 0.333333;, + 0.666667; 0.666667; 0.333333;, + -0.666667;-0.666667; 0.333333;, + 0.000000; 0.000000;-1.000000;, + 0.666667;-0.666667; 0.333333;, + -0.666667; 0.666667; 0.333333;, + 0.666667; 0.666667; 0.333333;, + -0.666667;-0.666667; 0.333333;, + 0.000000; 0.000000;-1.000000;, + 0.666667;-0.666667; 0.333333;, + -0.666667; 0.666667; 0.333333;, + 0.666667; 0.666667; 0.333333;, + -0.666667;-0.666667; 0.333333;, + -1.000000; 0.000000; 0.000000;, + 0.000000; 1.000000;-0.000000;, + 1.000000; 0.000000;-0.000000;, + 0.000000;-1.000000; 0.000000;, + -0.000000; 0.000000;-1.000000;, + -0.000000; 0.000000; 1.000000;, + -1.000000; 0.000000; 0.000000;, + 0.000000; 1.000000;-0.000000;, + 1.000000; 0.000000;-0.000000;, + 0.000000;-1.000000; 0.000000;, + -0.000000; 0.000000;-1.000000;, + -0.000000; 0.000000; 1.000000;; + 71; + 4;0;0;0;0;, + 4;1;1;1;1;, + 4;2;2;2;2;, + 4;3;3;3;3;, + 4;4;4;4;4;, + 4;5;5;5;5;, + 4;6;6;6;6;, + 4;7;7;7;7;, + 4;8;8;8;8;, + 4;9;9;9;9;, + 4;10;10;10;10;, + 4;11;11;11;11;, + 4;12;12;12;12;, + 4;13;13;13;13;, + 4;14;14;14;14;, + 4;15;15;15;15;, + 4;16;16;16;16;, + 4;17;17;17;17;, + 4;18;18;18;18;, + 4;19;19;19;19;, + 4;20;20;20;20;, + 4;21;21;21;21;, + 4;22;22;22;22;, + 4;23;23;23;23;, + 4;24;24;24;24;, + 4;25;25;25;25;, + 4;26;26;26;26;, + 4;27;27;27;27;, + 4;28;28;28;28;, + 4;29;29;29;29;, + 4;30;30;30;30;, + 4;31;31;31;31;, + 4;32;32;32;32;, + 4;33;33;33;33;, + 3;34;34;34;, + 3;35;35;35;, + 4;36;36;36;36;, + 3;37;37;37;, + 3;38;38;38;, + 4;39;39;39;39;, + 3;40;40;40;, + 3;41;41;41;, + 3;42;42;42;, + 3;43;43;43;, + 3;44;44;44;, + 4;45;45;45;45;, + 3;46;46;46;, + 3;47;47;47;, + 3;48;48;48;, + 4;49;49;49;49;, + 3;50;50;50;, + 3;51;51;51;, + 3;52;52;52;, + 3;53;53;53;, + 4;54;54;54;54;, + 3;55;55;55;, + 3;56;56;56;, + 3;57;57;57;, + 3;58;58;58;, + 4;59;59;59;59;, + 4;60;60;60;60;, + 4;61;61;61;61;, + 4;62;62;62;62;, + 4;63;63;63;63;, + 4;64;64;64;64;, + 4;65;65;65;65;, + 4;66;66;66;66;, + 4;67;67;67;67;, + 4;68;68;68;68;, + 4;69;69;69;69;, + 4;70;70;70;70;; + } // End of Cube normals + MeshTextureCoords { // Cube UV coordinates + 264; + 0.000000; 0.500000;, + 0.500000; 0.500000;, + 0.500000; 0.000000;, + 0.000000; 0.000000;, + -0.000104; 0.000450;, + -0.000014; 0.499950;, + 0.058184; 0.442464;, + 0.057363; 0.058619;, + 0.000000; 0.500000;, + 0.500000; 0.500000;, + 0.500000; 0.000000;, + 0.000000; 0.000000;, + 0.000000; 0.500000;, + 0.500000; 0.500000;, + 0.500000; 0.000000;, + 0.000000; 0.000000;, + 0.000000; 0.500000;, + 0.500000; 0.500000;, + 0.500000; 0.000000;, + 0.000000; 0.000000;, + 0.000000; 0.500000;, + 0.500000; 0.500000;, + 0.500000; 0.000000;, + 0.000000; 0.000000;, + 0.499960; 0.416620;, + 0.000040; 0.416620;, + 0.000040; 0.624921;, + 0.499960; 0.624921;, + 0.499489; 0.499550;, + 0.499399; 0.000050;, + 0.441201; 0.057536;, + 0.442021; 0.441381;, + 0.499399; 0.000050;, + -0.000104; 0.000450;, + 0.057363; 0.058619;, + 0.441201; 0.057536;, + -0.000014; 0.499950;, + 0.499489; 0.499550;, + 0.442021; 0.441381;, + 0.058184; 0.442464;, + -0.000014; 0.499950;, + 0.499489; 0.499550;, + 0.442022; 0.441382;, + 0.058184; 0.442464;, + 0.000040; 0.208320;, + 0.499960; 0.208320;, + 0.499960; 0.000020;, + 0.000040; 0.000020;, + 0.499960; 0.416621;, + 0.000040; 0.416620;, + 0.000040; 0.624921;, + 0.499960; 0.624921;, + 0.000046; 0.499908;, + 0.499954; 0.499909;, + 0.499954; 0.291613;, + 0.000046; 0.291613;, + 0.499969;-0.015638;, + 0.000031;-0.015639;, + 0.000031; 0.374937;, + 0.499969; 0.374938;, + -0.000104; 0.000450;, + -0.000014; 0.499950;, + 0.058184; 0.442464;, + 0.057364; 0.058619;, + 0.499489; 0.499550;, + 0.499399; 0.000050;, + 0.441201; 0.057536;, + 0.442022; 0.441382;, + 0.499399; 0.000050;, + -0.000104; 0.000450;, + 0.057364; 0.058619;, + 0.441201; 0.057536;, + 0.499950; 0.000050;, + 0.000050; 0.000050;, + 0.000050; 0.499950;, + 0.499950; 0.499950;, + 0.000031; 0.374937;, + 0.499969; 0.374938;, + 0.499969;-0.015638;, + 0.000031;-0.015639;, + 0.499969;-0.015639;, + 0.000031;-0.015639;, + 0.000031; 0.374937;, + 0.499969; 0.374938;, + 0.000031; 0.374937;, + 0.499969; 0.374938;, + 0.499969;-0.015639;, + 0.000031;-0.015639;, + 0.499960; 0.000019;, + 0.000040; 0.000019;, + 0.000040; 0.208320;, + 0.499960; 0.208320;, + 0.499960; 0.208320;, + 0.000040; 0.208320;, + 0.000040; 0.416620;, + 0.499960; 0.416620;, + 0.000040; 0.624921;, + 0.499960; 0.624921;, + 0.499960; 0.416620;, + 0.000040; 0.416621;, + 0.000040; 0.416621;, + 0.499960; 0.416620;, + 0.499960; 0.208320;, + 0.000040; 0.208320;, + 0.499961; 0.000021;, + 0.000040; 0.000020;, + 0.000040; 0.208320;, + 0.499960; 0.208321;, + 0.499960; 0.208321;, + 0.000040; 0.208320;, + 0.000040; 0.416620;, + 0.499960; 0.416621;, + 0.000046; 0.291430;, + 0.499954; 0.291430;, + 0.499954; 0.083135;, + 0.000046; 0.083134;, + 0.482632; 0.297524;, + 0.499972; 0.143183;, + 0.275451; 0.201614;, + 0.279529; 0.217376;, + 0.000023; 0.249954;, + 0.499977; 0.249954;, + 0.499977; 0.041640;, + 0.000023; 0.041640;, + 0.499972; 0.143183;, + 0.017368; 0.143350;, + 0.220471; 0.223498;, + 0.275451; 0.201614;, + 0.017368; 0.143350;, + 0.000028; 0.297691;, + 0.224549; 0.239260;, + 0.220471; 0.223498;, + 0.000035; 0.499630;, + 0.499965; 0.499457;, + 0.424889; 0.249031;, + 0.074938; 0.249151;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.000000; 1.000000;, + 1.000000; 1.000000;, + 1.000000; 0.000000;, + 0.000000; 0.000000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.000000; 1.000000;, + 1.000000; 1.000000;, + 1.000000; 0.000000;, + 0.000000; 0.000000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.000000; 1.000000;, + 1.000000; 1.000000;, + 1.000000; 0.000000;, + 0.000000; 0.000000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.000000; 1.000000;, + 1.000000; 1.000000;, + 1.000000; 0.000000;, + 0.000000; 0.000000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.000000; 1.000000;, + 1.000000; 1.000000;, + 1.000000; 0.000000;, + 0.000000; 0.000000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.843750; 0.500000;, + 0.750000; 0.375000;, + 0.656250; 0.500000;, + 0.562500; 0.562500;, + 0.500000; 0.500000;, + 0.500000; 1.000000;, + 0.562500; 0.937500;, + 0.000000; 1.000000;, + 1.000000; 1.000000;, + 1.000000; 0.000000;, + 0.000000; 0.000000;, + 1.000000; 0.500000;, + 0.937500; 0.562500;, + 0.937500; 0.937500;, + 0.999902; 0.999901;, + 0.968750; 0.031250;, + 0.843750; 0.031250;, + 0.843750; 0.156250;, + 0.968750; 0.156250;, + 0.562500; 0.937500;, + 0.500000; 1.000000;, + 0.999902; 0.999901;, + 0.937500; 0.937500;, + 0.937500; 0.562500;, + 1.000000; 0.500000;, + 0.500000; 0.500000;, + 0.562500; 0.562500;, + 0.562500; 0.562500;, + 0.500000; 0.500000;, + 0.500000; 1.000000;, + 0.562500; 0.937500;, + 0.000000; 1.000000;, + 1.000000; 1.000000;, + 1.000000; 0.000000;, + 0.000000; 0.000000;, + 1.000000; 0.500000;, + 0.937500; 0.562500;, + 0.937559; 0.942680;, + 0.999902; 0.999901;, + 0.656250; 0.031250;, + 0.531250; 0.031250;, + 0.531250; 0.156250;, + 0.656250; 0.156250;, + 0.562500; 0.937500;, + 0.500000; 1.000000;, + 0.999902; 0.999901;, + 0.937559; 0.942680;, + 0.937500; 0.562500;, + 1.000000; 0.500000;, + 0.500000; 0.500000;, + 0.562500; 0.562500;; + } // End of Cube UV coordinates + MeshMaterialList { // Cube material list + 1; + 71; + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0;; + Material Material { + 0.640000; 0.640000; 0.640000; 1.000000;; + 96.078431; + 0.500000; 0.500000; 0.500000;; + 0.000000; 0.000000; 0.000000;; + } + } // End of Cube material list + XSkinMeshHeader { + 5; + 15; + 5; + } + SkinWeights { + "Armature_Bone_003"; + 249; + 4, + 5, + 6, + 7, + 9, + 10, + 13, + 14, + 17, + 18, + 20, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 76, + 77, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263; + 0.097855, + 0.076266, + 0.083495, + 0.107234, + 0.089782, + 0.015282, + 0.015282, + 0.076266, + 0.076266, + 0.097855, + 0.089782, + 0.097855, + 0.297901, + 0.287737, + 0.176628, + 0.182485, + 0.015282, + 0.089782, + 0.098610, + 0.018636, + 0.089782, + 0.097855, + 0.107234, + 0.098610, + 0.076266, + 0.015282, + 0.018636, + 0.083495, + 0.176628, + 0.003327, + 0.018794, + 0.127925, + 0.020404, + 0.286977, + 0.174766, + 0.003327, + 0.286977, + 0.297901, + 0.182485, + 0.174766, + 0.287737, + 0.020404, + 0.003327, + 0.176628, + 0.126954, + 0.136859, + 0.062329, + 0.060353, + 0.182485, + 0.176628, + 0.127925, + 0.136859, + 0.003327, + 0.174766, + 0.126954, + 0.018794, + 0.174766, + 0.182485, + 0.136859, + 0.126954, + 0.060353, + 0.062329, + 0.059240, + 0.127925, + 0.018794, + 0.059240, + 0.136859, + 0.127925, + 0.059240, + 0.062329, + 0.018794, + 0.126954, + 0.060353, + 0.107234, + 0.083495, + 0.133455, + 0.171692, + 0.171692, + 0.133455, + 0.287737, + 0.297901, + 0.018636, + 0.098610, + 0.162048, + 0.016914, + 0.016914, + 0.162048, + 0.286977, + 0.020404, + 0.098610, + 0.107234, + 0.171692, + 0.162048, + 0.162048, + 0.171692, + 0.297901, + 0.286977, + 0.083495, + 0.018636, + 0.016914, + 0.133455, + 0.016914, + 0.020404, + 0.031793, + 0.024185, + 0.091978, + 0.024185, + 0.031793, + 0.098530, + 0.020404, + 0.287737, + 0.098530, + 0.031793, + 0.287737, + 0.133455, + 0.091978, + 0.098530, + 0.133455, + 0.016914, + 0.024185, + 0.091978, + 0.500298, + 0.488502, + 0.484399, + 0.484912, + 0.488502, + 0.484399, + 0.574901, + 0.567861, + 0.584345, + 0.581311, + 0.484399, + 0.488502, + 0.484912, + 0.484399, + 0.488502, + 0.500298, + 0.650639, + 0.642159, + 0.660270, + 0.658477, + 0.642159, + 0.652886, + 0.660270, + 0.658477, + 0.652886, + 0.650639, + 0.650639, + 0.652886, + 0.642159, + 0.567861, + 0.577104, + 0.584345, + 0.660270, + 0.652886, + 0.658477, + 0.484912, + 0.484399, + 0.500298, + 0.484399, + 0.581311, + 0.577104, + 0.574901, + 0.574901, + 0.577104, + 0.567861, + 0.584345, + 0.577104, + 0.581311, + 0.650637, + 0.658473, + 0.660266, + 0.642160, + 0.658473, + 0.652884, + 0.660266, + 0.642160, + 0.652884, + 0.650637, + 0.650637, + 0.652884, + 0.658473, + 0.660266, + 0.652884, + 0.642160, + 0.574890, + 0.581306, + 0.584343, + 0.567845, + 0.581306, + 0.577096, + 0.584343, + 0.567845, + 0.577096, + 0.574890, + 0.574890, + 0.577096, + 0.581306, + 0.584343, + 0.577096, + 0.567845, + 0.228406, + 0.235541, + 0.226562, + 0.226110, + 0.235541, + 0.218934, + 0.220409, + 0.226562, + 0.218934, + 0.223567, + 0.223362, + 0.220409, + 0.223567, + 0.228406, + 0.226110, + 0.223362, + 0.226110, + 0.226562, + 0.220409, + 0.223362, + 0.223567, + 0.218934, + 0.235541, + 0.228406, + 0.467092, + 0.474056, + 0.449203, + 0.459631, + 0.474056, + 0.473079, + 0.460570, + 0.449203, + 0.473079, + 0.467589, + 0.462597, + 0.460570, + 0.467589, + 0.467092, + 0.459631, + 0.462597, + 0.459631, + 0.449203, + 0.460570, + 0.462597, + 0.467589, + 0.473079, + 0.474056, + 0.467092; + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000,-1.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000,-1.600000, 0.000000, 1.000000;; + } // End of Armature_Bone_003 skin weights + SkinWeights { + "Armature_Bone_002"; + 210; + 0, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 13, + 14, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 136, + 137, + 138, + 139, + 140, + 141, + 142, + 143, + 144, + 145, + 146, + 147, + 148, + 149, + 150, + 151, + 152, + 153, + 154, + 155, + 156, + 157, + 158, + 159, + 160, + 161, + 162, + 163, + 164, + 165, + 166, + 167, + 168, + 169, + 170, + 171, + 172, + 173, + 174, + 175, + 176, + 177, + 178, + 179, + 180, + 181, + 182, + 183, + 184, + 185, + 186, + 187, + 188, + 189, + 190, + 191, + 192, + 193, + 194, + 195, + 196, + 197, + 198, + 199, + 200, + 201, + 202, + 203, + 204, + 205, + 206, + 207, + 208, + 209, + 210, + 211, + 212, + 213, + 214, + 215; + 0.026699, + 0.019645, + 0.225152, + 0.090050, + 0.096422, + 0.247592, + 0.026699, + 0.225915, + 0.091110, + 0.091110, + 0.090050, + 0.090050, + 0.225152, + 0.019645, + 0.225915, + 0.026699, + 0.019645, + 0.225152, + 0.378267, + 0.217186, + 0.396865, + 0.489530, + 0.091110, + 0.225915, + 0.248358, + 0.097508, + 0.225915, + 0.225152, + 0.247592, + 0.248358, + 0.090050, + 0.091110, + 0.097508, + 0.096422, + 0.396865, + 0.398905, + 0.438519, + 0.443076, + 0.218141, + 0.380147, + 0.489880, + 0.398905, + 0.380147, + 0.378267, + 0.489530, + 0.489880, + 0.217186, + 0.218141, + 0.398905, + 0.396865, + 0.504112, + 0.502873, + 0.231972, + 0.249788, + 0.489530, + 0.396865, + 0.443076, + 0.502873, + 0.398905, + 0.489880, + 0.504112, + 0.438519, + 0.489880, + 0.489530, + 0.502873, + 0.504112, + 0.249788, + 0.231972, + 0.219728, + 0.214257, + 0.443076, + 0.438519, + 0.214257, + 0.219728, + 0.502873, + 0.443076, + 0.219728, + 0.231972, + 0.438519, + 0.504112, + 0.249788, + 0.214257, + 0.247592, + 0.096422, + 0.123030, + 0.409380, + 0.409380, + 0.123030, + 0.217186, + 0.378267, + 0.097508, + 0.248358, + 0.410435, + 0.124284, + 0.124284, + 0.410435, + 0.380147, + 0.218141, + 0.248358, + 0.247592, + 0.409380, + 0.410435, + 0.410435, + 0.409380, + 0.378267, + 0.380147, + 0.096422, + 0.097508, + 0.124284, + 0.123030, + 0.124284, + 0.218141, + 0.103633, + 0.087149, + 0.092557, + 0.087149, + 0.103633, + 0.094423, + 0.218141, + 0.217186, + 0.094423, + 0.103633, + 0.217186, + 0.123030, + 0.092557, + 0.094423, + 0.123030, + 0.124284, + 0.087149, + 0.092557, + 0.499702, + 0.511498, + 0.515601, + 0.515088, + 0.511498, + 0.515601, + 0.425099, + 0.432139, + 0.415655, + 0.418689, + 0.515601, + 0.511498, + 0.515088, + 0.515601, + 0.511498, + 0.499702, + 0.349361, + 0.357841, + 0.339730, + 0.341523, + 0.357841, + 0.347114, + 0.339730, + 0.341523, + 0.347114, + 0.349361, + 0.349361, + 0.347114, + 0.357841, + 0.432139, + 0.422896, + 0.415655, + 0.339730, + 0.347114, + 0.341523, + 0.515088, + 0.515601, + 0.499702, + 0.515601, + 0.418689, + 0.422896, + 0.425099, + 0.425099, + 0.422896, + 0.432139, + 0.415655, + 0.422896, + 0.418689, + 0.349363, + 0.341527, + 0.339734, + 0.357840, + 0.341527, + 0.347116, + 0.339734, + 0.357840, + 0.347116, + 0.349363, + 0.349363, + 0.347116, + 0.341527, + 0.339734, + 0.347116, + 0.357840, + 0.425110, + 0.418694, + 0.415657, + 0.432155, + 0.418694, + 0.422904, + 0.415657, + 0.432155, + 0.422904, + 0.425110, + 0.425110, + 0.422904, + 0.418694, + 0.415657, + 0.422904, + 0.432155; + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000,-1.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000,-1.300000, 0.000000, 1.000000;; + } // End of Armature_Bone_002 skin weights + SkinWeights { + "Armature_Bone_001"; + 127; + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 75, + 76, + 77, + 78, + 80, + 81, + 84, + 85, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135; + 0.028090, + 0.044667, + 0.027228, + 0.014924, + 0.187604, + 0.200597, + 0.217670, + 0.204339, + 0.028090, + 0.204480, + 0.310430, + 0.044667, + 0.044667, + 0.310430, + 0.200597, + 0.027228, + 0.027228, + 0.200597, + 0.187604, + 0.014924, + 0.204480, + 0.028090, + 0.014924, + 0.187604, + 0.067482, + 0.064812, + 0.031709, + 0.033965, + 0.310430, + 0.204480, + 0.222012, + 0.341423, + 0.204480, + 0.187604, + 0.204339, + 0.222012, + 0.200597, + 0.310430, + 0.341423, + 0.217670, + 0.031709, + 0.101587, + 0.070428, + 0.017181, + 0.167670, + 0.078366, + 0.047455, + 0.101587, + 0.078366, + 0.067482, + 0.033965, + 0.047455, + 0.064812, + 0.167670, + 0.101587, + 0.031709, + 0.027631, + 0.014453, + 0.033965, + 0.031709, + 0.017181, + 0.014453, + 0.101587, + 0.047455, + 0.027631, + 0.070428, + 0.047455, + 0.033965, + 0.014453, + 0.027631, + 0.008199, + 0.017181, + 0.070428, + 0.008199, + 0.014453, + 0.017181, + 0.070428, + 0.027631, + 0.008199, + 0.204339, + 0.217670, + 0.123770, + 0.116219, + 0.116219, + 0.123770, + 0.064812, + 0.067482, + 0.341423, + 0.222012, + 0.131980, + 0.371812, + 0.371812, + 0.131980, + 0.078366, + 0.167670, + 0.222012, + 0.204339, + 0.116219, + 0.131980, + 0.131980, + 0.116219, + 0.067482, + 0.078366, + 0.217670, + 0.341423, + 0.371812, + 0.123770, + 0.371812, + 0.167670, + 0.122698, + 0.132035, + 0.078042, + 0.132035, + 0.122698, + 0.065986, + 0.167670, + 0.064812, + 0.065986, + 0.122698, + 0.064812, + 0.123770, + 0.078042, + 0.065986, + 0.123770, + 0.371812, + 0.132035, + 0.078042; + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000,-1.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000,-1.000000, 0.000000, 1.000000;; + } // End of Armature_Bone_001 skin weights + SkinWeights { + "Armature_Bone"; + 136; + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135; + 0.945211, + 0.955333, + 0.972772, + 0.965432, + 0.441407, + 0.557541, + 0.520087, + 0.387790, + 0.945211, + 0.424328, + 0.470645, + 0.955333, + 0.955333, + 0.470645, + 0.557541, + 0.972772, + 0.972772, + 0.557541, + 0.441407, + 0.965432, + 0.424328, + 0.945211, + 0.965432, + 0.441407, + 0.133775, + 0.243816, + 0.149205, + 0.083512, + 0.470645, + 0.424328, + 0.371115, + 0.418447, + 0.424328, + 0.441407, + 0.387790, + 0.371115, + 0.557541, + 0.470645, + 0.418447, + 0.520087, + 0.149205, + 0.102630, + 0.077530, + 0.107912, + 0.165882, + 0.124352, + 0.077496, + 0.102630, + 0.124352, + 0.133775, + 0.083512, + 0.077496, + 0.243816, + 0.165882, + 0.102630, + 0.149205, + 0.060886, + 0.066205, + 0.013250, + 0.011723, + 0.083512, + 0.149205, + 0.107912, + 0.066205, + 0.102630, + 0.077496, + 0.060886, + 0.077530, + 0.077496, + 0.083512, + 0.066205, + 0.060886, + 0.011723, + 0.013250, + 0.048094, + 0.022321, + 0.107912, + 0.077530, + 0.022321, + 0.048094, + 0.066205, + 0.107912, + 0.048094, + 0.013250, + 0.077530, + 0.060886, + 0.011723, + 0.022321, + 0.387790, + 0.520087, + 0.494225, + 0.226848, + 0.226848, + 0.494225, + 0.243816, + 0.133775, + 0.418447, + 0.371115, + 0.212428, + 0.276984, + 0.276984, + 0.212428, + 0.124352, + 0.165882, + 0.371115, + 0.387790, + 0.226848, + 0.212428, + 0.212428, + 0.226848, + 0.133775, + 0.124352, + 0.520087, + 0.418447, + 0.276984, + 0.494225, + 0.276984, + 0.165882, + 0.382664, + 0.483353, + 0.390460, + 0.483353, + 0.382664, + 0.298215, + 0.165882, + 0.243816, + 0.298215, + 0.382664, + 0.243816, + 0.494225, + 0.390460, + 0.298215, + 0.494225, + 0.276984, + 0.483353, + 0.390460; + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000,-1.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 1.000000;; + } // End of Armature_Bone skin weights + SkinWeights { + "Armature_Bone_004"; + 172; + 4, + 5, + 6, + 7, + 9, + 10, + 13, + 14, + 17, + 18, + 20, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57, + 58, + 59, + 60, + 61, + 62, + 63, + 64, + 65, + 66, + 67, + 68, + 69, + 70, + 71, + 72, + 73, + 74, + 75, + 76, + 77, + 78, + 79, + 80, + 81, + 82, + 83, + 84, + 85, + 86, + 87, + 88, + 89, + 90, + 91, + 92, + 93, + 94, + 95, + 96, + 97, + 98, + 99, + 100, + 101, + 102, + 103, + 104, + 105, + 106, + 107, + 108, + 109, + 110, + 111, + 112, + 113, + 114, + 115, + 116, + 117, + 118, + 119, + 120, + 121, + 122, + 123, + 124, + 125, + 126, + 127, + 128, + 129, + 130, + 131, + 132, + 133, + 134, + 135, + 216, + 217, + 218, + 219, + 220, + 221, + 222, + 223, + 224, + 225, + 226, + 227, + 228, + 229, + 230, + 231, + 232, + 233, + 234, + 235, + 236, + 237, + 238, + 239, + 240, + 241, + 242, + 243, + 244, + 245, + 246, + 247, + 248, + 249, + 250, + 251, + 252, + 253, + 254, + 255, + 256, + 257, + 258, + 259, + 260, + 261, + 262, + 263; + 0.047982, + 0.075546, + 0.082326, + 0.053045, + 0.055496, + 0.112532, + 0.112532, + 0.075546, + 0.075546, + 0.047982, + 0.055496, + 0.047982, + 0.122575, + 0.186448, + 0.245593, + 0.210507, + 0.112532, + 0.055496, + 0.059905, + 0.123986, + 0.055496, + 0.047982, + 0.053045, + 0.059905, + 0.075546, + 0.112532, + 0.123986, + 0.082326, + 0.245593, + 0.393551, + 0.394730, + 0.303906, + 0.427903, + 0.130157, + 0.210403, + 0.393551, + 0.130157, + 0.122575, + 0.210507, + 0.210403, + 0.186448, + 0.427903, + 0.393551, + 0.245593, + 0.280416, + 0.279610, + 0.692449, + 0.678135, + 0.210507, + 0.245593, + 0.303906, + 0.279610, + 0.393551, + 0.210403, + 0.280416, + 0.394730, + 0.210403, + 0.210507, + 0.279610, + 0.280416, + 0.678135, + 0.692449, + 0.672937, + 0.755223, + 0.303906, + 0.394730, + 0.755223, + 0.672937, + 0.279610, + 0.303906, + 0.672937, + 0.692449, + 0.394730, + 0.280416, + 0.678135, + 0.755223, + 0.053045, + 0.082326, + 0.125519, + 0.075862, + 0.075862, + 0.125519, + 0.186448, + 0.122575, + 0.123986, + 0.059905, + 0.083109, + 0.210006, + 0.210006, + 0.083109, + 0.130157, + 0.427903, + 0.059905, + 0.053045, + 0.075862, + 0.083109, + 0.083109, + 0.075862, + 0.122575, + 0.130157, + 0.082326, + 0.123986, + 0.210006, + 0.125519, + 0.210006, + 0.427903, + 0.359212, + 0.273279, + 0.346964, + 0.273279, + 0.359212, + 0.442846, + 0.427903, + 0.186448, + 0.442846, + 0.359212, + 0.186448, + 0.125519, + 0.346964, + 0.442846, + 0.125519, + 0.210006, + 0.273279, + 0.346964, + 0.771594, + 0.764459, + 0.773438, + 0.773890, + 0.764459, + 0.781066, + 0.779591, + 0.773438, + 0.781066, + 0.776433, + 0.776638, + 0.779591, + 0.776433, + 0.771594, + 0.773890, + 0.776638, + 0.773890, + 0.773438, + 0.779591, + 0.776638, + 0.776433, + 0.781066, + 0.764459, + 0.771594, + 0.532908, + 0.525944, + 0.550797, + 0.540369, + 0.525944, + 0.526921, + 0.539430, + 0.550797, + 0.526921, + 0.532411, + 0.537403, + 0.539430, + 0.532411, + 0.532908, + 0.540369, + 0.537403, + 0.540369, + 0.550797, + 0.539430, + 0.537403, + 0.532411, + 0.526921, + 0.525944, + 0.532908; + 1.000000, 0.000000, 0.000000, 0.000000, + 0.000000, 0.000000,-1.000000, 0.000000, + 0.000000, 1.000000, 0.000000, 0.000000, + 0.000000,-2.000000, 0.000000, 1.000000;; + } // End of Armature_Bone_004 skin weights + } // End of Cube mesh + } // End of Cube + } // End of Armature +} // End of Root +AnimationSet Global { + Animation { + {Armature} + AnimationKey { // Rotation + 0; + 31; + 0;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 1;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 2;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 3;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 4;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 5;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 6;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 7;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 8;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 9;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 10;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 11;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 12;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 13;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 14;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 15;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 16;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 17;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 18;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 19;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 20;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 21;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 22;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 23;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 24;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 25;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 26;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 27;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 28;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 29;4;-0.000000, 0.000000, 0.000000, 1.000000;;, + 30;4;-0.000000, 0.000000, 0.000000, 1.000000;;; + } + AnimationKey { // Scale + 1; + 31; + 0;3; 5.000000, 5.000000, 5.000000;;, + 1;3; 5.000000, 5.000000, 5.000000;;, + 2;3; 5.000000, 5.000000, 5.000000;;, + 3;3; 5.000000, 5.000000, 5.000000;;, + 4;3; 5.000000, 5.000000, 5.000000;;, + 5;3; 5.000000, 5.000000, 5.000000;;, + 6;3; 5.000000, 5.000000, 5.000000;;, + 7;3; 5.000000, 5.000000, 5.000000;;, + 8;3; 5.000000, 5.000000, 5.000000;;, + 9;3; 5.000000, 5.000000, 5.000000;;, + 10;3; 5.000000, 5.000000, 5.000000;;, + 11;3; 5.000000, 5.000000, 5.000000;;, + 12;3; 5.000000, 5.000000, 5.000000;;, + 13;3; 5.000000, 5.000000, 5.000000;;, + 14;3; 5.000000, 5.000000, 5.000000;;, + 15;3; 5.000000, 5.000000, 5.000000;;, + 16;3; 5.000000, 5.000000, 5.000000;;, + 17;3; 5.000000, 5.000000, 5.000000;;, + 18;3; 5.000000, 5.000000, 5.000000;;, + 19;3; 5.000000, 5.000000, 5.000000;;, + 20;3; 5.000000, 5.000000, 5.000000;;, + 21;3; 5.000000, 5.000000, 5.000000;;, + 22;3; 5.000000, 5.000000, 5.000000;;, + 23;3; 5.000000, 5.000000, 5.000000;;, + 24;3; 5.000000, 5.000000, 5.000000;;, + 25;3; 5.000000, 5.000000, 5.000000;;, + 26;3; 5.000000, 5.000000, 5.000000;;, + 27;3; 5.000000, 5.000000, 5.000000;;, + 28;3; 5.000000, 5.000000, 5.000000;;, + 29;3; 5.000000, 5.000000, 5.000000;;, + 30;3; 5.000000, 5.000000, 5.000000;;; + } + AnimationKey { // Position + 2; + 31; + 0;3; 0.000000, 0.000000,-5.000000;;, + 1;3; 0.000000, 0.000000,-5.000000;;, + 2;3; 0.000000, 0.000000,-5.000000;;, + 3;3; 0.000000, 0.000000,-5.000000;;, + 4;3; 0.000000, 0.000000,-5.000000;;, + 5;3; 0.000000, 0.000000,-5.000000;;, + 6;3; 0.000000, 0.000000,-5.000000;;, + 7;3; 0.000000, 0.000000,-5.000000;;, + 8;3; 0.000000, 0.000000,-5.000000;;, + 9;3; 0.000000, 0.000000,-5.000000;;, + 10;3; 0.000000, 0.000000,-5.000000;;, + 11;3; 0.000000, 0.000000,-5.000000;;, + 12;3; 0.000000, 0.000000,-5.000000;;, + 13;3; 0.000000, 0.000000,-5.000000;;, + 14;3; 0.000000, 0.000000,-5.000000;;, + 15;3; 0.000000, 0.000000,-5.000000;;, + 16;3; 0.000000, 0.000000,-5.000000;;, + 17;3; 0.000000, 0.000000,-5.000000;;, + 18;3; 0.000000, 0.000000,-5.000000;;, + 19;3; 0.000000, 0.000000,-5.000000;;, + 20;3; 0.000000, 0.000000,-5.000000;;, + 21;3; 0.000000, 0.000000,-5.000000;;, + 22;3; 0.000000, 0.000000,-5.000000;;, + 23;3; 0.000000, 0.000000,-5.000000;;, + 24;3; 0.000000, 0.000000,-5.000000;;, + 25;3; 0.000000, 0.000000,-5.000000;;, + 26;3; 0.000000, 0.000000,-5.000000;;, + 27;3; 0.000000, 0.000000,-5.000000;;, + 28;3; 0.000000, 0.000000,-5.000000;;, + 29;3; 0.000000, 0.000000,-5.000000;;, + 30;3; 0.000000, 0.000000,-5.000000;;; + } + } + Animation { + {Armature_Bone} + AnimationKey { // Rotation + 0; + 31; + 0;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 1;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 2;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 3;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 4;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 5;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 6;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 7;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 8;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 9;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 10;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 11;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 12;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 13;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 14;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 15;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 16;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 17;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 18;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 19;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 20;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 21;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 22;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 23;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 24;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 25;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 26;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 27;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 28;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 29;4;-0.707107, 0.707107, 0.000000, 0.000000;;, + 30;4;-0.707107, 0.707107, 0.000000, 0.000000;;; + } + AnimationKey { // Scale + 1; + 31; + 0;3; 1.000000, 1.000000, 1.000000;;, + 1;3; 1.000000, 1.000000, 1.000000;;, + 2;3; 1.000000, 1.000000, 1.000000;;, + 3;3; 1.000000, 1.000000, 1.000000;;, + 4;3; 1.000000, 1.000000, 1.000000;;, + 5;3; 1.000000, 1.000000, 1.000000;;, + 6;3; 1.000000, 1.000000, 1.000000;;, + 7;3; 1.000000, 1.000000, 1.000000;;, + 8;3; 1.000000, 1.000000, 1.000000;;, + 9;3; 1.000000, 1.000000, 1.000000;;, + 10;3; 1.000000, 1.000000, 1.000000;;, + 11;3; 1.000000, 1.000000, 1.000000;;, + 12;3; 1.000000, 1.000000, 1.000000;;, + 13;3; 1.000000, 1.000000, 1.000000;;, + 14;3; 1.000000, 1.000000, 1.000000;;, + 15;3; 1.000000, 1.000000, 1.000000;;, + 16;3; 1.000000, 1.000000, 1.000000;;, + 17;3; 1.000000, 1.000000, 1.000000;;, + 18;3; 1.000000, 1.000000, 1.000000;;, + 19;3; 1.000000, 1.000000, 1.000000;;, + 20;3; 1.000000, 1.000000, 1.000000;;, + 21;3; 1.000000, 1.000000, 1.000000;;, + 22;3; 1.000000, 1.000000, 1.000000;;, + 23;3; 1.000000, 1.000000, 1.000000;;, + 24;3; 1.000000, 1.000000, 1.000000;;, + 25;3; 1.000000, 1.000000, 1.000000;;, + 26;3; 1.000000, 1.000000, 1.000000;;, + 27;3; 1.000000, 1.000000, 1.000000;;, + 28;3; 1.000000, 1.000000, 1.000000;;, + 29;3; 1.000000, 1.000000, 1.000000;;, + 30;3; 1.000000, 1.000000, 1.000000;;; + } + AnimationKey { // Position + 2; + 31; + 0;3; 0.000000, 0.000000, 0.000000;;, + 1;3; 0.000000, 0.000000, 0.000000;;, + 2;3; 0.000000, 0.000000, 0.000000;;, + 3;3; 0.000000, 0.000000, 0.000000;;, + 4;3; 0.000000, 0.000000, 0.000000;;, + 5;3; 0.000000, 0.000000, 0.000000;;, + 6;3; 0.000000, 0.000000, 0.000000;;, + 7;3; 0.000000, 0.000000, 0.000000;;, + 8;3; 0.000000, 0.000000, 0.000000;;, + 9;3; 0.000000, 0.000000, 0.000000;;, + 10;3; 0.000000, 0.000000, 0.000000;;, + 11;3; 0.000000, 0.000000, 0.000000;;, + 12;3; 0.000000, 0.000000, 0.000000;;, + 13;3; 0.000000, 0.000000, 0.000000;;, + 14;3; 0.000000, 0.000000, 0.000000;;, + 15;3; 0.000000, 0.000000, 0.000000;;, + 16;3; 0.000000, 0.000000, 0.000000;;, + 17;3; 0.000000, 0.000000, 0.000000;;, + 18;3; 0.000000, 0.000000, 0.000000;;, + 19;3; 0.000000, 0.000000, 0.000000;;, + 20;3; 0.000000, 0.000000, 0.000000;;, + 21;3; 0.000000, 0.000000, 0.000000;;, + 22;3; 0.000000, 0.000000, 0.000000;;, + 23;3; 0.000000, 0.000000, 0.000000;;, + 24;3; 0.000000, 0.000000, 0.000000;;, + 25;3; 0.000000, 0.000000, 0.000000;;, + 26;3; 0.000000, 0.000000, 0.000000;;, + 27;3; 0.000000, 0.000000, 0.000000;;, + 28;3; 0.000000, 0.000000, 0.000000;;, + 29;3; 0.000000, 0.000000, 0.000000;;, + 30;3; 0.000000, 0.000000, 0.000000;;; + } + } + Animation { + {Armature_Bone_001} + AnimationKey { // Rotation + 0; + 31; + 0;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 1;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 2;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 3;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 4;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 5;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 6;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 7;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 8;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 9;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 10;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 11;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 12;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 13;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 14;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 15;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 16;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 17;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 18;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 19;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 20;4;-0.998539, 0.054035, 0.000000,-0.000000;;, + 21;4;-0.996147, 0.053932, 0.002355, 0.043499;;, + 22;4;-0.998539, 0.054035, 0.000000, 0.000000;;, + 23;4;-0.997589, 0.053983,-0.002357,-0.043556;;, + 24;4;-0.998539, 0.054035, 0.000000, 0.000000;;, + 25;4;-0.996639, 0.053932, 0.003045, 0.056265;;, + 26;4;-0.994740, 0.053829, 0.004709, 0.087028;;, + 27;4;-0.996147, 0.053932, 0.002355, 0.043519;;, + 28;4;-0.998539, 0.054035, 0.000000, 0.000000;;, + 29;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 30;4;-1.000000, 0.000000, 0.000000, 0.000000;;; + } + AnimationKey { // Scale + 1; + 31; + 0;3; 1.000000, 1.000000, 1.000000;;, + 1;3; 1.000000, 1.000000, 1.000000;;, + 2;3; 1.000000, 1.000000, 1.000000;;, + 3;3; 1.000000, 1.000000, 1.000000;;, + 4;3; 1.000000, 1.000000, 1.000000;;, + 5;3; 1.000000, 1.000000, 1.000000;;, + 6;3; 1.000000, 1.000000, 1.000000;;, + 7;3; 1.000000, 1.000000, 1.000000;;, + 8;3; 1.000000, 1.000000, 1.000000;;, + 9;3; 1.000000, 1.000000, 1.000000;;, + 10;3; 1.000000, 1.000000, 1.000000;;, + 11;3; 1.000000, 1.000000, 1.000000;;, + 12;3; 1.000000, 1.000000, 1.000000;;, + 13;3; 1.000000, 1.000000, 1.000000;;, + 14;3; 1.000000, 1.000000, 1.000000;;, + 15;3; 1.000000, 1.000000, 1.000000;;, + 16;3; 1.000000, 1.000000, 1.000000;;, + 17;3; 1.000000, 1.000000, 1.000000;;, + 18;3; 1.000000, 1.000000, 1.000000;;, + 19;3; 1.000000, 1.000000, 1.000000;;, + 20;3; 1.000000, 1.000000, 1.000000;;, + 21;3; 1.000000, 1.000000, 1.000000;;, + 22;3; 1.000000, 1.000000, 1.000000;;, + 23;3; 1.000000, 1.000000, 1.000000;;, + 24;3; 1.000000, 1.000000, 1.000000;;, + 25;3; 1.000000, 1.000000, 1.000000;;, + 26;3; 1.000000, 1.000000, 1.000000;;, + 27;3; 1.000000, 1.000000, 1.000000;;, + 28;3; 1.000000, 1.000000, 1.000000;;, + 29;3; 1.000000, 1.000000, 1.000000;;, + 30;3; 1.000000, 1.000000, 1.000000;;; + } + AnimationKey { // Position + 2; + 31; + 0;3; 0.000000, 2.000000, 0.000000;;, + 1;3; 0.000000, 2.000000, 0.000000;;, + 2;3; 0.000000, 2.000000, 0.000000;;, + 3;3; 0.000000, 2.000000, 0.000000;;, + 4;3; 0.000000, 2.000000, 0.000000;;, + 5;3; 0.000000, 2.000000, 0.000000;;, + 6;3; 0.000000, 2.000000, 0.000000;;, + 7;3; 0.000000, 2.000000, 0.000000;;, + 8;3; 0.000000, 2.000000, 0.000000;;, + 9;3; 0.000000, 2.000000, 0.000000;;, + 10;3; 0.000000, 2.000000, 0.000000;;, + 11;3; 0.000000, 2.000000, 0.000000;;, + 12;3; 0.000000, 2.000000, 0.000000;;, + 13;3; 0.000000, 2.000000, 0.000000;;, + 14;3; 0.000000, 2.000000, 0.000000;;, + 15;3; 0.000000, 2.000000, 0.000000;;, + 16;3; 0.000000, 2.000000, 0.000000;;, + 17;3; 0.000000, 2.000000, 0.000000;;, + 18;3; 0.000000, 2.000000, 0.000000;;, + 19;3; 0.000000, 2.000000, 0.000000;;, + 20;3; 0.000000, 2.000000, 0.000000;;, + 21;3; 0.000000, 2.000000, 0.000000;;, + 22;3; 0.000000, 2.000000, 0.000000;;, + 23;3; 0.000000, 2.000000, 0.000000;;, + 24;3; 0.000000, 2.000000, 0.000000;;, + 25;3; 0.000000, 2.000000, 0.000000;;, + 26;3; 0.000000, 2.000000, 0.000000;;, + 27;3; 0.000000, 2.000000, 0.000000;;, + 28;3; 0.000000, 2.000000, 0.000000;;, + 29;3; 0.000000, 2.000000, 0.000000;;, + 30;3; 0.000000, 2.000000, 0.000000;;; + } + } + Animation { + {Armature_Bone_002} + AnimationKey { // Rotation + 0; + 31; + 0;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 1;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 2;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 3;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 4;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 5;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 6;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 7;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 8;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 9;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 10;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 11;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 12;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 13;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 14;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 15;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 16;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 17;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 18;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 19;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 20;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 21;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 22;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 23;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 24;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 25;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 26;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 27;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 28;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 29;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 30;4;-1.000000, 0.000000, 0.000000, 0.000000;;; + } + AnimationKey { // Scale + 1; + 31; + 0;3; 1.000000, 1.000000, 1.000000;;, + 1;3; 1.000000, 1.000000, 1.000000;;, + 2;3; 1.000000, 1.000000, 1.000000;;, + 3;3; 1.000000, 1.000000, 1.000000;;, + 4;3; 1.000000, 1.000000, 1.000000;;, + 5;3; 1.000000, 1.000000, 1.000000;;, + 6;3; 1.000000, 1.000000, 1.000000;;, + 7;3; 1.000000, 1.000000, 1.000000;;, + 8;3; 1.000000, 1.000000, 1.000000;;, + 9;3; 1.000000, 1.000000, 1.000000;;, + 10;3; 1.000000, 1.000000, 1.000000;;, + 11;3; 1.000000, 1.000000, 1.000000;;, + 12;3; 1.000000, 1.000000, 1.000000;;, + 13;3; 1.000000, 1.000000, 1.000000;;, + 14;3; 1.000000, 1.000000, 1.000000;;, + 15;3; 1.000000, 1.000000, 1.000000;;, + 16;3; 1.000000, 1.000000, 1.000000;;, + 17;3; 1.000000, 1.000000, 1.000000;;, + 18;3; 1.000000, 1.000000, 1.000000;;, + 19;3; 1.000000, 1.000000, 1.000000;;, + 20;3; 1.000000, 1.000000, 1.000000;;, + 21;3; 1.000000, 1.000000, 1.000000;;, + 22;3; 1.000000, 1.000000, 1.000000;;, + 23;3; 1.000000, 1.000000, 1.000000;;, + 24;3; 1.000000, 1.000000, 1.000000;;, + 25;3; 1.000000, 1.000000, 1.000000;;, + 26;3; 1.000000, 1.000000, 1.000000;;, + 27;3; 1.000000, 1.000000, 1.000000;;, + 28;3; 1.000000, 1.000000, 1.000000;;, + 29;3; 1.000000, 1.000000, 1.000000;;, + 30;3; 1.000000, 1.000000, 1.000000;;; + } + AnimationKey { // Position + 2; + 31; + 0;3; 0.000000, 0.300000, 0.000000;;, + 1;3; 0.000000, 0.300000, 0.000000;;, + 2;3; 0.000000, 0.300000, 0.000000;;, + 3;3; 0.000000, 0.300000, 0.000000;;, + 4;3; 0.000000, 0.300000, 0.000000;;, + 5;3; 0.000000, 0.300000, 0.000000;;, + 6;3; 0.000000, 0.300000, 0.000000;;, + 7;3; 0.000000, 0.300000, 0.000000;;, + 8;3; 0.000000, 0.300000, 0.000000;;, + 9;3; 0.000000, 0.300000, 0.000000;;, + 10;3; 0.000000, 0.300000, 0.000000;;, + 11;3; 0.000000, 0.300000, 0.000000;;, + 12;3; 0.000000, 0.300000, 0.000000;;, + 13;3; 0.000000, 0.300000, 0.000000;;, + 14;3; 0.000000, 0.300000, 0.000000;;, + 15;3; 0.000000, 0.300000, 0.000000;;, + 16;3; 0.000000, 0.300000, 0.000000;;, + 17;3; 0.000000, 0.300000, 0.000000;;, + 18;3; 0.000000, 0.300000, 0.000000;;, + 19;3; 0.000000, 0.300000, 0.000000;;, + 20;3; 0.000000, 0.300000, 0.000000;;, + 21;3;-0.000000, 0.300000,-0.000000;;, + 22;3;-0.000000, 0.300000, 0.000000;;, + 23;3;-0.000000, 0.300000, 0.000000;;, + 24;3;-0.000000, 0.300000, 0.000000;;, + 25;3;-0.000000, 0.300000,-0.000000;;, + 26;3;-0.000000, 0.300000, 0.000000;;, + 27;3;-0.000000, 0.300000,-0.000000;;, + 28;3; 0.000000, 0.300000, 0.000000;;, + 29;3; 0.000000, 0.300000, 0.000000;;, + 30;3; 0.000000, 0.300000, 0.000000;;; + } + } + Animation { + {Armature_Bone_003} + AnimationKey { // Rotation + 0; + 31; + 0;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 1;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 2;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 3;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 4;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 5;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 6;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 7;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 8;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 9;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 10;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 11;4;-0.998097,-0.000000, 0.000000,-0.043583;;, + 12;4;-0.996195,-0.000000, 0.000000,-0.087156;;, + 13;4;-0.998097,-0.000000, 0.000000,-0.056342;;, + 14;4;-1.000000,-0.000000,-0.000000, 0.000000;;, + 15;4;-0.998097,-0.000000,-0.000000, 0.056342;;, + 16;4;-0.996195,-0.000000,-0.000000, 0.087156;;, + 17;4;-0.998097,-0.000000,-0.000000, 0.043583;;, + 18;4;-1.000000,-0.000000, 0.000000, 0.000000;;, + 19;4;-1.000000,-0.000000, 0.000000, 0.000000;;, + 20;4;-0.996195, 0.087156, 0.000000, 0.000000;;, + 21;4;-0.996434, 0.081668, 0.000000, 0.000000;;, + 22;4;-0.996936, 0.070172, 0.000000, 0.000000;;, + 23;4;-0.997535, 0.056459, 0.000000, 0.000000;;, + 24;4;-0.998149, 0.042383, 0.000000, 0.000000;;, + 25;4;-0.998729, 0.029106, 0.000000, 0.000000;;, + 26;4;-0.999236, 0.017493, 0.000000, 0.000000;;, + 27;4;-0.999638, 0.008289, 0.000000, 0.000000;;, + 28;4;-0.999904, 0.002208, 0.000000, 0.000000;;, + 29;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 30;4;-1.000000, 0.000000, 0.000000, 0.000000;;; + } + AnimationKey { // Scale + 1; + 31; + 0;3; 1.000000, 1.000000, 1.000000;;, + 1;3; 1.000000, 1.000000, 1.000000;;, + 2;3; 1.000000, 1.000000, 1.000000;;, + 3;3; 1.000000, 1.000000, 1.000000;;, + 4;3; 1.000000, 1.000000, 1.000000;;, + 5;3; 1.000000, 1.000000, 1.000000;;, + 6;3; 1.000000, 1.000000, 1.000000;;, + 7;3; 1.000000, 1.000000, 1.000000;;, + 8;3; 1.000000, 1.000000, 1.000000;;, + 9;3; 1.000000, 1.000000, 1.000000;;, + 10;3; 1.000000, 1.000000, 1.000000;;, + 11;3; 1.000000, 1.000000, 1.000000;;, + 12;3; 1.000000, 1.000000, 1.000000;;, + 13;3; 1.000000, 1.000000, 1.000000;;, + 14;3; 1.000000, 1.000000, 1.000000;;, + 15;3; 1.000000, 1.000000, 1.000000;;, + 16;3; 1.000000, 1.000000, 1.000000;;, + 17;3; 1.000000, 1.000000, 1.000000;;, + 18;3; 1.000000, 1.000000, 1.000000;;, + 19;3; 1.000000, 1.000000, 1.000000;;, + 20;3; 1.000000, 1.000000, 1.000000;;, + 21;3; 1.000000, 1.000000, 1.000000;;, + 22;3; 1.000000, 1.000000, 1.000000;;, + 23;3; 1.000000, 1.000000, 1.000000;;, + 24;3; 1.000000, 1.000000, 1.000000;;, + 25;3; 1.000000, 1.000000, 1.000000;;, + 26;3; 1.000000, 1.000000, 1.000000;;, + 27;3; 1.000000, 1.000000, 1.000000;;, + 28;3; 1.000000, 1.000000, 1.000000;;, + 29;3; 1.000000, 1.000000, 1.000000;;, + 30;3; 1.000000, 1.000000, 1.000000;;; + } + AnimationKey { // Position + 2; + 31; + 0;3; 0.000000, 0.300000, 0.000000;;, + 1;3; 0.000000, 0.300000, 0.000000;;, + 2;3; 0.000000, 0.300000, 0.000000;;, + 3;3; 0.000000, 0.300000, 0.000000;;, + 4;3; 0.000000, 0.300000, 0.000000;;, + 5;3; 0.000000, 0.300000, 0.000000;;, + 6;3; 0.000000, 0.300000, 0.000000;;, + 7;3; 0.000000, 0.300000, 0.000000;;, + 8;3; 0.000000, 0.300000, 0.000000;;, + 9;3; 0.000000, 0.300000, 0.000000;;, + 10;3; 0.000000, 0.300000, 0.000000;;, + 11;3; 0.000000, 0.300000, 0.000000;;, + 12;3; 0.000000, 0.300000, 0.000000;;, + 13;3; 0.000000, 0.300000, 0.000000;;, + 14;3; 0.000000, 0.300000, 0.000000;;, + 15;3; 0.000000, 0.300000, 0.000000;;, + 16;3; 0.000000, 0.300000, 0.000000;;, + 17;3; 0.000000, 0.300000, 0.000000;;, + 18;3; 0.000000, 0.300000, 0.000000;;, + 19;3; 0.000000, 0.300000, 0.000000;;, + 20;3; 0.000000, 0.300000, 0.000000;;, + 21;3;-0.000000, 0.300000,-0.000000;;, + 22;3;-0.000000, 0.300000,-0.000000;;, + 23;3; 0.000000, 0.300000, 0.000000;;, + 24;3;-0.000000, 0.300000,-0.000000;;, + 25;3; 0.000000, 0.300000,-0.000000;;, + 26;3;-0.000000, 0.300000,-0.000000;;, + 27;3;-0.000000, 0.300000,-0.000000;;, + 28;3;-0.000000, 0.300000, 0.000000;;, + 29;3; 0.000000, 0.300000, 0.000000;;, + 30;3; 0.000000, 0.300000, 0.000000;;; + } + } + Animation { + {Armature_Bone_004} + AnimationKey { // Rotation + 0; + 31; + 0;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 1;4;-0.999524, 0.021812, 0.000000,-0.000000;;, + 2;4;-0.999048, 0.043619, 0.000000,-0.000000;;, + 3;4;-0.999524, 0.028196, 0.000000,-0.000000;;, + 4;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 5;4;-0.999524,-0.028196,-0.000000, 0.000000;;, + 6;4;-0.999048,-0.043619,-0.000000, 0.000000;;, + 7;4;-0.999524,-0.021810,-0.000000, 0.000000;;, + 8;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 9;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 10;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 11;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 12;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 13;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 14;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 15;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 16;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 17;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 18;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 19;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 20;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 21;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 22;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 23;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 24;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 25;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 26;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 27;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 28;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 29;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 30;4;-1.000000, 0.000000, 0.000000, 0.000000;;; + } + AnimationKey { // Scale + 1; + 31; + 0;3; 1.000000, 1.000000, 1.000000;;, + 1;3; 1.000000, 1.000000, 1.000000;;, + 2;3; 1.000000, 1.000000, 1.000000;;, + 3;3; 1.000000, 1.000000, 1.000000;;, + 4;3; 1.000000, 1.000000, 1.000000;;, + 5;3; 1.000000, 1.000000, 1.000000;;, + 6;3; 1.000000, 1.000000, 1.000000;;, + 7;3; 1.000000, 1.000000, 1.000000;;, + 8;3; 1.000000, 1.000000, 1.000000;;, + 9;3; 1.000000, 1.000000, 1.000000;;, + 10;3; 1.000000, 1.000000, 1.000000;;, + 11;3; 1.000000, 1.000000, 1.000000;;, + 12;3; 1.000000, 1.000000, 1.000000;;, + 13;3; 1.000000, 1.000000, 1.000000;;, + 14;3; 1.000000, 1.000000, 1.000000;;, + 15;3; 1.000000, 1.000000, 1.000000;;, + 16;3; 1.000000, 1.000000, 1.000000;;, + 17;3; 1.000000, 1.000000, 1.000000;;, + 18;3; 1.000000, 1.000000, 1.000000;;, + 19;3; 1.000000, 1.000000, 1.000000;;, + 20;3; 1.000000, 1.000000, 1.000000;;, + 21;3; 1.000000, 1.000000, 1.000000;;, + 22;3; 1.000000, 1.000000, 1.000000;;, + 23;3; 1.000000, 1.000000, 1.000000;;, + 24;3; 1.000000, 1.000000, 1.000000;;, + 25;3; 1.000000, 1.000000, 1.000000;;, + 26;3; 1.000000, 1.000000, 1.000000;;, + 27;3; 1.000000, 1.000000, 1.000000;;, + 28;3; 1.000000, 1.000000, 1.000000;;, + 29;3; 1.000000, 1.000000, 1.000000;;, + 30;3; 1.000000, 1.000000, 1.000000;;; + } + AnimationKey { // Position + 2; + 31; + 0;3; 0.000000, 0.400000, 0.000000;;, + 1;3; 0.000000, 0.400000, 0.000000;;, + 2;3; 0.000000, 0.400000, 0.000000;;, + 3;3; 0.000000, 0.400000, 0.000000;;, + 4;3; 0.000000, 0.400000, 0.000000;;, + 5;3; 0.000000, 0.400000, 0.000000;;, + 6;3; 0.000000, 0.400000, 0.000000;;, + 7;3; 0.000000, 0.400000, 0.000000;;, + 8;3; 0.000000, 0.400000, 0.000000;;, + 9;3; 0.000000, 0.400000, 0.000000;;, + 10;3; 0.000000, 0.400000, 0.000000;;, + 11;3; 0.000000, 0.400000,-0.000000;;, + 12;3;-0.000000, 0.400000, 0.000000;;, + 13;3; 0.000000, 0.400000, 0.000000;;, + 14;3;-0.000000, 0.400000, 0.000000;;, + 15;3;-0.000000, 0.400000, 0.000000;;, + 16;3; 0.000000, 0.400000, 0.000000;;, + 17;3;-0.000000, 0.400000,-0.000000;;, + 18;3;-0.000000, 0.400000,-0.000000;;, + 19;3;-0.000000, 0.400000,-0.000000;;, + 20;3; 0.000000, 0.400000,-0.000000;;, + 21;3; 0.000000, 0.400000, 0.000000;;, + 22;3;-0.000000, 0.400000,-0.000000;;, + 23;3;-0.000000, 0.400000,-0.000000;;, + 24;3;-0.000000, 0.400000,-0.000000;;, + 25;3; 0.000000, 0.400000, 0.000000;;, + 26;3;-0.000000, 0.400000, 0.000000;;, + 27;3;-0.000000, 0.400000, 0.000000;;, + 28;3; 0.000000, 0.400000, 0.000000;;, + 29;3; 0.000000, 0.400000, 0.000000;;, + 30;3; 0.000000, 0.400000, 0.000000;;; + } + } + Animation { + {Cube} + AnimationKey { // Rotation + 0; + 31; + 0;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 1;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 2;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 3;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 4;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 5;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 6;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 7;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 8;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 9;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 10;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 11;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 12;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 13;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 14;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 15;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 16;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 17;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 18;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 19;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 20;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 21;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 22;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 23;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 24;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 25;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 26;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 27;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 28;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 29;4;-1.000000, 0.000000, 0.000000, 0.000000;;, + 30;4;-1.000000, 0.000000, 0.000000, 0.000000;;; + } + AnimationKey { // Scale + 1; + 31; + 0;3; 1.000000, 1.000000, 1.000000;;, + 1;3; 1.000000, 1.000000, 1.000000;;, + 2;3; 1.000000, 1.000000, 1.000000;;, + 3;3; 1.000000, 1.000000, 1.000000;;, + 4;3; 1.000000, 1.000000, 1.000000;;, + 5;3; 1.000000, 1.000000, 1.000000;;, + 6;3; 1.000000, 1.000000, 1.000000;;, + 7;3; 1.000000, 1.000000, 1.000000;;, + 8;3; 1.000000, 1.000000, 1.000000;;, + 9;3; 1.000000, 1.000000, 1.000000;;, + 10;3; 1.000000, 1.000000, 1.000000;;, + 11;3; 1.000000, 1.000000, 1.000000;;, + 12;3; 1.000000, 1.000000, 1.000000;;, + 13;3; 1.000000, 1.000000, 1.000000;;, + 14;3; 1.000000, 1.000000, 1.000000;;, + 15;3; 1.000000, 1.000000, 1.000000;;, + 16;3; 1.000000, 1.000000, 1.000000;;, + 17;3; 1.000000, 1.000000, 1.000000;;, + 18;3; 1.000000, 1.000000, 1.000000;;, + 19;3; 1.000000, 1.000000, 1.000000;;, + 20;3; 1.000000, 1.000000, 1.000000;;, + 21;3; 1.000000, 1.000000, 1.000000;;, + 22;3; 1.000000, 1.000000, 1.000000;;, + 23;3; 1.000000, 1.000000, 1.000000;;, + 24;3; 1.000000, 1.000000, 1.000000;;, + 25;3; 1.000000, 1.000000, 1.000000;;, + 26;3; 1.000000, 1.000000, 1.000000;;, + 27;3; 1.000000, 1.000000, 1.000000;;, + 28;3; 1.000000, 1.000000, 1.000000;;, + 29;3; 1.000000, 1.000000, 1.000000;;, + 30;3; 1.000000, 1.000000, 1.000000;;; + } + AnimationKey { // Position + 2; + 31; + 0;3; 0.000000, 0.000000, 1.000000;;, + 1;3; 0.000000, 0.000000, 1.000000;;, + 2;3; 0.000000, 0.000000, 1.000000;;, + 3;3; 0.000000, 0.000000, 1.000000;;, + 4;3; 0.000000, 0.000000, 1.000000;;, + 5;3; 0.000000, 0.000000, 1.000000;;, + 6;3; 0.000000, 0.000000, 1.000000;;, + 7;3; 0.000000, 0.000000, 1.000000;;, + 8;3; 0.000000, 0.000000, 1.000000;;, + 9;3; 0.000000, 0.000000, 1.000000;;, + 10;3; 0.000000, 0.000000, 1.000000;;, + 11;3; 0.000000, 0.000000, 1.000000;;, + 12;3; 0.000000, 0.000000, 1.000000;;, + 13;3; 0.000000, 0.000000, 1.000000;;, + 14;3; 0.000000, 0.000000, 1.000000;;, + 15;3; 0.000000, 0.000000, 1.000000;;, + 16;3; 0.000000, 0.000000, 1.000000;;, + 17;3; 0.000000, 0.000000, 1.000000;;, + 18;3; 0.000000, 0.000000, 1.000000;;, + 19;3; 0.000000, 0.000000, 1.000000;;, + 20;3; 0.000000, 0.000000, 1.000000;;, + 21;3; 0.000000, 0.000000, 1.000000;;, + 22;3; 0.000000, 0.000000, 1.000000;;, + 23;3; 0.000000, 0.000000, 1.000000;;, + 24;3; 0.000000, 0.000000, 1.000000;;, + 25;3; 0.000000, 0.000000, 1.000000;;, + 26;3; 0.000000, 0.000000, 1.000000;;, + 27;3; 0.000000, 0.000000, 1.000000;;, + 28;3; 0.000000, 0.000000, 1.000000;;, + 29;3; 0.000000, 0.000000, 1.000000;;, + 30;3; 0.000000, 0.000000, 1.000000;;; + } + } +} // End of AnimationSet Global diff --git a/games/devtest/mods/testentities/visuals.lua b/games/devtest/mods/testentities/visuals.lua index b61b5e819..6bb1b8282 100644 --- a/games/devtest/mods/testentities/visuals.lua +++ b/games/devtest/mods/testentities/visuals.lua @@ -79,6 +79,20 @@ core.register_entity("testentities:sam", { end, }) +core.register_entity("testentities:lava_flan", { + initial_properties = { + infotext = "Lava Flan (smoke test .x)", + visual = "mesh", + mesh = "testentities_lava_flan.x", + textures = { + "testentities_lava_flan.png" + }, + }, + on_activate = function(self) + self.object:set_animation({x = 0, y = 28}, 15, 0, true) + end, +}) + -- Advanced visual tests -- An entity for testing animated and yaw-modulated sprites diff --git a/irr/src/CXMeshFileLoader.cpp b/irr/src/CXMeshFileLoader.cpp index 508b632c0..c09f2e481 100644 --- a/irr/src/CXMeshFileLoader.cpp +++ b/irr/src/CXMeshFileLoader.cpp @@ -56,8 +56,9 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file) AnimatedMesh = new SkinnedMeshBuilder(); + SkinnedMesh *res = nullptr; if (load(file)) { - AnimatedMesh->finalize(); + res = AnimatedMesh->finalize(); } else { AnimatedMesh->drop(); AnimatedMesh = 0; @@ -89,7 +90,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file) delete Meshes[i]; Meshes.clear(); - return AnimatedMesh->finalize(); + return res; } bool CXMeshFileLoader::load(io::IReadFile *file) @@ -962,15 +963,16 @@ bool CXMeshFileLoader::parseDataObjectSkinWeights(SXMesh &mesh) u32 i; const u32 jointStart = joint->Weights.size(); - joint->Weights.resize(jointStart + nWeights); + joint->Weights.reserve(jointStart + nWeights); mesh.WeightJoint.reallocate(mesh.WeightJoint.size() + nWeights); mesh.WeightNum.reallocate(mesh.WeightNum.size() + nWeights); for (i = 0; i < nWeights; ++i) { mesh.WeightJoint.push_back(*n); - mesh.WeightNum.push_back(joint->Weights.size()); + mesh.WeightNum.push_back(joint->Weights.size()); // id of weight + // Note: This adds a weight to joint->Weights SkinnedMesh::SWeight *weight = AnimatedMesh->addWeight(joint); weight->buffer_id = 0; From 10f1e142f693da1d8f1da7485b69957a24e31069 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 20 Dec 2024 15:03:30 +0100 Subject: [PATCH 60/68] Fix threshold value for imageCleanTransparent fixes #15401 --- src/client/imagesource.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/client/imagesource.cpp b/src/client/imagesource.cpp index a14dac290..4adc39834 100644 --- a/src/client/imagesource.cpp +++ b/src/client/imagesource.cpp @@ -1511,10 +1511,18 @@ bool ImageSource::generateImagePart(std::string_view part_of_name, CHECK_BASEIMG(); - // Apply the "clean transparent" filter, if needed + /* Apply the "clean transparent" filter, if necessary + * This is needed since filtering will sample parts of the image + * that are transparent and PNG optimizers often discard the color + * information in those parts. */ if (m_setting_mipmap || m_setting_bilinear_filter || - m_setting_trilinear_filter || m_setting_anisotropic_filter) - imageCleanTransparent(baseimg, 127); + m_setting_trilinear_filter || m_setting_anisotropic_filter) { + /* Note: in theory we should pass either 0 or 127 depending on + * if the texture is used with an ALPHA or ALPHA_REF material, + * however we don't have this information here. + * It doesn't matter in practice. */ + imageCleanTransparent(baseimg, 0); + } /* Upscale textures to user's requested minimum size. This is a trick to make * filters look as good on low-res textures as on high-res ones, by making From b172e672956225633188d60f51111b03ce8fa714 Mon Sep 17 00:00:00 2001 From: cx384 Date: Fri, 20 Dec 2024 15:03:45 +0100 Subject: [PATCH 61/68] Remove game_ui from Client class --- src/client/client.cpp | 3 --- src/client/client.h | 4 ---- src/client/game.cpp | 2 +- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index f17d4debf..222c1a2ac 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -14,7 +14,6 @@ #include "network/networkpacket.h" #include "threading/mutex_auto_lock.h" #include "client/clientevent.h" -#include "client/gameui.h" #include "client/renderingengine.h" #include "client/sound.h" #include "client/texturepaths.h" @@ -94,7 +93,6 @@ Client::Client( ISoundManager *sound, MtEventManager *event, RenderingEngine *rendering_engine, - GameUI *game_ui, ELoginRegister allow_login_or_register ): m_tsrc(tsrc), @@ -117,7 +115,6 @@ Client::Client( m_chosen_auth_mech(AUTH_MECHANISM_NONE), m_media_downloader(new ClientMediaDownloader()), m_state(LC_Created), - m_game_ui(game_ui), m_modchannel_mgr(new ModChannelMgr()) { // Add local player diff --git a/src/client/client.h b/src/client/client.h index c6803cbe1..e4bb77ab2 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -99,7 +99,6 @@ private: }; class ClientScripting; -class GameUI; class Client : public con::PeerHandler, public InventoryManager, public IGameDef { @@ -119,7 +118,6 @@ public: ISoundManager *sound, MtEventManager *event, RenderingEngine *rendering_engine, - GameUI *game_ui, ELoginRegister allow_login_or_register ); @@ -572,8 +570,6 @@ private: // own state LocalClientState m_state; - GameUI *m_game_ui; - // Used for saving server map to disk client-side MapDatabase *m_localdb = nullptr; IntervalLimiter m_localdb_save_interval; diff --git a/src/client/game.cpp b/src/client/game.cpp index dc13095d1..8bd6f5f29 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1614,7 +1614,7 @@ bool Game::connectToServer(const GameStartData &start_data, start_data.password, *draw_control, texture_src, shader_src, itemdef_manager, nodedef_manager, sound_manager.get(), eventmgr, - m_rendering_engine, m_game_ui.get(), + m_rendering_engine, start_data.allow_login_or_register); } catch (const BaseException &e) { *error_message = fmtgettext("Error creating client: %s", e.what()); From f06383f78c98fcb8dd10c7616a3a770a360a198d Mon Sep 17 00:00:00 2001 From: cx384 Date: Fri, 20 Dec 2024 15:03:55 +0100 Subject: [PATCH 62/68] Minor API reference fixes --- doc/lua_api.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 7fe464a30..91d8f314a 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -6566,7 +6566,7 @@ Environment access * `pointabilities`: Allows overriding the `pointable` property of nodes and objects. Uses the same format as the `pointabilities` property of item definitions. Default is `nil`. -* `core.find_path(pos1,pos2,searchdistance,max_jump,max_drop,algorithm)` +* `core.find_path(pos1, pos2, searchdistance, max_jump, max_drop, algorithm)` * returns table containing path that can be walked on * returns a table of 3D points representing a path from `pos1` to `pos2` or `nil` on failure. @@ -6586,7 +6586,7 @@ Environment access Difference between `"A*"` and `"A*_noprefetch"` is that `"A*"` will pre-calculate the cost-data, the other will calculate it on-the-fly -* `core.spawn_tree (pos, {treedef})` +* `core.spawn_tree(pos, treedef)` * spawns L-system tree at given `pos` with definition in `treedef` table * `core.spawn_tree_on_vmanip(vmanip, pos, treedef)` * analogous to `core.spawn_tree`, but spawns a L-system tree onto the specified From 7354cbe4635a35aad2a6c1a77fac457084341714 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 11 Dec 2024 14:17:51 +0100 Subject: [PATCH 63/68] Fix core::array::reallocate when shrinking --- irr/include/irrArray.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/irr/include/irrArray.h b/irr/include/irrArray.h index 4a87382bc..834dc825c 100644 --- a/irr/include/irrArray.h +++ b/irr/include/irrArray.h @@ -59,8 +59,12 @@ public: { size_t allocated = m_data.capacity(); if (new_size < allocated) { - if (canShrink) - m_data.resize(new_size); + if (canShrink) { + // since capacity != size don't accidentally make it bigger + if (m_data.size() > new_size) + m_data.resize(new_size); + m_data.shrink_to_fit(); + } } else { m_data.reserve(new_size); } From eb8beb335e30ba6e3a35a56ace665ec59cd840dd Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 13 Dec 2024 13:49:49 +0100 Subject: [PATCH 64/68] Fix bloom with post_processing_texture_bits < 16 --- src/client/render/pipeline.cpp | 21 +++++++++++++++++---- src/client/render/secondstage.cpp | 13 ++++++++----- src/defaultsettings.cpp | 2 ++ 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/client/render/pipeline.cpp b/src/client/render/pipeline.cpp index d24aa3ce8..33b3e78e2 100644 --- a/src/client/render/pipeline.cpp +++ b/src/client/render/pipeline.cpp @@ -6,6 +6,7 @@ #include "client/client.h" #include "client/hud.h" #include "IRenderTarget.h" +#include "SColor.h" #include #include @@ -122,10 +123,19 @@ bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefini if (!modify) return false; - if (*texture) + if (*texture) { m_driver->removeTexture(*texture); + *texture = nullptr; + } if (definition.valid) { + if (!m_driver->queryTextureFormat(definition.format)) { + errorstream << "Failed to create texture \"" << definition.name + << "\": unsupported format " << video::ColorFormatNames[definition.format] + << std::endl; + return false; + } + if (definition.clear) { // We're not able to clear a render target texture // We're not able to create a normal texture with MSAA @@ -142,9 +152,12 @@ bool TextureBuffer::ensureTexture(video::ITexture **texture, const TextureDefini } else { *texture = m_driver->addRenderTargetTexture(size, definition.name.c_str(), definition.format); } - } - else { - *texture = nullptr; + + if (!*texture) { + errorstream << "Failed to create texture \"" << definition.name + << "\"" << std::endl; + return false; + } } return true; diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp index f6a0cf78e..35ebe8110 100644 --- a/src/client/render/secondstage.cpp +++ b/src/client/render/secondstage.cpp @@ -165,6 +165,9 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // Number of mipmap levels of the bloom downsampling texture const u8 MIPMAP_LEVELS = 4; + // color_format can be a normalized integer format, but bloom requires + // values outside of [0,1] so this needs to be a different one. + const auto bloom_format = video::ECF_A16B16G16R16F; // post-processing stage @@ -173,16 +176,16 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // common downsampling step for bloom or autoexposure if (enable_bloom || enable_auto_exposure) { - v2f downscale = scale * 0.5; + v2f downscale = scale * 0.5f; for (u8 i = 0; i < MIPMAP_LEVELS; i++) { - buffer->setTexture(TEXTURE_SCALE_DOWN + i, downscale, std::string("downsample") + std::to_string(i), color_format); + buffer->setTexture(TEXTURE_SCALE_DOWN + i, downscale, std::string("downsample") + std::to_string(i), bloom_format); if (enable_bloom) - buffer->setTexture(TEXTURE_SCALE_UP + i, downscale, std::string("upsample") + std::to_string(i), color_format); - downscale *= 0.5; + buffer->setTexture(TEXTURE_SCALE_UP + i, downscale, std::string("upsample") + std::to_string(i), bloom_format); + downscale *= 0.5f; } if (enable_bloom) { - buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", color_format); + buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", bloom_format); // get bright spots u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH); diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 674442469..b1094be2d 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -571,7 +571,9 @@ void set_default_settings() // Note: OpenGL ES 2.0 is not guaranteed to provide depth textures, // which we would need for PP. settings->setDefault("enable_post_processing", "false"); + // still set these two settings in case someone wants to enable it settings->setDefault("debanding", "false"); + settings->setDefault("post_processing_texture_bits", "8"); settings->setDefault("curl_verify_cert", "false"); // Apply settings according to screen size From a6293b9861b7e9ab8bb6ce91663f363970ecc816 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 13 Dec 2024 16:11:21 +0100 Subject: [PATCH 65/68] Initial refactoring on shader usage and generation `IShaderSource` was designed with the idea that if you want a shader, you must want it for a node. So it depends heavily on being given a tile material and the node drawtype. But this doesn't make sense neither in theory nor in practice. This commit takes a small step towards removing the incorrect abstraction. --- .../default_shader/opengl_fragment.glsl | 6 --- .../shaders/default_shader/opengl_vertex.glsl | 7 ---- .../shaders/nodes_shader/opengl_fragment.glsl | 10 ++--- .../shaders/nodes_shader/opengl_vertex.glsl | 6 +-- src/client/clouds.cpp | 2 +- src/client/hud.cpp | 12 +++--- src/client/minimap.cpp | 2 +- src/client/render/interlaced.cpp | 4 +- src/client/render/secondstage.cpp | 14 +++---- src/client/shader.cpp | 42 ++++++++++++++++--- src/client/shader.h | 25 ++++++++--- src/client/sky.cpp | 2 +- src/client/tile.h | 1 + src/nodedef.h | 1 + 14 files changed, 81 insertions(+), 53 deletions(-) delete mode 100644 client/shaders/default_shader/opengl_fragment.glsl delete mode 100644 client/shaders/default_shader/opengl_vertex.glsl diff --git a/client/shaders/default_shader/opengl_fragment.glsl b/client/shaders/default_shader/opengl_fragment.glsl deleted file mode 100644 index 300c0c589..000000000 --- a/client/shaders/default_shader/opengl_fragment.glsl +++ /dev/null @@ -1,6 +0,0 @@ -varying lowp vec4 varColor; - -void main(void) -{ - gl_FragData[0] = varColor; -} diff --git a/client/shaders/default_shader/opengl_vertex.glsl b/client/shaders/default_shader/opengl_vertex.glsl deleted file mode 100644 index d95a3c2d3..000000000 --- a/client/shaders/default_shader/opengl_vertex.glsl +++ /dev/null @@ -1,7 +0,0 @@ -varying lowp vec4 varColor; - -void main(void) -{ - gl_Position = mWorldViewProj * inVertexPosition; - varColor = inVertexColor; -} diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index c560a8505..639b658a5 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -1,7 +1,3 @@ -#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT) -#define MATERIAL_WAVING_LIQUID 1 -#endif - uniform sampler2D baseTexture; uniform vec3 dayLight; @@ -53,7 +49,7 @@ varying highp vec3 eyeVec; varying float nightRatio; #ifdef ENABLE_DYNAMIC_SHADOWS -#if (defined(MATERIAL_WAVING_LIQUID) && defined(ENABLE_WATER_REFLECTIONS) && ENABLE_WAVING_WATER) +#if (defined(ENABLE_WATER_REFLECTIONS) && MATERIAL_WAVING_LIQUID && ENABLE_WAVING_WATER) vec4 perm(vec4 x) { return mod(((x * 34.0) + 1.0) * x, 289.0); @@ -504,7 +500,7 @@ void main(void) vec3 viewVec = normalize(worldPosition + cameraOffset - cameraPosition); // Water reflections -#if (defined(MATERIAL_WAVING_LIQUID) && defined(ENABLE_WATER_REFLECTIONS) && ENABLE_WAVING_WATER) +#if (defined(ENABLE_WATER_REFLECTIONS) && MATERIAL_WAVING_LIQUID && ENABLE_WAVING_WATER) vec3 wavePos = worldPosition * vec3(2.0, 0.0, 2.0); float off = animationTimer * WATER_WAVE_SPEED * 10.0; wavePos.x /= WATER_WAVE_LENGTH * 3.0; @@ -532,7 +528,7 @@ void main(void) col.rgb += water_reflect_color * f_adj_shadow_strength * brightness_factor; #endif -#if (defined(ENABLE_NODE_SPECULAR) && !defined(MATERIAL_WAVING_LIQUID)) +#if (defined(ENABLE_NODE_SPECULAR) && !MATERIAL_WAVING_LIQUID) // Apply specular to blocks. if (dot(v_LightDirection, vNormal) < 0.0) { float intensity = 2.0 * (1.0 - (base.r * varColor.r)); diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index d5d6dd59e..15a39565c 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -108,8 +108,7 @@ float smoothTriangleWave(float x) return smoothCurve(triangleWave(x)) * 2.0 - 1.0; } -// OpenGL < 4.3 does not support continued preprocessor lines -#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC) && ENABLE_WAVING_WATER +#if MATERIAL_WAVING_LIQUID && ENABLE_WAVING_WATER // // Simple, fast noise function. @@ -166,8 +165,7 @@ void main(void) #endif vec4 pos = inVertexPosition; -// OpenGL < 4.3 does not support continued preprocessor lines -#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC) && ENABLE_WAVING_WATER +#if MATERIAL_WAVING_LIQUID && ENABLE_WAVING_WATER // Generate waves with Perlin-type noise. // The constants are calibrated such that they roughly // correspond to the old sine waves. diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index 82a85d7b0..55e89410d 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -38,7 +38,7 @@ Clouds::Clouds(scene::ISceneManager* mgr, IShaderSource *ssrc, m_material.FogEnable = true; m_material.AntiAliasing = video::EAAM_SIMPLE; { - auto sid = ssrc->getShader("cloud_shader", TILE_MATERIAL_ALPHA); + auto sid = ssrc->getShaderRaw("cloud_shader", true); m_material.MaterialType = ssrc->getShaderInfo(sid).material; } diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 4fbd8740f..0aa847cb7 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -86,10 +86,11 @@ Hud::Hud(Client *client, LocalPlayer *player, // Initialize m_selection_material IShaderSource *shdrsrc = client->getShaderSource(); - { - auto shader_id = shdrsrc->getShader( - m_mode == HIGHLIGHT_HALO ? "selection_shader" : "default_shader", TILE_MATERIAL_ALPHA); + if (m_mode == HIGHLIGHT_HALO) { + auto shader_id = shdrsrc->getShaderRaw("selection_shader", true); m_selection_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material; + } else { + m_selection_material.MaterialType = video::EMT_SOLID; } if (m_mode == HIGHLIGHT_BOX) { @@ -103,10 +104,7 @@ Hud::Hud(Client *client, LocalPlayer *player, } // Initialize m_block_bounds_material - { - auto shader_id = shdrsrc->getShader("default_shader", TILE_MATERIAL_ALPHA); - m_block_bounds_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material; - } + m_block_bounds_material.MaterialType = video::EMT_SOLID; m_block_bounds_material.Thickness = rangelim(g_settings->getS16("selectionbox_width"), 1, 5); diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index b11d8b345..a37790f71 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -599,7 +599,7 @@ void Minimap::drawMinimap(core::rect rect) material.TextureLayers[1].Texture = data->heightmap_texture; if (data->mode.type == MINIMAP_TYPE_SURFACE) { - auto sid = m_shdrsrc->getShader("minimap_shader", TILE_MATERIAL_ALPHA); + auto sid = m_shdrsrc->getShaderRaw("minimap_shader", true); material.MaterialType = m_shdrsrc->getShaderInfo(sid).material; } else { material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; diff --git a/src/client/render/interlaced.cpp b/src/client/render/interlaced.cpp index f56d8a3d9..c4014a74d 100644 --- a/src/client/render/interlaced.cpp +++ b/src/client/render/interlaced.cpp @@ -66,10 +66,12 @@ void populateInterlacedPipeline(RenderPipeline *pipeline, Client *client) } pipeline->addStep(0.0f); + IShaderSource *s = client->getShaderSource(); - u32 shader = s->getShader("3d_interlaced_merge", TILE_MATERIAL_BASIC); + auto shader = s->getShaderRaw("3d_interlaced_merge"); video::E_MATERIAL_TYPE material = s->getShaderInfo(shader).material; auto texture_map = { TEXTURE_LEFT, TEXTURE_RIGHT, TEXTURE_MASK }; + auto merge = pipeline->addStep(material, texture_map); merge->setRenderSource(buffer); merge->setRenderTarget(pipeline->createOwned()); diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp index 35ebe8110..42858fa84 100644 --- a/src/client/render/secondstage.cpp +++ b/src/client/render/secondstage.cpp @@ -188,7 +188,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep buffer->setTexture(TEXTURE_BLOOM, scale, "bloom", bloom_format); // get bright spots - u32 shader_id = client->getShaderSource()->getShader("extract_bloom", TILE_MATERIAL_PLAIN, NDT_MESH); + u32 shader_id = client->getShaderSource()->getShaderRaw("extract_bloom"); RenderStep *extract_bloom = pipeline->addStep(shader_id, std::vector { source, TEXTURE_EXPOSURE_1 }); extract_bloom->setRenderSource(buffer); extract_bloom->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_BLOOM)); @@ -198,7 +198,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep if (enable_volumetric_light) { buffer->setTexture(TEXTURE_VOLUME, scale, "volume", color_format); - shader_id = client->getShaderSource()->getShader("volumetric_light", TILE_MATERIAL_PLAIN, NDT_MESH); + shader_id = client->getShaderSource()->getShaderRaw("volumetric_light"); auto volume = pipeline->addStep(shader_id, std::vector { source, TEXTURE_DEPTH }); volume->setRenderSource(buffer); volume->setRenderTarget(pipeline->createOwned(buffer, TEXTURE_VOLUME)); @@ -206,7 +206,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep } // downsample - shader_id = client->getShaderSource()->getShader("bloom_downsample", TILE_MATERIAL_PLAIN, NDT_MESH); + shader_id = client->getShaderSource()->getShaderRaw("bloom_downsample"); for (u8 i = 0; i < MIPMAP_LEVELS; i++) { auto step = pipeline->addStep(shader_id, std::vector { source }); step->setRenderSource(buffer); @@ -219,7 +219,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // Bloom pt 2 if (enable_bloom) { // upsample - shader_id = client->getShaderSource()->getShader("bloom_upsample", TILE_MATERIAL_PLAIN, NDT_MESH); + shader_id = client->getShaderSource()->getShaderRaw("bloom_upsample"); for (u8 i = MIPMAP_LEVELS - 1; i > 0; i--) { auto step = pipeline->addStep(shader_id, std::vector { u8(TEXTURE_SCALE_DOWN + i - 1), source }); step->setRenderSource(buffer); @@ -232,7 +232,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep // Dynamic Exposure pt2 if (enable_auto_exposure) { - shader_id = client->getShaderSource()->getShader("update_exposure", TILE_MATERIAL_PLAIN, NDT_MESH); + shader_id = client->getShaderSource()->getShaderRaw("update_exposure"); auto update_exposure = pipeline->addStep(shader_id, std::vector { TEXTURE_EXPOSURE_1, u8(TEXTURE_SCALE_DOWN + MIPMAP_LEVELS - 1) }); update_exposure->setBilinearFilter(1, true); update_exposure->setRenderSource(buffer); @@ -246,7 +246,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep final_stage_source = TEXTURE_FXAA; buffer->setTexture(TEXTURE_FXAA, scale, "fxaa", color_format); - shader_id = client->getShaderSource()->getShader("fxaa", TILE_MATERIAL_PLAIN); + shader_id = client->getShaderSource()->getShaderRaw("fxaa"); PostProcessingStep *effect = pipeline->createOwned(shader_id, std::vector { TEXTURE_COLOR }); pipeline->addStep(effect); effect->setBilinearFilter(0, true); @@ -255,7 +255,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep } // final merge - shader_id = client->getShaderSource()->getShader("second_stage", TILE_MATERIAL_PLAIN, NDT_MESH); + shader_id = client->getShaderSource()->getShaderRaw("second_stage"); PostProcessingStep *effect = pipeline->createOwned(shader_id, std::vector { final_stage_source, TEXTURE_SCALE_UP, TEXTURE_EXPOSURE_2 }); pipeline->addStep(effect); if (enable_ssaa) diff --git a/src/client/shader.cpp b/src/client/shader.cpp index 8d2d5744a..0b328014a 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -271,7 +271,7 @@ public: The id 0 points to a null shader. Its material is EMT_SOLID. */ u32 getShaderIdDirect(const std::string &name, - MaterialType material_type, NodeDrawType drawtype) override; + MaterialType material_type, NodeDrawType drawtype); /* If shader specified by the name pointed by the id doesn't @@ -281,10 +281,18 @@ public: and not found in cache, the call is queued to the main thread for processing. */ - u32 getShader(const std::string &name, MaterialType material_type, NodeDrawType drawtype) override; + u32 getShaderRaw(const std::string &name, bool blendAlpha) override + { + // TODO: the shader system should be refactored to be much more generic. + // Just let callers pass arbitrary constants, this would also deal with + // runtime changes cleanly. + return getShader(name, blendAlpha ? TILE_MATERIAL_ALPHA : TILE_MATERIAL_BASIC, + NodeDrawType_END); + } + ShaderInfo getShaderInfo(u32 id) override; // Processes queued shader requests from other threads. @@ -607,11 +615,17 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, #define textureFlags texture2 )"; + /* Define constants for node and object shaders */ + const bool node_shader = drawtype != NodeDrawType_END; + if (node_shader) { + bool use_discard = fully_programmable; - // For renderers that should use discard instead of GL_ALPHA_TEST - const char *renderer = reinterpret_cast(GL.GetString(GL.RENDERER)); - if (strstr(renderer, "GC7000")) - use_discard = true; + if (!use_discard) { + // workaround for a certain OpenGL implementation lacking GL_ALPHA_TEST + const char *renderer = reinterpret_cast(GL.GetString(GL.RENDERER)); + if (strstr(renderer, "GC7000")) + use_discard = true; + } if (use_discard) { if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL) shaders_header << "#define USE_DISCARD 1\n"; @@ -664,9 +678,25 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, shaders_header << "#define WATER_WAVE_LENGTH " << g_settings->getFloat("water_wave_length") << "\n"; shaders_header << "#define WATER_WAVE_SPEED " << g_settings->getFloat("water_wave_speed") << "\n"; } + switch (material_type) { + case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: + case TILE_MATERIAL_WAVING_LIQUID_OPAQUE: + case TILE_MATERIAL_WAVING_LIQUID_BASIC: + case TILE_MATERIAL_LIQUID_TRANSPARENT: + shaders_header << "#define MATERIAL_WAVING_LIQUID 1\n"; + break; + default: + shaders_header << "#define MATERIAL_WAVING_LIQUID 0\n"; + break; + } shaders_header << "#define ENABLE_WAVING_LEAVES " << g_settings->getBool("enable_waving_leaves") << "\n"; shaders_header << "#define ENABLE_WAVING_PLANTS " << g_settings->getBool("enable_waving_plants") << "\n"; + + } + + /* Other constants */ + shaders_header << "#define ENABLE_TONE_MAPPING " << g_settings->getBool("tone_mapping") << "\n"; if (g_settings->getBool("enable_dynamic_shadows")) { diff --git a/src/client/shader.h b/src/client/shader.h index c1d02bf2c..c78b0078a 100644 --- a/src/client/shader.h +++ b/src/client/shader.h @@ -196,18 +196,33 @@ using CachedStructPixelShaderSetting = CachedStructShaderSettinggetShaderInfo(ssrc->getShader("stars_shader", TILE_MATERIAL_ALPHA)).material; + ssrc->getShaderInfo(ssrc->getShaderRaw("stars_shader", true)).material; m_materials[1] = baseMaterial(); m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; diff --git a/src/client/tile.h b/src/client/tile.h index df02d2244..a2c5bbdfb 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -20,6 +20,7 @@ enum MaterialType{ TILE_MATERIAL_WAVING_LIQUID_BASIC, TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT, TILE_MATERIAL_WAVING_LIQUID_OPAQUE, + // Note: PLAIN isn't a material actually used by tiles, rather just entities. TILE_MATERIAL_PLAIN, TILE_MATERIAL_PLAIN_ALPHA }; diff --git a/src/nodedef.h b/src/nodedef.h index c3f88ce83..ab3362872 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -222,6 +222,7 @@ enum NodeDrawType : u8 NDT_MESH, // Combined plantlike-on-solid NDT_PLANTLIKE_ROOTED, + // Dummy for validity check NodeDrawType_END }; From 7bf0b1fc7e7aea06988586544bd36b15948c63de Mon Sep 17 00:00:00 2001 From: siliconsniffer <97843108+siliconsniffer@users.noreply.github.com> Date: Fri, 20 Dec 2024 15:04:26 +0100 Subject: [PATCH 66/68] Add server url button to main menu (#15536) Co-authored-by: Zughy <63455151+Zughy@users.noreply.github.com> --- LICENSE.txt | 1 + builtin/mainmenu/tab_online.lua | 12 ++++++++++++ textures/base/pack/server_url.png | Bin 0 -> 356 bytes 3 files changed, 13 insertions(+) create mode 100644 textures/base/pack/server_url.png diff --git a/LICENSE.txt b/LICENSE.txt index fb055807b..f7930f528 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -63,6 +63,7 @@ Zughy: textures/base/pack/settings_btn.png textures/base/pack/settings_info.png textures/base/pack/settings_reset.png + textures/base/pack/server_url.png textures/base/pack/server_view_clients.png appgurueu: diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index b8257b90a..12192715f 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -179,6 +179,13 @@ local function get_formspec(tabview, name, tabdata) "server_view_clients.png") .. ";btn_view_clients;]" end + if selected_server.url then + retval = retval .. "tooltip[btn_server_url;" .. fgettext("Open server website") .. "]" + retval = retval .. "style[btn_server_url;padding=6]" + retval = retval .. "image_button[" .. (can_view_clients_list and "4" or "4.5") .. ",1.3;0.5,0.5;" .. + core.formspec_escape(defaulttexturedir .. "server_url.png") .. ";btn_server_url;]" + end + if is_selected_fav() then retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]" retval = retval .. "style[btn_delete_favorite;padding=6]" @@ -367,6 +374,11 @@ local function main_button_handler(tabview, fields, name, tabdata) return true end + if fields.btn_server_url then + core.open_url_dialog(find_selected_server().url) + return true + end + if fields.btn_view_clients then local dlg = create_clientslist_dialog(find_selected_server()) dlg:set_parent(tabview) diff --git a/textures/base/pack/server_url.png b/textures/base/pack/server_url.png new file mode 100644 index 0000000000000000000000000000000000000000..3f04900833fe4df446a6d7eedf2d00b22afbc404 GIT binary patch literal 356 zcmeAS@N?(olHy`uVBq!ia0vp^3qY8K8Aw(>tdav#asfUet_C`Ksqr!4K>?wDem<^l zKrT?`{ z*7;}I>^5_8L-IsTe*qlcq603 zfkO}0)l{th_vTbvqyB6rmNlGaroqaa1#X_n;u6@PR Date: Fri, 20 Dec 2024 19:04:56 +0100 Subject: [PATCH 67/68] Fix incorrect handling of skinned meshes as mesh nodes fixes #15576 --- src/nodedef.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 81348cf23..2f0307d11 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -13,6 +13,7 @@ #include "client/texturesource.h" #include "client/tile.h" #include +#include #endif #include "log.h" #include "settings.h" @@ -953,6 +954,12 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc infostream << "ContentFeatures: recalculating normals for mesh " << mesh << std::endl; meshmanip->recalculateNormals(mesh_ptr, true, false); + } else { + // Animation is not supported, but we need to reset it to + // default state if it is animated. + // Note: recalculateNormals() also does this hence the else-block + if (mesh_ptr->getMeshType() == scene::EAMT_SKINNED) + ((scene::SkinnedMesh*) mesh_ptr)->resetAnimation(); } } } From 83bc27d99dacbb249b95387a9511d9b38830bf55 Mon Sep 17 00:00:00 2001 From: cx384 Date: Fri, 20 Dec 2024 11:02:20 +0100 Subject: [PATCH 68/68] Move formspec code from game.cpp to separate file --- src/client/CMakeLists.txt | 1 + src/client/game.cpp | 512 ++-------------------------------- src/client/game_formspec.cpp | 524 +++++++++++++++++++++++++++++++++++ src/client/game_formspec.h | 62 +++++ src/client/gameui.cpp | 12 - src/client/gameui.h | 15 - 6 files changed, 605 insertions(+), 521 deletions(-) create mode 100644 src/client/game_formspec.cpp create mode 100644 src/client/game_formspec.h diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 0bcb667bc..c17148a35 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -51,6 +51,7 @@ set(client_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/fontengine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/game.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gameui.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/game_formspec.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiscalingfilter.cpp ${CMAKE_CURRENT_SOURCE_DIR}/hud.cpp ${CMAKE_CURRENT_SOURCE_DIR}/imagefilters.cpp diff --git a/src/client/game.cpp b/src/client/game.cpp index 8bd6f5f29..97b50183c 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -11,6 +11,7 @@ #include "client.h" #include "client/clientevent.h" #include "client/gameui.h" +#include "client/game_formspec.h" #include "client/inputhandler.h" #include "client/texturepaths.h" #include "client/keys.h" @@ -26,18 +27,13 @@ #include "client/event_manager.h" #include "fontengine.h" #include "gui/touchcontrols.h" -#include "gui/touchscreeneditor.h" #include "itemdef.h" #include "log.h" #include "log_internal.h" #include "gameparams.h" #include "gettext.h" #include "gui/guiChatConsole.h" -#include "gui/guiFormSpecMenu.h" -#include "gui/guiKeyChangeMenu.h" -#include "gui/guiPasswordChange.h" -#include "gui/guiOpenURL.h" -#include "gui/guiVolumeChange.h" +#include "texturesource.h" #include "gui/mainmenumanager.h" #include "gui/profilergraph.h" #include "minimap.h" @@ -69,176 +65,6 @@ #include "client/sound/sound_openal.h" #endif -/* - Text input system -*/ - -struct TextDestNodeMetadata : public TextDest -{ - TextDestNodeMetadata(v3s16 p, Client *client) - { - m_p = p; - m_client = client; - } - // This is deprecated I guess? -celeron55 - void gotText(const std::wstring &text) - { - std::string ntext = wide_to_utf8(text); - infostream << "Submitting 'text' field of node at (" << m_p.X << "," - << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl; - StringMap fields; - fields["text"] = ntext; - m_client->sendNodemetaFields(m_p, "", fields); - } - void gotText(const StringMap &fields) - { - m_client->sendNodemetaFields(m_p, "", fields); - } - - v3s16 m_p; - Client *m_client; -}; - -struct TextDestPlayerInventory : public TextDest -{ - TextDestPlayerInventory(Client *client) - { - m_client = client; - m_formname.clear(); - } - TextDestPlayerInventory(Client *client, const std::string &formname) - { - m_client = client; - m_formname = formname; - } - void gotText(const StringMap &fields) - { - m_client->sendInventoryFields(m_formname, fields); - } - - Client *m_client; -}; - -struct LocalFormspecHandler : public TextDest -{ - LocalFormspecHandler(const std::string &formname) - { - m_formname = formname; - } - - LocalFormspecHandler(const std::string &formname, Client *client): - m_client(client) - { - m_formname = formname; - } - - void gotText(const StringMap &fields) - { - if (m_formname == "MT_PAUSE_MENU") { - if (fields.find("btn_sound") != fields.end()) { - g_gamecallback->changeVolume(); - return; - } - - if (fields.find("btn_key_config") != fields.end()) { - g_gamecallback->keyConfig(); - return; - } - - if (fields.find("btn_touchscreen_layout") != fields.end()) { - g_gamecallback->touchscreenLayout(); - return; - } - - if (fields.find("btn_exit_menu") != fields.end()) { - g_gamecallback->disconnect(); - return; - } - - if (fields.find("btn_exit_os") != fields.end()) { - g_gamecallback->exitToOS(); -#ifndef __ANDROID__ - RenderingEngine::get_raw_device()->closeDevice(); -#endif - return; - } - - if (fields.find("btn_change_password") != fields.end()) { - g_gamecallback->changePassword(); - return; - } - - return; - } - - if (m_formname == "MT_DEATH_SCREEN") { - assert(m_client != nullptr); - - if (fields.find("quit") != fields.end()) - m_client->sendRespawnLegacy(); - - return; - } - - if (m_client->modsLoaded()) - m_client->getScript()->on_formspec_input(m_formname, fields); - } - - Client *m_client = nullptr; -}; - -/* Form update callback */ - -class NodeMetadataFormSource: public IFormSource -{ -public: - NodeMetadataFormSource(ClientMap *map, v3s16 p): - m_map(map), - m_p(p) - { - } - const std::string &getForm() const - { - static const std::string empty_string = ""; - NodeMetadata *meta = m_map->getNodeMetadata(m_p); - - if (!meta) - return empty_string; - - return meta->getString("formspec"); - } - - virtual std::string resolveText(const std::string &str) - { - NodeMetadata *meta = m_map->getNodeMetadata(m_p); - - if (!meta) - return str; - - return meta->resolveString(str); - } - - ClientMap *m_map; - v3s16 m_p; -}; - -class PlayerInventoryFormSource: public IFormSource -{ -public: - PlayerInventoryFormSource(Client *client): - m_client(client) - { - } - - const std::string &getForm() const - { - LocalPlayer *player = m_client->getEnv().getLocalPlayer(); - return player->inventory_formspec; - } - - Client *m_client; -}; - class NodeDugEvent : public MtEvent { public: @@ -596,8 +422,6 @@ public: } }; -#define SIZE_TAG "size[11,5.5,true]" // Fixed size (ignored in touchscreen mode) - /**************************************************************************** ****************************************************************************/ @@ -699,7 +523,6 @@ protected: void updateInteractTimers(f32 dtime); bool checkConnection(); - bool handleCallbacks(); void processQueues(); void updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime); void updateDebugState(); @@ -713,7 +536,6 @@ protected: bool shouldShowTouchControls(); void dropSelectedItem(bool single_item = false); - void openInventory(); void openConsole(float scale, const wchar_t *line=NULL); void toggleFreeMove(); void toggleFreeMoveAlt(); @@ -815,9 +637,6 @@ private: bool disable_camera_update = false; }; - void showDeathFormspecLegacy(); - void showPauseMenu(); - void pauseAnimation(); void resumeAnimation(); @@ -882,6 +701,7 @@ private: irr_ptr sky; Hud *hud = nullptr; Minimap *mapper = nullptr; + GameFormSpec m_game_formspec; // Map server hud ids to client hud ids std::unordered_map m_hud_server_to_client; @@ -1089,6 +909,8 @@ bool Game::startup(bool *kill, m_rendering_engine->initialize(client, hud); + m_game_formspec.init(client, m_rendering_engine, input); + return true; } @@ -1159,7 +981,7 @@ void Game::run() if (!checkConnection()) break; - if (!handleCallbacks()) + if (!m_game_formspec.handleCallbacks()) break; processQueues(); @@ -1191,7 +1013,7 @@ void Game::run() updateProfilerGraphs(&graph); if (m_does_lost_focus_pause_game && !device->isWindowFocused() && !isMenuActive()) { - showPauseMenu(); + m_game_formspec.showPauseMenu(); } } @@ -1203,10 +1025,6 @@ void Game::run() void Game::shutdown() { - auto formspec = m_game_ui->getFormspecGUI(); - if (formspec) - formspec->quitMenu(); - // Clear text when exiting. m_game_ui->clearText(); @@ -1228,8 +1046,6 @@ void Game::shutdown() g_menumgr.deleteFront(); } - m_game_ui->deleteFormspec(); - chat_backend->addMessage(L"", L"# Disconnected."); chat_backend->addMessage(L"", L""); @@ -1822,55 +1638,6 @@ inline bool Game::checkConnection() return true; } - -/* returns false if game should exit, otherwise true - */ -inline bool Game::handleCallbacks() -{ - if (g_gamecallback->disconnect_requested) { - g_gamecallback->disconnect_requested = false; - return false; - } - - if (g_gamecallback->changepassword_requested) { - (void)make_irr(guienv, guiroot, -1, - &g_menumgr, client, texture_src); - g_gamecallback->changepassword_requested = false; - } - - if (g_gamecallback->changevolume_requested) { - (void)make_irr(guienv, guiroot, -1, - &g_menumgr, texture_src); - g_gamecallback->changevolume_requested = false; - } - - if (g_gamecallback->keyconfig_requested) { - (void)make_irr(guienv, guiroot, -1, - &g_menumgr, texture_src); - g_gamecallback->keyconfig_requested = false; - } - - if (g_gamecallback->touchscreenlayout_requested) { - (new GUITouchscreenLayout(guienv, guiroot, -1, - &g_menumgr, texture_src))->drop(); - g_gamecallback->touchscreenlayout_requested = false; - } - - if (!g_gamecallback->show_open_url_dialog.empty()) { - (void)make_irr(guienv, guiroot, -1, - &g_menumgr, texture_src, g_gamecallback->show_open_url_dialog); - g_gamecallback->show_open_url_dialog.clear(); - } - - if (g_gamecallback->keyconfig_changed) { - input->keycache.populate(); // update the cache with new settings - g_gamecallback->keyconfig_changed = false; - } - - return true; -} - - void Game::processQueues() { texture_src->processQueue(); @@ -1897,10 +1664,7 @@ void Game::updateDebugState() if (!has_debug) { draw_control->show_wireframe = false; m_flags.disable_camera_update = false; - auto formspec = m_game_ui->getFormspecGUI(); - if (formspec) { - formspec->setDebugView(false); - } + m_game_formspec.disableDebugView(); } // noclip @@ -2044,10 +1808,7 @@ void Game::processUserInput(f32 dtime) input->step(dtime); #ifdef __ANDROID__ - auto formspec = m_game_ui->getFormspecGUI(); - if (formspec) - formspec->getAndroidUIInput(); - else + if (!m_game_formspec.handleAndroidUIInput()) handleAndroidChatInput(); #endif @@ -2072,13 +1833,13 @@ void Game::processKeyInput() if (g_settings->getBool("continuous_forward")) toggleAutoforward(); } else if (wasKeyDown(KeyType::INVENTORY)) { - openInventory(); + m_game_formspec.showPlayerInventory(); } else if (input->cancelPressed()) { #ifdef __ANDROID__ m_android_chat_open = false; #endif if (!gui_chat_console->isOpenInhibited()) { - showPauseMenu(); + m_game_formspec.showPauseMenu(); } } else if (wasKeyDown(KeyType::CHAT)) { openConsole(0.2, L""); @@ -2246,45 +2007,6 @@ void Game::dropSelectedItem(bool single_item) client->inventoryAction(a); } - -void Game::openInventory() -{ - /* - * Don't permit to open inventory is CAO or player doesn't exists. - * This prevent showing an empty inventory at player load - */ - - LocalPlayer *player = client->getEnv().getLocalPlayer(); - if (!player || !player->getCAO()) - return; - - infostream << "Game: Launching inventory" << std::endl; - - PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client); - - InventoryLocation inventoryloc; - inventoryloc.setCurrentPlayer(); - - if (client->modsLoaded() && client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) { - delete fs_src; - return; - } - - if (fs_src->getForm().empty()) { - delete fs_src; - return; - } - - TextDest *txt_dst = new TextDestPlayerInventory(client); - auto *&formspec = m_game_ui->updateFormspec(""); - GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), - sound_manager.get()); - - formspec->setFormSpec(fs_src->getForm(), inventoryloc); -} - - void Game::openConsole(float scale, const wchar_t *line) { assert(scale > 0.0f && scale <= 1.0f); @@ -2886,28 +2608,13 @@ void Game::handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientati void Game::handleClientEvent_DeathscreenLegacy(ClientEvent *event, CameraOrientation *cam) { - showDeathFormspecLegacy(); + m_game_formspec.showDeathFormspecLegacy(); } void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam) { - if (event->show_formspec.formspec->empty()) { - auto formspec = m_game_ui->getFormspecGUI(); - if (formspec && (event->show_formspec.formname->empty() - || *(event->show_formspec.formname) == m_game_ui->getFormspecName())) { - formspec->quitMenu(); - } - } else { - FormspecFormSource *fs_src = - new FormspecFormSource(*(event->show_formspec.formspec)); - TextDestPlayerInventory *txt_dst = - new TextDestPlayerInventory(client, *(event->show_formspec.formname)); - - auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname)); - GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), - sound_manager.get()); - } + m_game_formspec.showFormSpec(*event->show_formspec.formspec, + *event->show_formspec.formname); delete event->show_formspec.formspec; delete event->show_formspec.formname; @@ -2915,11 +2622,8 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam) { - FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec); - LocalFormspecHandler *txt_dst = - new LocalFormspecHandler(*event->show_formspec.formname, client); - GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), sound_manager.get()); + m_game_formspec.showLocalFormSpec(*event->show_formspec.formspec, + *event->show_formspec.formname); delete event->show_formspec.formspec; delete event->show_formspec.formname; @@ -3657,21 +3361,7 @@ bool Game::nodePlacement(const ItemDefinition &selected_def, if (nodedef_manager->get(map.getNode(nodepos)).rightclickable) client->interact(INTERACT_PLACE, pointed); - infostream << "Launching custom inventory view" << std::endl; - - InventoryLocation inventoryloc; - inventoryloc.setNodeMeta(nodepos); - - NodeMetadataFormSource *fs_src = new NodeMetadataFormSource( - &client->getEnv().getClientMap(), nodepos); - TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); - - auto *&formspec = m_game_ui->updateFormspec(""); - GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), - sound_manager.get()); - - formspec->setFormSpec(meta->getString("formspec"), inventoryloc); + m_game_formspec.showNodeFormspec(meta->getString("formspec"), nodepos); return false; } @@ -4207,34 +3897,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console.get(), dtime); - /* - make sure menu is on top - 1. Delete formspec menu reference if menu was removed - 2. Else, make sure formspec menu is on top - */ - auto formspec = m_game_ui->getFormspecGUI(); - do { // breakable. only runs for one iteration - if (!formspec) - break; - - if (formspec->getReferenceCount() == 1) { - // See GUIFormSpecMenu::create what refcnt = 1 means - m_game_ui->deleteFormspec(); - break; - } - - auto &loc = formspec->getFormspecLocation(); - if (loc.type == InventoryLocation::NODEMETA) { - NodeMetadata *meta = client->getEnv().getClientMap().getNodeMetadata(loc.p); - if (!meta || meta->getString("formspec").empty()) { - formspec->quitMenu(); - break; - } - } - - if (isMenuActive()) - guiroot->bringToFront(formspec); - } while (false); + m_game_formspec.update(); /* ==================== Drawing begins ==================== @@ -4451,145 +4114,6 @@ void Game::readSettings() m_does_lost_focus_pause_game = g_settings->getBool("pause_on_lost_focus"); } -/****************************************************************************/ -/**************************************************************************** - Shutdown / cleanup - ****************************************************************************/ -/****************************************************************************/ - -void Game::showDeathFormspecLegacy() -{ - static std::string formspec_str = - std::string("formspec_version[1]") + - SIZE_TAG - "bgcolor[#320000b4;true]" - "label[4.85,1.35;" + gettext("You died") + "]" - "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]" - ; - - /* Create menu */ - /* Note: FormspecFormSource and LocalFormspecHandler * - * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(formspec_str); - LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - - auto *&formspec = m_game_ui->getFormspecGUI(); - GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), - sound_manager.get()); - formspec->setFocus("btn_respawn"); -} - -#define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name()) -void Game::showPauseMenu() -{ - std::string control_text; - - if (g_touchcontrols) { - control_text = strgettext("Controls:\n" - "No menu open:\n" - "- slide finger: look around\n" - "- tap: place/punch/use (default)\n" - "- long tap: dig/use (default)\n" - "Menu/inventory open:\n" - "- double tap (outside):\n" - " --> close\n" - "- touch stack, touch slot:\n" - " --> move stack\n" - "- touch&drag, tap 2nd finger\n" - " --> place single item to slot\n" - ); - } - - float ypos = simple_singleplayer_mode ? 0.7f : 0.1f; - std::ostringstream os; - - os << "formspec_version[1]" << SIZE_TAG - << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;" - << strgettext("Continue") << "]"; - - if (!simple_singleplayer_mode) { - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;" - << strgettext("Change Password") << "]"; - } else { - os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]"; - } - -#ifndef __ANDROID__ -#if USE_SOUND - if (g_settings->getBool("enable_sound")) { - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" - << strgettext("Sound Volume") << "]"; - } -#endif -#endif - if (g_touchcontrols) { - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_touchscreen_layout;" - << strgettext("Touchscreen Layout") << "]"; - } else { - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" - << strgettext("Controls") << "]"; - } - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;" - << strgettext("Exit to Menu") << "]"; - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;" - << strgettext("Exit to OS") << "]"; - if (!control_text.empty()) { - os << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"; - } - os << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n" - << "\n" - << strgettext("Game info:") << "\n"; - const std::string &address = client->getAddressName(); - os << strgettext("- Mode: "); - if (!simple_singleplayer_mode) { - if (address.empty()) - os << strgettext("Hosting server"); - else - os << strgettext("Remote server"); - } else { - os << strgettext("Singleplayer"); - } - os << "\n"; - if (simple_singleplayer_mode || address.empty()) { - static const std::string on = strgettext("On"); - static const std::string off = strgettext("Off"); - // Note: Status of enable_damage and creative_mode settings is intentionally - // NOT shown here because the game might roll its own damage system and/or do - // a per-player Creative Mode, in which case writing it here would mislead. - bool damage = g_settings->getBool("enable_damage"); - const std::string &announced = g_settings->getBool("server_announce") ? on : off; - if (!simple_singleplayer_mode) { - if (damage) { - const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off; - //~ PvP = Player versus Player - os << strgettext("- PvP: ") << pvp << "\n"; - } - os << strgettext("- Public: ") << announced << "\n"; - std::string server_name = g_settings->get("server_name"); - str_formspec_escape(server_name); - if (announced == on && !server_name.empty()) - os << strgettext("- Server Name: ") << server_name; - - } - } - os << ";]"; - - /* Create menu */ - /* Note: FormspecFormSource and LocalFormspecHandler * - * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(os.str()); - LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - - auto *&formspec = m_game_ui->getFormspecGUI(); - GUIFormSpecMenu::create(formspec, client, m_rendering_engine->get_gui_env(), - &input->joystick, fs_src, txt_dst, client->getFormspecPrepend(), - sound_manager.get()); - formspec->setFocus("btn_continue"); - // game will be paused in next step, if in singleplayer (see m_is_paused) - formspec->doPause = true; -} - /****************************************************************************/ /**************************************************************************** extern function for launching the game diff --git a/src/client/game_formspec.cpp b/src/client/game_formspec.cpp new file mode 100644 index 000000000..4c3bbb04a --- /dev/null +++ b/src/client/game_formspec.cpp @@ -0,0 +1,524 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2010-2013 celeron55, Perttu Ahola + +#include "game_formspec.h" + +#include "gettext.h" +#include "nodemetadata.h" +#include "renderingengine.h" +#include "client.h" +#include "scripting_client.h" +#include "clientmap.h" +#include "gui/guiFormSpecMenu.h" +#include "gui/mainmenumanager.h" +#include "gui/touchcontrols.h" +#include "gui/touchscreeneditor.h" +#include "gui/guiPasswordChange.h" +#include "gui/guiKeyChangeMenu.h" +#include "gui/guiPasswordChange.h" +#include "gui/guiOpenURL.h" +#include "gui/guiVolumeChange.h" + +/* + Text input system +*/ + +struct TextDestNodeMetadata : public TextDest +{ + TextDestNodeMetadata(v3s16 p, Client *client) + { + m_p = p; + m_client = client; + } + // This is deprecated I guess? -celeron55 + void gotText(const std::wstring &text) + { + std::string ntext = wide_to_utf8(text); + infostream << "Submitting 'text' field of node at (" << m_p.X << "," + << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl; + StringMap fields; + fields["text"] = ntext; + m_client->sendNodemetaFields(m_p, "", fields); + } + void gotText(const StringMap &fields) + { + m_client->sendNodemetaFields(m_p, "", fields); + } + + v3s16 m_p; + Client *m_client; +}; + +struct TextDestPlayerInventory : public TextDest +{ + TextDestPlayerInventory(Client *client) + { + m_client = client; + m_formname.clear(); + } + TextDestPlayerInventory(Client *client, const std::string &formname) + { + m_client = client; + m_formname = formname; + } + void gotText(const StringMap &fields) + { + m_client->sendInventoryFields(m_formname, fields); + } + + Client *m_client; +}; + +struct LocalFormspecHandler : public TextDest +{ + LocalFormspecHandler(const std::string &formname) + { + m_formname = formname; + } + + LocalFormspecHandler(const std::string &formname, Client *client): + m_client(client) + { + m_formname = formname; + } + + void gotText(const StringMap &fields) + { + if (m_formname == "MT_PAUSE_MENU") { + if (fields.find("btn_sound") != fields.end()) { + g_gamecallback->changeVolume(); + return; + } + + if (fields.find("btn_key_config") != fields.end()) { + g_gamecallback->keyConfig(); + return; + } + + if (fields.find("btn_touchscreen_layout") != fields.end()) { + g_gamecallback->touchscreenLayout(); + return; + } + + if (fields.find("btn_exit_menu") != fields.end()) { + g_gamecallback->disconnect(); + return; + } + + if (fields.find("btn_exit_os") != fields.end()) { + g_gamecallback->exitToOS(); +#ifndef __ANDROID__ + RenderingEngine::get_raw_device()->closeDevice(); +#endif + return; + } + + if (fields.find("btn_change_password") != fields.end()) { + g_gamecallback->changePassword(); + return; + } + + return; + } + + if (m_formname == "MT_DEATH_SCREEN") { + assert(m_client != nullptr); + + if (fields.find("quit") != fields.end()) + m_client->sendRespawnLegacy(); + + return; + } + + if (m_client->modsLoaded()) + m_client->getScript()->on_formspec_input(m_formname, fields); + } + + Client *m_client = nullptr; +}; + +/* Form update callback */ + +class NodeMetadataFormSource: public IFormSource +{ +public: + NodeMetadataFormSource(ClientMap *map, v3s16 p): + m_map(map), + m_p(p) + { + } + const std::string &getForm() const + { + static const std::string empty_string = ""; + NodeMetadata *meta = m_map->getNodeMetadata(m_p); + + if (!meta) + return empty_string; + + return meta->getString("formspec"); + } + + virtual std::string resolveText(const std::string &str) + { + NodeMetadata *meta = m_map->getNodeMetadata(m_p); + + if (!meta) + return str; + + return meta->resolveString(str); + } + + ClientMap *m_map; + v3s16 m_p; +}; + +class PlayerInventoryFormSource: public IFormSource +{ +public: + PlayerInventoryFormSource(Client *client): + m_client(client) + { + } + + const std::string &getForm() const + { + LocalPlayer *player = m_client->getEnv().getLocalPlayer(); + return player->inventory_formspec; + } + + Client *m_client; +}; + + +//// GameFormSpec + +void GameFormSpec::deleteFormspec() +{ + if (m_formspec) { + m_formspec->drop(); + m_formspec = nullptr; + } + m_formname.clear(); +} + +GameFormSpec::~GameFormSpec() { + if (m_formspec) + m_formspec->quitMenu(); + this->deleteFormspec(); +} + +void GameFormSpec::showFormSpec(const std::string &formspec, const std::string &formname) +{ + if (formspec.empty()) { + if (m_formspec && (formname.empty() || formname == m_formname)) { + m_formspec->quitMenu(); + } + } else { + FormspecFormSource *fs_src = + new FormspecFormSource(formspec); + TextDestPlayerInventory *txt_dst = + new TextDestPlayerInventory(m_client, formname); + + m_formname = formname; + GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(), + &m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(), + m_client->getSoundManager()); + } +} + +void GameFormSpec::showLocalFormSpec(const std::string &formspec, const std::string &formname) +{ + FormspecFormSource *fs_src = new FormspecFormSource(formspec); + LocalFormspecHandler *txt_dst = + new LocalFormspecHandler(formname, m_client); + GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(), + &m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(), + m_client->getSoundManager()); +} + +void GameFormSpec::showNodeFormspec(const std::string &formspec, const v3s16 &nodepos) +{ + infostream << "Launching custom inventory view" << std::endl; + + InventoryLocation inventoryloc; + inventoryloc.setNodeMeta(nodepos); + + NodeMetadataFormSource *fs_src = new NodeMetadataFormSource( + &m_client->getEnv().getClientMap(), nodepos); + TextDest *txt_dst = new TextDestNodeMetadata(nodepos, m_client); + + m_formname = ""; + GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(), + &m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(), + m_client->getSoundManager()); + + m_formspec->setFormSpec(formspec, inventoryloc); +} + +void GameFormSpec::showPlayerInventory() +{ + /* + * Don't permit to open inventory is CAO or player doesn't exists. + * This prevent showing an empty inventory at player load + */ + + LocalPlayer *player = m_client->getEnv().getLocalPlayer(); + if (!player || !player->getCAO()) + return; + + infostream << "Game: Launching inventory" << std::endl; + + PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(m_client); + + InventoryLocation inventoryloc; + inventoryloc.setCurrentPlayer(); + + if (m_client->modsLoaded() && m_client->getScript()->on_inventory_open(m_client->getInventory(inventoryloc))) { + delete fs_src; + return; + } + + if (fs_src->getForm().empty()) { + delete fs_src; + return; + } + + TextDest *txt_dst = new TextDestPlayerInventory(m_client); + m_formname = ""; + GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(), + &m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(), + m_client->getSoundManager()); + + m_formspec->setFormSpec(fs_src->getForm(), inventoryloc); +} + +#define SIZE_TAG "size[11,5.5,true]" // Fixed size (ignored in touchscreen mode) + +void GameFormSpec::showPauseMenu() +{ + std::string control_text; + + if (g_touchcontrols) { + control_text = strgettext("Controls:\n" + "No menu open:\n" + "- slide finger: look around\n" + "- tap: place/punch/use (default)\n" + "- long tap: dig/use (default)\n" + "Menu/inventory open:\n" + "- double tap (outside):\n" + " --> close\n" + "- touch stack, touch slot:\n" + " --> move stack\n" + "- touch&drag, tap 2nd finger\n" + " --> place single item to slot\n" + ); + } + + auto simple_singleplayer_mode = m_client->m_simple_singleplayer_mode; + + float ypos = simple_singleplayer_mode ? 0.7f : 0.1f; + std::ostringstream os; + + os << "formspec_version[1]" << SIZE_TAG + << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;" + << strgettext("Continue") << "]"; + + if (!simple_singleplayer_mode) { + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;" + << strgettext("Change Password") << "]"; + } else { + os << "field[4.95,0;5,1.5;;" << strgettext("Game paused") << ";]"; + } + +#ifndef __ANDROID__ +#if USE_SOUND + if (g_settings->getBool("enable_sound")) { + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" + << strgettext("Sound Volume") << "]"; + } +#endif +#endif + if (g_touchcontrols) { + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_touchscreen_layout;" + << strgettext("Touchscreen Layout") << "]"; + } else { + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" + << strgettext("Controls") << "]"; + } + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;" + << strgettext("Exit to Menu") << "]"; + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;" + << strgettext("Exit to OS") << "]"; + if (!control_text.empty()) { + os << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"; + } + os << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n" + << "\n" + << strgettext("Game info:") << "\n"; + const std::string &address = m_client->getAddressName(); + os << strgettext("- Mode: "); + if (!simple_singleplayer_mode) { + if (address.empty()) + os << strgettext("Hosting server"); + else + os << strgettext("Remote server"); + } else { + os << strgettext("Singleplayer"); + } + os << "\n"; + if (simple_singleplayer_mode || address.empty()) { + static const std::string on = strgettext("On"); + static const std::string off = strgettext("Off"); + // Note: Status of enable_damage and creative_mode settings is intentionally + // NOT shown here because the game might roll its own damage system and/or do + // a per-player Creative Mode, in which case writing it here would mislead. + bool damage = g_settings->getBool("enable_damage"); + const std::string &announced = g_settings->getBool("server_announce") ? on : off; + if (!simple_singleplayer_mode) { + if (damage) { + const std::string &pvp = g_settings->getBool("enable_pvp") ? on : off; + //~ PvP = Player versus Player + os << strgettext("- PvP: ") << pvp << "\n"; + } + os << strgettext("- Public: ") << announced << "\n"; + std::string server_name = g_settings->get("server_name"); + str_formspec_escape(server_name); + if (announced == on && !server_name.empty()) + os << strgettext("- Server Name: ") << server_name; + + } + } + os << ";]"; + + /* Create menu */ + /* Note: FormspecFormSource and LocalFormspecHandler * + * are deleted by guiFormSpecMenu */ + FormspecFormSource *fs_src = new FormspecFormSource(os.str()); + LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); + + GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(), + &m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(), + m_client->getSoundManager()); + m_formspec->setFocus("btn_continue"); + // game will be paused in next step, if in singleplayer (see m_is_paused) + m_formspec->doPause = true; +} + +void GameFormSpec::showDeathFormspecLegacy() +{ + static std::string formspec_str = + std::string("formspec_version[1]") + + SIZE_TAG + "bgcolor[#320000b4;true]" + "label[4.85,1.35;" + gettext("You died") + "]" + "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]" + ; + + /* Create menu */ + /* Note: FormspecFormSource and LocalFormspecHandler * + * are deleted by guiFormSpecMenu */ + FormspecFormSource *fs_src = new FormspecFormSource(formspec_str); + LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", m_client); + + GUIFormSpecMenu::create(m_formspec, m_client, m_rendering_engine->get_gui_env(), + &m_input->joystick, fs_src, txt_dst, m_client->getFormspecPrepend(), + m_client->getSoundManager()); + m_formspec->setFocus("btn_respawn"); +} + +void GameFormSpec::update() +{ + /* + make sure menu is on top + 1. Delete formspec menu reference if menu was removed + 2. Else, make sure formspec menu is on top + */ + if (!m_formspec) + return; + + if (m_formspec->getReferenceCount() == 1) { + // See GUIFormSpecMenu::create what refcnt = 1 means + this->deleteFormspec(); + return; + } + + auto &loc = m_formspec->getFormspecLocation(); + if (loc.type == InventoryLocation::NODEMETA) { + NodeMetadata *meta = m_client->getEnv().getClientMap().getNodeMetadata(loc.p); + if (!meta || meta->getString("formspec").empty()) { + m_formspec->quitMenu(); + return; + } + } + + if (isMenuActive()) + guiroot->bringToFront(m_formspec); +} + +void GameFormSpec::disableDebugView() +{ + if (m_formspec) { + m_formspec->setDebugView(false); + } +} + +/* returns false if game should exit, otherwise true + */ +bool GameFormSpec::handleCallbacks() +{ + auto texture_src = m_client->getTextureSource(); + + if (g_gamecallback->disconnect_requested) { + g_gamecallback->disconnect_requested = false; + return false; + } + + if (g_gamecallback->changepassword_requested) { + (void)make_irr(guienv, guiroot, -1, + &g_menumgr, m_client, texture_src); + g_gamecallback->changepassword_requested = false; + } + + if (g_gamecallback->changevolume_requested) { + (void)make_irr(guienv, guiroot, -1, + &g_menumgr, texture_src); + g_gamecallback->changevolume_requested = false; + } + + if (g_gamecallback->keyconfig_requested) { + (void)make_irr(guienv, guiroot, -1, + &g_menumgr, texture_src); + g_gamecallback->keyconfig_requested = false; + } + + if (g_gamecallback->touchscreenlayout_requested) { + (new GUITouchscreenLayout(guienv, guiroot, -1, + &g_menumgr, texture_src))->drop(); + g_gamecallback->touchscreenlayout_requested = false; + } + + if (!g_gamecallback->show_open_url_dialog.empty()) { + (void)make_irr(guienv, guiroot, -1, + &g_menumgr, texture_src, g_gamecallback->show_open_url_dialog); + g_gamecallback->show_open_url_dialog.clear(); + } + + if (g_gamecallback->keyconfig_changed) { + m_input->keycache.populate(); // update the cache with new settings + g_gamecallback->keyconfig_changed = false; + } + + return true; +} + +#ifdef __ANDROID__ +bool GameFormSpec::handleAndroidUIInput() +{ + if (m_formspec) { + m_formspec->getAndroidUIInput(); + return true; + } + return false; +} +#endif diff --git a/src/client/game_formspec.h b/src/client/game_formspec.h new file mode 100644 index 000000000..370370151 --- /dev/null +++ b/src/client/game_formspec.h @@ -0,0 +1,62 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2024 cx384 + +#pragma once + +#include +#include "irr_v3d.h" + +class Client; +class RenderingEngine; +class InputHandler; +class ISoundManager; +class GUIFormSpecMenu; + +/* +This object intend to contain the core fromspec functionality. +It includes: + - methods to show specific formspec menus + - storing the opened fromspec + - handling fromspec related callbacks + */ +struct GameFormSpec +{ + void init(Client *client, RenderingEngine *rendering_engine, InputHandler *input) + { + m_client = client; + m_rendering_engine = rendering_engine; + m_input = input; + } + + ~GameFormSpec(); + + void showFormSpec(const std::string &formspec, const std::string &formname); + void showLocalFormSpec(const std::string &formspec, const std::string &formname); + void showNodeFormspec(const std::string &formspec, const v3s16 &nodepos); + void showPlayerInventory(); + void showDeathFormspecLegacy(); + void showPauseMenu(); + + void update(); + void disableDebugView(); + + bool handleCallbacks(); + +#ifdef __ANDROID__ + // Returns false if no formspec open + bool handleAndroidUIInput(); +#endif + +private: + Client *m_client; + RenderingEngine *m_rendering_engine; + InputHandler *m_input; + + // Default: "". If other than "": Empty show_formspec packets will only + // close the formspec when the formname matches + std::string m_formname; + GUIFormSpecMenu *m_formspec = nullptr; + + void deleteFormspec(); +}; diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index c2693c6ae..e09875934 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -8,7 +8,6 @@ #include #include "gui/mainmenumanager.h" #include "gui/guiChatConsole.h" -#include "gui/guiFormSpecMenu.h" #include "gui/touchcontrols.h" #include "util/enriched_string.h" #include "util/pointedthing.h" @@ -319,17 +318,6 @@ void GameUI::toggleProfiler() } } - -void GameUI::deleteFormspec() -{ - if (m_formspec) { - m_formspec->drop(); - m_formspec = nullptr; - } - - m_formname.clear(); -} - void GameUI::clearText() { if (m_guitext_chat) { diff --git a/src/client/gameui.h b/src/client/gameui.h index 95674232a..03cbb3b2c 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -13,7 +13,6 @@ using namespace irr; class Client; class EnrichedString; class GUIChatConsole; -class GUIFormSpecMenu; struct MapDrawControl; struct PointedThing; @@ -79,15 +78,6 @@ public: void toggleHud(); void toggleProfiler(); - GUIFormSpecMenu *&updateFormspec(const std::string &formname) - { - m_formname = formname; - return m_formspec; - } - - const std::string &getFormspecName() { return m_formname; } - GUIFormSpecMenu *&getFormspecGUI() { return m_formspec; } - void deleteFormspec(); void clearText(); private: @@ -113,9 +103,4 @@ private: gui::IGUIStaticText *m_guitext_profiler = nullptr; // Profiler text u8 m_profiler_current_page = 0; const u8 m_profiler_max_page = 3; - - // Default: "". If other than "": Empty show_formspec packets will only - // close the formspec when the formname matches - std::string m_formname; - GUIFormSpecMenu *m_formspec = nullptr; };