From 792fb13ac646dc8c2739e9f56a8fed566f4960f9 Mon Sep 17 00:00:00 2001 From: Zughy <63455151+Zughy@users.noreply.github.com> Date: Sat, 17 Aug 2024 15:16:37 +0200 Subject: [PATCH 01/75] Docs: Clarify rotation syntax of `model` formspec element (#14997) There has been confusion over this in the past, with users wrongly supplying rotation as `{x,y}`. --- 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 389ea73f2..6d486b56b 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -2861,14 +2861,14 @@ Elements * Requires formspec version >= 6. * See `background9[]` documentation for more information. -### `model[,;,;;;;;;;;]` +### `model[,;,;;;;;;;;]` * Show a mesh model. * `name`: Element name that can be used for styling * `mesh`: The mesh model to use. * `textures`: The mesh textures to use according to the mesh materials. Texture names must be separated by commas. -* `rotation {X,Y}` (Optional): Initial rotation of the camera. +* `rotation` (Optional): Initial rotation of the camera, format `x,y`. The axes are euler angles in degrees. * `continuous` (Optional): Whether the rotation is continuous. Default `false`. * `mouse control` (Optional): Whether the model can be controlled with the mouse. Default `true`. From b0ad9a6c3321c58044c5bcb5d2c0d904487d14fa Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 17 Aug 2024 15:49:53 +0100 Subject: [PATCH 02/75] Use JSON file for credits (#14956) --- builtin/mainmenu/credits.json | 85 +++++++++++++++++++++++++ builtin/mainmenu/tab_about.lua | 109 +++++---------------------------- 2 files changed, 99 insertions(+), 95 deletions(-) create mode 100644 builtin/mainmenu/credits.json diff --git a/builtin/mainmenu/credits.json b/builtin/mainmenu/credits.json new file mode 100644 index 000000000..261cf5407 --- /dev/null +++ b/builtin/mainmenu/credits.json @@ -0,0 +1,85 @@ +{ + "#": "https://github.com/orgs/minetest/teams/engine/members", + "core_developers": [ + "Perttu Ahola (celeron55) [Project founder]", + "sfan5 ", + "ShadowNinja ", + "Nathanaëlle Courant (Nore/Ekdohibs) ", + "Loic Blot (nerzhul/nrz) ", + "Andrew Ward (rubenwardy) ", + "Krock/SmallJoker ", + "Lars Hofhansl ", + "v-rob ", + "Desour/DS", + "srifqi", + "Gregor Parzefall (grorp)", + "Lars Müller (luatic)" + ], + "previous_core_developers": [ + "BlockMen", + "Maciej Kasatkin (RealBadAngel) [RIP]", + "Lisa Milne (darkrose) ", + "proller", + "Ilya Zhuravlev (xyz) ", + "PilzAdam ", + "est31 ", + "kahrl ", + "Ryan Kwolek (kwolekr) ", + "sapier", + "Zeno", + "Auke Kok (sofar) ", + "Aaron Suen ", + "paramat", + "Pierre-Yves Rollo ", + "hecks", + "Jude Melton-Houghton (TurkeyMcMac) [RIP]", + "Hugues Ross ", + "Dmitry Kostenko (x2048) " + ], + "#": "Currently only https://github.com/orgs/minetest/teams/triagers/members", + "core_team": [ + "Zughy [Issue triager]", + "wsor [Issue triager]", + "Hugo Locurcio (Calinou) [Issue triager]" + ], + "#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py", + "contributors": [ + "cx384", + "numzero", + "AFCMS", + "sfence", + "Wuzzy", + "ROllerozxa", + "JosiahWI", + "OgelGames", + "David Heidelberg", + "1F616EMO", + "HybridDog", + "Bradley Pierce (Thresher)", + "savilli", + "Stvk imension", + "y5nw", + "chmodsayshello", + "jordan4ibanez", + "superfloh247" + ], + "previous_contributors": [ + "Nils Dagsson Moskopp (erlehmann) [Minetest logo]", + "red-001 ", + "Giuseppe Bilotta", + "HybridDog", + "ClobberXD", + "Dániel Juhász (juhdanad) ", + "MirceaKitsune ", + "Jean-Patrick Guerrero (kilbith)", + "MoNTE48", + "Constantin Wenger (SpeedProg)", + "Ciaran Gultnieks (CiaranG)", + "Paul Ouellette (pauloue)", + "stujones11", + "Rogier ", + "Gregory Currie (gregorycu)", + "JacobF", + "Jeija " + ] +} diff --git a/builtin/mainmenu/tab_about.lua b/builtin/mainmenu/tab_about.lua index d798b5b09..0394ea507 100644 --- a/builtin/mainmenu/tab_about.lua +++ b/builtin/mainmenu/tab_about.lua @@ -15,96 +15,6 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --- https://github.com/orgs/minetest/teams/engine/members - -local core_developers = { - "Perttu Ahola (celeron55) [Project founder]", - "sfan5 ", - "ShadowNinja ", - "Nathanaëlle Courant (Nore/Ekdohibs) ", - "Loic Blot (nerzhul/nrz) ", - "Andrew Ward (rubenwardy) ", - "Krock/SmallJoker ", - "Lars Hofhansl ", - "v-rob ", - "Desour/DS", - "srifqi", - "Gregor Parzefall (grorp)", - "Lars Müller (luatic)", -} - --- currently only https://github.com/orgs/minetest/teams/triagers/members - -local core_team = { - "Zughy [Issue triager]", - "wsor [Issue triager]", - "Hugo Locurcio (Calinou) [Issue triager]", -} - --- For updating active/previous contributors, see the script in ./util/gather_git_credits.py - -local active_contributors = { - "cx384", - "numzero", - "AFCMS", - "sfence", - "Wuzzy", - "ROllerozxa", - "JosiahWI", - "OgelGames", - "David Heidelberg", - "1F616EMO", - "HybridDog", - "Bradley Pierce (Thresher)", - "savilli", - "Stvk imension", - "y5nw", - "chmodsayshello", - "jordan4ibanez", - "superfloh247", -} - -local previous_core_developers = { - "BlockMen", - "Maciej Kasatkin (RealBadAngel) [RIP]", - "Lisa Milne (darkrose) ", - "proller", - "Ilya Zhuravlev (xyz) ", - "PilzAdam ", - "est31 ", - "kahrl ", - "Ryan Kwolek (kwolekr) ", - "sapier", - "Zeno", - "Auke Kok (sofar) ", - "Aaron Suen ", - "paramat", - "Pierre-Yves Rollo ", - "hecks", - "Jude Melton-Houghton (TurkeyMcMac) [RIP]", - "Hugues Ross ", - "Dmitry Kostenko (x2048) ", -} - -local previous_contributors = { - "Nils Dagsson Moskopp (erlehmann) [Minetest logo]", - "red-001 ", - "Giuseppe Bilotta", - "HybridDog", - "ClobberXD", - "Dániel Juhász (juhdanad) ", - "MirceaKitsune ", - "Jean-Patrick Guerrero (kilbith)", - "MoNTE48", - "Constantin Wenger (SpeedProg)", - "Ciaran Gultnieks (CiaranG)", - "Paul Ouellette (pauloue)", - "stujones11", - "Rogier ", - "Gregory Currie (gregorycu)", - "JacobF", - "Jeija ", -} local function prepare_credits(dest, source) local string = table.concat(source, "\n") .. "\n" @@ -120,6 +30,13 @@ local function prepare_credits(dest, source) table.insert(dest, string) end +local function get_credits() + local f = assert(io.open(core.get_mainmenu_path() .. "/credits.json")) + local json = core.parse_json(f:read("*all")) + f:close() + return json +end + return { name = "about", caption = fgettext("About"), @@ -133,30 +50,32 @@ return { "", } + local credits = get_credits() + table.insert_all(hypertext, { "", fgettext_ne("Core Developers"), "\n", }) - prepare_credits(hypertext, core_developers) + prepare_credits(hypertext, credits.core_developers) table.insert_all(hypertext, { "\n", "", fgettext_ne("Core Team"), "\n", }) - prepare_credits(hypertext, core_team) + prepare_credits(hypertext, credits.core_team) table.insert_all(hypertext, { "\n", "", fgettext_ne("Active Contributors"), "\n", }) - prepare_credits(hypertext, active_contributors) + prepare_credits(hypertext, credits.contributors) table.insert_all(hypertext, { "\n", "", fgettext_ne("Previous Core Developers"), "\n", }) - prepare_credits(hypertext, previous_core_developers) + prepare_credits(hypertext, credits.previous_core_developers) table.insert_all(hypertext, { "\n", "", fgettext_ne("Previous Contributors"), "\n", }) - prepare_credits(hypertext, previous_contributors) + prepare_credits(hypertext, credits.previous_contributors) hypertext = table.concat(hypertext):sub(1, -2) From 1fb49e9ca7c66251503be47608243f124061bf44 Mon Sep 17 00:00:00 2001 From: j-r Date: Sat, 17 Aug 2024 19:48:40 +0200 Subject: [PATCH 03/75] Add shared mods path to get_modpaths ...because the documentation implies it should be. --- src/script/lua_api/l_mainmenu.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index a5913e807..97101955a 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -675,6 +675,11 @@ int ModApiMainMenu::l_get_modpaths(lua_State *L) ModApiMainMenu::l_get_modpath(L); lua_setfield(L, -2, "mods"); + std::string modpath = fs::RemoveRelativePathComponents( + porting::path_share + DIR_DELIM + "mods" + DIR_DELIM); + lua_pushstring(L, modpath.c_str()); + lua_setfield(L, -2, "share"); + for (const std::string &component : getEnvModPaths()) { lua_pushstring(L, component.c_str()); lua_setfield(L, -2, fs::AbsolutePath(component).c_str()); From 5acc2736db4583a4e7c23484ef3d89d4bf931b7b Mon Sep 17 00:00:00 2001 From: 1F616EMO~nya Date: Sun, 18 Aug 2024 01:48:54 +0800 Subject: [PATCH 04/75] Translate access denied strings (#14842) --- src/network/clientpackethandler.cpp | 22 +++++++++++++++++++--- src/network/networkprotocol.h | 16 ---------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index e2bcb51b5..4b9e8ceea 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -48,6 +48,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "particles.h" #include +const char *accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = { + N_("Invalid password"), + N_("Your client sent something the server didn't expect. Try reconnecting or updating your client."), + N_("The server is running in singleplayer mode. You cannot connect."), + N_("Your client's version is not supported.\nPlease contact the server administrator."), + N_("Player name contains disallowed characters"), + N_("Player name not allowed"), + N_("Too many users"), + N_("Empty passwords are disallowed. Set a password and try again."), + N_("Another client is connected with this name. If your client closed unexpectedly, try again in a minute."), + N_("Internal server error"), + "", + N_("Server shutting down"), + N_("The server has experienced an internal error. You will now be disconnected.") +}; + void Client::handleCommand_Deprecated(NetworkPacket* pkt) { infostream << "Got deprecated command " @@ -216,17 +232,17 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt) denyCode == SERVER_ACCESSDENIED_CRASH) { *pkt >> m_access_denied_reason; if (m_access_denied_reason.empty()) - m_access_denied_reason = accessDeniedStrings[denyCode]; + m_access_denied_reason = gettext(accessDeniedStrings[denyCode]); u8 reconnect; *pkt >> reconnect; m_access_denied_reconnect = reconnect & 1; } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) { *pkt >> m_access_denied_reason; } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) { - m_access_denied_reason = accessDeniedStrings[denyCode]; + m_access_denied_reason = gettext(accessDeniedStrings[denyCode]); m_access_denied_reconnect = true; } else if (denyCode < SERVER_ACCESSDENIED_MAX) { - m_access_denied_reason = accessDeniedStrings[denyCode]; + m_access_denied_reason = gettext(accessDeniedStrings[denyCode]); } else { // Allow us to add new error messages to the // protocol without raising the protocol version, if we want to. diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index e3618042f..d84c735b8 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -1150,22 +1150,6 @@ enum NetProtoCompressionMode { NETPROTO_COMPRESSION_NONE = 0, }; -constexpr const char *accessDeniedStrings[SERVER_ACCESSDENIED_MAX] = { - "Invalid password", - "Your client sent something the server didn't expect. Try reconnecting or updating your client.", - "The server is running in simple singleplayer mode. You cannot connect.", - "Your client's version is not supported.\nPlease contact the server administrator.", - "Player name contains disallowed characters", - "Player name not allowed", - "Too many users", - "Empty passwords are disallowed. Set a password and try again.", - "Another client is connected with this name. If your client closed unexpectedly, try again in a minute.", - "Internal server error", - "", - "Server shutting down", - "The server has experienced an internal error. You will now be disconnected." -}; - enum PlayerListModifer : u8 { PLAYER_LIST_INIT, From 5d226268df764c36fc4d550c3c0bc980c7e34f0d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 17 Aug 2024 19:49:11 +0200 Subject: [PATCH 05/75] Irrlicht cleanups (mostly getting rid of `core::array`) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com> --- irr/include/CMeshBuffer.h | 40 ++--- irr/include/IAttributes.h | 35 ---- irr/include/IFileSystem.h | 107 ----------- irr/include/ISceneNode.h | 1 + irr/include/IVideoDriver.h | 21 --- irr/include/SAnimatedMesh.h | 10 +- irr/include/SMaterial.h | 1 - irr/include/SMesh.h | 35 ++-- irr/include/SOverrideMaterial.h | 8 +- irr/include/SSkinMeshBuffer.h | 80 +++++---- irr/include/irrArray.h | 17 -- irr/src/CAttributeImpl.h | 3 - irr/src/CAttributes.cpp | 191 ++++---------------- irr/src/CAttributes.h | 65 ++----- irr/src/CB3DMeshFileLoader.cpp | 2 +- irr/src/CBillboardSceneNode.cpp | 6 +- irr/src/CFileSystem.cpp | 269 +--------------------------- irr/src/CFileSystem.h | 40 +---- irr/src/CMeshManipulator.cpp | 42 ++--- irr/src/CNullDriver.cpp | 33 +--- irr/src/CNullDriver.h | 17 +- irr/src/COGLESDriver.cpp | 57 +----- irr/src/COGLESDriver.h | 12 +- irr/src/COGLESExtensionHandler.cpp | 7 +- irr/src/COGLESExtensionHandler.h | 1 - irr/src/COpenGLCoreTexture.h | 36 ++-- irr/src/COpenGLDriver.cpp | 55 +----- irr/src/COpenGLDriver.h | 26 +-- irr/src/COpenGLExtensionHandler.cpp | 4 +- irr/src/COpenGLExtensionHandler.h | 2 - irr/src/CSceneManager.cpp | 89 +++++---- irr/src/CSceneManager.h | 16 +- irr/src/CXMeshFileLoader.cpp | 10 +- irr/src/CZipReader.cpp | 9 +- irr/src/CZipReader.h | 4 +- irr/src/IAttribute.h | 20 +-- irr/src/OpenGL/Driver.cpp | 65 ++----- irr/src/OpenGL/Driver.h | 23 +-- irr/src/OpenGL/MaterialRenderer.cpp | 19 +- irr/src/OpenGL/MaterialRenderer.h | 8 +- src/client/clouds.cpp | 8 +- src/client/hud.cpp | 31 ++-- src/client/mapblock_mesh.cpp | 2 +- src/client/minimap.cpp | 4 +- src/client/sky.cpp | 4 +- 45 files changed, 308 insertions(+), 1227 deletions(-) diff --git a/irr/include/CMeshBuffer.h b/irr/include/CMeshBuffer.h index 8f8158ff1..0b0c3bb92 100644 --- a/irr/include/CMeshBuffer.h +++ b/irr/include/CMeshBuffer.h @@ -4,7 +4,7 @@ #pragma once -#include "irrArray.h" +#include #include "IMeshBuffer.h" namespace irr @@ -43,21 +43,21 @@ public: /** \return Pointer to vertices. */ const void *getVertices() const override { - return Vertices.const_pointer(); + return Vertices.data(); } //! Get pointer to vertices /** \return Pointer to vertices. */ void *getVertices() override { - return Vertices.pointer(); + return Vertices.data(); } //! Get number of vertices /** \return Number of vertices. */ u32 getVertexCount() const override { - return Vertices.size(); + return static_cast(Vertices.size()); } //! Get type of index data which is stored in this meshbuffer. @@ -71,21 +71,21 @@ public: /** \return Pointer to indices. */ const u16 *getIndices() const override { - return Indices.const_pointer(); + return Indices.data(); } //! Get pointer to indices /** \return Pointer to indices. */ u16 *getIndices() override { - return Indices.pointer(); + return Indices.data(); } //! Get number of indices /** \return Number of indices. */ u32 getIndexCount() const override { - return Indices.size(); + return static_cast(Indices.size()); } //! Get the axis aligned bounding box @@ -160,27 +160,23 @@ public: } //! Append the vertices and indices to the current buffer - /** Only works for compatible types, i.e. either the same type - or the main buffer is of standard type. Otherwise, behavior is - undefined. - */ void append(const void *const vertices, u32 numVertices, const u16 *const indices, u32 numIndices) override { if (vertices == getVertices()) return; const u32 vertexCount = getVertexCount(); - u32 i; + const u32 indexCount = getIndexCount(); - Vertices.reallocate(vertexCount + numVertices); - for (i = 0; i < numVertices; ++i) { - Vertices.push_back(static_cast(vertices)[i]); - BoundingBox.addInternalPoint(static_cast(vertices)[i].Pos); - } + auto *vt = static_cast(vertices); + Vertices.insert(Vertices.end(), vt, vt + numVertices); + for (u32 i = vertexCount; i < getVertexCount(); i++) + BoundingBox.addInternalPoint(Vertices[i].Pos); - Indices.reallocate(getIndexCount() + numIndices); - for (i = 0; i < numIndices; ++i) { - Indices.push_back(indices[i] + vertexCount); + Indices.insert(Indices.end(), indices, indices + numIndices); + if (vertexCount != 0) { + for (u32 i = indexCount; i < getIndexCount(); i++) + Indices[i] += vertexCount; } } @@ -255,9 +251,9 @@ public: //! Material for this meshbuffer. video::SMaterial Material; //! Vertices of this buffer - core::array Vertices; + std::vector Vertices; //! Indices into the vertices of this buffer. - core::array Indices; + std::vector Indices; //! Bounding box of this meshbuffer. core::aabbox3d BoundingBox; //! Primitive type used for rendering (triangles, lines, ...) diff --git a/irr/include/IAttributes.h b/irr/include/IAttributes.h index 906d334a2..4606c5710 100644 --- a/irr/include/IAttributes.h +++ b/irr/include/IAttributes.h @@ -23,27 +23,13 @@ namespace io class IAttributes : public virtual IReferenceCounted { public: - //! Returns amount of attributes in this collection of attributes. - virtual u32 getAttributeCount() const = 0; - - //! Returns attribute name by index. - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - virtual const c8 *getAttributeName(s32 index) const = 0; - //! Returns the type of an attribute //! \param attributeName: Name for the attribute virtual E_ATTRIBUTE_TYPE getAttributeType(const c8 *attributeName) const = 0; - //! Returns attribute type by index. - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - virtual E_ATTRIBUTE_TYPE getAttributeType(s32 index) const = 0; - //! Returns if an attribute with a name exists virtual bool existsAttribute(const c8 *attributeName) const = 0; - //! Returns attribute index from name, -1 if not found - virtual s32 findAttribute(const c8 *attributeName) const = 0; - //! Removes all attributes virtual void clear() = 0; @@ -65,13 +51,6 @@ public: //! \return Returns value of the attribute previously set by setAttribute() virtual s32 getAttributeAsInt(const c8 *attributeName, irr::s32 defaultNotFound = 0) const = 0; - //! Gets an attribute as integer value - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - virtual s32 getAttributeAsInt(s32 index) const = 0; - - //! Sets an attribute as integer value - virtual void setAttribute(s32 index, s32 value) = 0; - /* Float Attribute @@ -90,13 +69,6 @@ public: //! \return Returns value of the attribute previously set by setAttribute() virtual f32 getAttributeAsFloat(const c8 *attributeName, irr::f32 defaultNotFound = 0.f) const = 0; - //! Gets an attribute as float value - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - virtual f32 getAttributeAsFloat(s32 index) const = 0; - - //! Sets an attribute as float value - virtual void setAttribute(s32 index, f32 value) = 0; - /* Bool Attribute */ @@ -112,13 +84,6 @@ public: //! \param defaultNotFound Value returned when attributeName was not found //! \return Returns value of the attribute previously set by setAttribute() virtual bool getAttributeAsBool(const c8 *attributeName, bool defaultNotFound = false) const = 0; - - //! Gets an attribute as boolean value - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - virtual bool getAttributeAsBool(s32 index) const = 0; - - //! Sets an attribute as boolean value - virtual void setAttribute(s32 index, bool value) = 0; }; } // end namespace io diff --git a/irr/include/IFileSystem.h b/irr/include/IFileSystem.h index 1fe9fe6f2..f144bbaee 100644 --- a/irr/include/IFileSystem.h +++ b/irr/include/IFileSystem.h @@ -85,113 +85,6 @@ public: See IReferenceCounted::drop() for more information. */ virtual IWriteFile *createAndWriteFile(const path &filename, bool append = false) = 0; - //! Adds an archive to the file system. - /** After calling this, the Irrlicht Engine will also search and open - files directly from this archive. This is useful for hiding data from - the end user, speeding up file access and making it possible to access - for example Quake3 .pk3 files, which are just renamed .zip files. By - default Irrlicht supports ZIP, PAK, TAR, PNK, and directories as - archives. You can provide your own archive types by implementing - IArchiveLoader and passing an instance to addArchiveLoader. - Irrlicht supports AES-encrypted zip files, and the advanced compression - techniques lzma and bzip2. - \param filename: Filename of the archive to add to the file system. - \param ignoreCase: If set to true, files in the archive can be accessed without - writing all letters in the right case. - \param ignorePaths: If set to true, files in the added archive can be accessed - without its complete path. - \param archiveType: If no specific E_FILE_ARCHIVE_TYPE is selected then - the type of archive will depend on the extension of the file name. If - you use a different extension then you can use this parameter to force - a specific type of archive. - \param password An optional password, which is used in case of encrypted archives. - \param retArchive A pointer that will be set to the archive that is added. - \return True if the archive was added successfully, false if not. */ - virtual bool addFileArchive(const path &filename, bool ignoreCase = true, - bool ignorePaths = true, - E_FILE_ARCHIVE_TYPE archiveType = EFAT_UNKNOWN, - const core::stringc &password = "", - IFileArchive **retArchive = 0) = 0; - - //! Adds an archive to the file system. - /** After calling this, the Irrlicht Engine will also search and open - files directly from this archive. This is useful for hiding data from - the end user, speeding up file access and making it possible to access - for example Quake3 .pk3 files, which are just renamed .zip files. By - default Irrlicht supports ZIP, PAK, TAR, PNK, and directories as - archives. You can provide your own archive types by implementing - IArchiveLoader and passing an instance to addArchiveLoader. - Irrlicht supports AES-encrypted zip files, and the advanced compression - techniques lzma and bzip2. - If you want to add a directory as an archive, prefix its name with a - slash in order to let Irrlicht recognize it as a folder mount (mypath/). - Using this technique one can build up a search order, because archives - are read first, and can be used more easily with relative filenames. - \param file: Archive to add to the file system. - \param ignoreCase: If set to true, files in the archive can be accessed without - writing all letters in the right case. - \param ignorePaths: If set to true, files in the added archive can be accessed - without its complete path. - \param archiveType: If no specific E_FILE_ARCHIVE_TYPE is selected then - the type of archive will depend on the extension of the file name. If - you use a different extension then you can use this parameter to force - a specific type of archive. - \param password An optional password, which is used in case of encrypted archives. - \param retArchive A pointer that will be set to the archive that is added. - \return True if the archive was added successfully, false if not. */ - virtual bool addFileArchive(IReadFile *file, bool ignoreCase = true, - bool ignorePaths = true, - E_FILE_ARCHIVE_TYPE archiveType = EFAT_UNKNOWN, - const core::stringc &password = "", - IFileArchive **retArchive = 0) = 0; - - //! Adds an archive to the file system. - /** \param archive: The archive to add to the file system. - \return True if the archive was added successfully, false if not. */ - virtual bool addFileArchive(IFileArchive *archive) = 0; - - //! Get the number of archives currently attached to the file system - virtual u32 getFileArchiveCount() const = 0; - - //! Removes an archive from the file system. - /** This will close the archive and free any file handles, but will not - close resources which have already been loaded and are now cached, for - example textures and meshes. - \param index: The index of the archive to remove - \return True on success, false on failure */ - virtual bool removeFileArchive(u32 index) = 0; - - //! Removes an archive from the file system. - /** This will close the archive and free any file handles, but will not - close resources which have already been loaded and are now cached, for - example textures and meshes. Note that a relative filename might be - interpreted differently on each call, depending on the current working - directory. In case you want to remove an archive that was added using - a relative path name, you have to change to the same working directory - again. This means, that the filename given on creation is not an - identifier for the archive, but just a usual filename that is used for - locating the archive to work with. - \param filename The archive pointed to by the name will be removed - \return True on success, false on failure */ - virtual bool removeFileArchive(const path &filename) = 0; - - //! Removes an archive from the file system. - /** This will close the archive and free any file handles, but will not - close resources which have already been loaded and are now cached, for - example textures and meshes. - \param archive The archive to remove. - \return True on success, false on failure */ - virtual bool removeFileArchive(const IFileArchive *archive) = 0; - - //! Changes the search order of attached archives. - /** - \param sourceIndex: The index of the archive to change - \param relative: The relative change in position, archives with a lower index are searched first */ - virtual bool moveFileArchive(u32 sourceIndex, s32 relative) = 0; - - //! Get the archive at a given index. - virtual IFileArchive *getFileArchive(u32 index) = 0; - //! Adds an external archive loader to the engine. /** Use this function to add support for new archive types to the engine, for example proprietary or encrypted file storage. */ diff --git a/irr/include/ISceneNode.h b/irr/include/ISceneNode.h index 7a03a2256..1eab3a3fd 100644 --- a/irr/include/ISceneNode.h +++ b/irr/include/ISceneNode.h @@ -10,6 +10,7 @@ #include "EDebugSceneTypes.h" #include "SMaterial.h" #include "irrString.h" +#include "irrArray.h" #include "aabbox3d.h" #include "matrix4.h" #include "IAttributes.h" diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index 2d651c6bf..888e4a9f7 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -182,7 +182,6 @@ public: 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. - MaxUserClipPlanes (int) Number of additional clip planes, which can be set by the user via dedicated driver methods. 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. MaxIndices (int) Number of indices which can be used in one render call (i.e. one mesh buffer). @@ -1109,26 +1108,6 @@ public: \return Pointer to loaded texture, or 0 if not found. */ virtual video::ITexture *findTexture(const io::path &filename) = 0; - //! Set or unset a clipping plane. - /** There are at least 6 clipping planes available for the user - to set at will. - \param index The plane index. Must be between 0 and - MaxUserClipPlanes. - \param plane The plane itself. - \param enable If true, enable the clipping plane else disable - it. - \return True if the clipping plane is usable. */ - virtual bool setClipPlane(u32 index, const core::plane3df &plane, bool enable = false) = 0; - - //! Enable or disable a clipping plane. - /** There are at least 6 clipping planes available for the user - to set at will. - \param index The plane index. Must be between 0 and - MaxUserClipPlanes. - \param enable If true, enable the clipping plane else disable - it. */ - virtual void enableClipPlane(u32 index, bool enable) = 0; - //! Set the minimum number of vertices for which a hw buffer will be created /** \param count Number of vertices to set as minimum. */ virtual void setMinHardwareBufferVertexCount(u32 count) = 0; diff --git a/irr/include/SAnimatedMesh.h b/irr/include/SAnimatedMesh.h index 8fdaae0ee..42ba6b952 100644 --- a/irr/include/SAnimatedMesh.h +++ b/irr/include/SAnimatedMesh.h @@ -4,10 +4,10 @@ #pragma once +#include #include "IAnimatedMesh.h" #include "IMesh.h" #include "aabbox3d.h" -#include "irrArray.h" namespace irr { @@ -32,15 +32,15 @@ struct SAnimatedMesh : public IAnimatedMesh virtual ~SAnimatedMesh() { // drop meshes - for (u32 i = 0; i < Meshes.size(); ++i) - Meshes[i]->drop(); + for (auto *mesh : Meshes) + mesh->drop(); } //! Gets the frame count of the animated mesh. /** \return Amount of frames. If the amount is 1, it is a static, non animated mesh. */ u32 getFrameCount() const override { - return Meshes.size(); + return static_cast(Meshes.size()); } //! Gets the default animation speed of the animated mesh. @@ -161,7 +161,7 @@ struct SAnimatedMesh : public IAnimatedMesh } //! All meshes defining the animated mesh - core::array Meshes; + std::vector Meshes; //! The bounding box of this mesh core::aabbox3d Box; diff --git a/irr/include/SMaterial.h b/irr/include/SMaterial.h index c803f5fde..8f24b9984 100644 --- a/irr/include/SMaterial.h +++ b/irr/include/SMaterial.h @@ -6,7 +6,6 @@ #include "SColor.h" #include "matrix4.h" -#include "irrArray.h" #include "irrMath.h" #include "EMaterialTypes.h" #include "EMaterialProps.h" diff --git a/irr/include/SMesh.h b/irr/include/SMesh.h index e865a5d2d..a391255a1 100644 --- a/irr/include/SMesh.h +++ b/irr/include/SMesh.h @@ -4,10 +4,10 @@ #pragma once +#include #include "IMesh.h" #include "IMeshBuffer.h" #include "aabbox3d.h" -#include "irrArray.h" namespace irr { @@ -28,15 +28,15 @@ struct SMesh : public IMesh virtual ~SMesh() { // drop buffers - for (u32 i = 0; i < MeshBuffers.size(); ++i) - MeshBuffers[i]->drop(); + for (auto *buf : MeshBuffers) + buf->drop(); } //! clean mesh virtual void clear() { - for (u32 i = 0; i < MeshBuffers.size(); ++i) - MeshBuffers[i]->drop(); + for (auto *buf : MeshBuffers) + buf->drop(); MeshBuffers.clear(); BoundingBox.reset(0.f, 0.f, 0.f); } @@ -44,7 +44,7 @@ struct SMesh : public IMesh //! returns amount of mesh buffers. u32 getMeshBufferCount() const override { - return MeshBuffers.size(); + return static_cast(MeshBuffers.size()); } //! returns pointer to a mesh buffer @@ -57,12 +57,11 @@ struct SMesh : public IMesh /** reverse search */ IMeshBuffer *getMeshBuffer(const video::SMaterial &material) const override { - for (s32 i = (s32)MeshBuffers.size() - 1; i >= 0; --i) { - if (material == MeshBuffers[i]->getMaterial()) - return MeshBuffers[i]; + for (auto it = MeshBuffers.rbegin(); it != MeshBuffers.rend(); it++) { + if (material == (*it)->getMaterial()) + return *it; } - - return 0; + return nullptr; } //! returns an axis aligned bounding box @@ -81,8 +80,8 @@ struct SMesh : public IMesh void recalculateBoundingBox() { bool hasMeshBufferBBox = false; - for (u32 i = 0; i < MeshBuffers.size(); ++i) { - const core::aabbox3df &bb = MeshBuffers[i]->getBoundingBox(); + for (auto *buf : MeshBuffers) { + const core::aabbox3df &bb = buf->getBoundingBox(); if (!bb.isEmpty()) { if (!hasMeshBufferBBox) { hasMeshBufferBBox = true; @@ -110,19 +109,19 @@ struct SMesh : public IMesh //! set the hardware mapping hint, for driver void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) override { - for (u32 i = 0; i < MeshBuffers.size(); ++i) - MeshBuffers[i]->setHardwareMappingHint(newMappingHint, buffer); + for (auto *buf : MeshBuffers) + buf->setHardwareMappingHint(newMappingHint, buffer); } //! flags the meshbuffer as changed, reloads hardware buffers void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) override { - for (u32 i = 0; i < MeshBuffers.size(); ++i) - MeshBuffers[i]->setDirty(buffer); + for (auto *buf : MeshBuffers) + buf->setDirty(buffer); } //! The meshbuffers of this mesh - core::array MeshBuffers; + std::vector MeshBuffers; //! The bounding box of this mesh core::aabbox3d BoundingBox; diff --git a/irr/include/SOverrideMaterial.h b/irr/include/SOverrideMaterial.h index 6de6e6ebb..c52a55b55 100644 --- a/irr/include/SOverrideMaterial.h +++ b/irr/include/SOverrideMaterial.h @@ -4,6 +4,7 @@ #pragma once +#include #include "SMaterial.h" namespace irr @@ -57,7 +58,7 @@ struct SOverrideMaterial }; //! To overwrite SMaterial::MaterialType - core::array MaterialTypes; + std::vector MaterialTypes; //! Default constructor SOverrideMaterial() : @@ -83,9 +84,8 @@ struct SOverrideMaterial void apply(SMaterial &material) { if (Enabled) { - for (u32 i = 0; i < MaterialTypes.size(); ++i) { - const SMaterialTypeReplacement &mtr = MaterialTypes[i]; - if (mtr.Original < 0 || (s32)mtr.Original == material.MaterialType) + for (const auto &mtr : MaterialTypes) { + if (mtr.Original < 0 || mtr.Original == (s32)material.MaterialType) material.MaterialType = (E_MATERIAL_TYPE)mtr.Replacement; } for (u32 f = 0; f < 32; ++f) { diff --git a/irr/include/SSkinMeshBuffer.h b/irr/include/SSkinMeshBuffer.h index 5ced6057d..fe9d78321 100644 --- a/irr/include/SSkinMeshBuffer.h +++ b/irr/include/SSkinMeshBuffer.h @@ -18,9 +18,8 @@ struct SSkinMeshBuffer : public IMeshBuffer //! Default constructor SSkinMeshBuffer(video::E_VERTEX_TYPE vt = video::EVT_STANDARD) : ChangedID_Vertex(1), ChangedID_Index(1), VertexType(vt), - PrimitiveType(EPT_TRIANGLES), + PrimitiveType(EPT_TRIANGLES), HWBuffer(nullptr), MappingHint_Vertex(EHM_NEVER), MappingHint_Index(EHM_NEVER), - HWBuffer(NULL), BoundingBoxNeedsRecalculated(true) { #ifdef _DEBUG @@ -58,11 +57,11 @@ struct SSkinMeshBuffer : public IMeshBuffer { switch (VertexType) { case video::EVT_2TCOORDS: - return Vertices_2TCoords.const_pointer(); + return Vertices_2TCoords.data(); case video::EVT_TANGENTS: - return Vertices_Tangents.const_pointer(); + return Vertices_Tangents.data(); default: - return Vertices_Standard.const_pointer(); + return Vertices_Standard.data(); } } @@ -71,11 +70,11 @@ struct SSkinMeshBuffer : public IMeshBuffer { switch (VertexType) { case video::EVT_2TCOORDS: - return Vertices_2TCoords.pointer(); + return Vertices_2TCoords.data(); case video::EVT_TANGENTS: - return Vertices_Tangents.pointer(); + return Vertices_Tangents.data(); default: - return Vertices_Standard.pointer(); + return Vertices_Standard.data(); } } @@ -84,11 +83,11 @@ struct SSkinMeshBuffer : public IMeshBuffer { switch (VertexType) { case video::EVT_2TCOORDS: - return Vertices_2TCoords.size(); + return static_cast(Vertices_2TCoords.size()); case video::EVT_TANGENTS: - return Vertices_Tangents.size(); + return static_cast(Vertices_Tangents.size()); default: - return Vertices_Standard.size(); + return static_cast(Vertices_Standard.size()); } } @@ -102,19 +101,19 @@ struct SSkinMeshBuffer : public IMeshBuffer //! Get pointer to index array const u16 *getIndices() const override { - return Indices.const_pointer(); + return Indices.data(); } //! Get pointer to index array u16 *getIndices() override { - return Indices.pointer(); + return Indices.data(); } //! Get index count u32 getIndexCount() const override { - return Indices.size(); + return static_cast(Indices.size()); } //! Get bounding box @@ -143,7 +142,7 @@ struct SSkinMeshBuffer : public IMeshBuffer BoundingBox.reset(0, 0, 0); else { BoundingBox.reset(Vertices_Standard[0].Pos); - for (u32 i = 1; i < Vertices_Standard.size(); ++i) + for (size_t i = 1; i < Vertices_Standard.size(); ++i) BoundingBox.addInternalPoint(Vertices_Standard[i].Pos); } break; @@ -153,7 +152,7 @@ struct SSkinMeshBuffer : public IMeshBuffer BoundingBox.reset(0, 0, 0); else { BoundingBox.reset(Vertices_2TCoords[0].Pos); - for (u32 i = 1; i < Vertices_2TCoords.size(); ++i) + for (size_t i = 1; i < Vertices_2TCoords.size(); ++i) BoundingBox.addInternalPoint(Vertices_2TCoords[i].Pos); } break; @@ -163,7 +162,7 @@ struct SSkinMeshBuffer : public IMeshBuffer BoundingBox.reset(0, 0, 0); else { BoundingBox.reset(Vertices_Tangents[0].Pos); - for (u32 i = 1; i < Vertices_Tangents.size(); ++i) + for (size_t i = 1; i < Vertices_Tangents.size(); ++i) BoundingBox.addInternalPoint(Vertices_Tangents[i].Pos); } break; @@ -181,12 +180,12 @@ struct SSkinMeshBuffer : public IMeshBuffer void convertTo2TCoords() { if (VertexType == video::EVT_STANDARD) { - for (u32 n = 0; n < Vertices_Standard.size(); ++n) { + for (const auto &Vertex_Standard : Vertices_Standard) { video::S3DVertex2TCoords Vertex; - Vertex.Color = Vertices_Standard[n].Color; - Vertex.Pos = Vertices_Standard[n].Pos; - Vertex.Normal = Vertices_Standard[n].Normal; - Vertex.TCoords = Vertices_Standard[n].TCoords; + Vertex.Color = Vertex_Standard.Color; + Vertex.Pos = Vertex_Standard.Pos; + Vertex.Normal = Vertex_Standard.Normal; + Vertex.TCoords = Vertex_Standard.TCoords; Vertices_2TCoords.push_back(Vertex); } Vertices_Standard.clear(); @@ -198,23 +197,23 @@ struct SSkinMeshBuffer : public IMeshBuffer void convertToTangents() { if (VertexType == video::EVT_STANDARD) { - for (u32 n = 0; n < Vertices_Standard.size(); ++n) { + for (const auto &Vertex_Standard : Vertices_Standard) { video::S3DVertexTangents Vertex; - Vertex.Color = Vertices_Standard[n].Color; - Vertex.Pos = Vertices_Standard[n].Pos; - Vertex.Normal = Vertices_Standard[n].Normal; - Vertex.TCoords = Vertices_Standard[n].TCoords; + Vertex.Color = Vertex_Standard.Color; + Vertex.Pos = Vertex_Standard.Pos; + Vertex.Normal = Vertex_Standard.Normal; + Vertex.TCoords = Vertex_Standard.TCoords; Vertices_Tangents.push_back(Vertex); } Vertices_Standard.clear(); VertexType = video::EVT_TANGENTS; } else if (VertexType == video::EVT_2TCOORDS) { - for (u32 n = 0; n < Vertices_2TCoords.size(); ++n) { + for (const auto &Vertex_2TCoords : Vertices_2TCoords) { video::S3DVertexTangents Vertex; - Vertex.Color = Vertices_2TCoords[n].Color; - Vertex.Pos = Vertices_2TCoords[n].Pos; - Vertex.Normal = Vertices_2TCoords[n].Normal; - Vertex.TCoords = Vertices_2TCoords[n].TCoords; + Vertex.Color = Vertex_2TCoords.Color; + Vertex.Pos = Vertex_2TCoords.Pos; + Vertex.Normal = Vertex_2TCoords.Normal; + Vertex.TCoords = Vertex_2TCoords.TCoords; Vertices_Tangents.push_back(Vertex); } Vertices_2TCoords.clear(); @@ -301,7 +300,10 @@ struct SSkinMeshBuffer : public IMeshBuffer } //! append the vertices and indices to the current buffer - void append(const void *const vertices, u32 numVertices, const u16 *const indices, u32 numIndices) override {} + void append(const void *const vertices, u32 numVertices, const u16 *const indices, u32 numIndices) override + { + _IRR_DEBUG_BREAK_IF(true); + } //! get the current hardware mapping hint for vertex buffers E_HARDWARE_MAPPING getHardwareMappingHint_Vertex() const override @@ -366,10 +368,10 @@ struct SSkinMeshBuffer : public IMeshBuffer //! Call this after changing the positions of any vertex. void boundingBoxNeedsRecalculated(void) { BoundingBoxNeedsRecalculated = true; } - core::array Vertices_Tangents; - core::array Vertices_2TCoords; - core::array Vertices_Standard; - core::array Indices; + std::vector Vertices_Tangents; + std::vector Vertices_2TCoords; + std::vector Vertices_Standard; + std::vector Indices; u32 ChangedID_Vertex; u32 ChangedID_Index; @@ -385,12 +387,12 @@ struct SSkinMeshBuffer : public IMeshBuffer //! Primitive type used for rendering (triangles, lines, ...) E_PRIMITIVE_TYPE PrimitiveType; + mutable void *HWBuffer; + // hardware mapping hint E_HARDWARE_MAPPING MappingHint_Vertex : 3; E_HARDWARE_MAPPING MappingHint_Index : 3; - mutable void *HWBuffer; - bool BoundingBoxNeedsRecalculated : 1; }; diff --git a/irr/include/irrArray.h b/irr/include/irrArray.h index b6f573a79..66978048f 100644 --- a/irr/include/irrArray.h +++ b/irr/include/irrArray.h @@ -167,13 +167,6 @@ public: return *this; } - array &operator=(std::vector &&other) - { - m_data = std::move(other); - is_sorted = false; - return *this; - } - //! Equality operator bool operator==(const array &other) const { @@ -400,16 +393,6 @@ public: std::swap(is_sorted, other.is_sorted); } - //! Pull the contents of this array as a vector. - // The array is left empty. - std::vector steal() - { - std::vector ret = std::move(m_data); - m_data.clear(); - is_sorted = true; - return ret; - } - typedef T value_type; typedef u32 size_type; diff --git a/irr/src/CAttributeImpl.h b/irr/src/CAttributeImpl.h index 9065a7342..e9436a407 100644 --- a/irr/src/CAttributeImpl.h +++ b/irr/src/CAttributeImpl.h @@ -3,9 +3,6 @@ // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CAttributes.h" -#include "fast_atof.h" -#include "ITexture.h" -#include "IVideoDriver.h" namespace irr { diff --git a/irr/src/CAttributes.cpp b/irr/src/CAttributes.cpp index 2696d6bcf..b1509e455 100644 --- a/irr/src/CAttributes.cpp +++ b/irr/src/CAttributes.cpp @@ -12,61 +12,34 @@ namespace irr namespace io { -CAttributes::CAttributes(video::IVideoDriver *driver) : - Driver(driver) +CAttributes::CAttributes() { #ifdef _DEBUG setDebugName("CAttributes"); #endif - - if (Driver) - Driver->grab(); } CAttributes::~CAttributes() { clear(); - - if (Driver) - Driver->drop(); } //! Removes all attributes void CAttributes::clear() { - for (u32 i = 0; i < Attributes.size(); ++i) - Attributes[i]->drop(); - + for (auto it : Attributes) + delete it.second; Attributes.clear(); } -//! Returns attribute index from name, -1 if not found -s32 CAttributes::findAttribute(const c8 *attributeName) const -{ - for (u32 i = 0; i < Attributes.size(); ++i) - if (Attributes[i]->Name == attributeName) - return i; - - return -1; -} - -IAttribute *CAttributes::getAttributeP(const c8 *attributeName) const -{ - for (u32 i = 0; i < Attributes.size(); ++i) - if (Attributes[i]->Name == attributeName) - return Attributes[i]; - - return 0; -} - //! Sets a attribute as boolean value void CAttributes::setAttribute(const c8 *attributeName, bool value) { - IAttribute *att = getAttributeP(attributeName); - if (att) - att->setBool(value); - else { - Attributes.push_back(new CBoolAttribute(attributeName, value)); + auto it = Attributes.find(attributeName); + if (it != Attributes.end()) { + it->second->setBool(value); + } else { + Attributes[attributeName] = new CBoolAttribute(attributeName, value); } } @@ -76,9 +49,9 @@ void CAttributes::setAttribute(const c8 *attributeName, bool value) //! or 0 if attribute is not set. bool CAttributes::getAttributeAsBool(const c8 *attributeName, bool defaultNotFound) const { - const IAttribute *att = getAttributeP(attributeName); - if (att) - return att->getBool(); + auto it = Attributes.find(attributeName); + if (it != Attributes.end()) + return it->second->getBool(); else return defaultNotFound; } @@ -86,11 +59,11 @@ bool CAttributes::getAttributeAsBool(const c8 *attributeName, bool defaultNotFou //! Sets a attribute as integer value void CAttributes::setAttribute(const c8 *attributeName, s32 value) { - IAttribute *att = getAttributeP(attributeName); - if (att) - att->setInt(value); - else { - Attributes.push_back(new CIntAttribute(attributeName, value)); + auto it = Attributes.find(attributeName); + if (it != Attributes.end()) { + it->second->setInt(value); + } else { + Attributes[attributeName] = new CIntAttribute(attributeName, value); } } @@ -100,9 +73,9 @@ void CAttributes::setAttribute(const c8 *attributeName, s32 value) //! or 0 if attribute is not set. s32 CAttributes::getAttributeAsInt(const c8 *attributeName, irr::s32 defaultNotFound) const { - const IAttribute *att = getAttributeP(attributeName); - if (att) - return att->getInt(); + auto it = Attributes.find(attributeName); + if (it != Attributes.end()) + return it->second->getInt(); else return defaultNotFound; } @@ -110,11 +83,12 @@ s32 CAttributes::getAttributeAsInt(const c8 *attributeName, irr::s32 defaultNotF //! Sets a attribute as float value void CAttributes::setAttribute(const c8 *attributeName, f32 value) { - IAttribute *att = getAttributeP(attributeName); - if (att) - att->setFloat(value); - else - Attributes.push_back(new CFloatAttribute(attributeName, value)); + auto it = Attributes.find(attributeName); + if (it != Attributes.end()) { + it->second->setFloat(value); + } else { + Attributes[attributeName] = new CFloatAttribute(attributeName, value); + } } //! Gets a attribute as integer value @@ -123,27 +97,11 @@ void CAttributes::setAttribute(const c8 *attributeName, f32 value) //! or 0 if attribute is not set. f32 CAttributes::getAttributeAsFloat(const c8 *attributeName, irr::f32 defaultNotFound) const { - const IAttribute *att = getAttributeP(attributeName); - if (att) - return att->getFloat(); - - return defaultNotFound; -} - -//! Returns amount of string attributes set in this scene manager. -u32 CAttributes::getAttributeCount() const -{ - return Attributes.size(); -} - -//! Returns string attribute name by index. -//! \param index: Index value, must be between 0 and getStringAttributeCount()-1. -const c8 *CAttributes::getAttributeName(s32 index) const -{ - if ((u32)index >= Attributes.size()) - return 0; - - return Attributes[index]->Name.c_str(); + auto it = Attributes.find(attributeName); + if (it != Attributes.end()) + return it->second->getFloat(); + else + return defaultNotFound; } //! Returns the type of an attribute @@ -151,98 +109,17 @@ E_ATTRIBUTE_TYPE CAttributes::getAttributeType(const c8 *attributeName) const { E_ATTRIBUTE_TYPE ret = EAT_UNKNOWN; - const IAttribute *att = getAttributeP(attributeName); - if (att) - ret = att->getType(); + auto it = Attributes.find(attributeName); + if (it != Attributes.end()) + ret = it->second->getType(); return ret; } -//! Returns attribute type by index. -//! \param index: Index value, must be between 0 and getAttributeCount()-1. -E_ATTRIBUTE_TYPE CAttributes::getAttributeType(s32 index) const -{ - if ((u32)index >= Attributes.size()) - return EAT_UNKNOWN; - - return Attributes[index]->getType(); -} - -//! Gets an attribute as integer value -//! \param index: Index value, must be between 0 and getAttributeCount()-1. -s32 CAttributes::getAttributeAsInt(s32 index) const -{ - if ((u32)index < Attributes.size()) - return Attributes[index]->getInt(); - else - return 0; -} - -//! Gets an attribute as float value -//! \param index: Index value, must be between 0 and getAttributeCount()-1. -f32 CAttributes::getAttributeAsFloat(s32 index) const -{ - if ((u32)index < Attributes.size()) - return Attributes[index]->getFloat(); - else - return 0.f; -} - -//! Gets an attribute as boolean value -//! \param index: Index value, must be between 0 and getAttributeCount()-1. -bool CAttributes::getAttributeAsBool(s32 index) const -{ - bool ret = false; - - if ((u32)index < Attributes.size()) - ret = Attributes[index]->getBool(); - - return ret; -} - -//! Adds an attribute as integer -void CAttributes::addInt(const c8 *attributeName, s32 value) -{ - Attributes.push_back(new CIntAttribute(attributeName, value)); -} - -//! Adds an attribute as float -void CAttributes::addFloat(const c8 *attributeName, f32 value) -{ - Attributes.push_back(new CFloatAttribute(attributeName, value)); -} - -//! Adds an attribute as bool -void CAttributes::addBool(const c8 *attributeName, bool value) -{ - Attributes.push_back(new CBoolAttribute(attributeName, value)); -} - //! Returns if an attribute with a name exists bool CAttributes::existsAttribute(const c8 *attributeName) const { - return getAttributeP(attributeName) != 0; -} - -//! Sets an attribute as boolean value -void CAttributes::setAttribute(s32 index, bool value) -{ - if ((u32)index < Attributes.size()) - Attributes[index]->setBool(value); -} - -//! Sets an attribute as integer value -void CAttributes::setAttribute(s32 index, s32 value) -{ - if ((u32)index < Attributes.size()) - Attributes[index]->setInt(value); -} - -//! Sets a attribute as float value -void CAttributes::setAttribute(s32 index, f32 value) -{ - if ((u32)index < Attributes.size()) - Attributes[index]->setFloat(value); + return Attributes.find(attributeName) != Attributes.end(); } } // end namespace io diff --git a/irr/src/CAttributes.h b/irr/src/CAttributes.h index 0fe2739b6..8ea3ecf19 100644 --- a/irr/src/CAttributes.h +++ b/irr/src/CAttributes.h @@ -4,16 +4,14 @@ #pragma once +#include +#include #include "IAttributes.h" #include "IAttribute.h" namespace irr { -namespace video -{ -class ITexture; -class IVideoDriver; -} + namespace io { @@ -21,30 +19,16 @@ namespace io class CAttributes : public IAttributes { public: - CAttributes(video::IVideoDriver *driver = 0); + CAttributes(); ~CAttributes(); - //! Returns amount of attributes in this collection of attributes. - u32 getAttributeCount() const override; - - //! Returns attribute name by index. - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - const c8 *getAttributeName(s32 index) const override; - //! Returns the type of an attribute //! \param attributeName: Name for the attribute E_ATTRIBUTE_TYPE getAttributeType(const c8 *attributeName) const override; - //! Returns attribute type by index. - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - E_ATTRIBUTE_TYPE getAttributeType(s32 index) const override; - //! Returns if an attribute with a name exists bool existsAttribute(const c8 *attributeName) const override; - //! Returns attribute index from name, -1 if not found - s32 findAttribute(const c8 *attributeName) const override; - //! Removes all attributes void clear() override; @@ -55,7 +39,9 @@ public: */ //! Adds an attribute as integer - void addInt(const c8 *attributeName, s32 value) override; + void addInt(const c8 *attributeName, s32 value) override { + setAttribute(attributeName, value); + } //! Sets an attribute as integer value void setAttribute(const c8 *attributeName, s32 value) override; @@ -66,13 +52,6 @@ public: //! \return Returns value of the attribute previously set by setAttribute() s32 getAttributeAsInt(const c8 *attributeName, irr::s32 defaultNotFound = 0) const override; - //! Gets an attribute as integer value - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - s32 getAttributeAsInt(s32 index) const override; - - //! Sets an attribute as integer value - void setAttribute(s32 index, s32 value) override; - /* Float Attribute @@ -80,7 +59,9 @@ public: */ //! Adds an attribute as float - void addFloat(const c8 *attributeName, f32 value) override; + void addFloat(const c8 *attributeName, f32 value) override { + setAttribute(attributeName, value); + } //! Sets a attribute as float value void setAttribute(const c8 *attributeName, f32 value) override; @@ -91,19 +72,14 @@ public: //! \return Returns value of the attribute previously set by setAttribute() f32 getAttributeAsFloat(const c8 *attributeName, irr::f32 defaultNotFound = 0.f) const override; - //! Gets an attribute as float value - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - f32 getAttributeAsFloat(s32 index) const override; - - //! Sets an attribute as float value - void setAttribute(s32 index, f32 value) override; - /* Bool Attribute */ //! Adds an attribute as bool - void addBool(const c8 *attributeName, bool value) override; + void addBool(const c8 *attributeName, bool value) override { + setAttribute(attributeName, value); + } //! Sets an attribute as boolean value void setAttribute(const c8 *attributeName, bool value) override; @@ -114,19 +90,12 @@ public: //! \return Returns value of the attribute previously set by setAttribute() bool getAttributeAsBool(const c8 *attributeName, bool defaultNotFound = false) const override; - //! Gets an attribute as boolean value - //! \param index: Index value, must be between 0 and getAttributeCount()-1. - bool getAttributeAsBool(s32 index) const override; - - //! Sets an attribute as boolean value - void setAttribute(s32 index, bool value) override; - protected: - core::array Attributes; + typedef std::basic_string string; - IAttribute *getAttributeP(const c8 *attributeName) const; - - video::IVideoDriver *Driver; + // specify a comparator so we can directly look up in the map with const c8* + // (works since C++14) + std::map> Attributes; }; } // end namespace io diff --git a/irr/src/CB3DMeshFileLoader.cpp b/irr/src/CB3DMeshFileLoader.cpp index 6ee24af0e..008169bd7 100644 --- a/irr/src/CB3DMeshFileLoader.cpp +++ b/irr/src/CB3DMeshFileLoader.cpp @@ -433,7 +433,7 @@ bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 m } const s32 memoryNeeded = B3dStack.getLast().length / sizeof(s32); - meshBuffer->Indices.reallocate(memoryNeeded + meshBuffer->Indices.size() + 1); + meshBuffer->Indices.reserve(memoryNeeded + meshBuffer->Indices.size() + 1); while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats { diff --git a/irr/src/CBillboardSceneNode.cpp b/irr/src/CBillboardSceneNode.cpp index ddb9d465a..7be139f96 100644 --- a/irr/src/CBillboardSceneNode.cpp +++ b/irr/src/CBillboardSceneNode.cpp @@ -26,8 +26,8 @@ CBillboardSceneNode::CBillboardSceneNode(ISceneNode *parent, ISceneManager *mgr, setSize(size); - Buffer->Vertices.set_used(4); - Buffer->Indices.set_used(6); + Buffer->Vertices.resize(4); + Buffer->Indices.resize(6); Buffer->Indices[0] = 0; Buffer->Indices[1] = 2; @@ -114,7 +114,7 @@ void CBillboardSceneNode::updateMesh(const irr::scene::ICameraSceneNode *camera) view *= -1.0f; - core::array &vertices = Buffer->Vertices; + auto *vertices = Buffer->Vertices.data(); for (s32 i = 0; i < 4; ++i) vertices[i].Normal = view; diff --git a/irr/src/CFileSystem.cpp b/irr/src/CFileSystem.cpp index bffb703dd..386bb389c 100644 --- a/irr/src/CFileSystem.cpp +++ b/irr/src/CFileSystem.cpp @@ -58,14 +58,8 @@ CFileSystem::CFileSystem() //! destructor CFileSystem::~CFileSystem() { - u32 i; - - for (i = 0; i < FileArchives.size(); ++i) { - FileArchives[i]->drop(); - } - - for (i = 0; i < ArchiveLoader.size(); ++i) { - ArchiveLoader[i]->drop(); + for (auto *it : ArchiveLoader) { + it->drop(); } } @@ -75,15 +69,6 @@ IReadFile *CFileSystem::createAndOpenFile(const io::path &filename) if (filename.empty()) return 0; - IReadFile *file = 0; - u32 i; - - for (i = 0; i < FileArchives.size(); ++i) { - file = FileArchives[i]->createAndOpenFile(filename); - if (file) - return file; - } - // Create the file using an absolute path so that it matches // the scheme used by CNullDriver::getTexture(). return CReadFile::createReadFile(getAbsolutePath(filename)); @@ -150,241 +135,6 @@ IArchiveLoader *CFileSystem::getArchiveLoader(u32 index) const return 0; } -//! move the hirarchy of the filesystem. moves sourceIndex relative up or down -bool CFileSystem::moveFileArchive(u32 sourceIndex, s32 relative) -{ - bool r = false; - const s32 dest = (s32)sourceIndex + relative; - const s32 dir = relative < 0 ? -1 : 1; - const s32 sourceEnd = ((s32)FileArchives.size()) - 1; - IFileArchive *t; - - for (s32 s = (s32)sourceIndex; s != dest; s += dir) { - if (s < 0 || s > sourceEnd || s + dir < 0 || s + dir > sourceEnd) - continue; - - t = FileArchives[s + dir]; - FileArchives[s + dir] = FileArchives[s]; - FileArchives[s] = t; - r = true; - } - return r; -} - -//! Adds an archive to the file system. -bool CFileSystem::addFileArchive(const io::path &filename, bool ignoreCase, - bool ignorePaths, E_FILE_ARCHIVE_TYPE archiveType, - const core::stringc &password, - IFileArchive **retArchive) -{ - IFileArchive *archive = 0; - bool ret = false; - - // see if archive is already added - - s32 i; - - // do we know what type it should be? - if (archiveType == EFAT_UNKNOWN) { - // try to load archive based on file name - for (i = ArchiveLoader.size() - 1; i >= 0; --i) { - if (ArchiveLoader[i]->isALoadableFileFormat(filename)) { - archive = ArchiveLoader[i]->createArchive(filename, ignoreCase, ignorePaths); - if (archive) - break; - } - } - - // try to load archive based on content - if (!archive) { - io::IReadFile *file = createAndOpenFile(filename); - if (file) { - for (i = ArchiveLoader.size() - 1; i >= 0; --i) { - file->seek(0); - if (ArchiveLoader[i]->isALoadableFileFormat(file)) { - file->seek(0); - archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths); - if (archive) - break; - } - } - file->drop(); - } - } - } else { - // try to open archive based on archive loader type - - io::IReadFile *file = 0; - - for (i = ArchiveLoader.size() - 1; i >= 0; --i) { - if (ArchiveLoader[i]->isALoadableFileFormat(archiveType)) { - // attempt to open file - if (!file) - file = createAndOpenFile(filename); - - // is the file open? - if (file) { - // attempt to open archive - file->seek(0); - if (ArchiveLoader[i]->isALoadableFileFormat(file)) { - file->seek(0); - archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths); - if (archive) - break; - } - } else { - // couldn't open file - break; - } - } - } - - // if open, close the file - if (file) - file->drop(); - } - - if (archive) { - FileArchives.push_back(archive); - if (password.size()) - archive->Password = password; - if (retArchive) - *retArchive = archive; - ret = true; - } else { - os::Printer::log("Could not create archive for", filename, ELL_ERROR); - } - - return ret; -} - -bool CFileSystem::addFileArchive(IReadFile *file, bool ignoreCase, - bool ignorePaths, E_FILE_ARCHIVE_TYPE archiveType, - const core::stringc &password, IFileArchive **retArchive) -{ - if (!file) - return false; - - if (file) { - IFileArchive *archive = 0; - s32 i; - - if (archiveType == EFAT_UNKNOWN) { - // try to load archive based on file name - for (i = ArchiveLoader.size() - 1; i >= 0; --i) { - if (ArchiveLoader[i]->isALoadableFileFormat(file->getFileName())) { - archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths); - if (archive) - break; - } - } - - // try to load archive based on content - if (!archive) { - for (i = ArchiveLoader.size() - 1; i >= 0; --i) { - file->seek(0); - if (ArchiveLoader[i]->isALoadableFileFormat(file)) { - file->seek(0); - archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths); - if (archive) - break; - } - } - } - } else { - // try to open archive based on archive loader type - for (i = ArchiveLoader.size() - 1; i >= 0; --i) { - if (ArchiveLoader[i]->isALoadableFileFormat(archiveType)) { - // attempt to open archive - file->seek(0); - if (ArchiveLoader[i]->isALoadableFileFormat(file)) { - file->seek(0); - archive = ArchiveLoader[i]->createArchive(file, ignoreCase, ignorePaths); - if (archive) - break; - } - } - } - } - - if (archive) { - FileArchives.push_back(archive); - if (password.size()) - archive->Password = password; - if (retArchive) - *retArchive = archive; - return true; - } else { - os::Printer::log("Could not create archive for", file->getFileName(), ELL_ERROR); - } - } - - return false; -} - -//! Adds an archive to the file system. -bool CFileSystem::addFileArchive(IFileArchive *archive) -{ - if (archive) { - for (u32 i = 0; i < FileArchives.size(); ++i) { - if (archive == FileArchives[i]) { - return false; - } - } - FileArchives.push_back(archive); - archive->grab(); - - return true; - } - - return false; -} - -//! removes an archive from the file system. -bool CFileSystem::removeFileArchive(u32 index) -{ - bool ret = false; - if (index < FileArchives.size()) { - FileArchives[index]->drop(); - FileArchives.erase(index); - ret = true; - } - return ret; -} - -//! removes an archive from the file system. -bool CFileSystem::removeFileArchive(const io::path &filename) -{ - const path absPath = getAbsolutePath(filename); - for (u32 i = 0; i < FileArchives.size(); ++i) { - if (absPath == FileArchives[i]->getFileList()->getPath()) - return removeFileArchive(i); - } - return false; -} - -//! Removes an archive from the file system. -bool CFileSystem::removeFileArchive(const IFileArchive *archive) -{ - for (u32 i = 0; i < FileArchives.size(); ++i) { - if (archive == FileArchives[i]) { - return removeFileArchive(i); - } - } - return false; -} - -//! gets an archive -u32 CFileSystem::getFileArchiveCount() const -{ - return FileArchives.size(); -} - -IFileArchive *CFileSystem::getFileArchive(u32 index) -{ - return index < getFileArchiveCount() ? FileArchives[index] : 0; -} - //! Returns the string of the current working directory const io::path &CFileSystem::getWorkingDirectory() { @@ -711,17 +461,6 @@ IFileList *CFileSystem::createFileList() //! parent r->addItem(Path + _IRR_TEXT(".."), 0, 0, true, 0); - - //! merge archives - for (u32 i = 0; i < FileArchives.size(); ++i) { - const IFileList *merge = FileArchives[i]->getFileList(); - - for (u32 j = 0; j < merge->getFileCount(); ++j) { - if (core::isInSameDirectory(Path, merge->getFullFileName(j)) == 0) { - r->addItem(merge->getFullFileName(j), merge->getFileOffset(j), merge->getFileSize(j), merge->isDirectory(j), 0); - } - } - } } if (r) @@ -738,10 +477,6 @@ IFileList *CFileSystem::createEmptyFileList(const io::path &path, bool ignoreCas //! determines if a file exists and would be able to be opened. bool CFileSystem::existFile(const io::path &filename) const { - for (u32 i = 0; i < FileArchives.size(); ++i) - if (FileArchives[i]->getFileList()->findFile(filename) != -1) - return true; - #if defined(_MSC_VER) return (_access(filename.c_str(), 0) != -1); #elif defined(F_OK) diff --git a/irr/src/CFileSystem.h b/irr/src/CFileSystem.h index 18bc7ff3f..208a1ac41 100644 --- a/irr/src/CFileSystem.h +++ b/irr/src/CFileSystem.h @@ -4,8 +4,8 @@ #pragma once +#include #include "IFileSystem.h" -#include "irrArray.h" namespace irr { @@ -41,25 +41,6 @@ public: //! Opens a file for write access. IWriteFile *createAndWriteFile(const io::path &filename, bool append = false) override; - //! Adds an archive to the file system. - virtual bool addFileArchive(const io::path &filename, - bool ignoreCase = true, bool ignorePaths = true, - E_FILE_ARCHIVE_TYPE archiveType = EFAT_UNKNOWN, - const core::stringc &password = "", - IFileArchive **retArchive = 0) override; - - //! Adds an archive to the file system. - virtual bool addFileArchive(IReadFile *file, bool ignoreCase = true, - bool ignorePaths = true, - E_FILE_ARCHIVE_TYPE archiveType = EFAT_UNKNOWN, - const core::stringc &password = "", - IFileArchive **retArchive = 0) override; - - //! Adds an archive to the file system. - bool addFileArchive(IFileArchive *archive) override; - - //! move the hirarchy of the filesystem. moves sourceIndex relative up or down - bool moveFileArchive(u32 sourceIndex, s32 relative) override; //! Adds an external archive loader to the engine. void addArchiveLoader(IArchiveLoader *loader) override; @@ -70,21 +51,6 @@ public: //! Gets the archive loader by index. IArchiveLoader *getArchiveLoader(u32 index) const override; - //! gets the file archive count - u32 getFileArchiveCount() const override; - - //! gets an archive - IFileArchive *getFileArchive(u32 index) override; - - //! removes an archive from the file system. - bool removeFileArchive(u32 index) override; - - //! removes an archive from the file system. - bool removeFileArchive(const io::path &filename) override; - - //! Removes an archive from the file system. - bool removeFileArchive(const IFileArchive *archive) override; - //! Returns the string of the current working directory const io::path &getWorkingDirectory() override; @@ -129,9 +95,7 @@ private: //! WorkingDirectory for Native and Virtual filesystems io::path WorkingDirectory[2]; //! currently attached ArchiveLoaders - core::array ArchiveLoader; - //! currently attached Archives - core::array FileArchives; + std::vector ArchiveLoader; }; } // end namespace irr diff --git a/irr/src/CMeshManipulator.cpp b/irr/src/CMeshManipulator.cpp index 3309fea3f..db6c39812 100644 --- a/irr/src/CMeshManipulator.cpp +++ b/irr/src/CMeshManipulator.cpp @@ -132,48 +132,30 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const case video::EVT_STANDARD: { SMeshBuffer *buffer = new SMeshBuffer(); buffer->Material = mb->getMaterial(); - const u32 vcount = mb->getVertexCount(); - buffer->Vertices.reallocate(vcount); - video::S3DVertex *vertices = (video::S3DVertex *)mb->getVertices(); - for (u32 i = 0; i < vcount; ++i) - buffer->Vertices.push_back(vertices[i]); - const u32 icount = mb->getIndexCount(); - buffer->Indices.reallocate(icount); - const u16 *indices = mb->getIndices(); - for (u32 i = 0; i < icount; ++i) - buffer->Indices.push_back(indices[i]); + auto *vt = static_cast(mb->getVertices()); + buffer->Vertices.insert(buffer->Vertices.end(), vt, vt + mb->getVertexCount()); + auto *indices = mb->getIndices(); + buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); buffer->drop(); } break; case video::EVT_2TCOORDS: { SMeshBufferLightMap *buffer = new SMeshBufferLightMap(); buffer->Material = mb->getMaterial(); - const u32 vcount = mb->getVertexCount(); - buffer->Vertices.reallocate(vcount); - video::S3DVertex2TCoords *vertices = (video::S3DVertex2TCoords *)mb->getVertices(); - for (u32 i = 0; i < vcount; ++i) - buffer->Vertices.push_back(vertices[i]); - const u32 icount = mb->getIndexCount(); - buffer->Indices.reallocate(icount); - const u16 *indices = mb->getIndices(); - for (u32 i = 0; i < icount; ++i) - buffer->Indices.push_back(indices[i]); + auto *vt = static_cast(mb->getVertices()); + buffer->Vertices.insert(buffer->Vertices.end(), vt, vt + mb->getVertexCount()); + auto *indices = mb->getIndices(); + buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); buffer->drop(); } break; case video::EVT_TANGENTS: { SMeshBufferTangents *buffer = new SMeshBufferTangents(); buffer->Material = mb->getMaterial(); - const u32 vcount = mb->getVertexCount(); - buffer->Vertices.reallocate(vcount); - video::S3DVertexTangents *vertices = (video::S3DVertexTangents *)mb->getVertices(); - for (u32 i = 0; i < vcount; ++i) - buffer->Vertices.push_back(vertices[i]); - const u32 icount = mb->getIndexCount(); - buffer->Indices.reallocate(icount); - const u16 *indices = mb->getIndices(); - for (u32 i = 0; i < icount; ++i) - buffer->Indices.push_back(indices[i]); + auto *vt = static_cast(mb->getVertices()); + buffer->Vertices.insert(buffer->Vertices.end(), vt, vt + mb->getVertexCount()); + auto *indices = mb->getIndices(); + buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); buffer->drop(); } break; diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index 92450e5d7..c1cecbe8c 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -64,7 +64,6 @@ CNullDriver::CNullDriver(io::IFileSystem *io, const core::dimension2d &scre DriverAttributes->addInt("MaxTextures", MATERIAL_MAX_TEXTURES); DriverAttributes->addInt("MaxSupportedTextures", MATERIAL_MAX_TEXTURES); DriverAttributes->addInt("MaxAnisotropy", 1); - // DriverAttributes->addInt("MaxUserClipPlanes", 0); // DriverAttributes->addInt("MaxAuxBuffers", 0); DriverAttributes->addInt("MaxMultipleRenderTargets", 1); DriverAttributes->addInt("MaxIndices", -1); @@ -361,7 +360,7 @@ ITexture *CNullDriver::addTextureCubemap(const io::path &name, IImage *imagePosX ITexture *t = 0; - core::array imageArray(6); + std::vector imageArray; imageArray.push_back(imagePosX); imageArray.push_back(imageNegX); imageArray.push_back(imagePosY); @@ -391,7 +390,7 @@ ITexture *CNullDriver::addTextureCubemap(const irr::u32 sideLen, const io::path return 0; } - core::array imageArray(6); + std::vector imageArray; for (int i = 0; i < 6; ++i) imageArray.push_back(new CImage(format, core::dimension2du(sideLen, sideLen))); @@ -548,7 +547,7 @@ ITexture *CNullDriver::createDeviceDependentTexture(const io::path &name, IImage return dummy; } -ITexture *CNullDriver::createDeviceDependentTextureCubemap(const io::path &name, const core::array &image) +ITexture *CNullDriver::createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) { return new SDummyTexture(name, ETT_CUBEMAP); } @@ -913,17 +912,17 @@ bool CNullDriver::checkImage(IImage *image) const return true; } -bool CNullDriver::checkImage(const core::array &image) const +bool CNullDriver::checkImage(const std::vector &image) const { - if (!image.size()) + if (image.empty()) return false; ECOLOR_FORMAT lastFormat = image[0]->getColorFormat(); - core::dimension2d lastSize = image[0]->getDimension(); + auto lastSize = image[0]->getDimension(); - for (u32 i = 0; i < image.size(); ++i) { + for (size_t i = 0; i < image.size(); ++i) { ECOLOR_FORMAT format = image[i]->getColorFormat(); - core::dimension2d size = image[i]->getDimension(); + auto size = image[i]->getDimension(); if (!checkImage(image[i])) return false; @@ -1699,22 +1698,6 @@ IVideoDriver *createNullDriver(io::IFileSystem *io, const core::dimension2d return nullDriver; } -//! Set/unset a clipping plane. -//! There are at least 6 clipping planes available for the user to set at will. -//! \param index: The plane index. Must be between 0 and MaxUserClipPlanes. -//! \param plane: The plane itself. -//! \param enable: If true, enable the clipping plane else disable it. -bool CNullDriver::setClipPlane(u32 index, const core::plane3df &plane, bool enable) -{ - return false; -} - -//! Enable/disable a clipping plane. -void CNullDriver::enableClipPlane(u32 index, bool enable) -{ - // not necessary -} - void CNullDriver::setMinHardwareBufferVertexCount(u32 count) { MinVertexCountForVBO = count; diff --git a/irr/src/CNullDriver.h b/irr/src/CNullDriver.h index d47212c0f..30911b7a6 100644 --- a/irr/src/CNullDriver.h +++ b/irr/src/CNullDriver.h @@ -494,19 +494,6 @@ public: //! looks if the image is already loaded video::ITexture *findTexture(const io::path &filename) override; - //! Set/unset a clipping plane. - //! There are at least 6 clipping planes available for the user to set at will. - //! \param index: The plane index. Must be between 0 and MaxUserClipPlanes. - //! \param plane: The plane itself. - //! \param enable: If true, enable the clipping plane else disable it. - bool setClipPlane(u32 index, const core::plane3df &plane, bool enable = false) override; - - //! Enable/disable a clipping plane. - //! There are at least 6 clipping planes available for the user to set at will. - //! \param index: The plane index. Must be between 0 and MaxUserClipPlanes. - //! \param enable: If true, enable the clipping plane else disable it. - void enableClipPlane(u32 index, bool enable) override; - //! Returns the graphics card vendor name. core::stringc getVendorInfo() override { return "Not available on this driver."; } @@ -565,14 +552,14 @@ protected: virtual ITexture *createDeviceDependentTexture(const io::path &name, IImage *image); - virtual ITexture *createDeviceDependentTextureCubemap(const io::path &name, const core::array &image); + virtual ITexture *createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image); //! checks triangle count and print warning if wrong bool checkPrimitiveCount(u32 prmcnt) const; bool checkImage(IImage *image) const; - bool checkImage(const core::array &image) const; + bool checkImage(const std::vector &image) const; // adds a material renderer and drops it afterwards. To be used for internal creation s32 addAndDropMaterialRenderer(IMaterialRenderer *m); diff --git a/irr/src/COGLESDriver.cpp b/irr/src/COGLESDriver.cpp index 524aedd50..d5feda58e 100644 --- a/irr/src/COGLESDriver.cpp +++ b/irr/src/COGLESDriver.cpp @@ -103,14 +103,6 @@ bool COGLES1Driver::genericDriverInit(const core::dimension2d &screenSize, glPixelStorei(GL_PACK_ALIGNMENT, 1); - UserClipPlane.reallocate(MaxUserClipPlanes); - UserClipPlaneEnabled.resize(MaxUserClipPlanes); - - for (s32 i = 0; i < MaxUserClipPlanes; ++i) { - UserClipPlane.push_back(core::plane3df()); - UserClipPlaneEnabled[i] = false; - } - for (s32 i = 0; i < ETS_COUNT; ++i) setTransform(static_cast(i), core::IdentityMatrix); @@ -195,10 +187,6 @@ void COGLES1Driver::setTransform(E_TRANSFORMATION_STATE state, const core::matri // OGLES1 only has a model matrix, view and world is not existent. so lets fake these two. glMatrixMode(GL_MODELVIEW); glLoadMatrixf((Matrices[ETS_VIEW] * Matrices[ETS_WORLD]).pointer()); - // we have to update the clip planes to the latest view matrix - for (u32 i = 0; i < MaxUserClipPlanes; ++i) - if (UserClipPlaneEnabled[i]) - uploadClipPlane(i); } break; case ETS_PROJECTION: { GLfloat glmat[16]; @@ -1149,15 +1137,14 @@ inline void COGLES1Driver::getGLTextureMatrix(GLfloat *o, const core::matrix4 &m ITexture *COGLES1Driver::createDeviceDependentTexture(const io::path &name, IImage *image) { - core::array imageArray(1); - imageArray.push_back(image); + std::vector tmp { image }; - COGLES1Texture *texture = new COGLES1Texture(name, imageArray, ETT_2D, this); + COGLES1Texture *texture = new COGLES1Texture(name, tmp, ETT_2D, this); return texture; } -ITexture *COGLES1Driver::createDeviceDependentTextureCubemap(const io::path &name, const core::array &image) +ITexture *COGLES1Driver::createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) { COGLES1Texture *texture = new COGLES1Texture(name, image, ETT_CUBEMAP, this); @@ -2158,44 +2145,6 @@ void COGLES1Driver::removeTexture(ITexture *texture) CNullDriver::removeTexture(texture); } -//! Set/unset a clipping plane. -bool COGLES1Driver::setClipPlane(u32 index, const core::plane3df &plane, bool enable) -{ - if (index >= MaxUserClipPlanes) - return false; - - UserClipPlane[index] = plane; - enableClipPlane(index, enable); - return true; -} - -void COGLES1Driver::uploadClipPlane(u32 index) -{ - // opengl needs an array of doubles for the plane equation - float clip_plane[4]; - clip_plane[0] = UserClipPlane[index].Normal.X; - clip_plane[1] = UserClipPlane[index].Normal.Y; - clip_plane[2] = UserClipPlane[index].Normal.Z; - clip_plane[3] = UserClipPlane[index].D; - glClipPlanef(GL_CLIP_PLANE0 + index, clip_plane); -} - -//! Enable/disable a clipping plane. -void COGLES1Driver::enableClipPlane(u32 index, bool enable) -{ - if (index >= MaxUserClipPlanes) - return; - if (enable) { - if (!UserClipPlaneEnabled[index]) { - uploadClipPlane(index); - glEnable(GL_CLIP_PLANE0 + index); - } - } else - glDisable(GL_CLIP_PLANE0 + index); - - UserClipPlaneEnabled[index] = enable; -} - core::dimension2du COGLES1Driver::getMaxTextureSize() const { return core::dimension2du(MaxTextureSize, MaxTextureSize); diff --git a/irr/src/COGLESDriver.h b/irr/src/COGLESDriver.h index 29e0d94f1..8b9152cea 100644 --- a/irr/src/COGLESDriver.h +++ b/irr/src/COGLESDriver.h @@ -215,12 +215,6 @@ public: //! checks if an OpenGL error has happened and prints it (+ some internal code which is usually the line number) bool testGLError(int code = 0); - //! Set/unset a clipping plane. - bool setClipPlane(u32 index, const core::plane3df &plane, bool enable = false) override; - - //! Enable/disable a clipping plane. - void enableClipPlane(u32 index, bool enable) override; - //! Returns the graphics card vendor name. core::stringc getVendorInfo() override { @@ -250,14 +244,12 @@ public: COGLES1CacheHandler *getCacheHandler() const; private: - void uploadClipPlane(u32 index); - //! inits the opengl-es driver bool genericDriverInit(const core::dimension2d &screenSize, bool stencilBuffer); ITexture *createDeviceDependentTexture(const io::path &name, IImage *image) override; - ITexture *createDeviceDependentTextureCubemap(const io::path &name, const core::array &image) override; + ITexture *createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) override; //! creates a transposed matrix in supplied GLfloat array to pass to OGLES1 inline void getGLMatrix(GLfloat gl_matrix[16], const core::matrix4 &m); @@ -306,8 +298,6 @@ private: u8 AntiAlias; SMaterial Material, LastMaterial; - core::array UserClipPlane; - std::vector UserClipPlaneEnabled; core::stringc VendorName; diff --git a/irr/src/COGLESExtensionHandler.cpp b/irr/src/COGLESExtensionHandler.cpp index 82f8e9261..866a984d8 100644 --- a/irr/src/COGLESExtensionHandler.cpp +++ b/irr/src/COGLESExtensionHandler.cpp @@ -25,7 +25,7 @@ namespace video COGLES1ExtensionHandler::COGLES1ExtensionHandler() : COGLESCoreExtensionHandler(), - MaxUserClipPlanes(0), MaxLights(0), pGlBlendEquationOES(0), pGlBlendFuncSeparateOES(0), + MaxLights(0), pGlBlendEquationOES(0), pGlBlendFuncSeparateOES(0), pGlBindFramebufferOES(0), pGlDeleteFramebuffersOES(0), pGlGenFramebuffersOES(0), pGlCheckFramebufferStatusOES(0), pGlFramebufferTexture2DOES(0), pGlGenerateMipmapOES(0) @@ -45,11 +45,6 @@ void COGLES1ExtensionHandler::initExtensions() GLint val = 0; - if (Version > 100 || FeatureAvailable[IRR_GL_IMG_user_clip_plane]) { - glGetIntegerv(GL_MAX_CLIP_PLANES, &val); - MaxUserClipPlanes = static_cast(val); - } - glGetIntegerv(GL_MAX_LIGHTS, &val); MaxLights = static_cast(val); diff --git a/irr/src/COGLESExtensionHandler.h b/irr/src/COGLESExtensionHandler.h index d9e1ac4bc..a316afaad 100644 --- a/irr/src/COGLESExtensionHandler.h +++ b/irr/src/COGLESExtensionHandler.h @@ -171,7 +171,6 @@ public: } protected: - u8 MaxUserClipPlanes; u8 MaxLights; PFNGLBLENDEQUATIONOESPROC pGlBlendEquationOES; diff --git a/irr/src/COpenGLCoreTexture.h b/irr/src/COpenGLCoreTexture.h index 79a855cc1..218eabece 100644 --- a/irr/src/COpenGLCoreTexture.h +++ b/irr/src/COpenGLCoreTexture.h @@ -4,7 +4,7 @@ #pragma once -#include "irrArray.h" +#include #include "SMaterialLayer.h" #include "ITexture.h" #include "EDriverFeatures.h" @@ -43,19 +43,19 @@ public: bool IsCached; }; - COpenGLCoreTexture(const io::path &name, const core::array &images, E_TEXTURE_TYPE type, TOpenGLDriver *driver) : + 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), KeepImage(false), MipLevelStored(0), LegacyAutoGenerateMipMaps(false) { - _IRR_DEBUG_BREAK_IF(images.size() == 0) + _IRR_DEBUG_BREAK_IF(srcImages.empty()) DriverType = Driver->getDriverType(); TextureType = TextureTypeIrrToGL(Type); HasMipMaps = Driver->getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); KeepImage = Driver->getTextureCreationFlag(ETCF_ALLOW_MEMORY_COPY); - getImageValues(images[0]); + getImageValues(srcImages[0]); if (!InternalFormat) return; @@ -71,22 +71,22 @@ public: os::Printer::log(lbuf, ELL_DEBUG); #endif - const core::array *tmpImages = &images; + const auto *tmpImages = &srcImages; if (KeepImage || OriginalSize != Size || OriginalColorFormat != ColorFormat) { - Images.set_used(images.size()); + Images.resize(srcImages.size()); - for (u32 i = 0; i < images.size(); ++i) { + for (size_t i = 0; i < srcImages.size(); ++i) { Images[i] = Driver->createImage(ColorFormat, Size); - if (images[i]->getDimension() == Size) - images[i]->copyTo(Images[i]); + if (srcImages[i]->getDimension() == Size) + srcImages[i]->copyTo(Images[i]); else - images[i]->copyToScaling(Images[i]); + srcImages[i]->copyToScaling(Images[i]); - if (images[i]->getMipMapsData()) { + if (srcImages[i]->getMipMapsData()) { if (OriginalSize == Size && OriginalColorFormat == ColorFormat) { - Images[i]->setMipMapsData(images[i]->getMipMapsData(), false); + Images[i]->setMipMapsData(srcImages[i]->getMipMapsData(), false); } else { // TODO: handle at least mipmap with changing color format os::Printer::log("COpenGLCoreTexture: Can't handle format changes for mipmap data. Mipmap data dropped", ELL_WARNING); @@ -118,19 +118,19 @@ public: TEST_GL_ERROR(Driver); - for (u32 i = 0; i < (*tmpImages).size(); ++i) + for (size_t i = 0; i < tmpImages->size(); ++i) uploadTexture(true, i, 0, (*tmpImages)[i]->getData()); if (HasMipMaps && !LegacyAutoGenerateMipMaps) { // Create mipmaps (either from image mipmaps or generate them) - for (u32 i = 0; i < (*tmpImages).size(); ++i) { + for (size_t i = 0; i < tmpImages->size(); ++i) { void *mipmapsData = (*tmpImages)[i]->getMipMapsData(); regenerateMipMapLevels(mipmapsData, i); } } if (!KeepImage) { - for (u32 i = 0; i < Images.size(); ++i) + for (size_t i = 0; i < Images.size(); ++i) Images[i]->drop(); Images.clear(); @@ -227,8 +227,8 @@ public: if (LockImage) LockImage->drop(); - for (u32 i = 0; i < Images.size(); ++i) - Images[i]->drop(); + for (auto *image : Images) + image->drop(); } void *lock(E_TEXTURE_LOCK_MODE mode = ETLM_READ_WRITE, u32 mipmapLevel = 0, u32 layer = 0, E_TEXTURE_LOCK_FLAGS lockFlags = ETLF_FLIP_Y_UP_RTT) override @@ -621,7 +621,7 @@ protected: u32 LockLayer; bool KeepImage; - core::array Images; + std::vector Images; u8 MipLevelStored; bool LegacyAutoGenerateMipMaps; diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index 1ad66aef9..1c6342090 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -122,7 +122,6 @@ bool COpenGLDriver::genericDriverInit() DriverAttributes->setAttribute("MaxSupportedTextures", (s32)Feature.MaxTextureUnits); DriverAttributes->setAttribute("MaxLights", MaxLights); DriverAttributes->setAttribute("MaxAnisotropy", MaxAnisotropy); - DriverAttributes->setAttribute("MaxUserClipPlanes", MaxUserClipPlanes); DriverAttributes->setAttribute("MaxAuxBuffers", MaxAuxBuffers); DriverAttributes->setAttribute("MaxMultipleRenderTargets", (s32)Feature.MultipleRenderTarget); DriverAttributes->setAttribute("MaxIndices", (s32)MaxIndices); @@ -135,10 +134,6 @@ bool COpenGLDriver::genericDriverInit() glPixelStorei(GL_PACK_ALIGNMENT, 1); - UserClipPlanes.reallocate(MaxUserClipPlanes); - for (i = 0; i < MaxUserClipPlanes; ++i) - UserClipPlanes.push_back(SUserClipPlane()); - for (i = 0; i < ETS_COUNT; ++i) setTransform(static_cast(i), core::IdentityMatrix); @@ -244,11 +239,6 @@ void COpenGLDriver::setTransform(E_TRANSFORMATION_STATE state, const core::matri // first load the viewing transformation for user clip planes glLoadMatrixf((Matrices[ETS_VIEW]).pointer()); - // we have to update the clip planes to the latest view matrix - for (u32 i = 0; i < MaxUserClipPlanes; ++i) - if (UserClipPlanes[i].Enabled) - uploadClipPlane(i); - // now the real model-view matrix glMultMatrixf(Matrices[ETS_WORLD].pointer()); } break; @@ -1597,15 +1587,14 @@ inline void COpenGLDriver::getGLTextureMatrix(GLfloat *o, const core::matrix4 &m ITexture *COpenGLDriver::createDeviceDependentTexture(const io::path &name, IImage *image) { - core::array imageArray(1); - imageArray.push_back(image); + std::vector tmp { image }; - COpenGLTexture *texture = new COpenGLTexture(name, imageArray, ETT_2D, this); + COpenGLTexture *texture = new COpenGLTexture(name, tmp, ETT_2D, this); return texture; } -ITexture *COpenGLDriver::createDeviceDependentTextureCubemap(const io::path &name, const core::array &image) +ITexture *COpenGLDriver::createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) { COpenGLTexture *texture = new COpenGLTexture(name, image, ETT_CUBEMAP, this); @@ -3062,44 +3051,6 @@ IImage *COpenGLDriver::createScreenShot(video::ECOLOR_FORMAT format, video::E_RE return newImage; } -//! Set/unset a clipping plane. -bool COpenGLDriver::setClipPlane(u32 index, const core::plane3df &plane, bool enable) -{ - if (index >= MaxUserClipPlanes) - return false; - - UserClipPlanes[index].Plane = plane; - enableClipPlane(index, enable); - return true; -} - -void COpenGLDriver::uploadClipPlane(u32 index) -{ - // opengl needs an array of doubles for the plane equation - GLdouble clip_plane[4]; - clip_plane[0] = UserClipPlanes[index].Plane.Normal.X; - clip_plane[1] = UserClipPlanes[index].Plane.Normal.Y; - clip_plane[2] = UserClipPlanes[index].Plane.Normal.Z; - clip_plane[3] = UserClipPlanes[index].Plane.D; - glClipPlane(GL_CLIP_PLANE0 + index, clip_plane); -} - -//! Enable/disable a clipping plane. -void COpenGLDriver::enableClipPlane(u32 index, bool enable) -{ - if (index >= MaxUserClipPlanes) - return; - if (enable) { - if (!UserClipPlanes[index].Enabled) { - uploadClipPlane(index); - glEnable(GL_CLIP_PLANE0 + index); - } - } else - glDisable(GL_CLIP_PLANE0 + index); - - UserClipPlanes[index].Enabled = enable; -} - core::dimension2du COpenGLDriver::getMaxTextureSize() const { return core::dimension2du(MaxTextureSize, MaxTextureSize); diff --git a/irr/src/COpenGLDriver.h b/irr/src/COpenGLDriver.h index 6fe8ac37d..3c0a718fc 100644 --- a/irr/src/COpenGLDriver.h +++ b/irr/src/COpenGLDriver.h @@ -285,19 +285,6 @@ public: //! for performance reasons only available in debug mode bool testGLError(int code = 0); - //! Set/unset a clipping plane. - //! There are at least 6 clipping planes available for the user to set at will. - //! \param index: The plane index. Must be between 0 and MaxUserClipPlanes. - //! \param plane: The plane itself. - //! \param enable: If true, enable the clipping plane else disable it. - bool setClipPlane(u32 index, const core::plane3df &plane, bool enable = false) override; - - //! Enable/disable a clipping plane. - //! There are at least 6 clipping planes available for the user to set at will. - //! \param index: The plane index. Must be between 0 and MaxUserClipPlanes. - //! \param enable: If true, enable the clipping plane else disable it. - void enableClipPlane(u32 index, bool enable) override; - //! Enable the 2d override material void enableMaterial2D(bool enable = true) override; @@ -343,14 +330,12 @@ private: bool updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer); bool updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer); - void uploadClipPlane(u32 index); - //! inits the parts of the open gl driver used on all platforms bool genericDriverInit(); ITexture *createDeviceDependentTexture(const io::path &name, IImage *image) override; - ITexture *createDeviceDependentTextureCubemap(const io::path &name, const core::array &image) override; + ITexture *createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) override; //! creates a transposed matrix in supplied GLfloat array to pass to OpenGL inline void getGLMatrix(GLfloat gl_matrix[16], const core::matrix4 &m); @@ -404,15 +389,6 @@ private: SMaterial Material, LastMaterial; - struct SUserClipPlane - { - SUserClipPlane() : - Enabled(false) {} - core::plane3df Plane; - bool Enabled; - }; - core::array UserClipPlanes; - core::stringc VendorName; core::matrix4 TextureFlipMatrix; diff --git a/irr/src/COpenGLExtensionHandler.cpp b/irr/src/COpenGLExtensionHandler.cpp index d5aade126..6127d6e46 100644 --- a/irr/src/COpenGLExtensionHandler.cpp +++ b/irr/src/COpenGLExtensionHandler.cpp @@ -20,7 +20,7 @@ bool COpenGLExtensionHandler::needsDSAFramebufferHack = true; COpenGLExtensionHandler::COpenGLExtensionHandler() : StencilBuffer(false), TextureCompressionExtension(false), MaxLights(1), - MaxAnisotropy(1), MaxUserClipPlanes(0), MaxAuxBuffers(0), MaxIndices(65535), + MaxAnisotropy(1), MaxAuxBuffers(0), MaxIndices(65535), MaxTextureSize(1), MaxGeometryVerticesOut(0), MaxTextureLODBias(0.f), Version(0), ShaderLanguageVersion(0), OcclusionQuerySupport(false), IsAtiRadeonX(false), pGlActiveTexture(0), pGlActiveTextureARB(0), pGlClientActiveTextureARB(0), @@ -428,8 +428,6 @@ void COpenGLExtensionHandler::initExtensions(video::IContextManager *cmgr, bool if (FeatureAvailable[IRR_EXT_texture_lod_bias]) glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS_EXT, &MaxTextureLODBias); #endif - glGetIntegerv(GL_MAX_CLIP_PLANES, &num); - MaxUserClipPlanes = static_cast(num); glGetIntegerv(GL_AUX_BUFFERS, &num); MaxAuxBuffers = static_cast(num); #ifdef GL_ARB_draw_buffers diff --git a/irr/src/COpenGLExtensionHandler.h b/irr/src/COpenGLExtensionHandler.h index ada1c9a3a..cdff911b9 100644 --- a/irr/src/COpenGLExtensionHandler.h +++ b/irr/src/COpenGLExtensionHandler.h @@ -1019,8 +1019,6 @@ public: u8 MaxLights; //! Maximal Anisotropy u8 MaxAnisotropy; - //! Number of user clipplanes - u8 MaxUserClipPlanes; //! Number of auxiliary buffers u8 MaxAuxBuffers; //! Optimal number of indices per meshbuffer diff --git a/irr/src/CSceneManager.cpp b/irr/src/CSceneManager.cpp index b20f6010a..cd88e1a6e 100644 --- a/irr/src/CSceneManager.cpp +++ b/irr/src/CSceneManager.cpp @@ -2,6 +2,8 @@ // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h +#include + #include "CSceneManager.h" #include "IVideoDriver.h" #include "IFileSystem.h" @@ -95,9 +97,8 @@ CSceneManager::~CSceneManager() if (CollisionManager) CollisionManager->drop(); - u32 i; - for (i = 0; i < MeshLoaderList.size(); ++i) - MeshLoaderList[i]->drop(); + for (auto *loader : MeshLoaderList) + loader->drop(); if (ActiveCamera) ActiveCamera->drop(); @@ -140,12 +141,11 @@ IAnimatedMesh *CSceneManager::getUncachedMesh(io::IReadFile *file, const io::pat IAnimatedMesh *msh = 0; // iterate the list in reverse order so user-added loaders can override the built-in ones - s32 count = MeshLoaderList.size(); - for (s32 i = count - 1; i >= 0; --i) { - if (MeshLoaderList[i]->isALoadableFileExtension(filename)) { + for (auto it = MeshLoaderList.rbegin(); it != MeshLoaderList.rend(); it++) { + if ((*it)->isALoadableFileExtension(filename)) { // reset file to avoid side effects of previous calls to createMesh file->seek(0); - msh = MeshLoaderList[i]->createMesh(file); + msh = (*it)->createMesh(file); if (msh) { MeshCache->addMesh(cachename, msh); msh->drop(); @@ -388,14 +388,8 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode *node, E_SCENE_NODE_RENDE switch (pass) { // take camera if it is not already registered case ESNRP_CAMERA: { - taken = 1; - for (u32 i = 0; i != CameraList.size(); ++i) { - if (CameraList[i] == node) { - taken = 0; - break; - } - } - if (taken) { + if (std::find(CameraList.begin(), CameraList.end(), node) == CameraList.end()) { + taken = 1; CameraList.push_back(node); } } break; @@ -405,19 +399,19 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode *node, E_SCENE_NODE_RENDE break; case ESNRP_SOLID: if (!isCulled(node)) { - SolidNodeList.push_back(node); + SolidNodeList.emplace_back(node); taken = 1; } break; case ESNRP_TRANSPARENT: if (!isCulled(node)) { - TransparentNodeList.push_back(TransparentNodeEntry(node, camWorldPos)); + TransparentNodeList.emplace_back(node, camWorldPos); taken = 1; } break; case ESNRP_TRANSPARENT_EFFECT: if (!isCulled(node)) { - TransparentEffectNodeList.push_back(TransparentNodeEntry(node, camWorldPos)); + TransparentEffectNodeList.emplace_back(node, camWorldPos); taken = 1; } break; @@ -429,8 +423,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode *node, E_SCENE_NODE_RENDE for (u32 i = 0; i < count; ++i) { if (Driver->needsTransparentRenderPass(node->getMaterial(i))) { // register as transparent node - TransparentNodeEntry e(node, camWorldPos); - TransparentNodeList.push_back(e); + TransparentNodeList.emplace_back(node, camWorldPos); taken = 1; break; } @@ -438,7 +431,7 @@ u32 CSceneManager::registerNodeForRendering(ISceneNode *node, E_SCENE_NODE_RENDE // not transparent, register as solid if (!taken) { - SolidNodeList.push_back(node); + SolidNodeList.emplace_back(node); taken = 1; } } @@ -509,10 +502,10 @@ void CSceneManager::drawAll() CurrentRenderPass = ESNRP_CAMERA; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); - for (i = 0; i < CameraList.size(); ++i) - CameraList[i]->render(); + for (auto *node : CameraList) + node->render(); - CameraList.set_used(0); + CameraList.clear(); } // render skyboxes @@ -520,10 +513,10 @@ void CSceneManager::drawAll() CurrentRenderPass = ESNRP_SKY_BOX; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); - for (i = 0; i < SkyBoxList.size(); ++i) - SkyBoxList[i]->render(); + for (auto *node : SkyBoxList) + node->render(); - SkyBoxList.set_used(0); + SkyBoxList.clear(); } // render default objects @@ -531,12 +524,12 @@ void CSceneManager::drawAll() CurrentRenderPass = ESNRP_SOLID; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); - SolidNodeList.sort(); // sort by textures + std::sort(SolidNodeList.begin(), SolidNodeList.end()); - for (i = 0; i < SolidNodeList.size(); ++i) - SolidNodeList[i].Node->render(); + for (auto &it : SolidNodeList) + it.Node->render(); - SolidNodeList.set_used(0); + SolidNodeList.clear(); } // render transparent objects. @@ -544,11 +537,12 @@ void CSceneManager::drawAll() CurrentRenderPass = ESNRP_TRANSPARENT; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); - TransparentNodeList.sort(); // sort by distance from camera - for (i = 0; i < TransparentNodeList.size(); ++i) - TransparentNodeList[i].Node->render(); + std::sort(TransparentNodeList.begin(), TransparentNodeList.end()); - TransparentNodeList.set_used(0); + for (auto &it : TransparentNodeList) + it.Node->render(); + + TransparentNodeList.clear(); } // render transparent effect objects. @@ -556,12 +550,12 @@ void CSceneManager::drawAll() CurrentRenderPass = ESNRP_TRANSPARENT_EFFECT; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); - TransparentEffectNodeList.sort(); // sort by distance from camera + std::sort(TransparentEffectNodeList.begin(), TransparentEffectNodeList.end()); - for (i = 0; i < TransparentEffectNodeList.size(); ++i) - TransparentEffectNodeList[i].Node->render(); + for (auto &it : TransparentEffectNodeList) + it.Node->render(); - TransparentEffectNodeList.set_used(0); + TransparentEffectNodeList.clear(); } // render custom gui nodes @@ -569,10 +563,10 @@ void CSceneManager::drawAll() CurrentRenderPass = ESNRP_GUI; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); - for (i = 0; i < GuiNodeList.size(); ++i) - GuiNodeList[i]->render(); + for (auto *node : GuiNodeList) + node->render(); - GuiNodeList.set_used(0); + GuiNodeList.clear(); } clearDeletionList(); @@ -592,7 +586,7 @@ void CSceneManager::addExternalMeshLoader(IMeshLoader *externalLoader) //! Returns the number of mesh loaders supported by Irrlicht at this time u32 CSceneManager::getMeshLoaderCount() const { - return MeshLoaderList.size(); + return static_cast(MeshLoaderList.size()); } //! Retrieve the given mesh loader @@ -629,12 +623,9 @@ void CSceneManager::addToDeletionQueue(ISceneNode *node) //! clears the deletion list void CSceneManager::clearDeletionList() { - if (DeletionList.empty()) - return; - - for (u32 i = 0; i < DeletionList.size(); ++i) { - DeletionList[i]->remove(); - DeletionList[i]->drop(); + for (auto *node : DeletionList) { + node->remove(); + node->drop(); } DeletionList.clear(); diff --git a/irr/src/CSceneManager.h b/irr/src/CSceneManager.h index 0e27290a8..32df145ec 100644 --- a/irr/src/CSceneManager.h +++ b/irr/src/CSceneManager.h @@ -277,15 +277,15 @@ private: ISceneCollisionManager *CollisionManager; //! render pass lists - core::array CameraList; - core::array SkyBoxList; - core::array SolidNodeList; - core::array TransparentNodeList; - core::array TransparentEffectNodeList; - core::array GuiNodeList; + std::vector CameraList; + std::vector SkyBoxList; + std::vector SolidNodeList; + std::vector TransparentNodeList; + std::vector TransparentEffectNodeList; + std::vector GuiNodeList; - core::array MeshLoaderList; - core::array DeletionList; + std::vector MeshLoaderList; + std::vector DeletionList; //! current active camera ICameraSceneNode *ActiveCamera; diff --git a/irr/src/CXMeshFileLoader.cpp b/irr/src/CXMeshFileLoader.cpp index 5978980f4..4e11fe352 100644 --- a/irr/src/CXMeshFileLoader.cpp +++ b/irr/src/CXMeshFileLoader.cpp @@ -273,12 +273,12 @@ bool CXMeshFileLoader::load(io::IReadFile *file) } if (mesh->TCoords2.size()) { for (i = 0; i != mesh->Buffers.size(); ++i) { - mesh->Buffers[i]->Vertices_2TCoords.reallocate(vCountArray[i]); + mesh->Buffers[i]->Vertices_2TCoords.reserve(vCountArray[i]); mesh->Buffers[i]->VertexType = video::EVT_2TCOORDS; } } else { for (i = 0; i != mesh->Buffers.size(); ++i) - mesh->Buffers[i]->Vertices_Standard.reallocate(vCountArray[i]); + mesh->Buffers[i]->Vertices_Standard.reserve(vCountArray[i]); } verticesLinkIndex.set_used(mesh->Vertices.size()); @@ -291,10 +291,10 @@ bool CXMeshFileLoader::load(io::IReadFile *file) if (mesh->TCoords2.size()) { verticesLinkIndex[i] = buffer->Vertices_2TCoords.size(); - buffer->Vertices_2TCoords.push_back(mesh->Vertices[i]); + buffer->Vertices_2TCoords.emplace_back(mesh->Vertices[i]); // We have a problem with correct tcoord2 handling here // crash fixed for now by checking the values - buffer->Vertices_2TCoords.getLast().TCoords2 = (i < mesh->TCoords2.size()) ? mesh->TCoords2[i] : mesh->Vertices[i].TCoords; + buffer->Vertices_2TCoords.back().TCoords2 = (i < mesh->TCoords2.size()) ? mesh->TCoords2[i] : mesh->Vertices[i].TCoords; } else { verticesLinkIndex[i] = buffer->Vertices_Standard.size(); buffer->Vertices_Standard.push_back(mesh->Vertices[i]); @@ -306,7 +306,7 @@ bool CXMeshFileLoader::load(io::IReadFile *file) for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) ++vCountArray[mesh->FaceMaterialIndices[i]]; for (i = 0; i != mesh->Buffers.size(); ++i) - mesh->Buffers[i]->Indices.reallocate(vCountArray[i]); + mesh->Buffers[i]->Indices.reserve(vCountArray[i]); delete[] vCountArray; // create indices per buffer for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) { diff --git a/irr/src/CZipReader.cpp b/irr/src/CZipReader.cpp index b761c72e8..2d2152719 100644 --- a/irr/src/CZipReader.cpp +++ b/irr/src/CZipReader.cpp @@ -325,7 +325,7 @@ bool CZipReader::scanZipHeader(bool ignoreGPBits) dirEnd.Offset = os::Byteswap::byteswap(dirEnd.Offset); dirEnd.CommentLength = os::Byteswap::byteswap(dirEnd.CommentLength); #endif - FileInfo.reallocate(dirEnd.TotalEntries); + FileInfo.reserve(dirEnd.TotalEntries); File->seek(dirEnd.Offset); while (scanCentralDirectoryHeader()) { } @@ -381,9 +381,10 @@ bool CZipReader::scanCentralDirectoryHeader() File->seek(entry.RelativeOffsetOfLocalHeader); scanZipHeader(true); File->seek(pos + entry.FilenameLength + entry.ExtraFieldLength + entry.FileCommentLength); - FileInfo.getLast().header.DataDescriptor.CompressedSize = entry.CompressedSize; - FileInfo.getLast().header.DataDescriptor.UncompressedSize = entry.UncompressedSize; - FileInfo.getLast().header.DataDescriptor.CRC32 = entry.CRC32; + auto &lastInfo = FileInfo.back(); + lastInfo.header.DataDescriptor.CompressedSize = entry.CompressedSize; + lastInfo.header.DataDescriptor.UncompressedSize = entry.UncompressedSize; + lastInfo.header.DataDescriptor.CRC32 = entry.CRC32; Files.getLast().Size = entry.UncompressedSize; return true; } diff --git a/irr/src/CZipReader.h b/irr/src/CZipReader.h index d9afd668a..b520c2030 100644 --- a/irr/src/CZipReader.h +++ b/irr/src/CZipReader.h @@ -4,8 +4,8 @@ #pragma once +#include #include "IReadFile.h" -#include "irrArray.h" #include "irrString.h" #include "IFileSystem.h" #include "CFileList.h" @@ -209,7 +209,7 @@ protected: IReadFile *File; // holds extended info about files - core::array FileInfo; + std::vector FileInfo; bool IsGZip; }; diff --git a/irr/src/IAttribute.h b/irr/src/IAttribute.h index b0dc76eee..23352a623 100644 --- a/irr/src/IAttribute.h +++ b/irr/src/IAttribute.h @@ -4,15 +4,8 @@ #pragma once -#include "IReferenceCounted.h" -#include "SColor.h" -#include "vector3d.h" -#include "vector2d.h" -#include "position2d.h" -#include "rect.h" -#include "dimension2d.h" +#include "irrTypes.h" #include "irrString.h" -#include "irrArray.h" #include "EAttributes.h" namespace irr @@ -20,16 +13,7 @@ namespace irr namespace io { -// All derived attribute types implement at least getter/setter for their own type (like CBoolAttribute will have setBool/getBool). -// Simple types will also implement getStringW and setString, but don't expect it to work for all types. -// String serialization makes no sense for some attribute-types (like stringw arrays or pointers), but is still useful for many types. -// (Note: I do _not_ know yet why the default string serialization is asymmetric with char* in set and wchar_t* in get). -// Additionally many attribute types will implement conversion functions like CBoolAttribute has p.E. getInt/setInt(). -// The reason for conversion functions is likely to make reading old formats easier which have changed in the meantime. For example -// an old xml can contain a bool attribute which is an int in a newer format. You can still call getInt() even thought the attribute has the wrong type. -// And please do _not_ confuse these attributes here with the ones used in the xml-reader (aka SAttribute which is just a key-value pair). - -class IAttribute : public virtual IReferenceCounted +class IAttribute { public: virtual ~IAttribute(){}; diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index 95d760548..1defa9abc 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -257,7 +257,6 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d &screenS DriverAttributes->setAttribute("MaxSupportedTextures", (s32)Feature.MaxTextureUnits); // DriverAttributes->setAttribute("MaxLights", MaxLights); DriverAttributes->setAttribute("MaxAnisotropy", MaxAnisotropy); - // DriverAttributes->setAttribute("MaxUserClipPlanes", MaxUserClipPlanes); // DriverAttributes->setAttribute("MaxAuxBuffers", MaxAuxBuffers); // DriverAttributes->setAttribute("MaxMultipleRenderTargets", MaxMultipleRenderTargets); DriverAttributes->setAttribute("MaxIndices", (s32)MaxIndices); @@ -268,8 +267,6 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d &screenS GL.PixelStorei(GL_PACK_ALIGNMENT, 1); - UserClipPlane.reallocate(0); - for (s32 i = 0; i < ETS_COUNT; ++i) setTransform(static_cast(i), core::IdentityMatrix); @@ -916,7 +913,8 @@ void COpenGL3DriverBase::draw2DImageBatch(const video::ITexture *texture, const irr::u32 drawCount = core::min_(positions.size(), sourceRects.size()); assert(6 * drawCount <= QuadIndexCount); // FIXME split the batch? or let it crash? - core::array vtx(drawCount * 4); + std::vector vtx; + vtx.reserve(drawCount * 4); for (u32 i = 0; i < drawCount; i++) { core::position2d targetPos = positions[i]; @@ -939,22 +937,22 @@ void COpenGL3DriverBase::draw2DImageBatch(const video::ITexture *texture, f32 down = 2.f - (f32)poss.LowerRightCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f; f32 top = 2.f - (f32)poss.UpperLeftCorner.Y / (f32)renderTargetSize.Height * 2.f - 1.f; - vtx.push_back(S3DVertex(left, top, 0.0f, + vtx.emplace_back(left, top, 0.0f, 0.0f, 0.0f, 0.0f, color, - tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y)); - vtx.push_back(S3DVertex(right, top, 0.0f, + tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y); + vtx.emplace_back(right, top, 0.0f, 0.0f, 0.0f, 0.0f, color, - tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y)); - vtx.push_back(S3DVertex(right, down, 0.0f, + tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y); + vtx.emplace_back(right, down, 0.0f, 0.0f, 0.0f, 0.0f, color, - tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y)); - vtx.push_back(S3DVertex(left, down, 0.0f, + tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y); + vtx.emplace_back(left, down, 0.0f, 0.0f, 0.0f, 0.0f, color, - tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y)); + tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y); } GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, QuadIndexBuffer); - drawElements(GL_TRIANGLES, vt2DImage, vtx.const_pointer(), vtx.size(), 0, 6 * drawCount); + drawElements(GL_TRIANGLES, vt2DImage, vtx.data(), vtx.size(), 0, 6 * drawCount); GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); if (clipRect) @@ -1104,15 +1102,14 @@ void COpenGL3DriverBase::endDraw(const VertexType &vertexType) ITexture *COpenGL3DriverBase::createDeviceDependentTexture(const io::path &name, IImage *image) { - core::array imageArray(1); - imageArray.push_back(image); + std::vector tmp { image }; - COpenGL3Texture *texture = new COpenGL3Texture(name, imageArray, ETT_2D, this); + COpenGL3Texture *texture = new COpenGL3Texture(name, tmp, ETT_2D, this); return texture; } -ITexture *COpenGL3DriverBase::createDeviceDependentTextureCubemap(const io::path &name, const core::array &image) +ITexture *COpenGL3DriverBase::createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) { COpenGL3Texture *texture = new COpenGL3Texture(name, image, ETT_CUBEMAP, this); @@ -1876,40 +1873,6 @@ void COpenGL3DriverBase::removeTexture(ITexture *texture) CNullDriver::removeTexture(texture); } -//! Set/unset a clipping plane. -bool COpenGL3DriverBase::setClipPlane(u32 index, const core::plane3df &plane, bool enable) -{ - if (index >= UserClipPlane.size()) - UserClipPlane.push_back(SUserClipPlane()); - - UserClipPlane[index].Plane = plane; - UserClipPlane[index].Enabled = enable; - return true; -} - -//! Enable/disable a clipping plane. -void COpenGL3DriverBase::enableClipPlane(u32 index, bool enable) -{ - UserClipPlane[index].Enabled = enable; -} - -//! Get the ClipPlane Count -u32 COpenGL3DriverBase::getClipPlaneCount() const -{ - return UserClipPlane.size(); -} - -const core::plane3df &COpenGL3DriverBase::getClipPlane(irr::u32 index) const -{ - if (index < UserClipPlane.size()) - return UserClipPlane[index].Plane; - else { - _IRR_DEBUG_BREAK_IF(true) // invalid index - static const core::plane3df dummy; - return dummy; - } -} - core::dimension2du COpenGL3DriverBase::getMaxTextureSize() const { return core::dimension2du(MaxTextureSize, MaxTextureSize); diff --git a/irr/src/OpenGL/Driver.h b/irr/src/OpenGL/Driver.h index 51554334b..be4e4db9e 100644 --- a/irr/src/OpenGL/Driver.h +++ b/irr/src/OpenGL/Driver.h @@ -7,7 +7,6 @@ #pragma once #include "SIrrCreationParameters.h" - #include "Common.h" #include "CNullDriver.h" #include "IMaterialRendererServices.h" @@ -227,18 +226,6 @@ public: // Does *nothing* unless in debug mode. bool testGLError(const char *file, int line); - //! Set/unset a clipping plane. - bool setClipPlane(u32 index, const core::plane3df &plane, bool enable = false) override; - - //! returns the current amount of user clip planes set. - u32 getClipPlaneCount() const; - - //! returns the 0 indexed Plane - const core::plane3df &getClipPlane(u32 index) const; - - //! Enable/disable a clipping plane. - void enableClipPlane(u32 index, bool enable) override; - //! Returns the graphics card vendor name. core::stringc getVendorInfo() override { @@ -278,7 +265,7 @@ protected: ITexture *createDeviceDependentTexture(const io::path &name, IImage *image) override; - ITexture *createDeviceDependentTextureCubemap(const io::path &name, const core::array &image) override; + ITexture *createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) override; //! Map Irrlicht wrap mode to OpenGL enum GLint getTextureWrapMode(u8 clamp) const; @@ -337,14 +324,6 @@ protected: bool LockRenderStateMode; u8 AntiAlias; - struct SUserClipPlane - { - core::plane3df Plane; - bool Enabled; - }; - - core::array UserClipPlane; - core::matrix4 TextureFlipMatrix; using FColorConverter = void (*)(const void *source, s32 count, void *dest); diff --git a/irr/src/OpenGL/MaterialRenderer.cpp b/irr/src/OpenGL/MaterialRenderer.cpp index 51a796d48..3c32ba318 100644 --- a/irr/src/OpenGL/MaterialRenderer.cpp +++ b/irr/src/OpenGL/MaterialRenderer.cpp @@ -262,35 +262,30 @@ bool COpenGL3MaterialRenderer::linkProgram() // seems that some implementations use an extra null terminator. ++maxlen; - c8 *buf = new c8[maxlen]; + std::vector buf(maxlen); UniformInfo.clear(); - UniformInfo.reallocate(num); + UniformInfo.reserve(num); for (GLint i = 0; i < num; ++i) { SUniformInfo ui; - memset(buf, 0, maxlen); + memset(buf.data(), 0, buf.size()); GLint size; - GL.GetActiveUniform(Program, i, maxlen, 0, &size, &ui.type, reinterpret_cast(buf)); - - core::stringc name = ""; + GL.GetActiveUniform(Program, i, maxlen, 0, &size, &ui.type, reinterpret_cast(buf.data())); // array support, workaround for some bugged drivers. for (s32 i = 0; i < maxlen; ++i) { if (buf[i] == '[' || buf[i] == '\0') break; - name += buf[i]; + ui.name += buf[i]; } - ui.name = name; - ui.location = GL.GetUniformLocation(Program, buf); + ui.location = GL.GetUniformLocation(Program, buf.data()); - UniformInfo.push_back(ui); + UniformInfo.push_back(std::move(ui)); } - - delete[] buf; } return true; diff --git a/irr/src/OpenGL/MaterialRenderer.h b/irr/src/OpenGL/MaterialRenderer.h index fa37cfff3..fca71478a 100644 --- a/irr/src/OpenGL/MaterialRenderer.h +++ b/irr/src/OpenGL/MaterialRenderer.h @@ -4,12 +4,12 @@ #pragma once +#include +#include #include "EMaterialTypes.h" #include "IMaterialRenderer.h" #include "IMaterialRendererServices.h" #include "IGPUProgrammingServices.h" -#include "irrArray.h" -#include "irrString.h" #include "Common.h" @@ -79,13 +79,13 @@ protected: struct SUniformInfo { - core::stringc name; + std::string name; GLenum type; GLint location; }; GLuint Program; - core::array UniformInfo; + std::vector UniformInfo; s32 UserData; }; diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index d53d52957..ebb8b9000 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -184,15 +184,15 @@ void Clouds::updateMesh() const u32 index_count = quad_count * 6; // reserve memory - mb->Vertices.reallocate(vertex_count); - mb->Indices.reallocate(index_count); + mb->Vertices.reserve(vertex_count); + mb->Indices.reserve(index_count); } #define GETINDEX(x, z, radius) (((z)+(radius))*(radius)*2 + (x)+(radius)) #define INAREA(x, z, radius) \ ((x) >= -(radius) && (x) < (radius) && (z) >= -(radius) && (z) < (radius)) - mb->Vertices.set_used(0); + mb->Vertices.clear(); for (s16 zi0= -m_cloud_radius_i; zi0 < m_cloud_radius_i; zi0++) for (s16 xi0= -m_cloud_radius_i; xi0 < m_cloud_radius_i; xi0++) { @@ -322,7 +322,7 @@ void Clouds::updateMesh() const u32 index_count = quad_count * 6; // rewrite index array as needed if (mb->getIndexCount() > index_count) { - mb->Indices.set_used(index_count); + mb->Indices.resize(index_count); mb->setDirty(scene::EBT_INDEX); } else if (mb->getIndexCount() < index_count) { const u32 start = mb->getIndexCount() / 6; diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 3a0e25a07..962818b8f 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -119,27 +119,28 @@ Hud::Hud(Client *client, LocalPlayer *player, } // Prepare mesh for compass drawing - m_rotation_mesh_buffer.Vertices.set_used(4); - m_rotation_mesh_buffer.Indices.set_used(6); + auto &b = m_rotation_mesh_buffer; + b.Vertices.resize(4); + b.Indices.resize(6); video::SColor white(255, 255, 255, 255); v3f normal(0.f, 0.f, 1.f); - m_rotation_mesh_buffer.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f)); - m_rotation_mesh_buffer.Vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f)); - m_rotation_mesh_buffer.Vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f)); - m_rotation_mesh_buffer.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f)); + b.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f)); + b.Vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f)); + b.Vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f)); + b.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f)); - m_rotation_mesh_buffer.Indices[0] = 0; - m_rotation_mesh_buffer.Indices[1] = 1; - m_rotation_mesh_buffer.Indices[2] = 2; - m_rotation_mesh_buffer.Indices[3] = 2; - m_rotation_mesh_buffer.Indices[4] = 3; - m_rotation_mesh_buffer.Indices[5] = 0; + b.Indices[0] = 0; + b.Indices[1] = 1; + b.Indices[2] = 2; + b.Indices[3] = 2; + b.Indices[4] = 3; + b.Indices[5] = 0; - m_rotation_mesh_buffer.getMaterial().Lighting = false; - m_rotation_mesh_buffer.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - m_rotation_mesh_buffer.setHardwareMappingHint(scene::EHM_STATIC); + b.getMaterial().Lighting = false; + b.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + b.setHardwareMappingHint(scene::EHM_STATIC); } void Hud::readScalingSetting() diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 429464e04..9b5612148 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -602,7 +602,7 @@ void PartialMeshBuffer::beforeDraw() const void PartialMeshBuffer::afterDraw() const { // Take the data back - m_vertex_indexes = m_buffer->Indices.steal(); + m_vertex_indexes = std::move(m_buffer->Indices); } /* diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index afac89843..9f1f04359 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -555,8 +555,8 @@ v3f Minimap::getYawVec() scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() { scene::SMeshBuffer *buf = new scene::SMeshBuffer(); - buf->Vertices.set_used(4); - buf->Indices.set_used(6); + buf->Vertices.resize(4); + buf->Indices.resize(6); static const video::SColor c(255, 255, 255, 255); buf->Vertices[0] = video::S3DVertex(-1, -1, 0, 0, 0, 1, c, 0, 1); diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 92e5df218..c68d13984 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -830,8 +830,8 @@ void Sky::updateStars() warningstream << "Requested " << m_star_params.count << " stars but " << 0x4000 << " is the max\n"; m_star_params.count = 0x4000; } - m_stars->Vertices.reallocate(4 * m_star_params.count); - m_stars->Indices.reallocate(6 * m_star_params.count); + m_stars->Vertices.reserve(4 * m_star_params.count); + m_stars->Indices.reserve(6 * m_star_params.count); video::SColor fallback_color = m_star_params.starcolor; // used on GLES 2 “without shaders” PcgRandom rgen(m_seed); From 3df070f352520c3678e509f7c096977b62d2e3bf Mon Sep 17 00:00:00 2001 From: Lars Date: Sat, 17 Aug 2024 09:34:22 -0700 Subject: [PATCH 06/75] Remove SAO::onAttach() and SAO::onDetach() --- src/server/serveractiveobject.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index d734b8469..486b3b230 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -259,9 +259,6 @@ protected: virtual void onMarkedForDeactivation() {} virtual void onMarkedForRemoval() {} - virtual void onAttach(object_t parent_id) {} - virtual void onDetach(object_t parent_id) {} - ServerEnvironment *m_env; v3f m_base_position; std::unordered_set m_attached_particle_spawners; From c65444c43b3aa446a57aa72bb887b83441925aad Mon Sep 17 00:00:00 2001 From: cx384 Date: Fri, 16 Aug 2024 11:53:16 +0200 Subject: [PATCH 07/75] Add whitespace checks to ci --- .github/workflows/whitespace_checks.yml | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 .github/workflows/whitespace_checks.yml diff --git a/.github/workflows/whitespace_checks.yml b/.github/workflows/whitespace_checks.yml new file mode 100644 index 000000000..cc16e7b22 --- /dev/null +++ b/.github/workflows/whitespace_checks.yml @@ -0,0 +1,45 @@ +name: whitespace_checks + +# Check whitespaces of the following file types +# Not checked: .lua, .yml, .properties, .conf, .java, .py, .svg, .gradle, .xml, ... +# (luacheck already checks .lua files) +on: + push: + paths: + - '**.txt' + - '**.md' + - '**.[ch]' + - '**.cpp' + - '**.hpp' + - '**.sh' + - '**.cmake' + - '**.glsl' + pull_request: + paths: + - '**.txt' + - '**.md' + - '**.[ch]' + - '**.cpp' + - '**.hpp' + - '**.sh' + - '**.cmake' + - '**.glsl' + +jobs: + trailing_whitespaces: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # Line endings are already ensured by .gitattributes + - name: Check trailing whitespaces + run: if git ls-files | grep -E '\.txt$|\.md$|\.[ch]$|\.cpp$|\.hpp$|\.sh$|\.cmake$|\.glsl$' | xargs grep -n '\s$'; then echo -e "\033[0;31mFound trailing whitespace"; (exit 1); else (exit 0); fi + + tabs_lua_api_files: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # Some files should not contain tabs + - name: Check tabs in Lua API files + run: if grep -n $'\t' doc/lua_api.md doc/client_lua_api.md; then echo -e "\033[0;31mFound tab in markdown file"; (exit 1); else (exit 0); fi + + From 03e600a721c79d5cdce699123e698cd380abcf1c Mon Sep 17 00:00:00 2001 From: cx384 Date: Fri, 16 Aug 2024 11:53:31 +0200 Subject: [PATCH 08/75] Fix whitespaces --- client/shaders/fxaa/opengl_fragment.glsl | 8 ++-- client/shaders/fxaa/opengl_vertex.glsl | 2 +- doc/client_lua_api.md | 18 ++++---- doc/lua_api.md | 48 ++++++++++----------- doc/world_format.md | 2 +- irr/README.md | 2 +- lib/gmp/mini-gmp.c | 38 ++++++++-------- src/client/game.cpp | 2 +- src/client/mesh.h | 2 +- src/client/shadows/dynamicshadowsrender.cpp | 2 +- src/script/lua_api/l_noise.cpp | 4 +- src/server/activeobjectmgr.cpp | 2 +- src/servermap.cpp | 2 +- 13 files changed, 66 insertions(+), 66 deletions(-) diff --git a/client/shaders/fxaa/opengl_fragment.glsl b/client/shaders/fxaa/opengl_fragment.glsl index 130e689ea..f70064b6d 100644 --- a/client/shaders/fxaa/opengl_fragment.glsl +++ b/client/shaders/fxaa/opengl_fragment.glsl @@ -58,11 +58,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define FXAA_SPAN_MAX 8.0 #endif -//optimized version for mobile, where dependent +//optimized version for mobile, where dependent //texture reads can be a bottleneck vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 inverseVP, - vec2 v_rgbNW, vec2 v_rgbNE, - vec2 v_rgbSW, vec2 v_rgbSE, + vec2 v_rgbNW, vec2 v_rgbNE, + vec2 v_rgbSW, vec2 v_rgbSE, vec2 v_rgbM) { vec4 color; vec3 rgbNW = texture2D(tex, v_rgbNW).xyz; @@ -111,6 +111,6 @@ void main(void) { vec2 uv = varTexCoord.st; - gl_FragColor = fxaa(rendered, uv, texelSize0, + gl_FragColor = fxaa(rendered, uv, texelSize0, sampleNW, sampleNE, sampleSW, sampleSE, uv); } diff --git a/client/shaders/fxaa/opengl_vertex.glsl b/client/shaders/fxaa/opengl_vertex.glsl index 26913c28e..68bb36d1c 100644 --- a/client/shaders/fxaa/opengl_vertex.glsl +++ b/client/shaders/fxaa/opengl_vertex.glsl @@ -12,7 +12,7 @@ varying vec2 sampleSW; varying vec2 sampleSE; /* -Based on +Based on https://github.com/mattdesl/glsl-fxaa/ Portions Copyright (c) 2011 by Armin Ronacher. */ diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index fac3f7b93..08d0317ab 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -315,7 +315,7 @@ Call these functions only at load time! * `minetest.register_globalstep(function(dtime))` * Called every client environment step - * `dtime` is the time since last execution in seconds. + * `dtime` is the time since last execution in seconds. * `minetest.register_on_mods_loaded(function())` * Called just after mods have finished loading. * `minetest.register_on_shutdown(function())` @@ -586,9 +586,9 @@ Call these functions only at load time! * `minetest.camera` * Reference to the camera object. See [`Camera`](#camera) class reference for methods. * `minetest.show_formspec(formname, formspec)` : returns true on success - * Shows a formspec to the player + * Shows a formspec to the player * `minetest.display_chat_message(message)` returns true on success - * Shows a chat message to the current player. + * Shows a chat message to the current player. Setting-related --------------- @@ -866,9 +866,9 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or ----------------- ### Definitions * `minetest.get_node_def(nodename)` - * Returns [node definition](#node-definition) table of `nodename` + * Returns [node definition](#node-definition) table of `nodename` * `minetest.get_item_def(itemstring)` - * Returns item definition table of `itemstring` + * Returns item definition table of `itemstring` #### Node Definition @@ -971,10 +971,10 @@ It can be created via `Raycast(pos1, pos2, objects, liquids)` or ```lua { - address = "minetest.example.org", -- The domain name/IP address of a remote server or "" for a local server. - ip = "203.0.113.156", -- The IP address of the server. - port = 30000, -- The port the client is connected to. - protocol_version = 30 -- Will not be accurate at start up as the client might not be connected to the server yet, in that case it will be 0. + address = "minetest.example.org", -- The domain name/IP address of a remote server or "" for a local server. + ip = "203.0.113.156", -- The IP address of the server. + port = 30000, -- The port the client is connected to. + protocol_version = 30 -- Will not be accurate at start up as the client might not be connected to the server yet, in that case it will be 0. } ``` diff --git a/doc/lua_api.md b/doc/lua_api.md index 6d486b56b..b37959eed 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -3658,7 +3658,7 @@ Player Inventory lists * `hand`: list containing an override for the empty hand * Is not created automatically, use `InvRef:set_size` * Is only used to enhance the empty hand's tool capabilities - + Custom lists can be added and deleted with `InvRef:set_size(name, size)` like any other inventory. @@ -3854,8 +3854,8 @@ vectors are written like this: `(x, y, z)`: * Returns a vector where the function `func` has been applied to each component. * `vector.combine(v, w, func)`: - * Returns a vector where the function `func` has combined both components of `v` and `w` - for each component + * Returns a vector where the function `func` has combined both components of `v` and `w` + for each component * `vector.equals(v1, v2)`: * Returns a boolean, `true` if the vectors are identical. * `vector.sort(v1, v2)`: @@ -3873,10 +3873,10 @@ vectors are written like this: `(x, y, z)`: by a `vector.*` function. * Returns `false` for anything else, including tables like `{x=3,y=1,z=4}`. * `vector.in_area(pos, min, max)`: - * Returns a boolean value indicating if `pos` is inside area formed by `min` and `max`. - * `min` and `max` are inclusive. - * If `min` is bigger than `max` on some axis, function always returns false. - * You can use `vector.sort` if you have two vectors and don't know which are the minimum and the maximum. + * Returns a boolean value indicating if `pos` is inside area formed by `min` and `max`. + * `min` and `max` are inclusive. + * If `min` is bigger than `max` on some axis, function always returns false. + * You can use `vector.sort` if you have two vectors and don't know which are the minimum and the maximum. For the following functions `x` can be either a vector or a number: @@ -5076,12 +5076,12 @@ Callbacks: used for updating the entity state. * `on_deactivate(self, removal)` * Called when the object is about to get removed or unloaded. - * `removal`: boolean indicating whether the object is about to get removed. - Calling `object:remove()` on an active object will call this with `removal=true`. - The mapblock the entity resides in being unloaded will call this with `removal=false`. - * Note that this won't be called if the object hasn't been activated in the first place. - In particular, `minetest.clear_objects({mode = "full"})` won't call this, - whereas `minetest.clear_objects({mode = "quick"})` might call this. + * `removal`: boolean indicating whether the object is about to get removed. + Calling `object:remove()` on an active object will call this with `removal=true`. + The mapblock the entity resides in being unloaded will call this with `removal=false`. + * Note that this won't be called if the object hasn't been activated in the first place. + In particular, `minetest.clear_objects({mode = "full"})` won't call this, + whereas `minetest.clear_objects({mode = "quick"})` might call this. * `on_step(self, dtime, moveresult)` * Called on every server tick, after movement and collision processing. * `dtime`: elapsed time since last call @@ -5723,7 +5723,7 @@ Call these functions only at load time! * `minetest.register_globalstep(function(dtime))` * Called every server step, usually interval of 0.1s. - * `dtime` is the time since last execution in seconds. + * `dtime` is the time since last execution in seconds. * `minetest.register_on_mods_loaded(function())` * Called after mods have finished loading and before the media is cached or the aliases handled. @@ -6151,7 +6151,7 @@ Environment access * **Warning**: The same warning as for `minetest.get_objects_inside_radius` applies. Use `minetest.objects_in_area` instead to iterate only valid objects. * `minetest.objects_in_area(min_pos, max_pos)` - * returns an iterator of valid objects + * returns an iterator of valid objects * `minetest.set_timeofday(val)`: set time of day * `val` is between `0` and `1`; `0` for midnight, `0.5` for midday * `minetest.get_timeofday()`: get time of day @@ -7961,13 +7961,13 @@ child will follow movement and rotation of that bone. object. * `set_detach()`: Detaches object. No-op if object was not attached. * `set_bone_position([bone, position, rotation])` - * Shorthand for `set_bone_override(bone, {position = position, rotation = rotation:apply(math.rad)})` using absolute values. - * **Note:** Rotation is in degrees, not radians. - * **Deprecated:** Use `set_bone_override` instead. + * Shorthand for `set_bone_override(bone, {position = position, rotation = rotation:apply(math.rad)})` using absolute values. + * **Note:** Rotation is in degrees, not radians. + * **Deprecated:** Use `set_bone_override` instead. * `get_bone_position(bone)`: returns the previously set position and rotation of the bone - * Shorthand for `get_bone_override(bone).position.vec, get_bone_override(bone).rotation.vec:apply(math.deg)`. - * **Note:** Returned rotation is in degrees, not radians. - * **Deprecated:** Use `get_bone_override` instead. + * Shorthand for `get_bone_override(bone).position.vec, get_bone_override(bone).rotation.vec:apply(math.deg)`. + * **Note:** Returned rotation is in degrees, not radians. + * **Deprecated:** Use `get_bone_override` instead. * `set_bone_override(bone, override)` * `bone`: string * `override`: `{ position = property, rotation = property, scale = property }` or `nil` @@ -7984,7 +7984,7 @@ child will follow movement and rotation of that bone. * Compatibility note: Clients prior to 5.9.0 only support absolute position and rotation. All values are treated as absolute and are set immediately (no interpolation). * `get_bone_override(bone)`: returns `override` in the above format - * **Note:** Unlike `get_bone_position`, the returned rotation is in radians, not degrees. + * **Note:** Unlike `get_bone_position`, the returned rotation is in radians, not degrees. * `get_bone_overrides()`: returns all bone overrides as table `{[bonename] = override, ...}` * `set_properties(object property table)` * `get_properties()`: returns a table of all object properties @@ -8081,8 +8081,8 @@ child will follow movement and rotation of that bone. * Fifth column: subject viewed from above * Sixth column: subject viewed from below * `get_luaentity()`: - * Returns the object's associated luaentity table, if there is one - * Otherwise returns `nil` (e.g. for players) + * Returns the object's associated luaentity table, if there is one + * Otherwise returns `nil` (e.g. for players) * `get_entity_name()`: * **Deprecated**: Will be removed in a future version, use `:get_luaentity().name` instead. diff --git a/doc/world_format.md b/doc/world_format.md index b5a2a3cfa..93920f391 100644 --- a/doc/world_format.md +++ b/doc/world_format.md @@ -394,7 +394,7 @@ Timestamp and node ID mappings were introduced in map format version 29. * `u8` `name_id_mapping_version` * Should be zero for map format version 29. - + * `u16` `num_name_id_mappings` * foreach `num_name_id_mappings`: * `u16` `id` diff --git a/irr/README.md b/irr/README.md index 96d3b0d97..640b82c6f 100644 --- a/irr/README.md +++ b/irr/README.md @@ -25,7 +25,7 @@ Aside from standard search options (`ZLIB_INCLUDE_DIR`, `ZLIB_LIBRARY`, ...) the * `USE_SDL2` (default: platform-dependent, usually `ON`) - Use SDL2 instead of older native device code However, IrrlichtMt cannot be built or installed separately. - + Platforms --------- diff --git a/lib/gmp/mini-gmp.c b/lib/gmp/mini-gmp.c index 69a72bfd4..ef9010be2 100644 --- a/lib/gmp/mini-gmp.c +++ b/lib/gmp/mini-gmp.c @@ -55,7 +55,7 @@ see https://www.gnu.org/licenses/. */ #include #endif - + /* Macros */ #define GMP_LIMB_BITS (sizeof(mp_limb_t) * CHAR_BIT) @@ -283,7 +283,7 @@ see https://www.gnu.org/licenses/. */ const int mp_bits_per_limb = GMP_LIMB_BITS; - + /* Memory allocation and other helper functions. */ static void gmp_die (const char *msg) @@ -384,7 +384,7 @@ gmp_free_limbs (mp_ptr old, mp_size_t size) gmp_free (old, size * sizeof (mp_limb_t)); } - + /* MPN interface */ void @@ -777,7 +777,7 @@ mpn_neg (mp_ptr rp, mp_srcptr up, mp_size_t n) return 1; } - + /* MPN division interface. */ /* The 3/2 inverse is defined as @@ -1169,7 +1169,7 @@ mpn_div_qr (mp_ptr qp, mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn) gmp_free_limbs (tp, dn); } - + /* MPN base conversion. */ static unsigned mpn_base_power_of_two_p (unsigned b) @@ -1425,7 +1425,7 @@ mpn_set_str (mp_ptr rp, const unsigned char *sp, size_t sn, int base) } } - + /* MPZ interface */ void mpz_init (mpz_t r) @@ -1480,7 +1480,7 @@ mpz_realloc (mpz_t r, mp_size_t size) #define MPZ_REALLOC(z,n) ((n) > (z)->_mp_alloc \ ? mpz_realloc(z,n) \ : (z)->_mp_d) - + /* MPZ assignment and basic conversions. */ void mpz_set_si (mpz_t r, signed long int x) @@ -1704,7 +1704,7 @@ mpz_roinit_n (mpz_t x, mp_srcptr xp, mp_size_t xs) return x; } - + /* Conversions and comparison to double. */ void mpz_set_d (mpz_t r, double x) @@ -1862,7 +1862,7 @@ mpz_cmp_d (const mpz_t x, double d) } } - + /* MPZ comparisons and the like. */ int mpz_sgn (const mpz_t u) @@ -1950,7 +1950,7 @@ mpz_swap (mpz_t u, mpz_t v) MPN_PTR_SWAP (u->_mp_d, u->_mp_size, v->_mp_d, v->_mp_size); } - + /* MPZ addition and subtraction */ @@ -2050,7 +2050,7 @@ mpz_sub (mpz_t r, const mpz_t a, const mpz_t b) r->_mp_size = a->_mp_size >= 0 ? rn : - rn; } - + /* MPZ multiplication */ void mpz_mul_si (mpz_t r, const mpz_t u, long int v) @@ -2186,7 +2186,7 @@ mpz_submul (mpz_t r, const mpz_t u, const mpz_t v) mpz_clear (t); } - + /* MPZ division */ enum mpz_div_round_mode { GMP_DIV_FLOOR, GMP_DIV_CEIL, GMP_DIV_TRUNC }; @@ -2661,7 +2661,7 @@ mpz_divisible_ui_p (const mpz_t n, unsigned long d) return mpz_div_qr_ui (NULL, NULL, n, d, GMP_DIV_TRUNC) == 0; } - + /* GCD */ static mp_limb_t mpn_gcd_11 (mp_limb_t u, mp_limb_t v) @@ -3054,7 +3054,7 @@ mpz_invert (mpz_t r, const mpz_t u, const mpz_t m) return invertible; } - + /* Higher level operations (sqrt, pow and root) */ void @@ -3334,7 +3334,7 @@ mpn_sqrtrem (mp_ptr sp, mp_ptr rp, mp_srcptr p, mp_size_t n) mpz_clear (r); return res; } - + /* Combinatorics */ void @@ -3378,7 +3378,7 @@ mpz_bin_uiui (mpz_t r, unsigned long n, unsigned long k) mpz_clear (t); } - + /* Primality testing */ /* Computes Kronecker (a/b) with odd b, a!=0 and GCD(a,b) = 1 */ @@ -3646,7 +3646,7 @@ mpz_probab_prime_p (const mpz_t n, int reps) return is_prime; } - + /* Logical operations and bit manipulation. */ /* Numbers are treated as if represented in two's complement (and @@ -4183,7 +4183,7 @@ mpz_scan0 (const mpz_t u, mp_bitcnt_t starting_bit) return mpn_common_scan (limb, i, up, un, ux); } - + /* MPZ base conversion. */ size_t @@ -4443,7 +4443,7 @@ mpz_out_str (FILE *stream, int base, const mpz_t x) return n; } - + static int gmp_detect_endian (void) { diff --git a/src/client/game.cpp b/src/client/game.cpp index 0906e9ca9..41c7972cb 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1446,7 +1446,7 @@ void Game::copyServerClientCache() { // It would be possible to let the client directly read the media files // from where the server knows they are. But aside from being more complicated - // it would also *not* fill the media cache and cause slower joining of + // it would also *not* fill the media cache and cause slower joining of // remote servers. // (Imagine that you launch a game once locally and then connect to a server.) diff --git a/src/client/mesh.h b/src/client/mesh.h index 0c3e8942e..106787af3 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -139,5 +139,5 @@ bool checkMeshNormals(scene::IMesh *mesh); Set the MinFilter, MagFilter and AnisotropicFilter properties of a texture layer according to the three relevant boolean values found in the Minetest settings. -*/ +*/ void setMaterialFilters(video::SMaterialLayer &tex, bool bilinear, bool trilinear, bool anisotropic); diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp index 1f49eed09..91992bc08 100644 --- a/src/client/shadows/dynamicshadowsrender.cpp +++ b/src/client/shadows/dynamicshadowsrender.cpp @@ -140,7 +140,7 @@ void ShadowRenderer::initialize() } createShaders(); - + m_texture_format = m_shadow_map_texture_32bit ? video::ECOLOR_FORMAT::ECF_R32F diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index 84da720db..9836f4b09 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -542,9 +542,9 @@ int LuaPcgRandom::l_set_state(lua_State *L) u64 state[2]; s_state_0 >> std::hex >> state[0]; s_state_1 >> std::hex >> state[1]; - + o->m_rnd.setState(state); - + return 0; } diff --git a/src/server/activeobjectmgr.cpp b/src/server/activeobjectmgr.cpp index c6f1010ea..f0216d9e3 100644 --- a/src/server/activeobjectmgr.cpp +++ b/src/server/activeobjectmgr.cpp @@ -91,7 +91,7 @@ bool ActiveObjectMgr::registerObject(std::unique_ptr obj) return false; } - auto obj_id = obj->getId(); + auto obj_id = obj->getId(); m_active_objects.put(obj_id, std::move(obj)); auto new_size = m_active_objects.size(); diff --git a/src/servermap.cpp b/src/servermap.cpp index eeabd74f9..0248497c1 100644 --- a/src/servermap.cpp +++ b/src/servermap.cpp @@ -52,7 +52,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif /* - ServerMap + ServerMap */ ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, From 48845de46ef400e532f2108dfb9ff9a3107bd66a Mon Sep 17 00:00:00 2001 From: cx384 Date: Sat, 17 Aug 2024 21:15:24 +0200 Subject: [PATCH 09/75] Fix trailing whitespace from #14945 --- 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 b37959eed..4d461f335 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -8940,7 +8940,7 @@ Player properties need to be saved manually. Entity definition ----------------- -Used by `minetest.register_entity`. +Used by `minetest.register_entity`. The entity definition table becomes a metatable of a newly created per-entity luaentity table, meaning its fields (e.g. `initial_properties`) will be shared between all instances of an entity. From b0107144261f6513bbde95323c9663a71ed8f3c9 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 19 Aug 2024 09:17:52 +0200 Subject: [PATCH 10/75] [no sq] Move shaders & remove dead Irrlicht tests (#15006) * Move irrlicht shaders to correct place * Remove unused Irrlicht tests --- client/shaders/Irrlicht | 1 - .../shaders/Irrlicht}/OneTextureBlend.fsh | 0 .../shaders/Irrlicht}/Renderer2D.fsh | 0 .../shaders/Irrlicht}/Renderer2D.vsh | 0 .../shaders/Irrlicht}/Renderer2D_noTex.fsh | 0 .../shaders/Irrlicht}/Solid.fsh | 0 .../shaders/Irrlicht}/Solid.vsh | 0 .../Irrlicht}/TransparentAlphaChannel.fsh | 0 .../Irrlicht}/TransparentAlphaChannelRef.fsh | 0 .../Irrlicht}/TransparentVertexAlpha.fsh | 0 irr/.github/workflows/build.yml | 310 ------------------ irr/CMakeLists.txt | 9 - irr/examples/AutomatedTest/main.cpp | 154 --------- irr/examples/AutomatedTest/test_array.cpp | 138 -------- irr/examples/AutomatedTest/test_helper.h | 33 -- irr/examples/AutomatedTest/test_string.cpp | 205 ------------ irr/examples/CMakeLists.txt | 17 - irr/media/coolguy_opt.x | 2 - irr/media/cooltexture.png | Bin 4828 -> 0 bytes irr/scripts/ci-build-android.sh | 123 ------- irr/scripts/ci-build-mingw.sh | 70 ---- irr/scripts/ci-get-mingw.sh | 9 - irr/scripts/sha256sums.txt | 9 - irr/test/CMakeLists.txt | 32 -- irr/test/data/sample_16bpp_v3.bmp | Bin 654 -> 0 bytes irr/test/data/sample_16bpp_v7.bmp | Bin 738 -> 0 bytes irr/test/data/sample_24bpp.png | Bin 580 -> 0 bytes irr/test/data/sample_24bpp_down.tga | Bin 899 -> 0 bytes irr/test/data/sample_24bpp_rle_down.tga | Bin 844 -> 0 bytes irr/test/data/sample_24bpp_rle_up.tga | Bin 844 -> 0 bytes irr/test/data/sample_24bpp_up.tga | Bin 899 -> 0 bytes irr/test/data/sample_24bpp_v3.bmp | Bin 954 -> 0 bytes irr/test/data/sample_24bpp_v7.bmp | Bin 1038 -> 0 bytes irr/test/data/sample_4bpp_v3.bmp | Bin 298 -> 0 bytes irr/test/data/sample_8bpp.png | Bin 346 -> 0 bytes irr/test/data/sample_8bpp_down.tga | Bin 419 -> 0 bytes irr/test/data/sample_8bpp_rle_down.tga | Bin 444 -> 0 bytes irr/test/data/sample_8bpp_rle_up.tga | Bin 444 -> 0 bytes irr/test/data/sample_8bpp_up.tga | Bin 419 -> 0 bytes irr/test/data/sample_8bpp_v3.bmp | Bin 474 -> 0 bytes irr/test/data/sample_8bpp_v3_rle.bmp | Bin 524 -> 0 bytes irr/test/data/sample_8bpp_v7.bmp | Bin 558 -> 0 bytes irr/test/data/sample_8bpp_v7_rle.bmp | Bin 608 -> 0 bytes irr/test/image_loader_test.cpp | 162 --------- 44 files changed, 1274 deletions(-) delete mode 120000 client/shaders/Irrlicht rename {irr/media/Shaders => client/shaders/Irrlicht}/OneTextureBlend.fsh (100%) rename {irr/media/Shaders => client/shaders/Irrlicht}/Renderer2D.fsh (100%) rename {irr/media/Shaders => client/shaders/Irrlicht}/Renderer2D.vsh (100%) rename {irr/media/Shaders => client/shaders/Irrlicht}/Renderer2D_noTex.fsh (100%) rename {irr/media/Shaders => client/shaders/Irrlicht}/Solid.fsh (100%) rename {irr/media/Shaders => client/shaders/Irrlicht}/Solid.vsh (100%) rename {irr/media/Shaders => client/shaders/Irrlicht}/TransparentAlphaChannel.fsh (100%) rename {irr/media/Shaders => client/shaders/Irrlicht}/TransparentAlphaChannelRef.fsh (100%) rename {irr/media/Shaders => client/shaders/Irrlicht}/TransparentVertexAlpha.fsh (100%) delete mode 100644 irr/.github/workflows/build.yml delete mode 100644 irr/examples/AutomatedTest/main.cpp delete mode 100644 irr/examples/AutomatedTest/test_array.cpp delete mode 100644 irr/examples/AutomatedTest/test_helper.h delete mode 100644 irr/examples/AutomatedTest/test_string.cpp delete mode 100644 irr/examples/CMakeLists.txt delete mode 100755 irr/media/coolguy_opt.x delete mode 100755 irr/media/cooltexture.png delete mode 100755 irr/scripts/ci-build-android.sh delete mode 100755 irr/scripts/ci-build-mingw.sh delete mode 100755 irr/scripts/ci-get-mingw.sh delete mode 100644 irr/scripts/sha256sums.txt delete mode 100644 irr/test/CMakeLists.txt delete mode 100644 irr/test/data/sample_16bpp_v3.bmp delete mode 100644 irr/test/data/sample_16bpp_v7.bmp delete mode 100644 irr/test/data/sample_24bpp.png delete mode 100644 irr/test/data/sample_24bpp_down.tga delete mode 100644 irr/test/data/sample_24bpp_rle_down.tga delete mode 100644 irr/test/data/sample_24bpp_rle_up.tga delete mode 100644 irr/test/data/sample_24bpp_up.tga delete mode 100644 irr/test/data/sample_24bpp_v3.bmp delete mode 100644 irr/test/data/sample_24bpp_v7.bmp delete mode 100644 irr/test/data/sample_4bpp_v3.bmp delete mode 100644 irr/test/data/sample_8bpp.png delete mode 100644 irr/test/data/sample_8bpp_down.tga delete mode 100644 irr/test/data/sample_8bpp_rle_down.tga delete mode 100644 irr/test/data/sample_8bpp_rle_up.tga delete mode 100644 irr/test/data/sample_8bpp_up.tga delete mode 100644 irr/test/data/sample_8bpp_v3.bmp delete mode 100644 irr/test/data/sample_8bpp_v3_rle.bmp delete mode 100644 irr/test/data/sample_8bpp_v7.bmp delete mode 100644 irr/test/data/sample_8bpp_v7_rle.bmp delete mode 100644 irr/test/image_loader_test.cpp diff --git a/client/shaders/Irrlicht b/client/shaders/Irrlicht deleted file mode 120000 index 9349d3073..000000000 --- a/client/shaders/Irrlicht +++ /dev/null @@ -1 +0,0 @@ -../../irr/media/Shaders \ No newline at end of file diff --git a/irr/media/Shaders/OneTextureBlend.fsh b/client/shaders/Irrlicht/OneTextureBlend.fsh similarity index 100% rename from irr/media/Shaders/OneTextureBlend.fsh rename to client/shaders/Irrlicht/OneTextureBlend.fsh diff --git a/irr/media/Shaders/Renderer2D.fsh b/client/shaders/Irrlicht/Renderer2D.fsh similarity index 100% rename from irr/media/Shaders/Renderer2D.fsh rename to client/shaders/Irrlicht/Renderer2D.fsh diff --git a/irr/media/Shaders/Renderer2D.vsh b/client/shaders/Irrlicht/Renderer2D.vsh similarity index 100% rename from irr/media/Shaders/Renderer2D.vsh rename to client/shaders/Irrlicht/Renderer2D.vsh diff --git a/irr/media/Shaders/Renderer2D_noTex.fsh b/client/shaders/Irrlicht/Renderer2D_noTex.fsh similarity index 100% rename from irr/media/Shaders/Renderer2D_noTex.fsh rename to client/shaders/Irrlicht/Renderer2D_noTex.fsh diff --git a/irr/media/Shaders/Solid.fsh b/client/shaders/Irrlicht/Solid.fsh similarity index 100% rename from irr/media/Shaders/Solid.fsh rename to client/shaders/Irrlicht/Solid.fsh diff --git a/irr/media/Shaders/Solid.vsh b/client/shaders/Irrlicht/Solid.vsh similarity index 100% rename from irr/media/Shaders/Solid.vsh rename to client/shaders/Irrlicht/Solid.vsh diff --git a/irr/media/Shaders/TransparentAlphaChannel.fsh b/client/shaders/Irrlicht/TransparentAlphaChannel.fsh similarity index 100% rename from irr/media/Shaders/TransparentAlphaChannel.fsh rename to client/shaders/Irrlicht/TransparentAlphaChannel.fsh diff --git a/irr/media/Shaders/TransparentAlphaChannelRef.fsh b/client/shaders/Irrlicht/TransparentAlphaChannelRef.fsh similarity index 100% rename from irr/media/Shaders/TransparentAlphaChannelRef.fsh rename to client/shaders/Irrlicht/TransparentAlphaChannelRef.fsh diff --git a/irr/media/Shaders/TransparentVertexAlpha.fsh b/client/shaders/Irrlicht/TransparentVertexAlpha.fsh similarity index 100% rename from irr/media/Shaders/TransparentVertexAlpha.fsh rename to client/shaders/Irrlicht/TransparentVertexAlpha.fsh diff --git a/irr/.github/workflows/build.yml b/irr/.github/workflows/build.yml deleted file mode 100644 index f31521bd2..000000000 --- a/irr/.github/workflows/build.yml +++ /dev/null @@ -1,310 +0,0 @@ -name: build - -# build on c/cpp changes or workflow changes -on: - - push - - pull_request - -jobs: - - linux-gl: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - name: Install deps - run: | - sudo apt-get update - sudo apt-get install g++ cmake libxi-dev libgl1-mesa-dev libpng-dev libjpeg-dev zlib1g-dev -qyy - - - name: Build - run: | - cmake . -DUSE_SDL2=OFF - make VERBOSE=1 -j2 - - - name: Test - run: | - ctest --output-on-failure - - - name: Package - run: | - make DESTDIR=$PWD/_install install - tar -c -I "gzip -9" -f irrlicht-linux.tar.gz -C ./_install/usr/local . - - - uses: actions/upload-artifact@v4 - with: - name: irrlicht-linux - path: ./irrlicht-linux.tar.gz - - linux-gles: - # Xvfb test is broken on 20.04 for unknown reasons (not our bug) - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - name: Install deps - run: | - sudo apt-get update - sudo apt-get install g++ cmake libxi-dev libgles2-mesa-dev libpng-dev libjpeg-dev zlib1g-dev xvfb -qyy - - - name: Build - run: | - cmake . -DBUILD_EXAMPLES=1 -DUSE_SDL2=OFF -DENABLE_OPENGL=OFF -DENABLE_GLES2=ON - make -j2 - - - name: Test (headless) - run: | - cd bin/Linux - ./AutomatedTest null - - - name: Test (Xvfb) - run: | - cd bin/Linux - LIBGL_ALWAYS_SOFTWARE=true xvfb-run ./AutomatedTest ogles2 - - linux-sdl: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - name: Install deps - run: | - sudo apt-get update - sudo apt-get install g++ cmake libsdl2-dev libpng-dev libjpeg-dev zlib1g-dev -qyy - - - name: Build - run: | - cmake . -DBUILD_EXAMPLES=1 -DUSE_SDL2=ON -DCMAKE_BUILD_TYPE=Debug - make -j2 - - - name: Test (headless) - run: | - cd bin/Linux - ./AutomatedTest null - - linux-sdl-gl3: - # Xvfb test is broken on 20.04 for unknown reasons (not our bug) - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v4 - - name: Install deps - run: | - sudo apt-get update - sudo apt-get install g++ cmake libsdl2-dev libpng-dev libjpeg-dev zlib1g-dev xvfb -qyy - - - name: Build - run: | - cmake . -DBUILD_EXAMPLES=1 -DUSE_SDL2=ON -DENABLE_OPENGL=OFF -DENABLE_OPENGL3=ON - make -j2 - - - name: Test (headless) - run: | - cd bin/Linux - ./AutomatedTest null - - - name: Test (Xvfb) - run: | - cd bin/Linux - LIBGL_ALWAYS_SOFTWARE=true xvfb-run ./AutomatedTest opengl3 - - linux-sdl-gles2: - runs-on: ubuntu-20.04 - steps: - - uses: actions/checkout@v4 - - name: Install deps - run: | - sudo apt-get update - sudo apt-get install g++ cmake libsdl2-dev libpng-dev libjpeg-dev zlib1g-dev xvfb -qyy - - - name: Build - run: | - cmake . -DBUILD_EXAMPLES=1 -DUSE_SDL2=ON -DENABLE_OPENGL=OFF -DENABLE_GLES2=ON - make -j2 - - - name: Test (headless) - run: | - cd bin/Linux - ./AutomatedTest null - - - name: Test (Xvfb) - run: | - cd bin/Linux - LIBGL_ALWAYS_SOFTWARE=true xvfb-run ./AutomatedTest ogles2 - - mingw: - name: "MinGW ${{matrix.config.variant}}${{matrix.config.extras}}" - runs-on: ubuntu-22.04 - strategy: - fail-fast: false - matrix: - config: - - {variant: win32, arch: i686} - - {variant: win64, arch: x86_64} - - {variant: win32, arch: i686, extras: "-sdl"} - - {variant: win64, arch: x86_64, extras: "-sdl"} - steps: - - uses: actions/checkout@v4 - - name: Install compiler - run: | - sudo apt-get update && sudo apt-get install cmake -qyy - ./scripts/ci-get-mingw.sh - - - name: Build - run: | - ./scripts/ci-build-mingw.sh package - env: - CC: ${{matrix.config.arch}}-w64-mingw32-clang - CXX: ${{matrix.config.arch}}-w64-mingw32-clang++ - extras: ${{matrix.config.extras}} - - - uses: actions/upload-artifact@v4 - with: - name: irrlicht-${{matrix.config.variant}}${{matrix.config.extras}} - path: ./irrlicht-${{matrix.config.variant}}${{matrix.config.extras}}.zip - - macos: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - name: Install deps - run: | - brew update --auto-update - brew install cmake libpng jpeg - env: - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - - - name: Build - run: | - cmake . -DCMAKE_FIND_FRAMEWORK=LAST -DBUILD_EXAMPLES=1 - make -j3 - - - name: Test (headless) - run: | - ./bin/OSX/AutomatedTest null - - macos-sdl: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - name: Install deps - run: | - brew update --auto-update - brew install cmake libpng jpeg sdl2 - env: - HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1 - HOMEBREW_NO_INSTALL_CLEANUP: 1 - - - name: Build - run: | - cmake . -DCMAKE_FIND_FRAMEWORK=LAST -DBUILD_EXAMPLES=1 -DUSE_SDL2=1 - make -j3 - - msvc: - name: VS 2019 ${{ matrix.config.arch }} ${{ matrix.sdl.label }} - runs-on: windows-2019 - env: - VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50 - # 2023.10.19 - vcpkg_packages: zlib libpng libjpeg-turbo - strategy: - fail-fast: false - matrix: - config: - - - arch: x86 - generator: "-G'Visual Studio 16 2019' -A Win32" - vcpkg_triplet: x86-windows - - - arch: x64 - generator: "-G'Visual Studio 16 2019' -A x64" - vcpkg_triplet: x64-windows - sdl: - - - use: FALSE - label: '(no SDL)' - vcpkg_packages: opengl-registry - - - use: TRUE - label: '(with SDL)' - vcpkg_packages: sdl2 - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Restore from cache and run vcpkg - uses: lukka/run-vcpkg@v7 - with: - vcpkgArguments: ${{env.vcpkg_packages}} ${{matrix.sdl.vcpkg_packages}} - vcpkgDirectory: '${{ github.workspace }}\vcpkg' - appendedCacheKey: ${{ matrix.config.vcpkg_triplet }} - vcpkgGitCommitId: ${{ env.VCPKG_VERSION }} - vcpkgTriplet: ${{ matrix.config.vcpkg_triplet }} - - - name: CMake - run: | - cmake ${{matrix.config.generator}} ` - -DUSE_SDL2=${{matrix.sdl.use}} ` - -DCMAKE_TOOLCHAIN_FILE="${{ github.workspace }}\vcpkg\scripts\buildsystems\vcpkg.cmake" ` - -DCMAKE_BUILD_TYPE=Release . - - - name: Build - run: cmake --build . --config Release - - - name: Create artifact folder - run: | - mkdir artifact/ - mkdir artifact/lib/ - - - name: Move dlls into artifact folder - run: move bin\Win32-VisualStudio\Release\* artifact\lib\ - - - name: Move includes into artifact folder - run: move include artifact/ - - - name: Upload Artifact - uses: actions/upload-artifact@v4 - with: - name: msvc-${{ matrix.config.arch }}-${{matrix.sdl.use}} - path: artifact/ - - android: - name: Android ${{ matrix.arch }} - runs-on: ubuntu-20.04 - env: - ndk_version: "r25c" - ANDROID_NDK: ${{ github.workspace }}/android-ndk - strategy: - matrix: - arch: [armeabi-v7a, arm64-v8a, x86, x86_64] - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Install deps - run: | - sudo rm /var/lib/man-db/auto-update - sudo apt-get update - sudo apt-get install -qyy wget unzip zip gcc-multilib make cmake - - - name: Cache NDK - id: cache-ndk - uses: actions/cache@v4 - with: - key: android-ndk-${{ env.ndk_version }}-linux - path: ${{ env.ANDROID_NDK }} - - - name: Install NDK - run: | - wget --progress=bar:force "http://dl.google.com/android/repository/android-ndk-${ndk_version}-linux.zip" - unzip -q "android-ndk-${ndk_version}-linux.zip" - rm "android-ndk-${ndk_version}-linux.zip" - mv "android-ndk-${ndk_version}" "${ANDROID_NDK}" - if: ${{ steps.cache-ndk.outputs.cache-hit != 'true' }} - - - name: Build - run: ./scripts/ci-build-android.sh ${{ matrix.arch }} - - #- name: Upload Artifact - # uses: actions/upload-artifact@v4 - # with: - # name: irrlicht-android-${{ matrix.arch }} - # path: ${{ runner.temp }}/pkg/${{ matrix.arch }} diff --git a/irr/CMakeLists.txt b/irr/CMakeLists.txt index ccc00f271..dfd6b189a 100644 --- a/irr/CMakeLists.txt +++ b/irr/CMakeLists.txt @@ -11,14 +11,5 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug or Release" FORCE) endif() -# FIXME: tests need to be moved to MT if we want to keep them - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") -#enable_testing() add_subdirectory(src) -#add_subdirectory(test) - -#option(BUILD_EXAMPLES "Build example applications" FALSE) -#if(BUILD_EXAMPLES) -# add_subdirectory(examples) -#endif() diff --git a/irr/examples/AutomatedTest/main.cpp b/irr/examples/AutomatedTest/main.cpp deleted file mode 100644 index c9e5bab68..000000000 --- a/irr/examples/AutomatedTest/main.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include -#include -#include "exampleHelper.h" - -using namespace irr; - -static IrrlichtDevice *device = nullptr; -static int test_fail = 0; - -void test_irr_array(); -void test_irr_string(); - -static video::E_DRIVER_TYPE chooseDriver(core::stringc arg_) -{ - if (arg_ == "null") - return video::EDT_NULL; - if (arg_ == "ogles1") - return video::EDT_OGLES1; - if (arg_ == "ogles2") - return video::EDT_OGLES2; - if (arg_ == "opengl") - return video::EDT_OPENGL; - if (arg_ == "opengl3") - return video::EDT_OPENGL3; - std::cerr << "Unknown driver type: " << arg_.c_str() << ". Trying OpenGL." << std::endl; - return video::EDT_OPENGL; -} - -static inline void check(bool ok, const char *msg) -{ - if (!ok) { - test_fail++; - device->getLogger()->log((core::stringc("FAILED TEST: ") + msg).c_str(), ELL_ERROR); - } -} - -void run_unit_tests() -{ - std::cout << "Running unit tests:" << std::endl; - try { - test_irr_array(); - test_irr_string(); - } catch (const std::exception &e) { - std::cerr << e.what() << std::endl; - test_fail++; - } - std::cout << std::endl; -} - -int main(int argc, char *argv[]) -{ - run_unit_tests(); - - SIrrlichtCreationParameters p; - p.DriverType = chooseDriver(argc > 1 ? argv[1] : ""); - p.WindowSize = core::dimension2du(640, 480); - p.Vsync = true; - p.LoggingLevel = ELL_DEBUG; - - device = createDeviceEx(p); - if (!device) - return 1; - - { - u32 total = 0; - device->getOSOperator()->getSystemMemory(&total, nullptr); - core::stringc message = core::stringc("Total RAM in MiB: ") + core::stringc(total >> 10); - device->getLogger()->log(message.c_str(), ELL_INFORMATION); - check(total > 130 * 1024, "RAM amount"); - } - - device->setWindowCaption(L"Hello World!"); - device->setResizable(true); - - video::IVideoDriver *driver = device->getVideoDriver(); - scene::ISceneManager *smgr = device->getSceneManager(); - gui::IGUIEnvironment *guienv = device->getGUIEnvironment(); - - guienv->addStaticText(L"sample text", core::rect(10, 10, 110, 22), false); - - gui::IGUIButton *button = guienv->addButton( - core::rect(10, 30, 110, 30 + 32), 0, -1, L"sample button", - L"sample tooltip"); - - gui::IGUIEditBox *editbox = guienv->addEditBox(L"", - core::rect(10, 70, 60, 70 + 16)); - - const io::path mediaPath = getExampleMediaPath(); - - auto mesh_file = device->getFileSystem()->createAndOpenFile(mediaPath + "coolguy_opt.x"); - check(mesh_file, "mesh file loading"); - scene::IAnimatedMesh *mesh = smgr->getMesh(mesh_file); - check(mesh, "mesh loading"); - if (mesh_file) - mesh_file->drop(); - if (mesh) { - video::ITexture *tex = driver->getTexture(mediaPath + "cooltexture.png"); - check(tex, "texture loading"); - scene::IAnimatedMeshSceneNode *node = smgr->addAnimatedMeshSceneNode(mesh); - if (node) { - node->forEachMaterial([tex](video::SMaterial &mat) { - mat.Lighting = false; - mat.setTexture(0, tex); - }); - node->setFrameLoop(0, 29); - node->setAnimationSpeed(30); - } - } - - smgr->addCameraSceneNode(0, core::vector3df(0, 4, 5), core::vector3df(0, 2, 0)); - - s32 n = 0; - SEvent event; - device->getTimer()->start(); - - while (device->run()) { - if (device->getTimer()->getTime() >= 1000) { - device->getTimer()->setTime(0); - ++n; - if (n == 1) { // Tooltip display - bzero(&event, sizeof(SEvent)); - event.EventType = irr::EET_MOUSE_INPUT_EVENT; - event.MouseInput.Event = irr::EMIE_MOUSE_MOVED; - event.MouseInput.X = button->getAbsolutePosition().getCenter().X; - event.MouseInput.Y = button->getAbsolutePosition().getCenter().Y; - device->postEventFromUser(event); - } else if (n == 2) // Text input focus - guienv->setFocus(editbox); - else if (n == 3) { // Keypress for Text input - bzero(&event, sizeof(SEvent)); - event.EventType = irr::EET_KEY_INPUT_EVENT; - event.KeyInput.Char = L'a'; - event.KeyInput.Key = KEY_KEY_A; - event.KeyInput.PressedDown = true; - device->postEventFromUser(event); - event.KeyInput.PressedDown = false; - device->postEventFromUser(event); - } else - device->closeDevice(); - } - - driver->beginScene(video::ECBF_COLOR | video::ECBF_DEPTH, - video::SColor(255, 100, 100, 150)); - smgr->drawAll(); - guienv->drawAll(); - driver->endScene(); - } - - check(core::stringw(L"a") == editbox->getText(), "EditBox text"); - - device->getLogger()->log("Done.", ELL_INFORMATION); - device->drop(); - return test_fail > 0 ? 1 : 0; -} diff --git a/irr/examples/AutomatedTest/test_array.cpp b/irr/examples/AutomatedTest/test_array.cpp deleted file mode 100644 index 42959e913..000000000 --- a/irr/examples/AutomatedTest/test_array.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include "test_helper.h" - -using namespace irr; -using core::array; - -static void test_basics() -{ - array v; - v.push_back(1); // 1 - v.push_front(2); // 2, 1 - v.insert(4, 0); // 4, 2, 1 - v.insert(3, 1); // 4, 3, 2, 1 - v.insert(0, 4); // 4, 3, 2, 1, 0 - UASSERTEQ(v.size(), 5); - UASSERTEQ(v[0], 4); - UASSERTEQ(v[1], 3); - UASSERTEQ(v[2], 2); - UASSERTEQ(v[3], 1); - UASSERTEQ(v[4], 0); - array w = v; - UASSERTEQ(w.size(), 5); - UASSERT(w == v); - w.clear(); - UASSERTEQ(w.size(), 0); - UASSERTEQ(w.allocated_size(), 0); - UASSERT(w.empty()); - w = v; - UASSERTEQ(w.size(), 5); - w.set_used(3); - UASSERTEQ(w.size(), 3); - UASSERTEQ(w[0], 4); - UASSERTEQ(w[1], 3); - UASSERTEQ(w[2], 2); - UASSERTEQ(w.getLast(), 2); - w.set_used(20); - UASSERTEQ(w.size(), 20); - w = v; - w.sort(); - UASSERTEQ(w.size(), 5); - UASSERTEQ(w[0], 0); - UASSERTEQ(w[1], 1); - UASSERTEQ(w[2], 2); - UASSERTEQ(w[3], 3); - UASSERTEQ(w[4], 4); - w.erase(0); - UASSERTEQ(w.size(), 4); - UASSERTEQ(w[0], 1); - UASSERTEQ(w[1], 2); - UASSERTEQ(w[2], 3); - UASSERTEQ(w[3], 4); - w.erase(1, 2); - UASSERTEQ(w.size(), 2); - UASSERTEQ(w[0], 1); - UASSERTEQ(w[1], 4); - w.swap(v); - UASSERTEQ(w.size(), 5); - UASSERTEQ(v.size(), 2); -} - -static void test_linear_searches() -{ - // Populate the array with 0, 1, 2, ..., 100, 100, 99, 98, 97, ..., 0 - array arr; - for (int i = 0; i <= 100; i++) - arr.push_back(i); - for (int i = 100; i >= 0; i--) - arr.push_back(i); - s32 end = arr.size() - 1; - for (int i = 0; i <= 100; i++) { - s32 index = arr.linear_reverse_search(i); - UASSERTEQ(index, end - i); - } - for (int i = 0; i <= 100; i++) { - s32 index = arr.linear_search(i); - UASSERTEQ(index, i); - } -} - -static void test_binary_searches() -{ - const auto &values = {3, 5, 1, 2, 5, 10, 19, 9, 7, 1, 2, 5, 8, 15}; - array arr; - for (int value : values) { - arr.push_back(value); - } - // Test the const form first, it uses a linear search without sorting - const array &carr = arr; - UASSERTEQ(carr.binary_search(20), -1); - UASSERTEQ(carr.binary_search(0), -1); - UASSERTEQ(carr.binary_search(1), 2); - - // Sorted: 1, 1, 2, 2, 3, 5, 5, 5, 7, 8, 9, 10, 15, 19 - UASSERTEQ(arr.binary_search(20), -1); - UASSERTEQ(arr.binary_search(0), -1); - - for (int value : values) { - s32 i = arr.binary_search(value); - UASSERTNE(i, -1); - UASSERTEQ(arr[i], value); - } - - s32 first, last; - first = arr.binary_search_multi(1, last); - UASSERTEQ(first, 0); - UASSERTEQ(last, 1); - - first = arr.binary_search_multi(2, last); - UASSERTEQ(first, 2); - UASSERTEQ(last, 3); - - first = arr.binary_search_multi(3, last); - UASSERTEQ(first, 4); - UASSERTEQ(last, 4); - - first = arr.binary_search_multi(4, last); - UASSERTEQ(first, -1); - - first = arr.binary_search_multi(5, last); - UASSERTEQ(first, 5); - UASSERTEQ(last, 7); - - first = arr.binary_search_multi(7, last); - UASSERTEQ(first, 8); - UASSERTEQ(last, 8); - - first = arr.binary_search_multi(19, last); - UASSERTEQ(first, 13); - UASSERTEQ(last, 13); -} - -void test_irr_array() -{ - test_basics(); - test_linear_searches(); - test_binary_searches(); - std::cout << " test_irr_array PASSED" << std::endl; -} diff --git a/irr/examples/AutomatedTest/test_helper.h b/irr/examples/AutomatedTest/test_helper.h deleted file mode 100644 index 5229eff29..000000000 --- a/irr/examples/AutomatedTest/test_helper.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include - -class TestFailedException : public std::exception -{ -}; - -// Asserts the comparison specified by CMP is true, or fails the current unit test -#define UASSERTCMP(CMP, actual, expected) \ - do { \ - const auto &a = (actual); \ - const auto &e = (expected); \ - if (!CMP(a, e)) { \ - std::cout \ - << "Test assertion failed: " << #actual << " " << #CMP << " " \ - << #expected << std::endl \ - << " at " << __FILE__ << ":" << __LINE__ << std::endl \ - << " actual: " << a << std::endl \ - << " expected: " \ - << e << std::endl; \ - throw TestFailedException(); \ - } \ - } while (0) - -#define CMPEQ(a, e) (a == e) -#define CMPTRUE(a, e) (a) -#define CMPNE(a, e) (a != e) - -#define UASSERTEQ(actual, expected) UASSERTCMP(CMPEQ, actual, expected) -#define UASSERTNE(actual, nexpected) UASSERTCMP(CMPNE, actual, nexpected) -#define UASSERT(actual) UASSERTCMP(CMPTRUE, actual, true) diff --git a/irr/examples/AutomatedTest/test_string.cpp b/irr/examples/AutomatedTest/test_string.cpp deleted file mode 100644 index 4d1291f18..000000000 --- a/irr/examples/AutomatedTest/test_string.cpp +++ /dev/null @@ -1,205 +0,0 @@ -#include -#include -#include -#include -#include "test_helper.h" - -using namespace irr; -using namespace irr::core; - -#define CMPSTR(a, b) (!strcmp(a, b)) -#define UASSERTSTR(actual, expected) UASSERTCMP(CMPSTR, actual.c_str(), expected) - -static void test_basics() -{ - // ctor - stringc s; - UASSERTEQ(s.c_str()[0], '\0'); - s = stringc(0.1234567); - UASSERTSTR(s, "0.123457"); - s = stringc(0x1p+53); - UASSERTSTR(s, "9007199254740992.000000"); - s = stringc(static_cast(-102400)); - UASSERTSTR(s, "-102400"); - s = stringc(static_cast(102400)); - UASSERTSTR(s, "102400"); - s = stringc(static_cast(-1024000)); - UASSERTSTR(s, "-1024000"); - s = stringc(static_cast(1024000)); - UASSERTSTR(s, "1024000"); - s = stringc("YESno", 3); - UASSERTSTR(s, "YES"); - s = stringc(L"test", 4); - UASSERTSTR(s, "test"); - s = stringc("Hello World!"); - UASSERTSTR(s, "Hello World!"); - // operator= - s = stringw(L"abcdef"); - UASSERTSTR(s, "abcdef"); - s = L"abcdef"; - UASSERTSTR(s, "abcdef"); - s = static_cast(nullptr); - UASSERTSTR(s, ""); - // operator+ - s = s + stringc("foo"); - UASSERTSTR(s, "foo"); - s = s + L"bar"; - UASSERTSTR(s, "foobar"); - // the rest - s = "f"; - UASSERTEQ(s[0], 'f'); - const auto &sref = s; - UASSERTEQ(sref[0], 'f'); - UASSERT(sref == "f"); - UASSERT(sref == stringc("f")); - s = "a"; - UASSERT(sref < stringc("aa")); - UASSERT(sref < stringc("b")); - UASSERT(stringc("Z") < sref); - UASSERT(!(sref < stringc("a"))); - UASSERT(sref.lower_ignore_case("AA")); - UASSERT(sref.lower_ignore_case("B")); - UASSERT(!sref.lower_ignore_case("A")); - s = "dog"; - UASSERT(sref != "cat"); - UASSERT(sref != stringc("cat")); -} - -static void test_methods() -{ - stringc s; - const auto &sref = s; - s = "irrlicht"; - UASSERTEQ(sref.size(), 8); - UASSERT(!sref.empty()); - s.clear(); - UASSERTEQ(sref.size(), 0); - UASSERT(sref.empty()); - UASSERT(sref[0] == 0); - s = "\tAz#`"; - s.make_lower(); - UASSERTSTR(s, "\taz#`"); - s.make_upper(); - UASSERTSTR(s, "\tAZ#`"); - UASSERT(sref.equals_ignore_case("\taz#`")); - UASSERT(sref.equals_substring_ignore_case("Z#`", 2)); - s = "irrlicht"; - UASSERT(sref.equalsn(stringc("irr"), 3)); - UASSERT(sref.equalsn("irr", 3)); - s = "fo"; - s.append('o'); - UASSERTSTR(s, "foo"); - s.append("bar", 1); - UASSERTSTR(s, "foob"); - s.append("ar", 999999); - UASSERTSTR(s, "foobar"); - s = "nyan"; - s.append(stringc("cat")); - UASSERTSTR(s, "nyancat"); - s.append(stringc("sam"), 1); - UASSERTSTR(s, "nyancats"); - s = "fbar"; - s.insert(1, "ooXX", 2); - UASSERTSTR(s, "foobar"); - UASSERTEQ(sref.findFirst('o'), 1); - UASSERTEQ(sref.findFirst('X'), -1); - UASSERTEQ(sref.findFirstChar("abff", 2), 3); - UASSERTEQ(sref.findFirstCharNotInList("fobb", 2), 3); - UASSERTEQ(sref.findLast('o'), 2); - UASSERTEQ(sref.findLast('X'), -1); - UASSERTEQ(sref.findLastChar("abrr", 2), 4); - UASSERTEQ(sref.findLastCharNotInList("rabb", 2), 3); - UASSERTEQ(sref.findNext('o', 2), 2); - UASSERTEQ(sref.findLast('o', 1), 1); - s = "ob-oob"; - UASSERTEQ(sref.find("ob", 1), 4); - UASSERTEQ(sref.find("ob"), 0); - UASSERTEQ(sref.find("?"), -1); - s = "HOMEOWNER"; - stringc s2 = sref.subString(2, 4); - UASSERTSTR(s2, "MEOW"); - s2 = sref.subString(2, 4, true); - UASSERTSTR(s2, "meow"); - s = "land"; - s.replace('l', 's'); - UASSERTSTR(s, "sand"); - s = ">dog<"; - s.replace("dog", "cat"); - UASSERTSTR(s, ">cat<"); - s.replace("cat", "horse"); - UASSERTSTR(s, ">horse<"); - s.replace("horse", "gnu"); - UASSERTSTR(s, ">gnu<"); - s = " h e l p "; - s.remove(' '); - UASSERTSTR(s, "help"); - s.remove("el"); - UASSERTSTR(s, "hp"); - s = "irrlicht"; - s.removeChars("it"); - UASSERTSTR(s, "rrlch"); - s = "\r\nfoo bar "; - s.trim(); - UASSERTSTR(s, "foo bar"); - s = "foxo"; - s.erase(2); - UASSERTSTR(s, "foo"); - s = "a"; - s.append('\0'); - s.append('b'); - UASSERTEQ(s.size(), 3); - s.validate(); - UASSERTEQ(s.size(), 1); - UASSERTEQ(s.lastChar(), 'a'); - std::vector res; - s = "a,,b,c"; - s.split(res, ",aa", 1, true, false); - UASSERTEQ(res.size(), 3); - UASSERTSTR(res[0], "a"); - UASSERTSTR(res[2], "c"); - res.clear(); - s.split(res, ",", 1, false, true); - UASSERTEQ(res.size(), 7); - UASSERTSTR(res[0], "a"); - UASSERTSTR(res[2], ""); - for (int i = 0; i < 3; i++) - UASSERTSTR(res[2 * i + 1], ","); -} - -static void test_conv() -{ - // locale-independent - - stringw out; - utf8ToWString(out, "†††"); - UASSERTEQ(out.size(), 3); - for (int i = 0; i < 3; i++) - UASSERTEQ(static_cast(out[i]), 0x2020); - - stringc out2; - wStringToUTF8(out2, L"†††"); - UASSERTEQ(out2.size(), 9); - for (int i = 0; i < 3; i++) { - UASSERTEQ(static_cast(out2[3 * i]), 0xe2); - UASSERTEQ(static_cast(out2[3 * i + 1]), 0x80); - UASSERTEQ(static_cast(out2[3 * i + 2]), 0xa0); - } - - // locale-dependent - if (!setlocale(LC_CTYPE, "C.UTF-8")) - setlocale(LC_CTYPE, "UTF-8"); // macOS - - stringw out3; - multibyteToWString(out3, "†††"); - UASSERTEQ(out3.size(), 3); - for (int i = 0; i < 3; i++) - UASSERTEQ(static_cast(out3[i]), 0x2020); -} - -void test_irr_string() -{ - test_basics(); - test_methods(); - test_conv(); - std::cout << " test_irr_string PASSED" << std::endl; -} diff --git a/irr/examples/CMakeLists.txt b/irr/examples/CMakeLists.txt deleted file mode 100644 index 03553048e..000000000 --- a/irr/examples/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -set(IRREXAMPLES - # removed -) -if(UNIX) - list(APPEND IRREXAMPLES AutomatedTest) -endif() - -foreach(exname IN ITEMS ${IRREXAMPLES}) - file(GLOB sources "${CMAKE_CURRENT_SOURCE_DIR}/${exname}/*.cpp") - add_executable(${exname} ${sources}) - - target_include_directories(${exname} PRIVATE - ${CMAKE_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/${exname} - ) - target_link_libraries(${exname} IrrlichtMt) -endforeach() diff --git a/irr/media/coolguy_opt.x b/irr/media/coolguy_opt.x deleted file mode 100755 index e806d8315..000000000 --- a/irr/media/coolguy_opt.x +++ /dev/null @@ -1,2 +0,0 @@ -xof 0303txt 0032 -AnimationSet{Animation{{Armature}AnimationKey{0;2;0;4;1,0,0,0;;,29;4;1,0,0,0;;;}AnimationKey{2;2;0;3;0,0,0;;,29;3;0,0,0;;;}}Animation{{Armature_knee_r}AnimationKey{0;16;0;4;0.864183,0.503177,0,0;;,1;4;0.829812,0.558043,0,0;;,3;4;0.708698,0.705512,0,0;;,5;4;0.589108,0.808054,0,0;;,7;4;0.593659,0.804717,0,0;;,9;4;0.748627,0.662991,0,0;;,11;4;0.910305,0.413938,0,0;;,13;4;0.975925,0.218107,0,0;;,15;4;0.981302,0.192476,0,0;;,17;4;0.975476,0.220108,0,0;;,19;4;0.963662,0.267124,0,0;;,21;4;0.945893,0.324478,0,0;;,23;4;0.923816,0.382838,0,0;;,25;4;0.901205,0.433394,0,0;;,27;4;0.883429,0.468566,0,0;;,29;4;0.876305,0.481757,0,0;;;}AnimationKey{2;2;0;3;0,0,1.10139;;,29;3;0,0,1.10139;;;}}Animation{{Armature_elbow_r}AnimationKey{0;16;0;4;0.756295,0.004619,-0.619265,0.210967;;,1;4;0.771977,0.005599,-0.60257,0.202311;;,3;4;0.825501,0.009164,-0.538259,0.169533;;,5;4;0.891859,0.014253,-0.436142,0.119019;;,7;4;0.949154,0.019821,-0.308768,0.058108;;,9;4;0.983251,0.024703,-0.18057,-0.001258;;,11;4;0.995416,0.028143,-0.07812,-0.047458;;,13;4;0.996672,0.02991,-0.020368,-0.073041;;,15;4;0.996672,0.02991,-0.020368,-0.073041;;,17;4;0.995416,0.028143,-0.07812,-0.047458;;,19;4;0.983251,0.024703,-0.18057,-0.001258;;,21;4;0.949154,0.019821,-0.308768,0.058108;;,23;4;0.891859,0.014253,-0.436142,0.119019;;,25;4;0.825501,0.009164,-0.538259,0.169533;;,27;4;0.771977,0.005599,-0.60257,0.202311;;,29;4;0.750682,0.004275,-0.625038,0.213976;;;}AnimationKey{2;2;0;3;0,0,0.754892;;,29;3;0,0,0.754892;;;}}Animation{{Armature_arm_r}AnimationKey{0;16;0;4;0.28219,0.629905,0.723388,-0.017285;;,1;4;0.277641,0.632543,0.722699,-0.022614;;,3;4;0.261375,0.641615,0.719924,-0.041507;;,5;4;0.238321,0.653533,0.715186,-0.067874;;,7;4;0.212026,0.665838,0.708676,-0.097381;;,9;4;0.186345,0.676585,0.701229,-0.125643;;,11;4;0.165298,0.684491,0.694351,-0.14841;;,13;4;0.152894,0.688778,0.68998,-0.161665;;,15;4;0.152894,0.688779,0.68998,-0.161665;;,17;4;0.165298,0.684491,0.694351,-0.14841;;,19;4;0.186345,0.676585,0.701229,-0.125643;;,21;4;0.212026,0.665838,0.708676,-0.097381;;,23;4;0.238321,0.653533,0.715186,-0.067874;;,25;4;0.261375,0.641615,0.719924,-0.041507;;,27;4;0.277641,0.632543,0.722699,-0.022614;;,29;4;0.283802,0.628959,0.723623,-0.015394;;;}AnimationKey{2;2;0;3;-0.545315,0,1;;,29;3;-0.545315,0,1;;;}}Animation{{Armature_knee_l}AnimationKey{0;16;0;4;0.981896,0.189423,0,0;;,1;4;0.9814,0.191974,0,0;;,3;4;0.979127,0.203251,0,0;;,5;4;0.974526,0.224276,0,0;;,7;4;0.96645,0.256853,0,0;;,9;4;0.953088,0.302692,0,0;;,11;4;0.931731,0.36315,0,0;;,13;4;0.898645,0.438676,0,0;;,15;4;0.848226,0.529634,0,0;;,17;4;0.773692,0.633562,0,0;;,19;4;0.689831,0.72397,0,0;;,21;4;0.629304,0.777159,0,0;;,23;4;0.648685,0.761057,0,0;;,25;4;0.812268,0.583284,0,0;;,27;4;0.948066,0.318074,0,0;;,29;4;0.982049,0.188624,0,0;;;}AnimationKey{2;2;0;3;0,0,1.10139;;,29;3;0,0,1.10139;;;}}Animation{{Armature_Bone_007}AnimationKey{0;16;0;4;0.993671,-0.112331,0,0;;,1;4;0.994784,-0.102002,0,0;;,3;4;0.997507,-0.070564,0,0;;,5;4;0.999237,-0.039056,0,0;;,7;4;0.999694,-0.024737,0,0;;,9;4;0.999079,-0.042907,0,0;;,11;4;0.99677,-0.080308,0,0;;,13;4;0.993798,-0.111199,0,0;;,15;4;0.993599,-0.112965,0,0;;,17;4;0.995813,-0.091409,0,0;;,19;4;0.998181,-0.060285,0,0;;,21;4;0.999479,-0.032286,0,0;;,23;4;0.999797,-0.020142,0,0;;,25;4;0.998983,-0.045097,0,0;;,27;4;0.995813,-0.091409,0,0;;,29;4;0.993221,-0.116243,0,0;;;}AnimationKey{2;2;0;3;0,0,1.221802;;,29;3;0,0,1.221802;;;}}Animation{{Armature_elbow_l}AnimationKey{0;16;0;4;0.995195,-0.034868,-0.015799,-0.090119;;,1;4;0.993465,-0.046368,-0.030155,-0.099838;;,3;4;0.983557,-0.0879,-0.082099,-0.134715;;,5;4;0.959324,-0.146904,-0.156177,-0.183648;;,7;4;0.917546,-0.212233,-0.238611,-0.236921;;,9;4;0.864109,-0.271657,-0.314022,-0.284443;;,11;4;0.813172,-0.315829,-0.370387,-0.319087;;,13;4;0.781004,-0.339668,-0.400938,-0.337501;;,15;4;0.781004,-0.339668,-0.400938,-0.337501;;,17;4;0.813172,-0.315829,-0.370387,-0.319087;;,19;4;0.864109,-0.271657,-0.314022,-0.284443;;,21;4;0.917546,-0.212233,-0.238611,-0.236921;;,23;4;0.959324,-0.146904,-0.156177,-0.183648;;,25;4;0.983557,-0.0879,-0.082099,-0.134715;;,27;4;0.993465,-0.046368,-0.030155,-0.099838;;,29;4;0.995701,-0.030812,-0.010739,-0.086685;;;}AnimationKey{2;2;0;3;0,0,0.754892;;,29;3;0,0,0.754892;;;}}Animation{{Armature_body}AnimationKey{0;16;0;4;-0,0,0.601298,0.799025;;,1;4;-0,0,0.608144,0.793827;;,3;4;-0,0,0.627465,0.778645;;,5;4;-0,0,0.643183,0.765712;;,7;4;-0,0,0.643755,0.765231;;,9;4;-0,0,0.631076,0.775721;;,11;4;-0,0,0.613775,0.789481;;,13;4;-0,0,0.6007,0.799474;;,15;4;-0,0,0.601488,0.798882;;,17;4;-0,0,0.619499,0.784997;;,19;4;-0,0,0.643196,0.765702;;,21;4;-0,0,0.660441,0.750878;;,23;4;-0,0,0.659666,0.751559;;,25;4;-0,0,0.638264,0.769817;;,27;4;-0,0,0.611752,0.791049;;,29;4;-0,0,0.598631,0.801025;;;}AnimationKey{2;2;0;3;0,2.580534,0;;,29;3;0,2.571201,0;;;}}Animation{{Armature_leg_l}AnimationKey{0;16;0;4;0.390287,0.920693,0,0;;,1;4;0.362565,0.931959,0,0;;,3;4;0.266163,0.963928,0,0;;,5;4;0.138294,0.990391,0,0;;,7;4;0.012725,0.999919,0,0;;,9;4;-0.090194,0.995924,0,0;;,11;4;-0.162502,0.986708,0,0;;,13;4;-0.201466,0.979496,0,0;;,15;4;-0.185641,0.982618,0,0;;,17;4;-0.013697,0.999906,0,0;;,19;4;0.24238,0.970181,0,0;;,21;4;0.417271,0.908782,0,0;;,23;4;0.439308,0.898336,0,0;;,25;4;0.424255,0.905543,0,0;;,27;4;0.407664,0.913132,0,0;;,29;4;0.400263,0.9164,0,0;;;}AnimationKey{2;2;0;3;0.246294,0,-0.171352;;,29;3;0.246294,0,-0.171351;;;}}Animation{{Armature_leg_r}AnimationKey{0;16;0;4;0.174933,-0.98458,0,0;;,1;4;0.082829,-0.996564,0,0;;,3;4;-0.21147,-0.977384,0,0;;,5;4;-0.442802,-0.89662,0,0;;,7;4;-0.47604,-0.879424,0,0;;,9;4;-0.47279,-0.881175,0,0;;,11;4;-0.459567,-0.888143,0,0;;,13;4;-0.427425,-0.904051,0,0;;,15;4;-0.361724,-0.932285,0,0;;,17;4;-0.251362,-0.967893,0,0;;,19;4;-0.114531,-0.99342,0,0;;,21;4;0.021053,-0.999778,0,0;;,23;4;0.12473,-0.992191,0,0;;,25;4;0.181473,-0.983396,0,0;;,27;4;0.204037,-0.978963,0,0;;,29;4;0.208187,-0.978089,0,0;;;}AnimationKey{2;2;0;3;-0.246294,0,-0.171352;;,29;3;-0.246294,0,-0.171351;;;}}Animation{{Armature_arm_l}AnimationKey{0;16;0;4;0.200754,-0.659656,-0.716264,-0.107316;;,1;4;0.192268,-0.660735,-0.716526,-0.114246;;,3;4;0.161871,-0.663925,-0.716753,-0.138802;;,5;4;0.118745,-0.666682,-0.715211,-0.17294;;,7;4;0.069733,-0.667364,-0.710872,-0.210767;;,9;4;0.022313,-0.665594,-0.704111,-0.246404;;,11;4;-0.016046,-0.662426,-0.696821,-0.274543;;,13;4;-0.038374,-0.659874,-0.691824,-0.290643;;,15;4;-0.038373,-0.659874,-0.691824,-0.290643;;,17;4;-0.016044,-0.662427,-0.696822,-0.274543;;,19;4;0.022312,-0.665594,-0.70411,-0.246404;;,21;4;0.069733,-0.667365,-0.710872,-0.210767;;,23;4;0.118745,-0.666682,-0.715211,-0.17294;;,25;4;0.161871,-0.663925,-0.716753,-0.138802;;,27;4;0.192268,-0.660735,-0.716526,-0.114246;;,29;4;0.203757,-0.659255,-0.716151,-0.104856;;;}AnimationKey{2;2;0;3;0.545315,0,1;;,29;3;0.545315,0,1;;;}}}Frame Root{FrameTransformMatrix{1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1;;}Frame Armature{FrameTransformMatrix{1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1;;}Frame Armature_body{FrameTransformMatrix{-1,0,0,0,0,0,1,0,0,1,0,0,0,2.571201,0,1;;}Frame Armature_arm_r{FrameTransformMatrix{-0.047733,0.997488,-0.05233,0,0.901521,0.020464,-0.432251,0,-0.430095,-0.067809,-0.900233,0,-0.545315,0,1,1;;}Frame Armature_elbow_r{FrameTransformMatrix{0.987983,0.151721,-0.029519,0,-0.153228,0.986478,-0.058162,0,0.020295,0.061987,0.997871,0,0,0,0.754892,1;;}}}Frame Armature_arm_l{FrameTransformMatrix{-0.047732,0.994072,-0.097683,0,0.901521,0.084983,0.424309,0,0.430095,-0.067809,-0.900233,0,0.545315,0,1,1;;}Frame Armature_elbow_l{FrameTransformMatrix{0.984741,0.173286,-0.016044,0,-0.171963,0.983073,0.063221,0,0.026727,-0.059497,0.99787,0,0,0,0.754892,1;;}}}Frame Armature_leg_l{FrameTransformMatrix{1,0,0,0,0,-0.998426,-0.056453,0,0,0.056453,-0.998405,0,0.246294,0,-0.171351,1;;}Frame Armature_knee_l{FrameTransformMatrix{1,0,0,0,0,0.993861,-0.110639,0,0,0.110639,0.993861,0,0,0,1.10139,1;;}}}Frame Armature_leg_r{FrameTransformMatrix{1,0,0,0,0,-0.998426,-0.056453,0,0,0.056453,-0.998405,0,-0.246294,0,-0.171351,1;;}Frame Armature_knee_r{FrameTransformMatrix{1,0,0,0,0,0.993861,-0.110639,0,0,0.110639,0.993861,0,0,0,1.10139,1;;}}}Frame Armature_Bone_007{FrameTransformMatrix{1,0,0,0,0,1,0,0,0,0,1,0,0,0,1.221802,1;;}}}Frame cool_dude{FrameTransformMatrix{-1,0,0,0,0,1,0,0,0,0,-1,0,0,0,0,1;;}Mesh{272;0;2.440814;0.219926;,0;3.688199;0.219926;,0.466212;3.688199;0.219926;,0.466212;2.440814;0.219926;,0.466212;2.440814;0.219926;,0.466212;3.688199;0.219926;,0.466212;3.688199;-0.219926;,0.466212;2.440814;-0.219926;,0;2.440814;0.219926;,0.466212;2.440814;0.219926;,0.466212;2.440814;-0.219926;,0;2.440814;-0.219926;,0.055633;1.27575;-0.190081;,0.055633;2.35741;-0.190081;,0.055633;2.35741;0.190081;,0.055633;1.27575;0.190081;,0.055633;1.27575;0.190081;,0.055633;2.35741;0.190081;,0.43017;2.35741;0.190081;,0.43017;1.27575;0.190081;,0.43017;1.27575;0.190081;,0.43017;2.35741;0.190081;,0.43017;2.35741;-0.190081;,0.43017;1.27575;-0.190081;,0.43017;1.27575;-0.190081;,0.43017;2.35741;-0.190081;,0.055633;2.35741;-0.190081;,0.055633;1.27575;-0.190081;,0.055633;1.27575;0.190081;,0.43017;1.27575;0.190081;,0.43017;1.27575;-0.190081;,0.055633;1.27575;-0.190081;,0.43017;2.35741;0.190081;,0.055633;2.35741;0.190081;,0.055633;2.35741;-0.190081;,0.43017;2.35741;-0.190081;,0.466212;3.688199;0.219926;,0;3.688199;0.219926;,0;3.688199;-0.219926;,0.466212;3.688199;-0.219926;,0.466212;2.440814;-0.219926;,0.466212;3.688199;-0.219926;,0;3.688199;-0.219926;,0;2.440814;-0.219926;,0.769341;2.834949;-0.041122;,0.440953;3.555781;-0.041122;,0.440953;3.555781;0.207294;,0.769341;2.834949;0.207294;,0.769341;2.834949;0.207294;,0.440953;3.555781;0.207294;,0.616273;3.635651;0.207294;,0.944661;2.914819;0.207294;,0.944661;2.914819;0.207294;,0.616273;3.635651;0.207294;,0.616273;3.635651;-0.041122;,0.944661;2.914819;-0.041122;,0.944661;2.914819;-0.041122;,0.616273;3.635651;-0.041122;,0.440953;3.555781;-0.041122;,0.769341;2.834949;-0.041122;,0.769341;2.834949;0.207294;,0.944661;2.914819;0.207294;,0.944661;2.914819;-0.041122;,0.769341;2.834949;-0.041122;,0.616273;3.635651;0.207294;,0.440953;3.555781;0.207294;,0.440953;3.555781;-0.041122;,0.616273;3.635651;-0.041122;,1.104504;2.080977;-0.086788;,0.776116;2.801809;-0.086788;,0.776116;2.801809;0.161627;,1.104504;2.080977;0.161627;,1.104504;2.080977;0.161627;,0.776116;2.801809;0.161627;,0.951436;2.881679;0.161627;,1.279824;2.160847;0.161627;,1.279824;2.160847;0.161627;,0.951436;2.881679;0.161627;,0.951436;2.881679;-0.086788;,1.279824;2.160847;-0.086788;,1.279824;2.160847;-0.086788;,0.951436;2.881679;-0.086788;,0.776116;2.801809;-0.086788;,1.104504;2.080977;-0.086788;,1.104504;2.080977;0.161627;,1.279824;2.160847;0.161627;,1.279824;2.160847;-0.086788;,1.104504;2.080977;-0.086788;,0.951436;2.881679;0.161627;,0.776116;2.801809;0.161627;,0.776116;2.801809;-0.086788;,0.951436;2.881679;-0.086788;,0.055633;0.093601;-0.190081;,0.055633;1.205294;-0.190081;,0.055633;1.205294;0.190081;,0.055633;0.093601;0.190081;,0.055633;0.093601;0.190081;,0.055633;1.205294;0.190081;,0.43017;1.205294;0.190081;,0.43017;0.093601;0.190081;,0.43017;0.093601;0.190081;,0.43017;1.205294;0.190081;,0.43017;1.205294;-0.190081;,0.43017;0.093601;-0.190081;,0.43017;0.093601;-0.190081;,0.43017;1.205294;-0.190081;,0.055633;1.205294;-0.190081;,0.055633;0.093601;-0.190081;,0.055633;0.093601;0.190081;,0.43017;0.093601;0.190081;,0.43017;0.093601;-0.190081;,0.055633;0.093601;-0.190081;,0.43017;1.205294;0.190081;,0.055633;1.205294;0.190081;,0.055633;1.205294;-0.190081;,0.43017;1.205294;-0.190081;,0;3.790919;0.428464;,0;4.579204;0.428464;,0.43344;4.560537;0.409797;,0.43344;3.809586;0.409797;,0.43344;3.809586;0.409797;,0.43344;4.560537;0.409797;,0.43344;4.560537;-0.284975;,0.43344;3.809586;-0.284975;,0;3.790919;0.428464;,0.43344;3.809586;0.409797;,0.43344;3.809586;-0.284975;,0;3.790919;-0.303642;,0.43344;4.560537;0.409797;,0;4.579204;0.428464;,0;4.579204;-0.303642;,0.43344;4.560537;-0.284975;,0.43344;3.809586;-0.284975;,0.43344;4.560537;-0.284975;,0;4.579204;-0.303642;,0;3.790919;-0.303642;,0;2.440814;0.219926;,-0.466212;2.440814;0.219926;,-0.466212;3.688199;0.219926;,0;3.688199;0.219926;,-0.466212;2.440814;0.219926;,-0.466212;2.440814;-0.219926;,-0.466212;3.688199;-0.219926;,-0.466212;3.688199;0.219926;,0;2.440814;0.219926;,0;2.440814;-0.219926;,-0.466212;2.440814;-0.219926;,-0.466212;2.440814;0.219926;,-0.055633;1.27575;-0.190081;,-0.055633;1.27575;0.190081;,-0.055633;2.35741;0.190081;,-0.055633;2.35741;-0.190081;,-0.055633;1.27575;0.190081;,-0.43017;1.27575;0.190081;,-0.43017;2.35741;0.190081;,-0.055633;2.35741;0.190081;,-0.43017;1.27575;0.190081;,-0.43017;1.27575;-0.190081;,-0.43017;2.35741;-0.190081;,-0.43017;2.35741;0.190081;,-0.43017;1.27575;-0.190081;,-0.055633;1.27575;-0.190081;,-0.055633;2.35741;-0.190081;,-0.43017;2.35741;-0.190081;,-0.055633;1.27575;0.190081;,-0.055633;1.27575;-0.190081;,-0.43017;1.27575;-0.190081;,-0.43017;1.27575;0.190081;,-0.43017;2.35741;0.190081;,-0.43017;2.35741;-0.190081;,-0.055633;2.35741;-0.190081;,-0.055633;2.35741;0.190081;,-0.466212;3.688199;0.219926;,-0.466212;3.688199;-0.219926;,0;3.688199;-0.219926;,0;3.688199;0.219926;,-0.466212;2.440814;-0.219926;,0;2.440814;-0.219926;,0;3.688199;-0.219926;,-0.466212;3.688199;-0.219926;,-0.769341;2.834949;-0.041122;,-0.769341;2.834949;0.207294;,-0.440953;3.555781;0.207294;,-0.440953;3.555781;-0.041122;,-0.769341;2.834949;0.207294;,-0.944661;2.914819;0.207294;,-0.616273;3.635651;0.207294;,-0.440953;3.555781;0.207294;,-0.944661;2.914819;0.207294;,-0.944661;2.914819;-0.041122;,-0.616273;3.635651;-0.041122;,-0.616273;3.635651;0.207294;,-0.944661;2.914819;-0.041122;,-0.769341;2.834949;-0.041122;,-0.440953;3.555781;-0.041122;,-0.616273;3.635651;-0.041122;,-0.769341;2.834949;0.207294;,-0.769341;2.834949;-0.041122;,-0.944661;2.914819;-0.041122;,-0.944661;2.914819;0.207294;,-0.616273;3.635651;0.207294;,-0.616273;3.635651;-0.041122;,-0.440953;3.555781;-0.041122;,-0.440953;3.555781;0.207294;,-1.104504;2.080977;-0.086788;,-1.104504;2.080977;0.161627;,-0.776116;2.801809;0.161627;,-0.776116;2.801809;-0.086788;,-1.104504;2.080977;0.161627;,-1.279824;2.160847;0.161627;,-0.951436;2.881679;0.161627;,-0.776116;2.801809;0.161627;,-1.279824;2.160847;0.161627;,-1.279824;2.160847;-0.086788;,-0.951436;2.881679;-0.086788;,-0.951436;2.881679;0.161627;,-1.279824;2.160847;-0.086788;,-1.104504;2.080977;-0.086788;,-0.776116;2.801809;-0.086788;,-0.951436;2.881679;-0.086788;,-1.104504;2.080977;0.161627;,-1.104504;2.080977;-0.086788;,-1.279824;2.160847;-0.086788;,-1.279824;2.160847;0.161627;,-0.951436;2.881679;0.161627;,-0.951436;2.881679;-0.086788;,-0.776116;2.801809;-0.086788;,-0.776116;2.801809;0.161627;,-0.055633;0.093601;-0.190081;,-0.055633;0.093601;0.190081;,-0.055633;1.205294;0.190081;,-0.055633;1.205294;-0.190081;,-0.055633;0.093601;0.190081;,-0.43017;0.093601;0.190081;,-0.43017;1.205294;0.190081;,-0.055633;1.205294;0.190081;,-0.43017;0.093601;0.190081;,-0.43017;0.093601;-0.190081;,-0.43017;1.205294;-0.190081;,-0.43017;1.205294;0.190081;,-0.43017;0.093601;-0.190081;,-0.055633;0.093601;-0.190081;,-0.055633;1.205294;-0.190081;,-0.43017;1.205294;-0.190081;,-0.055633;0.093601;0.190081;,-0.055633;0.093601;-0.190081;,-0.43017;0.093601;-0.190081;,-0.43017;0.093601;0.190081;,-0.43017;1.205294;0.190081;,-0.43017;1.205294;-0.190081;,-0.055633;1.205294;-0.190081;,-0.055633;1.205294;0.190081;,0;3.790919;0.428464;,-0.43344;3.809586;0.409797;,-0.43344;4.560537;0.409797;,0;4.579204;0.428464;,-0.43344;3.809586;0.409797;,-0.43344;3.809586;-0.284975;,-0.43344;4.560537;-0.284975;,-0.43344;4.560537;0.409797;,0;3.790919;0.428464;,0;3.790919;-0.303642;,-0.43344;3.809586;-0.284975;,-0.43344;3.809586;0.409797;,-0.43344;4.560537;0.409797;,-0.43344;4.560537;-0.284975;,0;4.579204;-0.303642;,0;4.579204;0.428464;,-0.43344;3.809586;-0.284975;,0;3.790919;-0.303642;,0;4.579204;-0.303642;,-0.43344;4.560537;-0.284975;;68;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;,4;139,138,137,136;,4;143,142,141,140;,4;147,146,145,144;,4;151,150,149,148;,4;155,154,153,152;,4;159,158,157,156;,4;163,162,161,160;,4;167,166,165,164;,4;171,170,169,168;,4;175,174,173,172;,4;179,178,177,176;,4;183,182,181,180;,4;187,186,185,184;,4;191,190,189,188;,4;195,194,193,192;,4;199,198,197,196;,4;203,202,201,200;,4;207,206,205,204;,4;211,210,209,208;,4;215,214,213,212;,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;,4;267,266,265,264;,4;271,270,269,268;;MeshNormals{272;0;-0.707083;0.707083;,0;0.707083;0.707083;,0.577349;0.577349;0.577349;,0.577349;-0.577349;0.577349;,0.577349;-0.577349;0.577349;,0.577349;0.577349;0.577349;,0.577349;0.577349;-0.577349;,0.577349;-0.577349;-0.577349;,0;-0.707083;0.707083;,0.577349;-0.577349;0.577349;,0.577349;-0.577349;-0.577349;,0;-0.707083;-0.707083;,-0.577349;-0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,-0.577349;0.577349;0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;0.577349;0.577349;,0.577349;0.577349;0.577349;,0.577349;-0.577349;0.577349;,0.577349;-0.577349;0.577349;,0.577349;0.577349;0.577349;,0.577349;0.577349;-0.577349;,0.577349;-0.577349;-0.577349;,0.577349;-0.577349;-0.577349;,0.577349;0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,-0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;0.577349;,0.577349;-0.577349;0.577349;,0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;-0.577349;,0.577349;0.577349;0.577349;,-0.577349;0.577349;0.577349;,-0.577349;0.577349;-0.577349;,0.577349;0.577349;-0.577349;,0.577349;0.577349;0.577349;,0;0.707083;0.707083;,0;0.707083;-0.707083;,0.577349;0.577349;-0.577349;,0.577349;-0.577349;-0.577349;,0.577349;0.577349;-0.577349;,0;0.707083;-0.707083;,0;-0.707083;-0.707083;,-0.286019;-0.764733;-0.577349;,-0.764733;0.286019;-0.577349;,-0.764733;0.286019;0.577349;,-0.286019;-0.764733;0.577349;,-0.286019;-0.764733;0.577349;,-0.764733;0.286019;0.577349;,0.286019;0.764733;0.577349;,0.764733;-0.286019;0.577349;,0.764733;-0.286019;0.577349;,0.286019;0.764733;0.577349;,0.286019;0.764733;-0.577349;,0.764733;-0.286019;-0.577349;,0.764733;-0.286019;-0.577349;,0.286019;0.764733;-0.577349;,-0.764733;0.286019;-0.577349;,-0.286019;-0.764733;-0.577349;,-0.286019;-0.764733;0.577349;,0.764733;-0.286019;0.577349;,0.764733;-0.286019;-0.577349;,-0.286019;-0.764733;-0.577349;,0.286019;0.764733;0.577349;,-0.764733;0.286019;0.577349;,-0.764733;0.286019;-0.577349;,0.286019;0.764733;-0.577349;,-0.286019;-0.764733;-0.577349;,-0.764733;0.286019;-0.577349;,-0.764733;0.286019;0.577349;,-0.286019;-0.764733;0.577349;,-0.286019;-0.764733;0.577349;,-0.764733;0.286019;0.577349;,0.286019;0.764733;0.577349;,0.764733;-0.286019;0.577349;,0.764733;-0.286019;0.577349;,0.286019;0.764733;0.577349;,0.286019;0.764733;-0.577349;,0.764733;-0.286019;-0.577349;,0.764733;-0.286019;-0.577349;,0.286019;0.764733;-0.577349;,-0.764733;0.286019;-0.577349;,-0.286019;-0.764733;-0.577349;,-0.286019;-0.764733;0.577349;,0.764733;-0.286019;0.577349;,0.764733;-0.286019;-0.577349;,-0.286019;-0.764733;-0.577349;,0.286019;0.764733;0.577349;,-0.764733;0.286019;0.577349;,-0.764733;0.286019;-0.577349;,0.286019;0.764733;-0.577349;,-0.577349;-0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,-0.577349;0.577349;0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;0.577349;0.577349;,0.577349;0.577349;0.577349;,0.577349;-0.577349;0.577349;,0.577349;-0.577349;0.577349;,0.577349;0.577349;0.577349;,0.577349;0.577349;-0.577349;,0.577349;-0.577349;-0.577349;,0.577349;-0.577349;-0.577349;,0.577349;0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,-0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;0.577349;,0.577349;-0.577349;0.577349;,0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;-0.577349;,0.577349;0.577349;0.577349;,-0.577349;0.577349;0.577349;,-0.577349;0.577349;-0.577349;,0.577349;0.577349;-0.577349;,0;-0.707083;0.707083;,0;0.707083;0.707083;,0.599902;0.565722;0.565722;,0.599902;-0.565722;0.565722;,0.599902;-0.565722;0.565722;,0.599902;0.565722;0.565722;,0.599902;0.565722;-0.565722;,0.599902;-0.565722;-0.565722;,0;-0.707083;0.707083;,0.599902;-0.565722;0.565722;,0.599902;-0.565722;-0.565722;,0;-0.707083;-0.707083;,0.599902;0.565722;0.565722;,0;0.707083;0.707083;,0;0.707083;-0.707083;,0.599902;0.565722;-0.565722;,0.599902;-0.565722;-0.565722;,0.599902;0.565722;-0.565722;,0;0.707083;-0.707083;,0;-0.707083;-0.707083;,0;-0.707083;0.707083;,-0.577349;-0.577349;0.577349;,-0.577349;0.577349;0.577349;,0;0.707083;0.707083;,-0.577349;-0.577349;0.577349;,-0.577349;-0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,-0.577349;0.577349;0.577349;,0;-0.707083;0.707083;,0;-0.707083;-0.707083;,-0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;0.577349;,0.577349;-0.577349;-0.577349;,0.577349;-0.577349;0.577349;,0.577349;0.577349;0.577349;,0.577349;0.577349;-0.577349;,0.577349;-0.577349;0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;0.577349;0.577349;,0.577349;0.577349;0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;-0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,-0.577349;0.577349;0.577349;,-0.577349;-0.577349;-0.577349;,0.577349;-0.577349;-0.577349;,0.577349;0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,0.577349;-0.577349;0.577349;,0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;0.577349;0.577349;,-0.577349;0.577349;-0.577349;,0.577349;0.577349;-0.577349;,0.577349;0.577349;0.577349;,-0.577349;0.577349;0.577349;,-0.577349;0.577349;-0.577349;,0;0.707083;-0.707083;,0;0.707083;0.707083;,-0.577349;-0.577349;-0.577349;,0;-0.707083;-0.707083;,0;0.707083;-0.707083;,-0.577349;0.577349;-0.577349;,0.286019;-0.764733;-0.577349;,0.286019;-0.764733;0.577349;,0.764733;0.286019;0.577349;,0.764733;0.286019;-0.577349;,0.286019;-0.764733;0.577349;,-0.764733;-0.286019;0.577349;,-0.286019;0.764733;0.577349;,0.764733;0.286019;0.577349;,-0.764733;-0.286019;0.577349;,-0.764733;-0.286019;-0.577349;,-0.286019;0.764733;-0.577349;,-0.286019;0.764733;0.577349;,-0.764733;-0.286019;-0.577349;,0.286019;-0.764733;-0.577349;,0.764733;0.286019;-0.577349;,-0.286019;0.764733;-0.577349;,0.286019;-0.764733;0.577349;,0.286019;-0.764733;-0.577349;,-0.764733;-0.286019;-0.577349;,-0.764733;-0.286019;0.577349;,-0.286019;0.764733;0.577349;,-0.286019;0.764733;-0.577349;,0.764733;0.286019;-0.577349;,0.764733;0.286019;0.577349;,0.286019;-0.764733;-0.577349;,0.286019;-0.764733;0.577349;,0.764733;0.286019;0.577349;,0.764733;0.286019;-0.577349;,0.286019;-0.764733;0.577349;,-0.764733;-0.286019;0.577349;,-0.286019;0.764733;0.577349;,0.764733;0.286019;0.577349;,-0.764733;-0.286019;0.577349;,-0.764733;-0.286019;-0.577349;,-0.286019;0.764733;-0.577349;,-0.286019;0.764733;0.577349;,-0.764733;-0.286019;-0.577349;,0.286019;-0.764733;-0.577349;,0.764733;0.286019;-0.577349;,-0.286019;0.764733;-0.577349;,0.286019;-0.764733;0.577349;,0.286019;-0.764733;-0.577349;,-0.764733;-0.286019;-0.577349;,-0.764733;-0.286019;0.577349;,-0.286019;0.764733;0.577349;,-0.286019;0.764733;-0.577349;,0.764733;0.286019;-0.577349;,0.764733;0.286019;0.577349;,0.577349;-0.577349;-0.577349;,0.577349;-0.577349;0.577349;,0.577349;0.577349;0.577349;,0.577349;0.577349;-0.577349;,0.577349;-0.577349;0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;0.577349;0.577349;,0.577349;0.577349;0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;-0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,-0.577349;0.577349;0.577349;,-0.577349;-0.577349;-0.577349;,0.577349;-0.577349;-0.577349;,0.577349;0.577349;-0.577349;,-0.577349;0.577349;-0.577349;,0.577349;-0.577349;0.577349;,0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;-0.577349;,-0.577349;-0.577349;0.577349;,-0.577349;0.577349;0.577349;,-0.577349;0.577349;-0.577349;,0.577349;0.577349;-0.577349;,0.577349;0.577349;0.577349;,0;-0.707083;0.707083;,-0.599902;-0.565722;0.565722;,-0.599872;0.565722;0.565722;,0;0.707083;0.707083;,-0.599902;-0.565722;0.565722;,-0.599902;-0.565722;-0.565722;,-0.599872;0.565722;-0.565722;,-0.599872;0.565722;0.565722;,0;-0.707083;0.707083;,0;-0.707083;-0.707083;,-0.599902;-0.565722;-0.565722;,-0.599902;-0.565722;0.565722;,-0.599872;0.565722;0.565722;,-0.599872;0.565722;-0.565722;,0;0.707083;-0.707083;,0;0.707083;0.707083;,-0.599902;-0.565722;-0.565722;,0;-0.707083;-0.707083;,0;0.707083;-0.707083;,-0.599872;0.565722;-0.565722;;68;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;,4;139,138,137,136;,4;143,142,141,140;,4;147,146,145,144;,4;151,150,149,148;,4;155,154,153,152;,4;159,158,157,156;,4;163,162,161,160;,4;167,166,165,164;,4;171,170,169,168;,4;175,174,173,172;,4;179,178,177,176;,4;183,182,181,180;,4;187,186,185,184;,4;191,190,189,188;,4;195,194,193,192;,4;199,198,197,196;,4;203,202,201,200;,4;207,206,205,204;,4;211,210,209,208;,4;215,214,213,212;,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;,4;267,266,265,264;,4;271,270,269,268;;}MeshTextureCoords{272;0.849264;0.899246;,0.849264;0.931916;,0.861547;0.931916;,0.861547;0.899246;,0.916988;0.931916;,0.916988;0.899246;,0.9054;0.899246;,0.9054;0.931916;,0.84857;0.844707;,0.84857;0.83254;,0.836981;0.83254;,0.836981;0.844707;,0.927004;0.903587;,0.927004;0.931916;,0.937019;0.931916;,0.937019;0.903587;,0.937019;0.903587;,0.937019;0.931916;,0.946887;0.931916;,0.946887;0.903587;,0.888533;0.856954;,0.888533;0.828625;,0.878517;0.828625;,0.878517;0.856954;,0.939292;0.870917;,0.939292;0.899246;,0.949159;0.899246;,0.949159;0.870917;,0.946887;0.91117;,0.956719;0.91117;,0.956719;0.901213;,0.946887;0.901213;,0.865118;0.813135;,0.855286;0.813135;,0.855286;0.823092;,0.865118;0.823092;,0.866874;0.847426;,0.866874;0.835259;,0.855286;0.835259;,0.855286;0.847426;,0.598002;0.973516;,0.598002;0.206739;,0.309722;0.206739;,0.309722;0.973516;,0.909393;0.822135;,0.909393;0.841014;,0.915938;0.841014;,0.915938;0.822135;,0.951962;0.931916;,0.951962;0.91117;,0.946887;0.91117;,0.946887;0.931916;,0.948762;0.841801;,0.948762;0.822921;,0.942217;0.822921;,0.942217;0.841801;,0.893608;0.838075;,0.893608;0.817329;,0.888533;0.817329;,0.888533;0.838075;,0.900724;0.909292;,0.90515;0.909292;,0.90515;0.902786;,0.900724;0.902786;,0.953585;0.871994;,0.949159;0.871994;,0.949159;0.8785;,0.953585;0.8785;,0.84857;0.837995;,0.84857;0.856874;,0.855114;0.856874;,0.855114;0.837995;,0.902881;0.83746;,0.902881;0.816714;,0.897805;0.816714;,0.897805;0.83746;,0.942217;0.841801;,0.942217;0.822921;,0.935673;0.822921;,0.935673;0.841801;,0.949159;0.8785;,0.949159;0.899246;,0.954235;0.899246;,0.954235;0.8785;,0.919226;0.822135;,0.923651;0.822135;,0.923651;0.815629;,0.919226;0.815629;,0.928077;0.815629;,0.923651;0.815629;,0.923651;0.822135;,0.928077;0.822135;,0.865301;0.847426;,0.865301;0.876542;,0.875317;0.876542;,0.875317;0.847426;,0.909393;0.841014;,0.909393;0.87013;,0.919261;0.87013;,0.919261;0.841014;,0.855286;0.847426;,0.855286;0.876542;,0.865301;0.876542;,0.865301;0.847426;,0.919261;0.841014;,0.919261;0.87013;,0.929128;0.87013;,0.929128;0.841014;,0.878517;0.828625;,0.888349;0.828625;,0.88835;0.818668;,0.878517;0.818668;,0.836981;0.83254;,0.846814;0.83254;,0.846814;0.822583;,0.836981;0.822583;,0.857749;0.887894;,0.836981;0.887894;,0.837473;0.899246;,0.857257;0.899246;,0.855286;0.876542;,0.855286;0.856874;,0.836981;0.856874;,0.836981;0.876542;,0.897805;0.887893;,0.897313;0.876622;,0.879009;0.876622;,0.878517;0.887893;,0.886604;0.909292;,0.886112;0.920645;,0.9054;0.920645;,0.904908;0.909292;,0.977665;0.442421;,0.977665;0.131438;,0.799225;0.123708;,0.799225;0.450151;,0.849264;0.899246;,0.836981;0.899246;,0.836981;0.931916;,0.849264;0.931916;,0.909393;0.866576;,0.897805;0.866576;,0.897805;0.899246;,0.909393;0.899246;,0.84857;0.844707;,0.836981;0.844707;,0.836981;0.856874;,0.84857;0.856874;,0.929276;0.899246;,0.939292;0.899246;,0.939292;0.870917;,0.929276;0.870917;,0.876741;0.819096;,0.866874;0.819096;,0.866874;0.847426;,0.876741;0.847426;,0.939144;0.841801;,0.929128;0.841801;,0.929128;0.87013;,0.939144;0.87013;,0.949011;0.841801;,0.939144;0.841801;,0.939144;0.87013;,0.949011;0.87013;,0.836981;0.812626;,0.836981;0.822583;,0.846814;0.822583;,0.846814;0.812626;,0.909393;0.812178;,0.909393;0.822135;,0.919226;0.822135;,0.919226;0.812178;,0.866874;0.823092;,0.855286;0.823092;,0.855286;0.835259;,0.866874;0.835259;,0.021442;0.973516;,0.309722;0.973516;,0.309722;0.206739;,0.021442;0.206739;,0.916039;0.841014;,0.922583;0.841014;,0.922583;0.822135;,0.916039;0.822135;,0.907956;0.816714;,0.902881;0.816714;,0.902881;0.83746;,0.907956;0.83746;,0.929128;0.822135;,0.922583;0.822135;,0.922583;0.841014;,0.929128;0.841014;,0.853645;0.817249;,0.84857;0.817249;,0.84857;0.837995;,0.853645;0.837995;,0.900724;0.909292;,0.900724;0.902786;,0.895944;0.902786;,0.895944;0.909292;,0.93896;0.816415;,0.93896;0.822921;,0.94374;0.822921;,0.94374;0.816415;,0.935673;0.822921;,0.929128;0.822921;,0.929128;0.841801;,0.935673;0.841801;,0.954087;0.849384;,0.949011;0.849384;,0.949011;0.87013;,0.954087;0.87013;,0.895077;0.838075;,0.888533;0.838075;,0.888533;0.856954;,0.895077;0.856954;,0.948762;0.841801;,0.953838;0.841801;,0.953838;0.821055;,0.948762;0.821055;,0.94374;0.816415;,0.94374;0.822921;,0.94852;0.822921;,0.94852;0.816415;,0.949011;0.842878;,0.949011;0.849384;,0.953791;0.849384;,0.953791;0.842878;,0.919409;0.87013;,0.909393;0.87013;,0.909393;0.899246;,0.919409;0.899246;,0.897805;0.866576;,0.907672;0.866576;,0.907672;0.83746;,0.897805;0.83746;,0.927004;0.9028;,0.916988;0.9028;,0.916988;0.931916;,0.927004;0.931916;,0.929276;0.87013;,0.919409;0.87013;,0.919409;0.899246;,0.929276;0.899246;,0.93896;0.822921;,0.93896;0.812965;,0.929128;0.812965;,0.929128;0.822921;,0.886112;0.899336;,0.886112;0.909292;,0.895944;0.909292;,0.895944;0.899336;,0.857749;0.887894;,0.857257;0.876542;,0.837473;0.876542;,0.836981;0.887894;,0.896821;0.856954;,0.878517;0.856954;,0.878517;0.876622;,0.896821;0.876622;,0.897805;0.887893;,0.878517;0.887893;,0.879009;0.899246;,0.897313;0.899246;,0.886604;0.931916;,0.904908;0.931916;,0.9054;0.920645;,0.886112;0.920645;,0.620785;0.44242;,0.799225;0.450151;,0.799225;0.123708;,0.620785;0.131438;;}XSkinMeshHeader{3;9;10;}SkinWeights{"Armature_arm_l";24;44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,67,66;1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;-0.047733,0.901521,0.430095,0,-0.097683,0.424309,-0.900233,0,-0.994073,-0.084983,0.06781,0,0.374873,-2.006904,2.980378,1;;}SkinWeights{"Armature_elbow_r";24;216,219,218,213,212,215,214,209,224,208,227,211,226,210,206,221,207,220,204,223,205,222,225,217;1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;0.102316,0.92166,-0.374266,0,-0.090709,-0.366028,-0.926173,0,-0.990608,0.128712,0.046152,0,0.402018,1.853661,2.350172,1;;}SkinWeights{"Armature_arm_r";24;186,187,184,185,182,183,180,194,195,203,202,192,193,201,200,199,190,198,191,197,188,196,189,181;1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;-0.047733,0.901521,-0.430095,0,-0.05233,-0.432251,-0.900234,0,-0.997489,-0.020464,0.067809,0,0.160852,2.035269,2.980378,1;;}SkinWeights{"Armature_knee_l";24;105,99,114,106,98,115,107,101,93,108,100,92,109,103,95,110,102,94,111,97,112,104,113,96;1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;1,0,0,0,0,0.054357,-0.998522,0,0,0.998501,0.054355,0,-0.246294,-0.008592,1.301673,1;;}SkinWeights{"Armature_Bone_007";40;132,133,134,135,124,125,126,252,253,254,255,121,122,264,265,123,267,268,269,270,116,256,258,259,260,261,262,263,271,266,120,119,117,128,129,127,130,118,131,257;1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;1,0,0,0,0,0,1,0,0,-1,0,0,0,0,-3.793003,1;;}SkinWeights{"Armature_elbow_l";24;88,80,72,91,83,75,90,82,74,70,85,77,71,84,76,68,87,79,69,86,78,89,81,73;1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;0.102316,0.92166,0.374266,0,-0.008222,0.377011,-0.926173,0,-0.994719,0.091686,0.046152,0,-0.014321,-1.896701,2.350171,1;;}SkinWeights{"Armature_knee_r";24;249,235,250,234,251,229,244,228,245,231,246,230,247,240,241,242,243,237,236,239,238,233,248,232;1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;1,0,0,0,0,0.054357,-0.998522,0,0,0.998501,0.054355,0,0.246294,-0.008592,1.301673,1;;}SkinWeights{"Armature_leg_l";38;0,3,4,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,40,43,136,145,177,144;0.055873,0.852304,0.852304,0.82998,0.055873,0.852304,0.82998,0.054606,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.82998,0.054606,0.055873,0.054606,0.054606,0.055873;1,0,0,0,0,-0.056452,-0.998405,0,0,0.998385,-0.056452,0,-0.246294,0.135476,2.396023,1;;}SkinWeights{"Armature_leg_r";38;0,170,169,11,168,151,150,149,148,147,146,176,145,177,144,159,158,157,156,155,154,153,167,136,166,137,165,164,163,140,162,141,161,43,160,152,8,171;0.055873,1,1,0.054606,1,1,1,1,1,0.852304,0.82998,0.82998,0.054606,0.054606,0.055873,1,1,1,1,1,1,1,1,0.055873,1,0.852304,1,1,1,0.852304,1,0.82998,1,0.054606,1,1,0.055873,1;1,0,0,0,0,-0.056452,-0.998405,0,0,0.998385,-0.056452,0,0.246294,0.135476,2.396023,1;;}SkinWeights{"Armature_body";40;0,1,2,3,4,5,6,7,8,9,10,11,36,37,38,39,40,41,42,43,136,137,138,139,140,147,141,146,142,145,143,144,179,174,178,173,177,172,176,175;0.888255,1,1,0.147696,0.147696,1,1,0.17002,0.888255,0.147696,0.17002,0.890788,1,1,1,1,0.17002,1,1,0.890788,0.888255,0.147696,1,1,0.147696,0.147696,0.17002,0.17002,1,0.890788,1,0.888255,1,1,1,1,0.890788,1,0.17002,1;1,0,0,0,0,0,1,0,0,-1,0,0,0,0,-2.571201,1;;}}}}} \ No newline at end of file diff --git a/irr/media/cooltexture.png b/irr/media/cooltexture.png deleted file mode 100755 index fcc219ac7ea95c2ad9b49ad0064835638cb51c69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4828 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 diff --git a/irr/scripts/ci-build-android.sh b/irr/scripts/ci-build-android.sh deleted file mode 100755 index a865e56a4..000000000 --- a/irr/scripts/ci-build-android.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/bash -e - -# NOTE: this code is mostly copied from minetest_android_deps -# - -png_ver=1.6.40 -jpeg_ver=3.0.1 - -download () { - get_tar_archive libpng "https://download.sourceforge.net/libpng/libpng-${png_ver}.tar.gz" - get_tar_archive libjpeg "https://download.sourceforge.net/libjpeg-turbo/libjpeg-turbo-${jpeg_ver}.tar.gz" -} - -build () { - # Build libjpg and libpng first because Irrlicht needs them - mkdir -p libpng - pushd libpng - $srcdir/libpng/configure --host=$CROSS_PREFIX - make && make DESTDIR=$PWD install - popd - - mkdir -p libjpeg - pushd libjpeg - cmake $srcdir/libjpeg "${CMAKE_FLAGS[@]}" -DENABLE_SHARED=OFF - make && make DESTDIR=$PWD install - popd - - local libpng=$PWD/libpng/usr/local/lib/libpng.a - local libjpeg=$(echo $PWD/libjpeg/opt/libjpeg-turbo/lib*/libjpeg.a) - cmake $srcdir/irrlicht "${CMAKE_FLAGS[@]}" \ - -DBUILD_SHARED_LIBS=OFF \ - -DPNG_LIBRARY=$libpng \ - -DPNG_PNG_INCLUDE_DIR=$(dirname "$libpng")/../include \ - -DJPEG_LIBRARY=$libjpeg \ - -DJPEG_INCLUDE_DIR=$(dirname "$libjpeg")/../include - make - - cp -p lib/Android/libIrrlichtMt.a $libpng $libjpeg $pkgdir/ - cp -a $srcdir/irrlicht/include $pkgdir/include - cp -a $srcdir/irrlicht/media/Shaders $pkgdir/Shaders -} - -get_tar_archive () { - # $1: folder to extract to, $2: URL - local filename="${2##*/}" - [ -d "$1" ] && return 0 - wget -c "$2" -O "$filename" - mkdir -p "$1" - tar -xaf "$filename" -C "$1" --strip-components=1 - rm "$filename" -} - -_setup_toolchain () { - local toolchain=$(echo "$ANDROID_NDK"/toolchains/llvm/prebuilt/*) - if [ ! -d "$toolchain" ]; then - echo "Android NDK path not specified or incorrect"; return 1 - fi - export PATH="$toolchain/bin:$ANDROID_NDK:$PATH" - - unset CFLAGS CPPFLAGS CXXFLAGS - - TARGET_ABI="$1" - API=21 - if [ "$TARGET_ABI" == armeabi-v7a ]; then - CROSS_PREFIX=armv7a-linux-androideabi - CFLAGS="-mthumb" - CXXFLAGS="-mthumb" - elif [ "$TARGET_ABI" == arm64-v8a ]; then - CROSS_PREFIX=aarch64-linux-android - elif [ "$TARGET_ABI" == x86 ]; then - CROSS_PREFIX=i686-linux-android - CFLAGS="-mssse3 -mfpmath=sse" - CXXFLAGS="-mssse3 -mfpmath=sse" - elif [ "$TARGET_ABI" == x86_64 ]; then - CROSS_PREFIX=x86_64-linux-android - else - echo "Invalid ABI given"; return 1 - fi - export CC=$CROSS_PREFIX$API-clang - export CXX=$CROSS_PREFIX$API-clang++ - export AR=llvm-ar - export RANLIB=llvm-ranlib - export CFLAGS="-fPIC ${CFLAGS}" - export CXXFLAGS="-fPIC ${CXXFLAGS}" - - CMAKE_FLAGS=( - "-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake" - "-DANDROID_ABI=$TARGET_ABI" "-DANDROID_NATIVE_API_LEVEL=$API" - "-DCMAKE_BUILD_TYPE=Release" - ) - - # make sure pkg-config doesn't interfere - export PKG_CONFIG=/bin/false - - export MAKEFLAGS="-j$(nproc)" -} - -_run_build () { - local abi=$1 - irrdir=$PWD - - mkdir -p $RUNNER_TEMP/src - cd $RUNNER_TEMP/src - srcdir=$PWD - [ -d irrlicht ] || ln -s $irrdir irrlicht - download - - builddir=$RUNNER_TEMP/build/irrlicht-$abi - pkgdir=$RUNNER_TEMP/pkg/$abi/Irrlicht - rm -rf "$pkgdir" - mkdir -p "$builddir" "$pkgdir" - - cd "$builddir" - build -} - -if [ $# -lt 1 ]; then - echo "Usage: ci-build-android.sh " - exit 1 -fi - -_setup_toolchain $1 -_run_build $1 diff --git a/irr/scripts/ci-build-mingw.sh b/irr/scripts/ci-build-mingw.sh deleted file mode 100755 index b1fdd7b99..000000000 --- a/irr/scripts/ci-build-mingw.sh +++ /dev/null @@ -1,70 +0,0 @@ -#!/bin/bash -e -topdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -[[ -z "$CC" || -z "$CXX" ]] && exit 255 -variant=win32 -[[ "$(basename "$CXX")" == "x86_64-"* ]] && variant=win64 -with_sdl=0 -[[ "$extras" == *"-sdl"* ]] && with_sdl=1 -#with_gl3=0 -#[[ "$extras" == *"-gl3"* ]] && with_gl3=1 - -libjpeg_version=3.0.1 -libpng_version=1.6.40 -sdl2_version=2.28.5 -zlib_version=1.3.1 - -download () { - local url=$1 - local filename=${url##*/} - local foldername=${filename%%[.-]*} - - [ -d "./$foldername" ] && return 0 - [ -e "$filename" ] || wget "$url" -O "$filename" - sha256sum -w -c <(grep -F "$filename" "$topdir/sha256sums.txt") - unzip -o "$filename" -d "$foldername" -} - -libs=$PWD/libs -mkdir -p libs -pushd libs -libhost="http://minetest.kitsunemimi.pw" -download "$libhost/llvm/libjpeg-$libjpeg_version-$variant.zip" -download "$libhost/llvm/libpng-$libpng_version-$variant.zip" -[ $with_sdl -eq 1 ] && download "$libhost/llvm/sdl2-$sdl2_version-$variant.zip" -download "$libhost/llvm/zlib-$zlib_version-$variant.zip" -popd - -tmp=( - -DCMAKE_SYSTEM_NAME=Windows \ - -DPNG_LIBRARY=$libs/libpng/lib/libpng.dll.a \ - -DPNG_PNG_INCLUDE_DIR=$libs/libpng/include \ - -DJPEG_LIBRARY=$libs/libjpeg/lib/libjpeg.dll.a \ - -DJPEG_INCLUDE_DIR=$libs/libjpeg/include \ - -DZLIB_LIBRARY=$libs/zlib/lib/libz.dll.a \ - -DZLIB_INCLUDE_DIR=$libs/zlib/include -) -if [ $with_sdl -eq 1 ]; then - tmp+=( - -DUSE_SDL2=ON - -DCMAKE_PREFIX_PATH=$libs/sdl2/lib/cmake - ) -else - tmp+=(-DUSE_SDL2=OFF) -fi -#[ $with_gl3 -eq 1 ] && tmp+=(-DENABLE_OPENGL=OFF -DENABLE_OPENGL3=ON) - -cmake . "${tmp[@]}" -make -j$(nproc) - -if [ "$1" = "package" ]; then - make DESTDIR=$PWD/_install install - # strip library - "${CXX%-*}-strip" --strip-unneeded _install/usr/local/lib/*.dll - # bundle the DLLs that are specific to Irrlicht (kind of a hack) - shopt -s nullglob - cp -p $libs/*/bin/{libjpeg,libpng,SDL}*.dll _install/usr/local/lib/ - # create a ZIP - (cd _install/usr/local; zip -9r "$OLDPWD/irrlicht-$variant$extras.zip" -- *) -fi -exit 0 diff --git a/irr/scripts/ci-get-mingw.sh b/irr/scripts/ci-get-mingw.sh deleted file mode 100755 index 9cf933fea..000000000 --- a/irr/scripts/ci-get-mingw.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -set -e -topdir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -name=llvm-mingw-20231128-ucrt-ubuntu-20.04-x86_64.tar.xz -wget "https://github.com/mstorsjo/llvm-mingw/releases/download/20231128/$name" -O "$name" -sha256sum -w -c <(grep -F "$name" "$topdir/sha256sums.txt") -sudo tar -xaf "$name" -C /usr --strip-components=1 -rm -f "$name" diff --git a/irr/scripts/sha256sums.txt b/irr/scripts/sha256sums.txt deleted file mode 100644 index 0c080de3b..000000000 --- a/irr/scripts/sha256sums.txt +++ /dev/null @@ -1,9 +0,0 @@ -0f21ff3be90311092fe32e0e30878ef3ae9d9437b8d9ac25ef279e0d84e9bb8e llvm-mingw-20231128-ucrt-ubuntu-20.04-x86_64.tar.xz -53dfd31285f470fcf0dca88217c5cf9c557729af6d103afae5936e72ddc38d3c libjpeg-3.0.1-win32.zip -3d44e0740914e6878300e30653aad39e974821b5d7f6c2567e246b4eb04a5324 libjpeg-3.0.1-win64.zip -6baf4e819bfb3573760524b5dc9a04b5e479090d6d2046b86cf39a3107c0071f libpng-1.6.40-win32.zip -c02e029f01fce44baea7f4aecfd2564bd8a03507c0c6af8b03339ae0452c8b7d libpng-1.6.40-win64.zip -f9f890af960e92fd3f532f2e9ac00681c33bc67a722e000dfdaeb41b0064f1a0 sdl2-2.28.5-win32.zip -8dde2c6963544b7d8a2e87c128ebbdf51ad0e70c7e2df986ff4e963ce9996d9b sdl2-2.28.5-win64.zip -8af10515d57dbfee5d2106cd66cafa2adeb4270d4c6047ccbf7e8b5d2d50681c zlib-1.3.1-win32.zip -ad43f5d23052590c65633530743e5d622cc76b33c109072e6fd7b487aff56bca zlib-1.3.1-win64.zip diff --git a/irr/test/CMakeLists.txt b/irr/test/CMakeLists.txt deleted file mode 100644 index 6518f69c1..000000000 --- a/irr/test/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -link_libraries(IrrlichtMt::IrrlichtMt) -add_executable(image_loader_test image_loader_test.cpp) - -function(test_image_loader format expected input) - string(TOLOWER ${format} suffix) - add_test(NAME ImageLoader${format}-${input} COMMAND image_loader_test ${expected} data/sample_${input}.${suffix} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) -endfunction() - -test_image_loader(BMP 16color-16bpp 4bpp_v3) -# test_image_loader(BMP 16color-16bpp 4bpp_v7) -# test_image_loader(BMP 16color-16bpp 4bpp_v3_rle) -# test_image_loader(BMP 16color-16bpp 4bpp_v7_rle) -test_image_loader(BMP 30color-16bpp 8bpp_v3) -# test_image_loader(BMP 30color-16bpp 8bpp_v7) -test_image_loader(BMP 30color-16bpp 8bpp_v3_rle) -# test_image_loader(BMP 30color-16bpp 8bpp_v7_rle) -# test_image_loader(BMP 30color-16bpp 16bpp_v3) -# test_image_loader(BMP 30color-16bpp 16bpp_v7) -test_image_loader(BMP 30color-24bpp 24bpp_v3) -test_image_loader(BMP 30color-24bpp 24bpp_v7) - -test_image_loader(PNG 30color-24bpp 8bpp) -test_image_loader(PNG 30color-24bpp 24bpp) - -test_image_loader(TGA 30color-32bpp 8bpp_up) -test_image_loader(TGA 30color-32bpp 8bpp_down) -# test_image_loader(TGA 30color-16bpp 8bpp_rle_up) -# test_image_loader(TGA 30color-16bpp 8bpp_rle_down) -test_image_loader(TGA 30color-24bpp 24bpp_up) -test_image_loader(TGA 30color-24bpp 24bpp_down) -test_image_loader(TGA 30color-24bpp 24bpp_rle_up) -test_image_loader(TGA 30color-24bpp 24bpp_rle_down) diff --git a/irr/test/data/sample_16bpp_v3.bmp b/irr/test/data/sample_16bpp_v3.bmp deleted file mode 100644 index 4b6fbcf501a0fb43d42dc6ab2d6a2b67acb8a0e0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 654 zcmZ{iF=~ZC5QaCoKmq~VEz+b)nc~4)!OkL<5*B3Om6mqy%FHdEC$SVyky^+_HZ!|f zw28}1p&$Ru|F6~O&!dXZ2lqFw7p^C+2ldQdsW1Ni`kpGj;HIs%We={y2|bQD{zEDi>Xk$h2^`XdhGq;dDQ^AUlO+ocbJxVJk@Kh{C$n3hIGLY0B+)aZ z`HZZ$;64-{ZlVW`aHUyE6cg96-pI^8EV%V#TQ5}RLanVK@uGP{&z!hkFma`UN+~j> z3AK=Af4H7wK84Tu2nSWT3YT&Ocg|7VNRmoz7cxlfz-kIEIB@&0^^&(Zfac)XK~iLP zAHZ$>#LS4$95*9+NMvMkaGC4=IH(p(J7z8_My}*EI@S`sv?9vxe z@1!`+QmDlVBY*sN|1X!XKev#cPuY8^N2zOc;by*8^p)uvh}HEo|H>Su~65W6Eb0=6saHGH#U1IikPLQQc^g z0OOi1u{dysqmLf_)Q*kjtq!nSIB}2_g`HaTW77-M5mw91s2(v}?Hv8g)n^XNf%1$q vmlP9MNE)?hsorwe%8hGHR}|A^;(4!RAS`8Un{Os_%1S!P^^(r@-vjUq7yl1M diff --git a/irr/test/data/sample_24bpp.png b/irr/test/data/sample_24bpp.png deleted file mode 100644 index d38f5989c712b9edb7992dd8f84b78a2a962420a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 580 zcmV-K0=xZ*P);t$FE~(5E9%T9 zp8{gF0Uiu{H{aNfAR>SFUL78mO;lMkQQvsH-FeCVxTmSEyRTzHK*v(Ob(Lkea*hmi z0`$Al$^G&u5W;YDgp3K(=|&42Nz~&0TDBnVI+ThvYPu+?b?)Xp<(b8o>G6Cc2ogYJ zRVw?b`0CKsTawjYyhU@HFtM`Mk;@klxX38fA>0D~!VjsR}z+dq~9&4Dz4H3rxW zb|(zO05Ya35W*tmT~QEN1L#DB+ZSuVki+=tAoQkfq#0&d>Ky?jSq7F?&q$A%Uph7W zWNfZ@B68@;+>XeYV`QZx?Pnb1DvAVlEdO0<@44BsR zWkW?>(aG1|z% S%MUvM0000sbf}{zguo6k3 zVrRNEqJelxcKpts*>N!%4$HFP+y8QAjhQm~%b01CF*CFFZ0l!A1smjT5ZYqX?mjS4 zdGK-&Js*xAYvY@v(e?4@r2ci<{5MyS>>YV>TqD`2HR42d*G*Mh=tA<-JpMazQADV?_l? z=RuzixvQRl_{8n-RuBOZC24t}VYL%*+%ize3Fy;lnQ@;&l?Rh$(kGM$t!yFua|KD4#O&kTcrqyTj>PDrGj)4gTfY@B*h^(Wx^r{$Oy$|6VNFJ zc~5x|NDN`+tcNU-CDD$Rh!D%bV+Qc z+VAgtyzcqW2d&3^>*la|b<{jAe4muQ&MKb*AkB&$a74N87i+d~4`cVQZLSCf;_q&X z&98(NI=^cS1HC`_+~X$&Q8^Z{P1)@nBCfqI$ap|@5|-;q5)m;zcqMr>LLsBMc<3rS zzEqaTy&$s%U@#lL%UUCflBDFV-#P|Tm^6w}r`1Nlg}h-Pba0X1@lsg+M)(|C|hdKg0Sn*(jpO&kr0H7;LsG& zP|=oKOH`nh-qZJZt|u{v%N?H2`905bu1dvK_cyK*YC?(5l;e8$G1&WjI|yD5+fRk| z%~9+6xOGzeIW2#m*S;?9nwhJ5(^LwT5mzb{dVQ5!R3>yf`~Iq_r~KnN0Py7O#;vAY zR~qhMUA*rc?U$6!`J+0$?jSD~(ow4xLfaG_S*ZPz;3LinK?5+71T;*C#G!&xol$hY zN;!%!UXZ3Vm+34FV7G^{+e0y=06oVL!T$Du zF07eEqzacAb;~q?IE52BRpgnlCjdJ)aAptPJRBMVM3D$FE6w?|=`InOu+j|J>6Ely$xfZ*^qv{R z$iFGOkTD?!=6oAT$GxdSWao}eVx?gRGb&%Tkne^p;pm8U%()4QatC3T_KO~Jm+=4N Z@BUjB=a-VpsfE7CeZUl+5g^f)R2}WEH#3qo!O2k6N z&U7}SfkaJqJa_Nx7?Z+qS++TQzWdE22&RJUHxo<;vw^L|GP{pSwflCEyc`anqT$Wa z;OcmAQvW`Uzs}m9=Xd?f&iHt6lm+~kwdK9{SF6&G=L9cLyuDkC)L!?z)Yk9uTGNyX zjV+fv$cuuc9kd;&fl2E@jqO21YQzP_kQO6g((JIQt+kvnH1e{ug-E3yq$LGG7+7>9 zym;sn(iu~bO<5C{Rs^N>LTSCiW+1L)f7?byUIa{8z_EG(l_f2M)ET3q;N_A7vooco z$up&4KxJ*CfRybdXC)>ig)o5RWHmidcLbrKUJptqnN+|^ES*%lP)I?f-q7f|_=|)Q zP&U=H=te;5^&lUI^_zOoNi$GdGED(PqeiXh_ad=lqd-CliInZq)dZ}yl;tAHXGs+m pHLzTy6m(BHm#p5u=Q8=1$^K+(E9<$9{AzxAX>M~NznGg3egMO@v-AJ} diff --git a/irr/test/data/sample_24bpp_v3.bmp b/irr/test/data/sample_24bpp_v3.bmp deleted file mode 100644 index 4684f14669f8f4a1601a63dcb70a92ed998200fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 954 zcmaizF-yZh7=|M{>)_(<;^gQLaQDY_3w7vXTy!uMq+SLWmm*TE7!bsz(7{Q>LB-8( zE~16Dn&y1Ie91MogK~F-!jtFy-iw=Ciwj=%x52r_US=<`=e!k8&)b>zyw!Ex*$*5x zh-K|Rr}h5FQTlc~eu>6+C&|rea@PDgkH0Uvqsxckb#Ho=*)k6MFD`8D;Pc(8`s+2t z&WU|+Z&BOZfm7S&BX(=rvO<%wRmbw6EU}Yr&T4XEJ67|TS#;{f2*@HfUf8tVV`|&z zgA)zgr)6K}>cH6vrx#sIZ&KrLMGf<>1e?us|^c@{Rq zVW$d*Rh-x?)Dv&y^AcOD=4^4-ls0?UbPlVycv}_JOe%qcn6&i6f+a9x>P73lFf4_a zS*fT{3me2zQN0g{8cGTejb4`f%+w1jQ)_IbC6>Y+%lpCI)MYkT7p#rknQW1-$(h=*(ilrFt0?^MWzGI`!Tti@bg?i1 diff --git a/irr/test/data/sample_24bpp_v7.bmp b/irr/test/data/sample_24bpp_v7.bmp deleted file mode 100644 index 1fa343188bc7deadf985d9ea5d274a1363e35c11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1038 zcma)*J4*vW6otn}t%Z%fy+0z2m5u)-*n}vgF&1j!Mv%B$*w_S-V8nnRHh~mYA{Huk z+S!N(5;fWJyK{FZYf@;qGfW=eoO93J}ygv!mXrjQ=$Mg=ahrDDRSvnI6hS(7=e;-Xv6 zBTc7bxZK1o`B1PJrcE8R&V^iTTGY>9?N8s<=g(PF QQ8KJF@ZZB4|8v3q0IVCfB>(^b diff --git a/irr/test/data/sample_4bpp_v3.bmp b/irr/test/data/sample_4bpp_v3.bmp deleted file mode 100644 index 0cc06f3161f8e3f81cd682e77503e8d1d8b594ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 298 zcmZ?r)na4-gEAng0mQ;U%n!tj3@kvBfnf^}3v)xT0FVvB|9jdP{Ib5hL4Z_ zGYFcccg$LI?%lfw3=D$CW)+=Kw!HAS?>BFqiwgzX!EX#??^?4j4ycA-{@dLfxAw$^ zg4iEFU%Rn;bsQgv&3HMteCMhd1_cHN1wmo6^qI5HfXq~21hOk;tpc%~{>tvoj-1L0 zV*4}5N~e}KtU3c?D~L;Hm$qtw*$mv$rl}3H)&Rv_?sD%o4Qo(Z1!B7ZotIY8x#|D| F0|4G7a;N|R diff --git a/irr/test/data/sample_8bpp.png b/irr/test/data/sample_8bpp.png deleted file mode 100644 index c81f816f4f031b4596304b336d80942b8d598b48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 346 zcmV-g0j2(lP)++atplo7P@c)0aK|9UknLF9QaZMnUG3b{yq`d9m91+wK&Byvx&U4-laBY@Q zRWa7FSO+(^Q&Fv0Vn|l&kxCmaynv`FY;1yv7%?D- zjgZ1h#6rc+bT*h+fMqj*L$k=ZVtMES-5c{ zBI1M$1SA{5g)5OPG-RhcHxdO$9lKI9NG{yX$KjrHE)Wmk1!MpcVzd0Eo}1BgU-ZIk zx%^qrclE}!{<^Lg&-Hpo?|sm#hvm}k^-6iM_Vjz>>3FlRw|^aM^>?-(%g*I)_d)-> z+dsPRAL(zWy}J|r{j7KMr~Gl*`+KE7dKLx3(B;Cf)?^sf8|Y!`K@taUh5I!NZ5q|H z$SZ6M5*EAMcce4WWzsMphKa!E(y=0JK+^~B4IJo{?b$`nQcP?S2QuX7(5$H} zbC4hd7X=$yV;!5Ih)t1zNo1Ks56o${eb7$tx-r2qLv9q(YbJu^6{0oHzNg#>h->?%?$RROJQbYwYTBNZN z5pg98fsiI(kt&1~CfI3bBPzH_*10*mMNawfa(LczfCT_k&<2_mG)S;3%Xa0hef_-J zcx6`yc4x`1ZQD0L?RMWDF50ax)%xw#W_AAV{@2d^*Y_j4_v6#_)uw<&hJkVK^wrWOt1~_&-QK}-$HKrnoWXD6Jc$bO7 zS9!gW#+V3#++i+91|;B+fW{aSY(WQ&<2t}lfR9Q{OtFa_FmB+Mg~%~;RT}A7qQ~1E zlLRHDbCIg3F10sqWpq%wz_Kw5i8+(K6r{ZbeHnV%o3zD%+#yRNzb3x=h>6F>G|H%-s49P7G`?0(+}Yva>r}H diff --git a/irr/test/data/sample_8bpp_up.tga b/irr/test/data/sample_8bpp_up.tga deleted file mode 100644 index a314e8689590abe043c1c5a60eb908aa77de8fe6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 419 zcmYk(Jxjwt7zgmn`=yukl3bH(lB?f9zeRB!|fM`3oxD*kwVnGlW zp@Wl%gNmEoTttOdZF62y2mj-b-LhEb_hUWc%}NH+k6~JkdF*CipXMUR(|*(=?A2lot`VtS7s_xlcNjcm5K5g_yu3qUMc_p diff --git a/irr/test/data/sample_8bpp_v3.bmp b/irr/test/data/sample_8bpp_v3.bmp deleted file mode 100644 index a890e64655127621d12b5418e1799341e990ad14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 474 zcmYk2y)Oh&6vfYczwu^wzIOJRomq`WA^r%xKSn1MDjEtOiLeTdMg*}!A`y*5p%PIb zIy#LYvDlsGzFCEvoSVE~PTsxej!%r~C~Kek2q`A{Bn^x@FscE=V|@+LTS?ANla?SC zW>BooL(ZK*Zcafiw;&hqAzN+8gGtEs3lu9KcwH;-w9&)EW)D|eUC5nVoNafpyYqvh z{fxbnZ^-jc9A18*dns{r)4}Jp#PMAR?~esepF4PakuXh%dzGpmMz#82UY24C`u`xb zrs-rntE6E<#vo_R28cCzRuy3q)fCHf#j=d2LikA(*9UTD=g^x3dFly2j-!AzZ1U+% z%}gCGD%CiQB4WA$OV_l&(P0?X>kaZ*1~j!J@9$V^G;+9BmYO0-cpUg4v+Y5}GBOG? z=0Sz|b{U441uap`BW9={6*EB0VV>>VWoo#t5J=*{8DL)3uq+Bg%yo4dU0$Mp9{d7t CQCp}0 diff --git a/irr/test/data/sample_8bpp_v3_rle.bmp b/irr/test/data/sample_8bpp_v3_rle.bmp deleted file mode 100644 index d460d0fd66c87c67af2e4d3f302a7a44b1bdec51..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 524 zcmXw$y-wUf6orqy{u!@-#`dgtc6Zh^X{dOJ^mzruE2N7^k%}}>Btb%CDbmm&1SlYo zkWiXP6saOa0Z}^WXi$R0CfW7mj=hX@Mw(A&=AL`!`HLkR_1dR>o+L<|WPy=@McSS% zG@7Hck$$sGdI!Dw8rAw+=#>-btyj?RN6>56(4#}>gO|{oUr~K{!2L#rn~zic*qY+o zrwR1VCC;`d*xmVu>hKnOCx4;8JmT~DA56|QjxNS{{H}3)HOB9s6;5x*_;sg&*m3+Y ziW8pYoo)}_kisOy4CD=T4HH)B_$pL!KP$+X4jJo<;DI0h*zg=2R-w$P+h%C>E+pYQ?*S4%#oGi<`-NDT7wqTj; zVUxWx7&LVQSM?oP6ro6Y#yqe0#1&nTjtJA3anCe6^D7rtVR%HmWh&Hl14R{x`z-Rf i*G$Ls6$DbE5UJm0QPOfTpr5#bFeY=>{xa(`Kc^Flsbf zlxG>x)E)Wsj`db6hihf2DUyW8fgdv4ZYs+tQJ66gs?4{`X2>jPi87DOP=8crfXrc@ g?b~J7a9tsg#DP;`Ud^y9YKF{pbt+xH#PoUa1n?(co&W#< diff --git a/irr/test/data/sample_8bpp_v7_rle.bmp b/irr/test/data/sample_8bpp_v7_rle.bmp deleted file mode 100644 index d197b09a615ea5b00dc78ab57a7f2dc44d43fed9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 608 zcmb7?KTi}v7{;I7+ds4HpV{5Hz2RHbhLQppgPFF$x6@4Kc)c zMiLTZV?tqNj0F-p>1Yg)aKf?UJG%=#lX)jIzdSSV^S+sx$8$F7VOzjU+6f9yVS!Nq zPicAhsEG%3HUa3{S)XQRpFFQO_gLDrcQ^Fn3+TmF=vNDul$DI z7(st{2L1LMs`U%}eN*AjyBi#B-r)Pz1bXKezHCqMe&-t1=mfiaSI}QC@$ujvCI=dy zevWbZL*w(|80W_o_D{w*JEb~e$MM4`PI#7gd(-fSWGNwLAa9^+n6OI6SD}jgSwX^d z$e3r`3lJ|umB_5fyM_;qyyi-UeJOd7i{8|b)lWm%W$6%)Bo`u2*}!Y{c4;|%Ka_DI zvOFhlcj|Xs+p=0aS(f*DgI4c$U>WaWc1X7|9sXxV{q~p3&G+`$Ix5-f5x2Bo& GiT(q@QD6!H diff --git a/irr/test/image_loader_test.cpp b/irr/test/image_loader_test.cpp deleted file mode 100644 index 1dd4888f2..000000000 --- a/irr/test/image_loader_test.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#include -#include -#include -#include -#include - -using namespace irr; - -struct ImageDesc -{ - const char *name; - video::ECOLOR_FORMAT format; - std::vector data; -}; - -template -std::vector to_byte_array(std::initializer_list in_data) -{ - std::vector data; - data.resize(sizeof(T) * in_data.size()); - memcpy(data.data(), in_data.begin(), data.size()); - return data; -} - -static const ImageDesc test_images[] = { - {"16color-16bpp", video::ECF_A1R5G5B5, to_byte_array({ - 0xb6df, 0xb6df, 0xb6df, 0xb6df, 0xc23f, 0xc23f, 0xd61f, 0xd61f, 0xea3f, 0xea3f, 0xfe5e, 0xfe5e, 0xfe7c, 0xfe7a, 0xfe7a, 0xfe97, 0xfeb5, 0xfeb5, 0xff16, - 0xb6df, 0xb6df, 0xff3f, 0xff3f, 0xc23f, 0xf2df, 0xf2df, 0xf2df, 0xea3f, 0xda7f, 0xda7f, 0xfe5e, 0xfe7a, 0xc23f, 0xbe7f, 0xbe7f, 0xfeb5, 0xfeb5, 0xfb76, - 0xb6df, 0xff9e, 0xc23f, 0xc23f, 0xc23f, 0xf2df, 0xd61f, 0xf2df, 0xea3f, 0xda7f, 0xfe5e, 0xda7f, 0xfe7a, 0xc23f, 0xfe97, 0xfeb5, 0xfeb5, 0xff16, 0xfb76, - 0xb6df, 0xff9e, 0xbe7f, 0xc23f, 0xd61f, 0xfe7c, 0xd61f, 0xf2df, 0xfe5e, 0xf2df, 0xfe7c, 0xda7f, 0xfe7a, 0xda7f, 0xbe7f, 0xfeb5, 0xff16, 0xff16, 0xfb76, - 0xb6df, 0xff9e, 0xc23f, 0xc23f, 0xd61f, 0xff3f, 0xd61f, 0xf2df, 0xfe5e, 0xda7f, 0xfe7c, 0xda7f, 0xfe7a, 0xc23f, 0xfeb5, 0xfeb5, 0xff16, 0xff16, 0xfb76, - 0xb6df, 0xbe7f, 0xff9e, 0xff3f, 0xd61f, 0xff3f, 0xf2df, 0xf2df, 0xfe5e, 0xf2df, 0xda7f, 0xfe97, 0xfe97, 0xda7f, 0xc23f, 0xda7f, 0xfb76, 0xfb76, 0xfb76, - 0xbe7f, 0xc23f, 0xc23f, 0xd61f, 0xd61f, 0xea3f, 0xea3f, 0xfe5e, 0xfe5e, 0xfe7c, 0xfe7c, 0xfe7a, 0xfe97, 0xfeb5, 0xfeb5, 0xfeb5, 0xfb76, 0xfb76, 0xfb76, - 0xbe7f, 0xc23f, 0xd61f, 0xd61f, 0xd61f, 0xea3f, 0xea3f, 0xfe5e, 0xfe5e, 0xfe7c, 0xfe97, 0xfe97, 0xfe97, 0xfeb5, 0xff16, 0xff16, 0xfb76, 0xfb76, 0xfb76, - 0xc23f, 0xff9e, 0xc23f, 0xd61f, 0xff3f, 0xea3f, 0xfe5e, 0xff3f, 0xfe7c, 0xfe7c, 0xf2df, 0xfe97, 0xfeb5, 0xfeb5, 0xda7f, 0xff16, 0xc23f, 0xc23f, 0xbe7f, - 0xc23f, 0xff9e, 0xff9e, 0xd61f, 0xff9e, 0xea3f, 0xff3f, 0xfe5e, 0xff3f, 0xfe7a, 0xf2df, 0xf2df, 0xfeb5, 0xf2df, 0xda7f, 0xfb76, 0xc23f, 0xfb76, 0xfb76, - 0xc23f, 0xff9e, 0xd61f, 0xff9e, 0xff9e, 0xfe5e, 0xff3f, 0xff3f, 0xff3f, 0xfe7a, 0xff3f, 0xfeb5, 0xf2df, 0xff16, 0xda7f, 0xfb76, 0xda7f, 0xda7f, 0xfb76, - 0xc23f, 0xff9e, 0xea3f, 0xea3f, 0xff9e, 0xfe5e, 0xff9e, 0xfe7c, 0xff3f, 0xfe97, 0xf2df, 0xfeb5, 0xfeb5, 0xff16, 0xf2df, 0xfb76, 0xda7f, 0xfb76, 0xfb76, - 0xd61f, 0xff9e, 0xd61f, 0xea3f, 0xff9e, 0xfe5e, 0xff9e, 0xfe7c, 0xff3f, 0xfe97, 0xff3f, 0xfeb5, 0xff16, 0xfb76, 0xda7f, 0xfb76, 0xda7f, 0xda7f, 0xc23f, - 0xd61f, 0xd61f, 0xea3f, 0xea3f, 0xea3f, 0xfe5e, 0xfe7c, 0xfe7a, 0xfe7a, 0xfe97, 0xfeb5, 0xff16, 0xff16, 0xfb76, 0xfb76, 0xfb76, 0xfb76, 0xfb76, 0xfb76, - 0xd61f, 0xd61f, 0xea3f, 0xfe5e, 0xfe5e, 0xfe7c, 0xfe7a, 0xfe7a, 0xfe97, 0xfeb5, 0xfeb5, 0xff16, 0xff16, 0xfb76, 0xfb76, 0xfb76, 0xfb76, 0xfb76, 0xfb76, - })}, - - {"30color-16bpp", video::ECF_A1R5G5B5, to_byte_array({ - 0xbabf, 0xbabf, 0xbabf, 0xbabf, 0xbe3f, 0xbdff, 0xc9ff, 0xda1f, 0xee3f, 0xee3f, 0xfa5f, 0xfe5e, 0xfe7c, 0xfe7b, 0xfe79, 0xfe97, 0xfeb5, 0xfed5, 0xff16, - 0xbabf, 0xbabf, 0xff1f, 0xff1f, 0xbdff, 0xfaff, 0xf2df, 0xeabf, 0xee3f, 0xe27f, 0xda7f, 0xfe5e, 0xfe7b, 0xc25f, 0xbe7f, 0xbe7f, 0xfeb5, 0xfed5, 0xff56, - 0xbabf, 0xff7e, 0xbe7f, 0xc25f, 0xbdff, 0xfaff, 0xda1f, 0xf2df, 0xee3f, 0xda7f, 0xfe5e, 0xce5f, 0xfe79, 0xce5f, 0xfe97, 0xfeb5, 0xfed5, 0xff16, 0xff56, - 0xbabf, 0xff7e, 0xbe7f, 0xbdff, 0xc9ff, 0xfaff, 0xda1f, 0xf2df, 0xfa5f, 0xeabf, 0xfe5e, 0xda7f, 0xfe79, 0xce5f, 0xc25f, 0xfeb5, 0xff16, 0xff16, 0xff76, - 0xbabf, 0xff7e, 0xc25f, 0xbdff, 0xc9ff, 0xff1f, 0xda1f, 0xf2df, 0xfa5f, 0xeabf, 0xfe7c, 0xda7f, 0xfe79, 0xce5f, 0xfeb5, 0xfed5, 0xff16, 0xff56, 0xff97, - 0xbabf, 0xbe7f, 0xff7e, 0xff5e, 0xda1f, 0xff1f, 0xfaff, 0xfaff, 0xfa5f, 0xf2df, 0xeabf, 0xfe79, 0xfe97, 0xda7f, 0xce5f, 0xce5f, 0xff56, 0xff76, 0xff97, - 0xbe7f, 0xc25f, 0xbdff, 0xc9ff, 0xda1f, 0xda1f, 0xee3f, 0xfa5f, 0xfa5f, 0xfe7c, 0xfe7b, 0xfe79, 0xfe97, 0xfeb5, 0xfed5, 0xfed5, 0xff76, 0xff76, 0xff97, - 0xbe7f, 0xbe3f, 0xc9ff, 0xc9ff, 0xda1f, 0xee3f, 0xee3f, 0xfa5f, 0xfe5e, 0xfe7c, 0xfe79, 0xfe97, 0xfe97, 0xfeb5, 0xff16, 0xff16, 0xff76, 0xff97, 0xff97, - 0xbe3f, 0xffbe, 0xc9ff, 0xda1f, 0xff5e, 0xee3f, 0xfa5f, 0xff1f, 0xfe7c, 0xfe7b, 0xf2df, 0xfe97, 0xfeb5, 0xfeb5, 0xda7f, 0xff76, 0xce5f, 0xbe3f, 0xc25f, - 0xbe3f, 0xffbe, 0xffbe, 0xda1f, 0xff7e, 0xfa5f, 0xff5e, 0xfe5e, 0xff1f, 0xfe79, 0xf2df, 0xf2df, 0xfeb5, 0xeabf, 0xda7f, 0xff76, 0xce5f, 0xff97, 0xff97, - 0xbdff, 0xffbe, 0xda1f, 0xff7e, 0xff7e, 0xfa5f, 0xff5e, 0xff1f, 0xff1f, 0xfe79, 0xfaff, 0xfeb5, 0xf2df, 0xff16, 0xe27f, 0xff97, 0xda7f, 0xce5f, 0xff97, - 0xbdff, 0xffbe, 0xda1f, 0xee3f, 0xffbe, 0xfa5f, 0xff7e, 0xfe7b, 0xff1f, 0xfe97, 0xfaff, 0xfeb5, 0xfed5, 0xff56, 0xeabf, 0xff97, 0xda7f, 0xff97, 0xff97, - 0xc9ff, 0xffbe, 0xda1f, 0xee3f, 0xff7e, 0xfa5f, 0xff7e, 0xfe7c, 0xff5e, 0xfe97, 0xfaff, 0xfed5, 0xff16, 0xff56, 0xeabf, 0xff97, 0xda7f, 0xda7f, 0xce5f, - 0xda1f, 0xda1f, 0xee3f, 0xee3f, 0xfa5f, 0xfe5e, 0xfe7c, 0xfe79, 0xfe79, 0xfe97, 0xfeb5, 0xfed5, 0xff16, 0xff56, 0xff97, 0xff97, 0xff97, 0xff97, 0xff97, - 0xda1f, 0xda1f, 0xee3f, 0xfa5f, 0xfe5e, 0xfe7c, 0xfe7b, 0xfe79, 0xfe97, 0xfeb5, 0xfed5, 0xff16, 0xff56, 0xff97, 0xff97, 0xff97, 0xff97, 0xff97, 0xff97, - })}, - - {"30color-24bpp", video::ECF_R8G8B8, { - 0x71, 0xaf, 0xff, 0x71, 0xaf, 0xff, 0x71, 0xaf, 0xff, 0x71, 0xaf, 0xff, 0x7b, 0x8b, 0xff, 0x7a, 0x7f, 0xff, 0x90, 0x7f, 0xff, 0xb6, 0x85, 0xff, 0xd9, 0x8c, 0xff, 0xd9, 0x8c, 0xff, 0xf4, 0x92, 0xfe, 0xfe, 0x96, 0xf0, 0xfd, 0x99, 0xe3, 0xfd, 0x9b, 0xda, 0xfc, 0x9f, 0xca, 0xfc, 0xa4, 0xbc, 0xfb, 0xa9, 0xab, 0xfa, 0xb6, 0xad, 0xf9, 0xc6, 0xb1, - 0x71, 0xaf, 0xff, 0x71, 0xaf, 0xff, 0xff, 0xc6, 0xfc, 0xff, 0xc6, 0xfc, 0x7a, 0x7f, 0xff, 0xf7, 0xbe, 0xff, 0xe4, 0xb2, 0xfe, 0xd4, 0xa9, 0xff, 0xd9, 0x8c, 0xff, 0xc3, 0x98, 0xff, 0xb2, 0x9a, 0xff, 0xfe, 0x96, 0xf0, 0xfd, 0x9b, 0xda, 0x86, 0x93, 0xfe, 0x78, 0x9b, 0xff, 0x78, 0x9b, 0xff, 0xfb, 0xa9, 0xab, 0xfa, 0xb6, 0xad, 0xf8, 0xd2, 0xb3, - 0x71, 0xaf, 0xff, 0xfe, 0xdc, 0xf3, 0x78, 0x9b, 0xff, 0x86, 0x93, 0xfe, 0x7a, 0x7f, 0xff, 0xf7, 0xbe, 0xff, 0xb6, 0x85, 0xff, 0xe4, 0xb2, 0xfe, 0xd9, 0x8c, 0xff, 0xb2, 0x9a, 0xff, 0xfe, 0x96, 0xf0, 0x9a, 0x91, 0xff, 0xfc, 0x9f, 0xca, 0x9a, 0x91, 0xff, 0xfc, 0xa4, 0xbc, 0xfb, 0xa9, 0xab, 0xfa, 0xb6, 0xad, 0xf9, 0xc6, 0xb1, 0xf8, 0xd2, 0xb3, - 0x71, 0xaf, 0xff, 0xfe, 0xdc, 0xf3, 0x78, 0x9b, 0xff, 0x7a, 0x7f, 0xff, 0x90, 0x7f, 0xff, 0xf7, 0xbe, 0xff, 0xb6, 0x85, 0xff, 0xe4, 0xb2, 0xfe, 0xf4, 0x92, 0xfe, 0xd4, 0xa9, 0xff, 0xfe, 0x96, 0xf0, 0xb2, 0x9a, 0xff, 0xfc, 0x9f, 0xca, 0x9a, 0x91, 0xff, 0x86, 0x93, 0xfe, 0xfb, 0xa9, 0xab, 0xf9, 0xc6, 0xb1, 0xf9, 0xc6, 0xb1, 0xf8, 0xd8, 0xb5, - 0x71, 0xaf, 0xff, 0xfe, 0xdc, 0xf3, 0x86, 0x93, 0xfe, 0x7a, 0x7f, 0xff, 0x90, 0x7f, 0xff, 0xff, 0xc6, 0xfc, 0xb6, 0x85, 0xff, 0xe4, 0xb2, 0xfe, 0xf4, 0x92, 0xfe, 0xd4, 0xa9, 0xff, 0xfd, 0x99, 0xe3, 0xb2, 0x9a, 0xff, 0xfc, 0x9f, 0xca, 0x9a, 0x91, 0xff, 0xfb, 0xa9, 0xab, 0xfa, 0xb6, 0xad, 0xf9, 0xc6, 0xb1, 0xf8, 0xd2, 0xb3, 0xf8, 0xe4, 0xb9, - 0x71, 0xaf, 0xff, 0x78, 0x9b, 0xff, 0xfe, 0xdc, 0xf3, 0xff, 0xd0, 0xf7, 0xb6, 0x85, 0xff, 0xff, 0xc6, 0xfc, 0xf7, 0xbe, 0xff, 0xf7, 0xbe, 0xff, 0xf4, 0x92, 0xfe, 0xe4, 0xb2, 0xfe, 0xd4, 0xa9, 0xff, 0xfc, 0x9f, 0xca, 0xfc, 0xa4, 0xbc, 0xb2, 0x9a, 0xff, 0x9a, 0x91, 0xff, 0x9a, 0x91, 0xff, 0xf8, 0xd2, 0xb3, 0xf8, 0xd8, 0xb5, 0xf8, 0xe4, 0xb9, - 0x78, 0x9b, 0xff, 0x86, 0x93, 0xfe, 0x7a, 0x7f, 0xff, 0x90, 0x7f, 0xff, 0xb6, 0x85, 0xff, 0xb6, 0x85, 0xff, 0xd9, 0x8c, 0xff, 0xf4, 0x92, 0xfe, 0xf4, 0x92, 0xfe, 0xfd, 0x99, 0xe3, 0xfd, 0x9b, 0xda, 0xfc, 0x9f, 0xca, 0xfc, 0xa4, 0xbc, 0xfb, 0xa9, 0xab, 0xfa, 0xb6, 0xad, 0xfa, 0xb6, 0xad, 0xf8, 0xd8, 0xb5, 0xf8, 0xd8, 0xb5, 0xf8, 0xe4, 0xb9, - 0x78, 0x9b, 0xff, 0x7b, 0x8b, 0xff, 0x90, 0x7f, 0xff, 0x90, 0x7f, 0xff, 0xb6, 0x85, 0xff, 0xd9, 0x8c, 0xff, 0xd9, 0x8c, 0xff, 0xf4, 0x92, 0xfe, 0xfe, 0x96, 0xf0, 0xfd, 0x99, 0xe3, 0xfc, 0x9f, 0xca, 0xfc, 0xa4, 0xbc, 0xfc, 0xa4, 0xbc, 0xfb, 0xa9, 0xab, 0xf9, 0xc6, 0xb1, 0xf9, 0xc6, 0xb1, 0xf8, 0xd8, 0xb5, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, - 0x7b, 0x8b, 0xff, 0xff, 0xeb, 0xf2, 0x90, 0x7f, 0xff, 0xb6, 0x85, 0xff, 0xff, 0xd0, 0xf7, 0xd9, 0x8c, 0xff, 0xf4, 0x92, 0xfe, 0xff, 0xc6, 0xfc, 0xfd, 0x99, 0xe3, 0xfd, 0x9b, 0xda, 0xe4, 0xb2, 0xfe, 0xfc, 0xa4, 0xbc, 0xfb, 0xa9, 0xab, 0xfb, 0xa9, 0xab, 0xb2, 0x9a, 0xff, 0xf8, 0xd8, 0xb5, 0x9a, 0x91, 0xff, 0x7b, 0x8b, 0xff, 0x86, 0x93, 0xfe, - 0x7b, 0x8b, 0xff, 0xff, 0xeb, 0xf2, 0xff, 0xeb, 0xf2, 0xb6, 0x85, 0xff, 0xfe, 0xdc, 0xf3, 0xf4, 0x92, 0xfe, 0xff, 0xd0, 0xf7, 0xfe, 0x96, 0xf0, 0xff, 0xc6, 0xfc, 0xfc, 0x9f, 0xca, 0xe4, 0xb2, 0xfe, 0xe4, 0xb2, 0xfe, 0xfb, 0xa9, 0xab, 0xd4, 0xa9, 0xff, 0xb2, 0x9a, 0xff, 0xf8, 0xd8, 0xb5, 0x9a, 0x91, 0xff, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, - 0x7a, 0x7f, 0xff, 0xff, 0xeb, 0xf2, 0xb6, 0x85, 0xff, 0xfe, 0xdc, 0xf3, 0xfe, 0xdc, 0xf3, 0xf4, 0x92, 0xfe, 0xff, 0xd0, 0xf7, 0xff, 0xc6, 0xfc, 0xff, 0xc6, 0xfc, 0xfc, 0x9f, 0xca, 0xf7, 0xbe, 0xff, 0xfb, 0xa9, 0xab, 0xe4, 0xb2, 0xfe, 0xf9, 0xc6, 0xb1, 0xc3, 0x98, 0xff, 0xf8, 0xe4, 0xb9, 0xb2, 0x9a, 0xff, 0x9a, 0x91, 0xff, 0xf8, 0xe4, 0xb9, - 0x7a, 0x7f, 0xff, 0xff, 0xeb, 0xf2, 0xb6, 0x85, 0xff, 0xd9, 0x8c, 0xff, 0xff, 0xeb, 0xf2, 0xf4, 0x92, 0xfe, 0xfe, 0xdc, 0xf3, 0xfd, 0x9b, 0xda, 0xff, 0xc6, 0xfc, 0xfc, 0xa4, 0xbc, 0xf7, 0xbe, 0xff, 0xfb, 0xa9, 0xab, 0xfa, 0xb6, 0xad, 0xf8, 0xd2, 0xb3, 0xd4, 0xa9, 0xff, 0xf8, 0xe4, 0xb9, 0xb2, 0x9a, 0xff, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, - 0x90, 0x7f, 0xff, 0xff, 0xeb, 0xf2, 0xb6, 0x85, 0xff, 0xd9, 0x8c, 0xff, 0xfe, 0xdc, 0xf3, 0xf4, 0x92, 0xfe, 0xfe, 0xdc, 0xf3, 0xfd, 0x99, 0xe3, 0xff, 0xd0, 0xf7, 0xfc, 0xa4, 0xbc, 0xf7, 0xbe, 0xff, 0xfa, 0xb6, 0xad, 0xf9, 0xc6, 0xb1, 0xf8, 0xd2, 0xb3, 0xd4, 0xa9, 0xff, 0xf8, 0xe4, 0xb9, 0xb2, 0x9a, 0xff, 0xb2, 0x9a, 0xff, 0x9a, 0x91, 0xff, - 0xb6, 0x85, 0xff, 0xb6, 0x85, 0xff, 0xd9, 0x8c, 0xff, 0xd9, 0x8c, 0xff, 0xf4, 0x92, 0xfe, 0xfe, 0x96, 0xf0, 0xfd, 0x99, 0xe3, 0xfc, 0x9f, 0xca, 0xfc, 0x9f, 0xca, 0xfc, 0xa4, 0xbc, 0xfb, 0xa9, 0xab, 0xfa, 0xb6, 0xad, 0xf9, 0xc6, 0xb1, 0xf8, 0xd2, 0xb3, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, - 0xb6, 0x85, 0xff, 0xb6, 0x85, 0xff, 0xd9, 0x8c, 0xff, 0xf4, 0x92, 0xfe, 0xfe, 0x96, 0xf0, 0xfd, 0x99, 0xe3, 0xfd, 0x9b, 0xda, 0xfc, 0x9f, 0xca, 0xfc, 0xa4, 0xbc, 0xfb, 0xa9, 0xab, 0xfa, 0xb6, 0xad, 0xf9, 0xc6, 0xb1, 0xf8, 0xd2, 0xb3, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, 0xf8, 0xe4, 0xb9, - }}, - - {"30color-32bpp", video::ECF_A8R8G8B8, to_byte_array({ - 0xff71afff, 0xff71afff, 0xff71afff, 0xff71afff, 0xff7b8bff, 0xff7a7fff, 0xff907fff, 0xffb685ff, 0xffd98cff, 0xffd98cff, 0xfff492fe, 0xfffe96f0, 0xfffd99e3, 0xfffd9bda, 0xfffc9fca, 0xfffca4bc, 0xfffba9ab, 0xfffab6ad, 0xfff9c6b1, - 0xff71afff, 0xff71afff, 0xffffc6fc, 0xffffc6fc, 0xff7a7fff, 0xfff7beff, 0xffe4b2fe, 0xffd4a9ff, 0xffd98cff, 0xffc398ff, 0xffb29aff, 0xfffe96f0, 0xfffd9bda, 0xff8693fe, 0xff789bff, 0xff789bff, 0xfffba9ab, 0xfffab6ad, 0xfff8d2b3, - 0xff71afff, 0xfffedcf3, 0xff789bff, 0xff8693fe, 0xff7a7fff, 0xfff7beff, 0xffb685ff, 0xffe4b2fe, 0xffd98cff, 0xffb29aff, 0xfffe96f0, 0xff9a91ff, 0xfffc9fca, 0xff9a91ff, 0xfffca4bc, 0xfffba9ab, 0xfffab6ad, 0xfff9c6b1, 0xfff8d2b3, - 0xff71afff, 0xfffedcf3, 0xff789bff, 0xff7a7fff, 0xff907fff, 0xfff7beff, 0xffb685ff, 0xffe4b2fe, 0xfff492fe, 0xffd4a9ff, 0xfffe96f0, 0xffb29aff, 0xfffc9fca, 0xff9a91ff, 0xff8693fe, 0xfffba9ab, 0xfff9c6b1, 0xfff9c6b1, 0xfff8d8b5, - 0xff71afff, 0xfffedcf3, 0xff8693fe, 0xff7a7fff, 0xff907fff, 0xffffc6fc, 0xffb685ff, 0xffe4b2fe, 0xfff492fe, 0xffd4a9ff, 0xfffd99e3, 0xffb29aff, 0xfffc9fca, 0xff9a91ff, 0xfffba9ab, 0xfffab6ad, 0xfff9c6b1, 0xfff8d2b3, 0xfff8e4b9, - 0xff71afff, 0xff789bff, 0xfffedcf3, 0xffffd0f7, 0xffb685ff, 0xffffc6fc, 0xfff7beff, 0xfff7beff, 0xfff492fe, 0xffe4b2fe, 0xffd4a9ff, 0xfffc9fca, 0xfffca4bc, 0xffb29aff, 0xff9a91ff, 0xff9a91ff, 0xfff8d2b3, 0xfff8d8b5, 0xfff8e4b9, - 0xff789bff, 0xff8693fe, 0xff7a7fff, 0xff907fff, 0xffb685ff, 0xffb685ff, 0xffd98cff, 0xfff492fe, 0xfff492fe, 0xfffd99e3, 0xfffd9bda, 0xfffc9fca, 0xfffca4bc, 0xfffba9ab, 0xfffab6ad, 0xfffab6ad, 0xfff8d8b5, 0xfff8d8b5, 0xfff8e4b9, - 0xff789bff, 0xff7b8bff, 0xff907fff, 0xff907fff, 0xffb685ff, 0xffd98cff, 0xffd98cff, 0xfff492fe, 0xfffe96f0, 0xfffd99e3, 0xfffc9fca, 0xfffca4bc, 0xfffca4bc, 0xfffba9ab, 0xfff9c6b1, 0xfff9c6b1, 0xfff8d8b5, 0xfff8e4b9, 0xfff8e4b9, - 0xff7b8bff, 0xffffebf2, 0xff907fff, 0xffb685ff, 0xffffd0f7, 0xffd98cff, 0xfff492fe, 0xffffc6fc, 0xfffd99e3, 0xfffd9bda, 0xffe4b2fe, 0xfffca4bc, 0xfffba9ab, 0xfffba9ab, 0xffb29aff, 0xfff8d8b5, 0xff9a91ff, 0xff7b8bff, 0xff8693fe, - 0xff7b8bff, 0xffffebf2, 0xffffebf2, 0xffb685ff, 0xfffedcf3, 0xfff492fe, 0xffffd0f7, 0xfffe96f0, 0xffffc6fc, 0xfffc9fca, 0xffe4b2fe, 0xffe4b2fe, 0xfffba9ab, 0xffd4a9ff, 0xffb29aff, 0xfff8d8b5, 0xff9a91ff, 0xfff8e4b9, 0xfff8e4b9, - 0xff7a7fff, 0xffffebf2, 0xffb685ff, 0xfffedcf3, 0xfffedcf3, 0xfff492fe, 0xffffd0f7, 0xffffc6fc, 0xffffc6fc, 0xfffc9fca, 0xfff7beff, 0xfffba9ab, 0xffe4b2fe, 0xfff9c6b1, 0xffc398ff, 0xfff8e4b9, 0xffb29aff, 0xff9a91ff, 0xfff8e4b9, - 0xff7a7fff, 0xffffebf2, 0xffb685ff, 0xffd98cff, 0xffffebf2, 0xfff492fe, 0xfffedcf3, 0xfffd9bda, 0xffffc6fc, 0xfffca4bc, 0xfff7beff, 0xfffba9ab, 0xfffab6ad, 0xfff8d2b3, 0xffd4a9ff, 0xfff8e4b9, 0xffb29aff, 0xfff8e4b9, 0xfff8e4b9, - 0xff907fff, 0xffffebf2, 0xffb685ff, 0xffd98cff, 0xfffedcf3, 0xfff492fe, 0xfffedcf3, 0xfffd99e3, 0xffffd0f7, 0xfffca4bc, 0xfff7beff, 0xfffab6ad, 0xfff9c6b1, 0xfff8d2b3, 0xffd4a9ff, 0xfff8e4b9, 0xffb29aff, 0xffb29aff, 0xff9a91ff, - 0xffb685ff, 0xffb685ff, 0xffd98cff, 0xffd98cff, 0xfff492fe, 0xfffe96f0, 0xfffd99e3, 0xfffc9fca, 0xfffc9fca, 0xfffca4bc, 0xfffba9ab, 0xfffab6ad, 0xfff9c6b1, 0xfff8d2b3, 0xfff8e4b9, 0xfff8e4b9, 0xfff8e4b9, 0xfff8e4b9, 0xfff8e4b9, - 0xffb685ff, 0xffb685ff, 0xffd98cff, 0xfff492fe, 0xfffe96f0, 0xfffd99e3, 0xfffd9bda, 0xfffc9fca, 0xfffca4bc, 0xfffba9ab, 0xfffab6ad, 0xfff9c6b1, 0xfff8d2b3, 0xfff8e4b9, 0xfff8e4b9, 0xfff8e4b9, 0xfff8e4b9, 0xfff8e4b9, 0xfff8e4b9, - })}, - -}; - -void printImageBytes(const video::IImage *img) -{ - const auto *data = (u8 *)img->getData(); - const auto w = img->getPitch(); - const auto h = img->getDimension().Height; - for (int y = 0; y < h; y++) { - for (int k = 0; k < w; k++) { - std::printf("0x%02x, ", *data++); - } - std::printf("\n"); - } -} - -int main(int argc, char *argv[]) -try { - if (argc != 3) - throw std::runtime_error("Invalid arguments. Expected sample ID and image file name"); - - const ImageDesc *sample = nullptr; - for (auto &&image : test_images) { - if (strcmp(argv[1], image.name) == 0) - sample = ℑ - } - if (!sample) - throw std::runtime_error("Sample not found"); - - SIrrlichtCreationParameters p; - p.DriverType = video::EDT_NULL; - p.WindowSize = core::dimension2du(640, 480); - p.LoggingLevel = ELL_DEBUG; - - auto *device = createDeviceEx(p); - if (!device) - throw std::runtime_error("Failed to create device"); - - auto *driver = device->getVideoDriver(); - - auto *img = driver->createImageFromFile(argv[2]); - if (!img) - throw std::runtime_error("Failed to load image"); - - if (img->getDimension() != core::dimension2du{19, 15}) - throw std::runtime_error("Wrong image dimensions"); - - if (img->getColorFormat() != sample->format) - throw std::runtime_error("Wrong image format"); - - if (img->getImageDataSizeInBytes() != sample->data.size()) - throw std::logic_error("Image data size not equal to sample size"); - - if (memcmp(img->getData(), sample->data.data(), sample->data.size()) != 0) { - printImageBytes(img); - throw std::runtime_error("Wrong image contents"); - } - - img->drop(); - device->drop(); - - return 0; -} catch (const std::exception &e) { - std::printf("Test failed: %s\n", e.what()); - return 1; -} From ec115ffe2ae89ef633d3a7e5a9f01b4e59b62c47 Mon Sep 17 00:00:00 2001 From: Daniel Hajjar <33489389+DanTGL@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:49:15 +0200 Subject: [PATCH 11/75] Make SecureRandom non-failable --- doc/lua_api.md | 2 +- src/script/lua_api/l_noise.cpp | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 4d461f335..27a9fef96 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -8669,7 +8669,7 @@ In multiplayer mode, the error may be arbitrarily large. Interface for the operating system's crypto-secure PRNG. -It can be created via `SecureRandom()`. The constructor returns nil if a +It can be created via `SecureRandom()`. The constructor throws an error if a secure random device cannot be found on the system. ### Methods diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index 9836f4b09..ead14fec0 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -641,10 +641,9 @@ int LuaSecureRandom::create_object(lua_State *L) { LuaSecureRandom *o = new LuaSecureRandom(); - // Fail and return nil if we can't securely fill the buffer if (!o->fillRandBuf()) { delete o; - return 0; + throw LuaError("SecureRandom: Failed to find secure random device on system"); } *(void **)(lua_newuserdata(L, sizeof(void *))) = o; From 9ccd9d341ffa6fc5c73c42501e92181840cbb930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:49:41 +0200 Subject: [PATCH 12/75] Revert empty form name deprecation warnings --- doc/lua_api.md | 3 ++- src/script/lua_api/l_server.cpp | 12 +----------- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 27a9fef96..dde797325 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -6463,7 +6463,8 @@ Formspec * `playername`: name of player to show formspec * `formname`: name passed to `on_player_receive_fields` callbacks. It should follow the `"modname:"` naming convention. - `formname` must not be empty. + * `formname` must not be empty, unless you want to reshow + the inventory formspec without updating it for future opens. * `formspec`: formspec to display * `minetest.close_formspec(playername, formname)` * `playername`: name of player to close formspec diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index af9a526e0..69f5cf9a0 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -426,18 +426,8 @@ int ModApiServer::l_show_formspec(lua_State *L) NO_MAP_LOCK_REQUIRED; const char *playername = luaL_checkstring(L, 1); const char *formname = luaL_checkstring(L, 2); - if (*formname == '\0') { - log_deprecated(L, "Deprecated call to `minetest.show_formspec`:" - "`formname` must not be empty"); - } const char *formspec = luaL_checkstring(L, 3); - - if(getServer(L)->showFormspec(playername,formspec,formname)) - { - lua_pushboolean(L, true); - }else{ - lua_pushboolean(L, false); - } + lua_pushboolean(L, getServer(L)->showFormspec(playername,formspec,formname)); return 1; } From 2664afd83230e3f1509f0d66cba0b8c18808a9ad Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Tue, 20 Aug 2024 10:50:29 +0100 Subject: [PATCH 13/75] Fix Windows enabling touch controls due to existence of touchscreen (#15003) We want to check for the form factor instead. --- src/defaultsettings.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index f8cfd18b4..7d3273271 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -76,13 +76,6 @@ static bool detect_touch() return false; } - return false; -#elif defined(_WIN32) - // 0x01 The device has an integrated touch digitizer - // 0x80 The device is ready to receive digitizer input. - if ((GetSystemMetrics(SM_DIGITIZER) & 0x81) == 0x81) - return true; - return false; #else // we don't know, return default From dc21924f31ff52174553c4441ce8c861f842c6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Tue, 20 Aug 2024 11:51:52 +0200 Subject: [PATCH 14/75] Fix animations not being restartable (#15016) --- src/client/content_cao.cpp | 5 ++--- src/server/unit_sao.cpp | 5 +---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 7913e477a..19bee6f7f 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -1518,9 +1518,8 @@ void GenericCAO::updateAnimation() if (!m_animated_meshnode) return; - if (m_animated_meshnode->getStartFrame() != m_animation_range.X || - m_animated_meshnode->getEndFrame() != m_animation_range.Y) - m_animated_meshnode->setFrameLoop(m_animation_range.X, m_animation_range.Y); + // Note: This sets the current frame as well, (re)starting the animation. + m_animated_meshnode->setFrameLoop(m_animation_range.X, m_animation_range.Y); if (m_animated_meshnode->getAnimationSpeed() != m_animation_speed) m_animated_meshnode->setAnimationSpeed(m_animation_speed); m_animated_meshnode->setTransitionTime(m_animation_blend); diff --git a/src/server/unit_sao.cpp b/src/server/unit_sao.cpp index d764b5d16..a4f547146 100644 --- a/src/server/unit_sao.cpp +++ b/src/server/unit_sao.cpp @@ -55,10 +55,7 @@ const ItemGroupList &UnitSAO::getArmorGroups() const void UnitSAO::setAnimation( v2f frame_range, float frame_speed, float frame_blend, bool frame_loop) { - if (std::tie(m_animation_range, m_animation_speed, m_animation_blend, - m_animation_loop) == - std::tie(frame_range, frame_speed, frame_blend, frame_loop)) - return; // no change + // Note: Always resend (even if parameters are unchanged) to restart animations. m_animation_range = frame_range; m_animation_speed = frame_speed; m_animation_blend = frame_blend; From f2c66b9ceb4930dfd7b510c77037c235b161e603 Mon Sep 17 00:00:00 2001 From: sfence Date: Wed, 21 Aug 2024 20:24:43 +0200 Subject: [PATCH 15/75] Add possibility to easier override HP and breath engine logic by Lua (#14179) Co-authored-by: Lars Mueller --- doc/lua_api.md | 8 +++++++ src/script/common/c_content.cpp | 2 +- src/script/lua_api/l_object.cpp | 37 +++++++++++++++++++++++++++++++++ src/script/lua_api/l_object.h | 6 ++++++ src/server/player_sao.cpp | 11 +++++++--- src/server/player_sao.h | 6 ++++++ 6 files changed, 66 insertions(+), 4 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index dde797325..07c7b3c2e 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -8495,6 +8495,14 @@ child will follow movement and rotation of that bone. * Result is a table with the same fields as `light_definition` in `set_lighting`. * `respawn()`: Respawns the player using the same mechanism as the death screen, including calling `on_respawnplayer` callbacks. +* `get_flags()`: returns a table of player flags (the following boolean fields): + * `breathing`: Whether breathing (regaining air) is enabled, default `true`. + * `drowning`: Whether drowning (losing air) is enabled, default `true`. + * `node_damage`: Whether the player takes damage from nodes, default `true`. +* `set_flags(flags)`: sets flags + * takes a table in the same format as returned by `get_flags` + * absent fields are left unchanged + `PcgRandom` ----------- diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 6f7d86447..bd76a3ad3 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -291,7 +291,7 @@ const std::array object_property_keys = { "use_texture_alpha", "shaded", "damage_texture_modifier", - "show_on_minimap" + "show_on_minimap", }; /******************************************************************************/ diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 00c825ddc..eb0a375d4 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -2688,6 +2688,41 @@ int ObjectRef::l_respawn(lua_State *L) return 1; } +// set_flags(self, flags) +int ObjectRef::l_set_flags(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + auto *psao = getplayersao(ref); + if (psao == nullptr) + return 0; + if (!lua_istable(L, -1)) + throw LuaError("expected a table of flags"); + auto &flags = psao->m_flags; + flags.drowning = getboolfield_default(L, -1, "drowning", flags.drowning); + flags.breathing = getboolfield_default(L, -1, "breathing", flags.breathing); + flags.node_damage = getboolfield_default(L, -1, "node_damage", flags.node_damage); + return 0; +} + +// get_flags(self) +int ObjectRef::l_get_flags(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + ObjectRef *ref = checkObject(L, 1); + const auto *psao = getplayersao(ref); + if (psao == nullptr) + return 0; + lua_createtable(L, 0, 3); + lua_pushboolean(L, psao->m_flags.drowning); + lua_setfield(L, -2, "drowning"); + lua_pushboolean(L, psao->m_flags.breathing); + lua_setfield(L, -2, "breathing"); + lua_pushboolean(L, psao->m_flags.node_damage); + lua_setfield(L, -2, "node_damage"); + return 1; +} + ObjectRef::ObjectRef(ServerActiveObject *object): m_object(object) @@ -2838,6 +2873,8 @@ luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_lighting), luamethod(ObjectRef, get_lighting), luamethod(ObjectRef, respawn), + luamethod(ObjectRef, set_flags), + luamethod(ObjectRef, get_flags), {0,0} }; diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index ace19e1f0..75e961438 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -411,4 +411,10 @@ private: // respawn(self) static int l_respawn(lua_State *L); + + // set_flags(self, flags) + static int l_set_flags(lua_State *L); + + // get_flags(self) + static int l_get_flags(lua_State *L); }; diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 4abb1f920..11922b2c6 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -156,7 +156,10 @@ void PlayerSAO::getStaticData(std::string * result) const void PlayerSAO::step(float dtime, bool send_recommended) { - if (!isImmortal() && m_drowning_interval.step(dtime, 2.0f)) { + bool not_immortal = !isImmortal(); + + if (not_immortal && m_flags.drowning + && m_drowning_interval.step(dtime, 2.0f)) { // Get nose/mouth position, approximate with eye position v3s16 p = floatToInt(getEyePosition(), BS); MapNode n = m_env->getMap().getNode(p); @@ -174,7 +177,8 @@ void PlayerSAO::step(float dtime, bool send_recommended) } } - if (m_breathing_interval.step(dtime, 0.5f) && !isImmortal()) { + if (not_immortal && m_flags.breathing + && m_breathing_interval.step(dtime, 0.5f)) { // Get nose/mouth position, approximate with eye position v3s16 p = floatToInt(getEyePosition(), BS); MapNode n = m_env->getMap().getNode(p); @@ -185,7 +189,8 @@ void PlayerSAO::step(float dtime, bool send_recommended) setBreath(m_breath + 1); } - if (!isImmortal() && m_node_hurt_interval.step(dtime, 1.0f)) { + if (not_immortal && m_flags.node_damage + && m_node_hurt_interval.step(dtime, 1.0f)) { u32 damage_per_second = 0; std::string nodename; v3s16 node_pos; diff --git a/src/server/player_sao.h b/src/server/player_sao.h index b26304589..95bd1d109 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -228,6 +228,12 @@ private: SimpleMetadata m_meta; public: + struct { + bool breathing : 1; + bool drowning : 1; + bool node_damage : 1; + } m_flags = {true, true, true}; + bool m_physics_override_sent = false; }; From 1bccb4e48ce4db8851d3d6eb4a574e7f56a9f8fd Mon Sep 17 00:00:00 2001 From: wrrrzr <161970349+wrrrzr@users.noreply.github.com> Date: Wed, 21 Aug 2024 21:24:59 +0300 Subject: [PATCH 16/75] Refactor tool.cpp (#14873) Co-authored-by: sfan5 --- src/tool.cpp | 107 ++++++++++++++++++++++++++++++--------------------- src/tool.h | 4 ++ 2 files changed, 68 insertions(+), 43 deletions(-) diff --git a/src/tool.cpp b/src/tool.cpp index 9df69eccd..0cb1724de 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -44,18 +44,25 @@ void ToolGroupCap::toJson(Json::Value &object) const void ToolGroupCap::fromJson(const Json::Value &json) { - if (json.isObject()) { - if (json["maxlevel"].isInt()) - maxlevel = json["maxlevel"].asInt(); - if (json["uses"].isInt()) - uses = json["uses"].asInt(); - const Json::Value ×_object = json["times"]; - if (times_object.isArray()) { - Json::ArrayIndex size = times_object.size(); - for (Json::ArrayIndex i = 0; i < size; ++i) - if (times_object[i].isDouble()) - times[i] = times_object[i].asFloat(); - } + if (!json.isObject()) + return; + + if (json["maxlevel"].isInt()) + maxlevel = json["maxlevel"].asInt(); + + if (json["uses"].isInt()) + uses = json["uses"].asInt(); + + const Json::Value ×_object = json["times"]; + + if (!times_object.isArray()) + return; + + Json::ArrayIndex size = times_object.size(); + + for (Json::ArrayIndex i = 0; i < size; ++i) { + if (times_object[i].isDouble()) + times[i] = times_object[i].asFloat(); } } @@ -65,9 +72,11 @@ void ToolCapabilities::serialize(std::ostream &os, u16 protocol_version) const writeU8(os, 5); else writeU8(os, 4); // proto == 37 + writeF32(os, full_punch_interval); writeS16(os, max_drop_level); writeU32(os, groupcaps.size()); + for (const auto &groupcap : groupcaps) { const std::string *name = &groupcap.first; const ToolGroupCap *cap = &groupcap.second; @@ -102,6 +111,7 @@ void ToolCapabilities::deSerialize(std::istream &is) max_drop_level = readS16(is); groupcaps.clear(); u32 groupcaps_size = readU32(is); + for (u32 i = 0; i < groupcaps_size; i++) { std::string name = deSerializeString16(is); ToolGroupCap cap; @@ -135,15 +145,19 @@ void ToolCapabilities::serializeJson(std::ostream &os) const root["punch_attack_uses"] = punch_attack_uses; Json::Value groupcaps_object; + for (const auto &groupcap : groupcaps) { groupcap.second.toJson(groupcaps_object[groupcap.first]); } + root["groupcaps"] = std::move(groupcaps_object); Json::Value damage_groups_object; + for (const auto &damagegroup : damageGroups) { damage_groups_object[damagegroup.first] = damagegroup.second; } + root["damage_groups"] = std::move(damage_groups_object); fastWriteJson(root, os); @@ -153,36 +167,44 @@ void ToolCapabilities::deserializeJson(std::istream &is) { Json::Value root; is >> root; - if (root.isObject()) { - if (root["full_punch_interval"].isDouble()) - full_punch_interval = root["full_punch_interval"].asFloat(); - if (root["max_drop_level"].isInt()) - max_drop_level = root["max_drop_level"].asInt(); - if (root["punch_attack_uses"].isInt()) - punch_attack_uses = root["punch_attack_uses"].asInt(); - Json::Value &groupcaps_object = root["groupcaps"]; - if (groupcaps_object.isObject()) { - Json::ValueIterator gciter; - for (gciter = groupcaps_object.begin(); - gciter != groupcaps_object.end(); ++gciter) { - ToolGroupCap groupcap; - groupcap.fromJson(*gciter); - groupcaps[gciter.key().asString()] = groupcap; - } - } + if (!root.isObject()) + return; - Json::Value &damage_groups_object = root["damage_groups"]; - if (damage_groups_object.isObject()) { - Json::ValueIterator dgiter; - for (dgiter = damage_groups_object.begin(); - dgiter != damage_groups_object.end(); ++dgiter) { - Json::Value &value = *dgiter; - if (value.isInt()) - damageGroups[dgiter.key().asString()] = - value.asInt(); - } - } + if (root["full_punch_interval"].isDouble()) + full_punch_interval = root["full_punch_interval"].asFloat(); + + if (root["max_drop_level"].isInt()) + max_drop_level = root["max_drop_level"].asInt(); + + if (root["punch_attack_uses"].isInt()) + punch_attack_uses = root["punch_attack_uses"].asInt(); + + deserializeJsonGroupcaps(root["groupcaps"]); + deserializeJsonDamageGroups(root["damage_groups"]); +} + +void ToolCapabilities::deserializeJsonGroupcaps(Json::Value &json) +{ + if (!json.isObject()) + return; + + for (Json::ValueIterator iter = json.begin(); iter != json.end(); ++iter) { + ToolGroupCap value; + value.fromJson(*iter); + groupcaps[iter.key().asString()] = value; + } +} + +void ToolCapabilities::deserializeJsonDamageGroups(Json::Value &json) +{ + if (!json.isObject()) + return; + + for (Json::ValueIterator iter = json.begin(); iter != json.end(); ++iter) { + Json::Value &value = *iter; + if (value.isInt()) + damageGroups[iter.key().asString()] = value.asInt(); } } @@ -362,9 +384,8 @@ u32 calculateResultWear(const u32 uses, const u16 initial_wear) player. */ u16 wear_extra_at = blocks_normal * wear_normal; - if (initial_wear >= wear_extra_at) { + if (initial_wear >= wear_extra_at) wear_extra = 1; - } } result_wear = wear_normal + wear_extra; return result_wear; @@ -410,6 +431,7 @@ DigParams getDigParams(const ItemGroupList &groups, if (leveldiff > 1) time /= leveldiff; + if (!result_diggable || time < result_time) { result_time = time; result_diggable = true; @@ -509,4 +531,3 @@ f32 getToolRange(const ItemStack &wielded_item, const ItemStack &hand_item, return max_d; } - diff --git a/src/tool.h b/src/tool.h index 4b25d3a62..c3d811dd4 100644 --- a/src/tool.h +++ b/src/tool.h @@ -83,6 +83,10 @@ struct ToolCapabilities void deSerialize(std::istream &is); void serializeJson(std::ostream &os) const; void deserializeJson(std::istream &is); + +private: + void deserializeJsonGroupcaps(Json::Value &json); + void deserializeJsonDamageGroups(Json::Value &json); }; struct WearBarParams From 6cc0452503fad764a9f64d30540b37737879eeea Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 21 Aug 2024 19:25:18 +0100 Subject: [PATCH 17/75] Generate Android versionCode from Major.Minor.Patch (#14963) --- android/app/build.gradle | 14 +------------- android/build.gradle | 9 ++++----- android/native/build.gradle | 2 +- util/bump_version.sh | 29 +---------------------------- 4 files changed, 7 insertions(+), 47 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index fe6c4ab0d..cefc473af 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -8,7 +8,7 @@ android { compileSdk 34 targetSdkVersion 34 versionName "${versionMajor}.${versionMinor}.${versionPatch}" - versionCode project.versionCode + versionCode versionMajor * 1000000 + versionMinor * 10000 + versionPatch * 100 + versionBuild } buildFeatures { @@ -116,18 +116,6 @@ clean { delete new File("src/main/assets", "Minetest.zip") } -// Map for the version code that gives each ABI a value. -import com.android.build.OutputFile - -def abiCodes = ['armeabi-v7a': 0, 'arm64-v8a': 1] -android.applicationVariants.all { variant -> - variant.outputs.each { - output -> - def abiName = output.getFilter(OutputFile.ABI) - output.versionCodeOverride = abiCodes.get(abiName, 0) + variant.versionCode - } -} - dependencies { implementation project(':native') implementation 'androidx.appcompat:appcompat:1.6.1' diff --git a/android/build.gradle b/android/build.gradle index 69fd26625..2017c536c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,13 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. project.ext.set("versionMajor", 5) // Version Major -project.ext.set("versionMinor", 10) // Version Minor +project.ext.set("versionMinor", 10) // Version Minor project.ext.set("versionPatch", 0) // Version Patch // ^ keep in sync with cmake -project.ext.set("versionCode", 48) // Android Version Code -// NOTE: +2 after each release! -// +1 for ARM and +1 for ARM64 APK's, because -// each APK must have a larger `versionCode` than the previous + +project.ext.set("versionBuild", 0) // Version Build +// ^ fourth version number to allow releasing Android-only fixes and beta versions buildscript { ext.ndk_version = '26.2.11394342' diff --git a/android/native/build.gradle b/android/native/build.gradle index 0be7c1d99..806dda2f0 100644 --- a/android/native/build.gradle +++ b/android/native/build.gradle @@ -8,7 +8,7 @@ android { compileSdk 34 targetSdkVersion 34 externalNativeBuild { - cmake { + cmake { arguments "-DANDROID_STL=c++_shared", "-DENABLE_CURL=1", "-DENABLE_SOUND=1", "-DENABLE_GETTEXT=1", diff --git a/util/bump_version.sh b/util/bump_version.sh index 45452c295..699bbcf77 100755 --- a/util/bump_version.sh +++ b/util/bump_version.sh @@ -16,20 +16,18 @@ prompt_for() { } # Reads current versions -# out: VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_IS_DEV CURRENT_VERSION ANDROID_VERSION_CODE +# out: VERSION_MAJOR VERSION_MINOR VERSION_PATCH VERSION_IS_DEV CURRENT_VERSION read_versions() { VERSION_MAJOR=$(grep -oE '^set\(VERSION_MAJOR [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) VERSION_MINOR=$(grep -oE '^set\(VERSION_MINOR [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) VERSION_PATCH=$(grep -oE '^set\(VERSION_PATCH [0-9]+\)$' CMakeLists.txt | tr -dC 0-9) VERSION_IS_DEV=$(grep -oE '^set\(DEVELOPMENT_BUILD [A-Z]+\)$' CMakeLists.txt) - ANDROID_VERSION_CODE=$(grep -oE '\("versionCode", [0-9]+\)' android/build.gradle | tr -dC 0-9) # Make sure they all exist [ -n "$VERSION_MAJOR" ] [ -n "$VERSION_MINOR" ] [ -n "$VERSION_PATCH" ] [ -n "$VERSION_IS_DEV" ] - [ -n "$ANDROID_VERSION_CODE" ] if echo "$VERSION_IS_DEV" | grep -q ' TRUE'; then VERSION_IS_DEV=1 @@ -39,7 +37,6 @@ read_versions() { CURRENT_VERSION="$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH" echo "Current Minetest version: $CURRENT_VERSION" - echo "Current Android version code: $ANDROID_VERSION_CODE" } # Retrieves protocol version from header @@ -49,18 +46,6 @@ read_proto_ver() { git show "$ref":src/network/networkprotocol.h | grep -oE 'LATEST_PROTOCOL_VERSION [0-9]+' | tr -dC 0-9 } -## Prompts for new android version code -# in: ANDROID_VERSION_CODE -# out: NEW_ANDROID_VERSION_CODE -bump_android_ver() { - # +1 for ARM and +1 for ARM64 APKs - NEW_ANDROID_VERSION_CODE=$(expr $ANDROID_VERSION_CODE + 2) - NEW_ANDROID_VERSION_CODE=$(prompt_for "Set android version code" '[0-9]+' $NEW_ANDROID_VERSION_CODE) - - echo - echo "New android version code: $NEW_ANDROID_VERSION_CODE" -} - ## Prompts for new version # in: VERSION_{MAJOR,MINOR,PATCH} DO_PATCH_REL # out: NEXT_VERSION NEXT_VERSION_{MAJOR,MINOR,PATCH} @@ -108,14 +93,6 @@ set_dev_build() { git add -f CMakeLists.txt android/build.gradle } -## Writes new android version code -# in: NEW_ANDROID_VERSION_CODE -write_android_version() { - sed -i -re "s/\"versionCode\", [0-9]+/\"versionCode\", $NEW_ANDROID_VERSION_CODE/" android/build.gradle - - git add -f android/build.gradle -} - ## Writes new version to the right files # in: NEXT_VERSION NEXT_VERSION_{MAJOR,MINOR,PATCH} write_new_version() { @@ -198,8 +175,6 @@ if [ "$DO_PATCH_REL" -eq 0 ]; then exit 1 fi - bump_android_ver - write_android_version set_dev_build 0 perform_release "$CURRENT_VERSION" @@ -212,8 +187,6 @@ if [ "$DO_PATCH_REL" -eq 0 ]; then else # On a patch release the version moves from 5.7.0 -> 5.7.1 (new tag) - bump_android_ver - write_android_version bump_version write_new_version From b2f6a65bc9d8b9af394c15db277db4cb6d2e8a55 Mon Sep 17 00:00:00 2001 From: Zemtzov7 <72821250+zmv7@users.noreply.github.com> Date: Wed, 21 Aug 2024 23:25:41 +0500 Subject: [PATCH 18/75] Sort clients in `minetest.get_server_status` and privs in `minetest.privs_to_string` (#15023) --- builtin/common/misc_helpers.lua | 1 + builtin/game/chat.lua | 1 + src/server.cpp | 9 +++------ 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index fb38c1b35..21752ce7f 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -702,6 +702,7 @@ function core.privs_to_string(privs, delim) list[#list + 1] = priv end end + table.sort(list) return table.concat(list, delim) end diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index baab5212f..b7e2aea50 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -221,6 +221,7 @@ core.register_chatcommand("haspriv", { return true, S("No online player has the \"@1\" privilege.", param) else + table.sort(players_with_priv) return true, S("Players online with the \"@1\" privilege: @2", param, table.concat(players_with_priv, ", ")) diff --git a/src/server.cpp b/src/server.cpp index 0b0786209..1d98e4634 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -3181,14 +3181,11 @@ std::string Server::getStatusString() bool first = true; os << " | clients: "; if (m_env) { - std::vector clients = m_clients.getClientIDs(); - for (session_t client_id : clients) { - RemotePlayer *player = m_env->getPlayer(client_id); + std::vector player_names = m_clients.getPlayerNames(); - // Get name of player - const std::string name = player ? player->getName() : ""; + std::sort(player_names.begin(), player_names.end()); - // Add name to information string + for (const std::string& name : player_names) { if (!first) os << ", "; else From 66b3db360115de5d46cc036b2387810bf1460bd5 Mon Sep 17 00:00:00 2001 From: grorp Date: Wed, 21 Aug 2024 20:25:58 +0200 Subject: [PATCH 19/75] Fix mods folder being read twice with RUN_IN_PLACE=1 (#15024) --- doc/menu_lua_api.md | 2 +- src/script/lua_api/l_mainmenu.cpp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index 9f0e23a11..8f945052c 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -282,7 +282,7 @@ Package - content which is downloadable from the content db, may or may not be i ```lua { mods = "/home/user/.minetest/mods", - share = "/usr/share/minetest/mods", + share = "/usr/share/minetest/mods", -- only provided when RUN_IN_PLACE=0 -- Custom dirs can be specified by the MINETEST_MOD_DIR env variable ["/path/to/custom/dir"] = "/path/to/custom/dir", diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 97101955a..78808792b 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -675,10 +675,12 @@ int ModApiMainMenu::l_get_modpaths(lua_State *L) ModApiMainMenu::l_get_modpath(L); lua_setfield(L, -2, "mods"); - std::string modpath = fs::RemoveRelativePathComponents( - porting::path_share + DIR_DELIM + "mods" + DIR_DELIM); - lua_pushstring(L, modpath.c_str()); - lua_setfield(L, -2, "share"); + if (porting::path_share != porting::path_user) { + std::string modpath = fs::RemoveRelativePathComponents( + porting::path_share + DIR_DELIM + "mods" + DIR_DELIM); + lua_pushstring(L, modpath.c_str()); + lua_setfield(L, -2, "share"); + } for (const std::string &component : getEnvModPaths()) { lua_pushstring(L, component.c_str()); From ab7af5d15a8250c24628b283940401ccc3759c30 Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Wed, 21 Aug 2024 20:30:58 +0200 Subject: [PATCH 20/75] Fix trailing whitespace from #14179 --- src/script/lua_api/l_object.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 75e961438..8225aa470 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -414,7 +414,7 @@ private: // set_flags(self, flags) static int l_set_flags(lua_State *L); - + // get_flags(self) static int l_get_flags(lua_State *L); }; From c6ef5ab2595d55192e7952694c6b301f2b64f65c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 21 Aug 2024 21:34:46 +0200 Subject: [PATCH 21/75] Sanitize formspec fields server-side (#14878) --- doc/lua_api.md | 3 ++ src/network/serverpackethandler.cpp | 17 ++++++++--- src/unittest/test_utilities.cpp | 27 +++++++++++++++++ src/util/string.cpp | 47 +++++++++++++++++++++++++++++ src/util/string.h | 10 ++++++ 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 07c7b3c2e..a43f517a0 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -2625,6 +2625,9 @@ background elements are drawn before all other elements. **WARNING**: do _not_ use an element name starting with `key_`; those names are reserved to pass key press events to formspec! +**WARNING**: names and values of elements cannot contain binary data such as ASCII +control characters. For values, escape sequences used by the engine are an exception to this. + **WARNING**: Minetest allows you to add elements to every single formspec instance using `player:set_formspec_prepend()`, which may be the reason backgrounds are appearing when you don't expect them to, or why things are styled differently diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 9de39229b..c0718d43b 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -1351,15 +1351,22 @@ static bool pkt_read_formspec_fields(NetworkPacket *pkt, StringMap &fields) u16 field_count; *pkt >> field_count; - u64 length = 0; + size_t length = 0; for (u16 k = 0; k < field_count; k++) { - std::string fieldname; + std::string fieldname, fieldvalue; *pkt >> fieldname; - fields[fieldname] = pkt->readLongString(); + fieldvalue = pkt->readLongString(); - length += fieldname.size(); - length += fields[fieldname].size(); + fieldname = sanitize_untrusted(fieldname, false); + // We'd love to strip escapes here but some formspec elements reflect data + // from the server (e.g. dropdown), which can contain translations. + fieldvalue = sanitize_untrusted(fieldvalue); + + length += fieldname.size() + fieldvalue.size(); + + fields[std::move(fieldname)] = std::move(fieldvalue); } + // 640K ought to be enough for anyone return length < 640 * 1024; } diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index 996b418e3..a05421c9b 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -61,6 +61,7 @@ public: void testSanitizeDirName(); void testIsBlockInSight(); void testColorizeURL(); + void testSanitizeUntrusted(); }; static TestUtilities g_test_instance; @@ -95,6 +96,7 @@ void TestUtilities::runTests(IGameDef *gamedef) TEST(testSanitizeDirName); TEST(testIsBlockInSight); TEST(testColorizeURL); + TEST(testSanitizeUntrusted); } //////////////////////////////////////////////////////////////////////////////// @@ -743,3 +745,28 @@ void TestUtilities::testColorizeURL() warningstream << "Test skipped." << std::endl; #endif } + +void TestUtilities::testSanitizeUntrusted() +{ + std::string_view t1{u8"Anästhesieausrüstung"}; + UASSERTEQ(auto, sanitize_untrusted(t1), t1); + + std::string_view t2{"stop\x00here", 9}; + UASSERTEQ(auto, sanitize_untrusted(t2), "stop"); + + UASSERTEQ(auto, sanitize_untrusted("\x01\x08\x13\x1dhello\r\n\tworld"), "hello\n\tworld"); + + std::string_view t3{"some \x1b(T@whatever)text\x1b" "E here"}; + UASSERTEQ(auto, sanitize_untrusted(t3), t3); + auto t3_sanitized = sanitize_untrusted(t3, false); + UASSERT(str_starts_with(t3_sanitized, "some ") && str_ends_with(t3_sanitized, " here")); + UASSERT(t3_sanitized.find('\x1b') == std::string::npos); + + UASSERTEQ(auto, sanitize_untrusted("\x1b[31m"), "[31m"); + + // edge cases + for (bool keep : {true, false}) { + UASSERTEQ(auto, sanitize_untrusted("\x1b", keep), ""); + UASSERTEQ(auto, sanitize_untrusted("\x1b(", keep), "("); + } +} diff --git a/src/util/string.cpp b/src/util/string.cpp index 0c896e6ec..73d1d6907 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -950,6 +950,53 @@ std::string sanitizeDirName(std::string_view str, std::string_view optional_pref return wide_to_utf8(safe_name); } +template +void remove_indexed(std::string &s, F pred) +{ + size_t j = 0; + for (size_t i = 0; i < s.length();) { + if (pred(s, i++)) + j++; + if (i != j) + s[j] = s[i]; + } + s.resize(j); +} + +std::string sanitize_untrusted(std::string_view str, bool keep_escapes) +{ + // truncate on NULL + std::string s{str.substr(0, str.find('\0'))}; + + // remove control characters except tab, feed and escape + s.erase(std::remove_if(s.begin(), s.end(), [] (unsigned char c) { + return c < 9 || (c >= 13 && c < 27) || (c >= 28 && c < 32); + }), s.end()); + + if (!keep_escapes) { + s.erase(std::remove(s.begin(), s.end(), '\x1b'), s.end()); + return s; + } + // Note: Minetest escapes generally just look like \x1b# or \x1b(###) + // where # is a single character and ### any number of characters. + // Here we additionally assume that the first character in the sequence + // is [A-Za-z], to enable us to filter foreign types of escapes that might + // be unsafe e.g. ANSI escapes in a terminal. + const auto &check = [] (char c) { + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + }; + remove_indexed(s, [&check] (const std::string &s, size_t i) { + if (s[i] != '\x1b') + return true; + if (i+1 >= s.length()) + return false; + if (s[i+1] == '(') + return i+2 < s.length() && check(s[i+2]); // long-form escape + else + return check(s[i+1]); // short-form escape + }); + return s; +} void safe_print_string(std::ostream &os, std::string_view str) { diff --git a/src/util/string.h b/src/util/string.h index bddbc62ce..ad3d09818 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -761,6 +761,16 @@ inline irr::core::stringw utf8_to_stringw(std::string_view input) */ std::string sanitizeDirName(std::string_view str, std::string_view optional_prefix); +/** + * Sanitize an untrusted string (e.g. from the network). This will get strip + * control characters and (optionally) any MT-style escape sequences too. + * Note that they won't be removed cleanly but rather just broken, unlike with + * unescape_enriched. + * Line breaks and UTF-8 is permitted. + */ +[[nodiscard]] +std::string sanitize_untrusted(std::string_view str, bool keep_escapes = true); + /** * Prints a sanitized version of a string without control characters. * '\t' and '\n' are allowed, as are UTF-8 control characters (e.g. RTL). From 7968ab69281103bb99280e70603e01a33df92c9a Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 19 Aug 2024 21:20:20 +0200 Subject: [PATCH 22/75] Move network protocol implementation behind an interface --- src/client/client.cpp | 7 +- src/client/client.h | 8 +- src/network/CMakeLists.txt | 5 +- src/network/connection.cpp | 1671 +---------------- src/network/connection.h | 362 +--- src/network/mtp/impl.cpp | 1667 ++++++++++++++++ src/network/mtp/impl.h | 325 ++++ .../{connection_internal.h => mtp/internal.h} | 33 +- .../threads.cpp} | 2 +- .../{connectionthreads.h => mtp/threads.h} | 2 +- src/network/networkexceptions.h | 22 - src/network/networkprotocol.h | 3 - src/network/peerhandler.h | 16 +- src/server.cpp | 10 +- src/server.h | 8 +- src/server/clientiface.cpp | 3 +- src/server/clientiface.h | 6 +- src/unittest/test_connection.cpp | 13 +- 18 files changed, 2109 insertions(+), 2054 deletions(-) create mode 100644 src/network/mtp/impl.cpp create mode 100644 src/network/mtp/impl.h rename src/network/{connection_internal.h => mtp/internal.h} (95%) rename src/network/{connectionthreads.cpp => mtp/threads.cpp} (99%) rename src/network/{connectionthreads.h => mtp/threads.h} (99%) diff --git a/src/client/client.cpp b/src/client/client.cpp index af32b6db8..d15c9608a 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -392,8 +392,7 @@ void Client::connect(const Address &address, const std::string &address_name, } m_address_name = address_name; - m_con.reset(new con::Connection(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, - address.isIPv6(), this)); + m_con.reset(con::createMTP(CONNECTION_TIMEOUT, address.isIPv6(), this)); infostream << "Connecting to server at "; address.print(infostream); @@ -866,13 +865,13 @@ bool Client::loadMedia(const std::string &data, const std::string &filename, } // Virtual methods from con::PeerHandler -void Client::peerAdded(con::Peer *peer) +void Client::peerAdded(con::IPeer *peer) { infostream << "Client::peerAdded(): peer->id=" << peer->id << std::endl; } -void Client::deletingPeer(con::Peer *peer, bool timeout) +void Client::deletingPeer(con::IPeer *peer, bool timeout) { infostream << "Client::deletingPeer(): " "Server Peer is getting deleted " diff --git a/src/client/client.h b/src/client/client.h index b2ff9a0da..64ab90eb3 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -71,7 +71,7 @@ class Camera; struct PlayerControl; class NetworkPacket; namespace con { -class Connection; +class IConnection; } using sound_handle_t = int; @@ -452,8 +452,8 @@ private: void loadMods(); // Virtual methods from con::PeerHandler - void peerAdded(con::Peer *peer) override; - void deletingPeer(con::Peer *peer, bool timeout) override; + void peerAdded(con::IPeer *peer) override; + void deletingPeer(con::IPeer *peer, bool timeout) override; void initLocalMapSaving(const Address &address, const std::string &hostname, @@ -493,7 +493,7 @@ private: std::unique_ptr m_mesh_update_manager; ClientEnvironment m_env; std::unique_ptr m_particle_manager; - std::unique_ptr m_con; + std::unique_ptr m_con; std::string m_address_name; ELoginRegister m_allow_login_or_register = ELoginRegister::Any; Camera *m_camera = nullptr; diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt index d2e2f52e9..8f17e58af 100644 --- a/src/network/CMakeLists.txt +++ b/src/network/CMakeLists.txt @@ -1,10 +1,11 @@ set(common_network_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/address.cpp ${CMAKE_CURRENT_SOURCE_DIR}/connection.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/connectionthreads.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mtp/impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/mtp/threads.cpp ${CMAKE_CURRENT_SOURCE_DIR}/networkpacket.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/serverpackethandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serveropcodes.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/serverpackethandler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/socket.cpp PARENT_SCOPE ) diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 00b4fe4b0..3ffd06fc7 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -1,1672 +1,17 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - -#include -#include -#include -#include -#include "connection_internal.h" -#include "serialization.h" -#include "log.h" -#include "porting.h" -#include "network/connectionthreads.h" -#include "network/networkpacket.h" -#include "network/peerhandler.h" -#include "util/serialize.h" -#include "util/numeric.h" -#include "util/string.h" -#include "settings.h" -#include "profiler.h" +#include "network/connection.h" +#include "network/mtp/impl.h" namespace con { -/******************************************************************************/ -/* defines used for debugging and profiling */ -/******************************************************************************/ -#ifdef NDEBUG - #define PROFILE(a) -#else - #define PROFILE(a) a -#endif - -// TODO: Clean this up. -#define LOG(a) a - -#define PING_TIMEOUT 5.0f - -// exponent base -#define RESEND_SCALE_BASE 1.5f - -// since spacing is exponential the numbers here shouldn't be too high -// (it's okay to start out quick) -#define RESEND_TIMEOUT_MIN 0.1f -#define RESEND_TIMEOUT_MAX 2.0f -#define RESEND_TIMEOUT_FACTOR 2 - -u16 BufferedPacket::getSeqnum() const -{ - if (size() < BASE_HEADER_SIZE + 3) - return 0; // should never happen - - return readU16(&data[BASE_HEADER_SIZE + 1]); -} - -BufferedPacketPtr makePacket(const Address &address, const SharedBuffer &data, - u32 protocol_id, session_t sender_peer_id, u8 channel) -{ - u32 packet_size = data.getSize() + BASE_HEADER_SIZE; - - auto p = std::make_shared(packet_size); - p->address = address; - - writeU32(&p->data[0], protocol_id); - writeU16(&p->data[4], sender_peer_id); - writeU8(&p->data[6], channel); - - memcpy(&p->data[BASE_HEADER_SIZE], *data, data.getSize()); - - return p; -} - -SharedBuffer makeOriginalPacket(const SharedBuffer &data) -{ - u32 header_size = 1; - u32 packet_size = data.getSize() + header_size; - SharedBuffer b(packet_size); - - writeU8(&(b[0]), PACKET_TYPE_ORIGINAL); - if (data.getSize() > 0) { - memcpy(&(b[header_size]), *data, data.getSize()); - } - return b; -} - -// Split data in chunks and add TYPE_SPLIT headers to them -void makeSplitPacket(const SharedBuffer &data, u32 chunksize_max, u16 seqnum, - std::list> *chunks) -{ - // Chunk packets, containing the TYPE_SPLIT header - const u32 chunk_header_size = 7; - const u32 maximum_data_size = chunksize_max - chunk_header_size; - u32 start = 0, end = 0; - u16 chunk_num = 0; - do { - end = start + maximum_data_size - 1; - if (end > data.getSize() - 1) - end = data.getSize() - 1; - - u32 payload_size = end - start + 1; - u32 packet_size = chunk_header_size + payload_size; - - SharedBuffer chunk(packet_size); - - writeU8(&chunk[0], PACKET_TYPE_SPLIT); - writeU16(&chunk[1], seqnum); - // [3] u16 chunk_count is written at next stage - writeU16(&chunk[5], chunk_num); - memcpy(&chunk[chunk_header_size], &data[start], payload_size); - - chunks->push_back(chunk); - - start = end + 1; - sanity_check(chunk_num < 0xFFFF); // overflow - chunk_num++; - } - while (end != data.getSize() - 1); - - for (auto &chunk : *chunks) { - // Write chunk_count - writeU16(&chunk[3], chunk_num); - } -} - -void makeAutoSplitPacket(const SharedBuffer &data, u32 chunksize_max, - u16 &split_seqnum, std::list> *list) -{ - u32 original_header_size = 1; - - if (data.getSize() + original_header_size > chunksize_max) { - makeSplitPacket(data, chunksize_max, split_seqnum, list); - split_seqnum++; - return; - } - - list->push_back(makeOriginalPacket(data)); -} - -SharedBuffer makeReliablePacket(const SharedBuffer &data, u16 seqnum) -{ - u32 header_size = 3; - u32 packet_size = data.getSize() + header_size; - SharedBuffer b(packet_size); - - writeU8(&b[0], PACKET_TYPE_RELIABLE); - writeU16(&b[1], seqnum); - - memcpy(&b[header_size], *data, data.getSize()); - - return b; -} - -/* - ReliablePacketBuffer -*/ - -void ReliablePacketBuffer::print() -{ - MutexAutoLock listlock(m_list_mutex); - LOG(dout_con<<"Dump of ReliablePacketBuffer:" << std::endl); - unsigned int index = 0; - for (BufferedPacketPtr &packet : m_list) { - LOG(dout_con<getSeqnum() << std::endl); - index++; - } -} - -bool ReliablePacketBuffer::empty() -{ - MutexAutoLock listlock(m_list_mutex); - return m_list.empty(); -} - -u32 ReliablePacketBuffer::size() -{ - MutexAutoLock listlock(m_list_mutex); - return m_list.size(); -} - -ReliablePacketBuffer::FindResult ReliablePacketBuffer::findPacketNoLock(u16 seqnum) -{ - for (auto it = m_list.begin(); it != m_list.end(); ++it) { - if ((*it)->getSeqnum() == seqnum) - return it; - } - return m_list.end(); -} - -bool ReliablePacketBuffer::getFirstSeqnum(u16& result) -{ - MutexAutoLock listlock(m_list_mutex); - if (m_list.empty()) - return false; - result = m_list.front()->getSeqnum(); - return true; -} - -BufferedPacketPtr ReliablePacketBuffer::popFirst() -{ - MutexAutoLock listlock(m_list_mutex); - if (m_list.empty()) - throw NotFoundException("Buffer is empty"); - - BufferedPacketPtr p(m_list.front()); - m_list.pop_front(); - - if (m_list.empty()) { - m_oldest_non_answered_ack = 0; - } else { - m_oldest_non_answered_ack = m_list.front()->getSeqnum(); - } - return p; -} - -BufferedPacketPtr ReliablePacketBuffer::popSeqnum(u16 seqnum) +IConnection *createMTP(float timeout, bool ipv6, PeerHandler *handler) { - MutexAutoLock listlock(m_list_mutex); - auto r = findPacketNoLock(seqnum); - if (r == m_list.end()) { - LOG(dout_con<<"Sequence number: " << seqnum - << " not found in reliable buffer"<getSeqnum(); - } - return p; + // safe minimum across internet networks for ipv4 and ipv6 + constexpr u32 MAX_PACKET_SIZE = 512; + return new con::Connection(MAX_PACKET_SIZE, timeout, ipv6, handler); } -void ReliablePacketBuffer::insert(BufferedPacketPtr &p_ptr, u16 next_expected) -{ - MutexAutoLock listlock(m_list_mutex); - const BufferedPacket &p = *p_ptr; - - if (p.size() < BASE_HEADER_SIZE + 3) { - errorstream << "ReliablePacketBuffer::insert(): Invalid data size for " - "reliable packet" << std::endl; - return; - } - u8 type = readU8(&p.data[BASE_HEADER_SIZE + 0]); - if (type != PACKET_TYPE_RELIABLE) { - errorstream << "ReliablePacketBuffer::insert(): type is not reliable" - << std::endl; - return; - } - const u16 seqnum = p.getSeqnum(); - - if (!seqnum_in_window(seqnum, next_expected, MAX_RELIABLE_WINDOW_SIZE)) { - errorstream << "ReliablePacketBuffer::insert(): seqnum is outside of " - "expected window " << std::endl; - return; - } - if (seqnum == next_expected) { - errorstream << "ReliablePacketBuffer::insert(): seqnum is next expected" - << std::endl; - return; - } - - sanity_check(m_list.size() <= SEQNUM_MAX); // FIXME: Handle the error? - - // Find the right place for the packet and insert it there - // If list is empty, just add it - if (m_list.empty()) { - m_list.push_back(p_ptr); - m_oldest_non_answered_ack = seqnum; - // Done. - return; - } - - // Otherwise find the right place - auto it = m_list.begin(); - // Find the first packet in the list which has a higher seqnum - u16 s = (*it)->getSeqnum(); - - /* case seqnum is smaller then next_expected seqnum */ - /* this is true e.g. on wrap around */ - if (seqnum < next_expected) { - while(((s < seqnum) || (s >= next_expected)) && (it != m_list.end())) { - ++it; - if (it != m_list.end()) - s = (*it)->getSeqnum(); - } - } - /* non wrap around case (at least for incoming and next_expected */ - else - { - while(((s < seqnum) && (s >= next_expected)) && (it != m_list.end())) { - ++it; - if (it != m_list.end()) - s = (*it)->getSeqnum(); - } - } - - if (s == seqnum) { - /* nothing to do this seems to be a resent packet */ - /* for paranoia reason data should be compared */ - auto &i = *it; - if ( - (i->getSeqnum() != seqnum) || - (i->size() != p.size()) || - (i->address != p.address) - ) - { - /* if this happens your maximum transfer window may be to big */ - char buf[200]; - snprintf(buf, sizeof(buf), - "Duplicated seqnum %d non matching packet detected:\n", - seqnum); - warningstream << buf; - snprintf(buf, sizeof(buf), - "Old: seqnum: %05d size: %04zu, address: %s\n", - i->getSeqnum(), i->size(), - i->address.serializeString().c_str()); - warningstream << buf; - snprintf(buf, sizeof(buf), - "New: seqnum: %05d size: %04zu, address: %s\n", - p.getSeqnum(), p.size(), - p.address.serializeString().c_str()); - warningstream << buf << std::flush; - throw IncomingDataCorruption("duplicated packet isn't same as original one"); - } - } - /* insert or push back */ - else if (it != m_list.end()) { - m_list.insert(it, p_ptr); - } else { - m_list.push_back(p_ptr); - } - - /* update last packet number */ - m_oldest_non_answered_ack = m_list.front()->getSeqnum(); } - -void ReliablePacketBuffer::incrementTimeouts(float dtime) -{ - MutexAutoLock listlock(m_list_mutex); - for (auto &packet : m_list) { - packet->time += dtime; - packet->totaltime += dtime; - } -} - -u32 ReliablePacketBuffer::getTimedOuts(float timeout) -{ - MutexAutoLock listlock(m_list_mutex); - u32 count = 0; - for (auto &packet : m_list) { - if (packet->totaltime >= timeout) - count++; - } - return count; -} - -std::vector> - ReliablePacketBuffer::getResend(float timeout, u32 max_packets) -{ - MutexAutoLock listlock(m_list_mutex); - std::vector> timed_outs; - for (auto &packet : m_list) { - // resend time scales exponentially with each cycle - const float pkt_timeout = timeout * powf(RESEND_SCALE_BASE, packet->resend_count); - - if (packet->time < pkt_timeout) - continue; - - // caller will resend packet so reset time and increase counter - packet->time = 0.0f; - packet->resend_count++; - - timed_outs.emplace_back(packet); - - if (timed_outs.size() >= max_packets) - break; - } - return timed_outs; -} - -/* - IncomingSplitPacket -*/ - -bool IncomingSplitPacket::insert(u32 chunk_num, SharedBuffer &chunkdata) -{ - sanity_check(chunk_num < chunk_count); - - // If chunk already exists, ignore it. - // Sometimes two identical packets may arrive when there is network - // lag and the server re-sends stuff. - if (chunks.find(chunk_num) != chunks.end()) - return false; - - // Set chunk data in buffer - chunks[chunk_num] = chunkdata; - - return true; -} - -SharedBuffer IncomingSplitPacket::reassemble() -{ - sanity_check(allReceived()); - - // Calculate total size - u32 totalsize = 0; - for (const auto &chunk : chunks) - totalsize += chunk.second.getSize(); - - SharedBuffer fulldata(totalsize); - - // Copy chunks to data buffer - u32 start = 0; - for (u32 chunk_i = 0; chunk_i < chunk_count; chunk_i++) { - const SharedBuffer &buf = chunks[chunk_i]; - memcpy(&fulldata[start], *buf, buf.getSize()); - start += buf.getSize(); - } - - return fulldata; -} - -/* - IncomingSplitBuffer -*/ - -IncomingSplitBuffer::~IncomingSplitBuffer() -{ - MutexAutoLock listlock(m_map_mutex); - for (auto &i : m_buf) { - delete i.second; - } -} - -SharedBuffer IncomingSplitBuffer::insert(BufferedPacketPtr &p_ptr, bool reliable) -{ - MutexAutoLock listlock(m_map_mutex); - const BufferedPacket &p = *p_ptr; - - u32 headersize = BASE_HEADER_SIZE + 7; - if (p.size() < headersize) { - errorstream << "Invalid data size for split packet" << std::endl; - return SharedBuffer(); - } - u8 type = readU8(&p.data[BASE_HEADER_SIZE+0]); - u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]); - u16 chunk_count = readU16(&p.data[BASE_HEADER_SIZE+3]); - u16 chunk_num = readU16(&p.data[BASE_HEADER_SIZE+5]); - - if (type != PACKET_TYPE_SPLIT) { - errorstream << "IncomingSplitBuffer::insert(): type is not split" - << std::endl; - return SharedBuffer(); - } - if (chunk_num >= chunk_count) { - errorstream << "IncomingSplitBuffer::insert(): chunk_num=" << chunk_num - << " >= chunk_count=" << chunk_count << std::endl; - return SharedBuffer(); - } - - // Add if doesn't exist - IncomingSplitPacket *sp; - if (m_buf.find(seqnum) == m_buf.end()) { - sp = new IncomingSplitPacket(chunk_count, reliable); - m_buf[seqnum] = sp; - } else { - sp = m_buf[seqnum]; - } - - if (chunk_count != sp->chunk_count) { - errorstream << "IncomingSplitBuffer::insert(): chunk_count=" - << chunk_count << " != sp->chunk_count=" << sp->chunk_count - << std::endl; - return SharedBuffer(); - } - if (reliable != sp->reliable) - LOG(derr_con<<"Connection: WARNING: reliable="<reliable="<reliable - < chunkdata(chunkdatasize); - memcpy(*chunkdata, &(p.data[headersize]), chunkdatasize); - - if (!sp->insert(chunk_num, chunkdata)) - return SharedBuffer(); - - // If not all chunks are received, return empty buffer - if (!sp->allReceived()) - return SharedBuffer(); - - SharedBuffer fulldata = sp->reassemble(); - - // Remove sp from buffer - m_buf.erase(seqnum); - delete sp; - - return fulldata; -} - -void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout) -{ - MutexAutoLock listlock(m_map_mutex); - std::vector remove_queue; - { - for (const auto &i : m_buf) { - IncomingSplitPacket *p = i.second; - // Reliable ones are not removed by timeout - if (p->reliable) - continue; - p->time += dtime; - if (p->time >= timeout) - remove_queue.push_back(i.first); - } - } - for (u16 j : remove_queue) { - LOG(dout_con<<"NOTE: Removing timed out unreliable split packet"<second; - m_buf.erase(it); - } -} - -/* - ConnectionCommand - */ - -ConnectionCommandPtr ConnectionCommand::create(ConnectionCommandType type) -{ - return ConnectionCommandPtr(new ConnectionCommand(type)); -} - -ConnectionCommandPtr ConnectionCommand::serve(Address address) -{ - auto c = create(CONNCMD_SERVE); - c->address = address; - return c; -} - -ConnectionCommandPtr ConnectionCommand::connect(Address address) -{ - auto c = create(CONNCMD_CONNECT); - c->address = address; - return c; -} - -ConnectionCommandPtr ConnectionCommand::disconnect() -{ - return create(CONNCMD_DISCONNECT); -} - -ConnectionCommandPtr ConnectionCommand::disconnect_peer(session_t peer_id) -{ - auto c = create(CONNCMD_DISCONNECT_PEER); - c->peer_id = peer_id; - return c; -} - -ConnectionCommandPtr ConnectionCommand::resend_one(session_t peer_id) -{ - auto c = create(CONNCMD_RESEND_ONE); - c->peer_id = peer_id; - c->channelnum = 0; // must be same as createPeer - c->reliable = true; - return c; -} - -ConnectionCommandPtr ConnectionCommand::send(session_t peer_id, u8 channelnum, - NetworkPacket *pkt, bool reliable) -{ - auto c = create(CONNCMD_SEND); - c->peer_id = peer_id; - c->channelnum = channelnum; - c->reliable = reliable; - c->data = pkt->oldForgePacket(); - return c; -} - -ConnectionCommandPtr ConnectionCommand::ack(session_t peer_id, u8 channelnum, const Buffer &data) -{ - auto c = create(CONCMD_ACK); - c->peer_id = peer_id; - c->channelnum = channelnum; - c->reliable = false; - data.copyTo(c->data); - return c; -} - -ConnectionCommandPtr ConnectionCommand::createPeer(session_t peer_id, const Buffer &data) -{ - auto c = create(CONCMD_CREATE_PEER); - c->peer_id = peer_id; - c->channelnum = 0; - c->reliable = true; - c->raw = true; - data.copyTo(c->data); - return c; -} - -/* - Channel -*/ - -u16 Channel::readNextIncomingSeqNum() -{ - MutexAutoLock internal(m_internal_mutex); - return next_incoming_seqnum; -} - -u16 Channel::incNextIncomingSeqNum() -{ - MutexAutoLock internal(m_internal_mutex); - u16 retval = next_incoming_seqnum; - next_incoming_seqnum++; - return retval; -} - -u16 Channel::readNextSplitSeqNum() -{ - MutexAutoLock internal(m_internal_mutex); - return next_outgoing_split_seqnum; -} -void Channel::setNextSplitSeqNum(u16 seqnum) -{ - MutexAutoLock internal(m_internal_mutex); - next_outgoing_split_seqnum = seqnum; -} - -u16 Channel::getOutgoingSequenceNumber(bool& successful) -{ - MutexAutoLock internal(m_internal_mutex); - - u16 retval = next_outgoing_seqnum; - successful = false; - - /* shortcut if there ain't any packet in outgoing list */ - if (outgoing_reliables_sent.empty()) { - successful = true; - next_outgoing_seqnum++; - return retval; - } - - u16 lowest_unacked_seqnumber; - if (outgoing_reliables_sent.getFirstSeqnum(lowest_unacked_seqnumber)) { - if (lowest_unacked_seqnumber < next_outgoing_seqnum) { - // ugly cast but this one is required in order to tell compiler we - // know about difference of two unsigned may be negative in general - // but we already made sure it won't happen in this case - if (((u16)(next_outgoing_seqnum - lowest_unacked_seqnumber)) > m_window_size) { - return 0; - } - } else { - // ugly cast but this one is required in order to tell compiler we - // know about difference of two unsigned may be negative in general - // but we already made sure it won't happen in this case - if ((next_outgoing_seqnum + (u16)(SEQNUM_MAX - lowest_unacked_seqnumber)) > - m_window_size) { - return 0; - } - } - } - - successful = true; - next_outgoing_seqnum++; - return retval; -} - -u16 Channel::readOutgoingSequenceNumber() -{ - MutexAutoLock internal(m_internal_mutex); - return next_outgoing_seqnum; -} - -bool Channel::putBackSequenceNumber(u16 seqnum) -{ - if (((seqnum + 1) % (SEQNUM_MAX+1)) == next_outgoing_seqnum) { - - next_outgoing_seqnum = seqnum; - return true; - } - return false; -} - -void Channel::UpdateBytesSent(unsigned int bytes, unsigned int packets) -{ - MutexAutoLock internal(m_internal_mutex); - current_bytes_transfered += bytes; - current_packet_successful += packets; -} - -void Channel::UpdateBytesReceived(unsigned int bytes) { - MutexAutoLock internal(m_internal_mutex); - current_bytes_received += bytes; -} - -void Channel::UpdateBytesLost(unsigned int bytes) -{ - MutexAutoLock internal(m_internal_mutex); - current_bytes_lost += bytes; -} - - -void Channel::UpdatePacketLossCounter(unsigned int count) -{ - MutexAutoLock internal(m_internal_mutex); - current_packet_loss += count; -} - -void Channel::UpdatePacketTooLateCounter() -{ - MutexAutoLock internal(m_internal_mutex); - current_packet_too_late++; -} - -void Channel::UpdateTimers(float dtime) -{ - bpm_counter += dtime; - packet_loss_counter += dtime; - - if (packet_loss_counter > 1.0f) { - packet_loss_counter -= 1.0f; - - unsigned int packet_loss = 11; /* use a neutral value for initialization */ - unsigned int packets_successful = 0; - //unsigned int packet_too_late = 0; - - bool reasonable_amount_of_data_transmitted = false; - - { - MutexAutoLock internal(m_internal_mutex); - packet_loss = current_packet_loss; - //packet_too_late = current_packet_too_late; - packets_successful = current_packet_successful; - - if (current_bytes_transfered > (unsigned int) (m_window_size*512/2)) { - reasonable_amount_of_data_transmitted = true; - } - current_packet_loss = 0; - current_packet_too_late = 0; - current_packet_successful = 0; - } - - /* dynamic window size */ - float successful_to_lost_ratio = 0.0f; - bool done = false; - - if (packets_successful > 0) { - successful_to_lost_ratio = packet_loss/packets_successful; - } else if (packet_loss > 0) { - setWindowSize(m_window_size - 10); - done = true; - } - - if (!done) { - if (successful_to_lost_ratio < 0.01f) { - /* don't even think about increasing if we didn't even - * use major parts of our window */ - if (reasonable_amount_of_data_transmitted) - setWindowSize(m_window_size + 100); - } else if (successful_to_lost_ratio < 0.05f) { - /* don't even think about increasing if we didn't even - * use major parts of our window */ - if (reasonable_amount_of_data_transmitted) - setWindowSize(m_window_size + 50); - } else if (successful_to_lost_ratio > 0.15f) { - setWindowSize(m_window_size - 100); - } else if (successful_to_lost_ratio > 0.1f) { - setWindowSize(m_window_size - 50); - } - } - } - - if (bpm_counter > 10.0f) { - { - MutexAutoLock internal(m_internal_mutex); - cur_kbps = - (((float) current_bytes_transfered)/bpm_counter)/1024.0f; - current_bytes_transfered = 0; - cur_kbps_lost = - (((float) current_bytes_lost)/bpm_counter)/1024.0f; - current_bytes_lost = 0; - cur_incoming_kbps = - (((float) current_bytes_received)/bpm_counter)/1024.0f; - current_bytes_received = 0; - bpm_counter = 0.0f; - } - - if (cur_kbps > max_kbps) { - max_kbps = cur_kbps; - } - - if (cur_kbps_lost > max_kbps_lost) { - max_kbps_lost = cur_kbps_lost; - } - - if (cur_incoming_kbps > max_incoming_kbps) { - max_incoming_kbps = cur_incoming_kbps; - } - - rate_samples = MYMIN(rate_samples+1,10); - float old_fraction = ((float) (rate_samples-1) )/( (float) rate_samples); - avg_kbps = avg_kbps * old_fraction + - cur_kbps * (1.0 - old_fraction); - avg_kbps_lost = avg_kbps_lost * old_fraction + - cur_kbps_lost * (1.0 - old_fraction); - avg_incoming_kbps = avg_incoming_kbps * old_fraction + - cur_incoming_kbps * (1.0 - old_fraction); - } -} - - -/* - Peer -*/ - -PeerHelper::~PeerHelper() -{ - if (m_peer) - m_peer->DecUseCount(); - - m_peer = nullptr; -} - -PeerHelper& PeerHelper::operator=(Peer* peer) -{ - if (m_peer) - m_peer->DecUseCount(); - m_peer = peer; - if (peer && !peer->IncUseCount()) - m_peer = nullptr; - return *this; -} - -bool Peer::IncUseCount() -{ - MutexAutoLock lock(m_exclusive_access_mutex); - - if (!m_pending_deletion) { - this->m_usage++; - return true; - } - - return false; -} - -void Peer::DecUseCount() -{ - { - MutexAutoLock lock(m_exclusive_access_mutex); - sanity_check(m_usage > 0); - m_usage--; - - if (!((m_pending_deletion) && (m_usage == 0))) - return; - } - delete this; -} - -void Peer::RTTStatistics(float rtt, const std::string &profiler_id, - unsigned int num_samples) { - - if (m_last_rtt > 0) { - /* set min max values */ - if (rtt < m_rtt.min_rtt) - m_rtt.min_rtt = rtt; - if (rtt >= m_rtt.max_rtt) - m_rtt.max_rtt = rtt; - - /* do average calculation */ - if (m_rtt.avg_rtt < 0.0) - m_rtt.avg_rtt = rtt; - else - m_rtt.avg_rtt = m_rtt.avg_rtt * (num_samples/(num_samples-1)) + - rtt * (1/num_samples); - - /* do jitter calculation */ - - //just use some neutral value at beginning - float jitter = m_rtt.jitter_min; - - if (rtt > m_last_rtt) - jitter = rtt-m_last_rtt; - - if (rtt <= m_last_rtt) - jitter = m_last_rtt - rtt; - - if (jitter < m_rtt.jitter_min) - m_rtt.jitter_min = jitter; - if (jitter >= m_rtt.jitter_max) - m_rtt.jitter_max = jitter; - - if (m_rtt.jitter_avg < 0.0) - m_rtt.jitter_avg = jitter; - else - m_rtt.jitter_avg = m_rtt.jitter_avg * (num_samples/(num_samples-1)) + - jitter * (1/num_samples); - - if (!profiler_id.empty()) { - g_profiler->graphAdd(profiler_id + " RTT [ms]", rtt * 1000.f); - g_profiler->graphAdd(profiler_id + " jitter [ms]", jitter * 1000.f); - } - } - /* save values required for next loop */ - m_last_rtt = rtt; -} - -bool Peer::isTimedOut(float timeout, std::string &reason) -{ - MutexAutoLock lock(m_exclusive_access_mutex); - - { - u64 current_time = porting::getTimeMs(); - float dtime = CALC_DTIME(m_last_timeout_check, current_time); - m_last_timeout_check = current_time; - m_timeout_counter += dtime; - } - if (m_timeout_counter > timeout) { - reason = "timeout counter"; - return true; - } - - return false; -} - -void Peer::Drop() -{ - { - MutexAutoLock usage_lock(m_exclusive_access_mutex); - m_pending_deletion = true; - if (m_usage != 0) - return; - } - - PROFILE(std::stringstream peerIdentifier1); - PROFILE(peerIdentifier1 << "runTimeouts[" << m_connection->getDesc() - << ";" << id << ";RELIABLE]"); - PROFILE(g_profiler->remove(peerIdentifier1.str())); - PROFILE(std::stringstream peerIdentifier2); - PROFILE(peerIdentifier2 << "sendPackets[" << m_connection->getDesc() - << ";" << id << ";RELIABLE]"); - PROFILE(ScopeProfiler peerprofiler(g_profiler, peerIdentifier2.str(), SPT_AVG)); - - delete this; -} - -UDPPeer::UDPPeer(session_t id, const Address &address, Connection *connection) : - Peer(id, address, connection) -{ - for (Channel &channel : channels) - channel.setWindowSize(START_RELIABLE_WINDOW_SIZE); -} - -bool UDPPeer::isTimedOut(float timeout, std::string &reason) -{ - if (Peer::isTimedOut(timeout, reason)) - return true; - - MutexAutoLock lock(m_exclusive_access_mutex); - - for (int i = 0; i < CHANNEL_COUNT; i++) { - Channel &channel = channels[i]; - if (channel.outgoing_reliables_sent.getTimedOuts(timeout) > 0) { - reason = "outgoing reliables channel=" + itos(i); - return true; - } - } - - return false; -} - -void UDPPeer::reportRTT(float rtt) -{ - if (rtt < 0.0) { - return; - } - RTTStatistics(rtt,"rudp",MAX_RELIABLE_WINDOW_SIZE*10); - - // use this value to decide the resend timeout - float timeout = getStat(AVG_RTT) * RESEND_TIMEOUT_FACTOR; - if (timeout < RESEND_TIMEOUT_MIN) - timeout = RESEND_TIMEOUT_MIN; - if (timeout > RESEND_TIMEOUT_MAX) - timeout = RESEND_TIMEOUT_MAX; - - setResendTimeout(timeout); -} - -bool UDPPeer::Ping(float dtime,SharedBuffer& data) -{ - m_ping_timer += dtime; - if (!isHalfOpen() && m_ping_timer >= PING_TIMEOUT) - { - // Create and send PING packet - writeU8(&data[0], PACKET_TYPE_CONTROL); - writeU8(&data[1], CONTROLTYPE_PING); - m_ping_timer = 0.0f; - return true; - } - return false; -} - -void UDPPeer::PutReliableSendCommand(ConnectionCommandPtr &c, - unsigned int max_packet_size) -{ - if (m_pending_disconnect) - return; - - Channel &chan = channels[c->channelnum]; - - if (chan.queued_commands.empty() && - /* don't queue more packets then window size */ - (chan.queued_reliables.size() + 1 < chan.getWindowSize() / 2)) { - LOG(dout_con<getDesc() - <<" processing reliable command for peer id: " << c->peer_id - <<" data size: " << c->data.getSize() << std::endl); - if (processReliableSendCommand(c, max_packet_size)) - return; - } else { - LOG(dout_con<getDesc() - <<" Queueing reliable command for peer id: " << c->peer_id - <<" data size: " << c->data.getSize() <= chan.getWindowSize() / 2) { - LOG(derr_con << m_connection->getDesc() - << "Possible packet stall to peer id: " << c->peer_id - << " queued_commands=" << chan.queued_commands.size() - << std::endl); - } - } - chan.queued_commands.push_back(c); -} - -bool UDPPeer::processReliableSendCommand( - ConnectionCommandPtr &c_ptr, - unsigned int max_packet_size) -{ - if (m_pending_disconnect) - return true; - - const auto &c = *c_ptr; - Channel &chan = channels[c.channelnum]; - - const u32 chunksize_max = max_packet_size - - BASE_HEADER_SIZE - - RELIABLE_HEADER_SIZE; - - std::list> originals; - - if (c.raw) { - originals.emplace_back(c.data); - } else { - u16 split_seqnum = chan.readNextSplitSeqNum(); - makeAutoSplitPacket(c.data, chunksize_max, split_seqnum, &originals); - chan.setNextSplitSeqNum(split_seqnum); - } - - sanity_check(originals.size() < MAX_RELIABLE_WINDOW_SIZE); - - bool have_sequence_number = false; - bool have_initial_sequence_number = false; - std::queue toadd; - u16 initial_sequence_number = 0; - - for (SharedBuffer &original : originals) { - u16 seqnum = chan.getOutgoingSequenceNumber(have_sequence_number); - - /* oops, we don't have enough sequence numbers to send this packet */ - if (!have_sequence_number) - break; - - if (!have_initial_sequence_number) - { - initial_sequence_number = seqnum; - have_initial_sequence_number = true; - } - - SharedBuffer reliable = makeReliablePacket(original, seqnum); - - // Add base headers and make a packet - BufferedPacketPtr p = con::makePacket(address, reliable, - m_connection->GetProtocolID(), m_connection->GetPeerID(), - c.channelnum); - - toadd.push(p); - } - - if (have_sequence_number) { - while (!toadd.empty()) { - BufferedPacketPtr p = toadd.front(); - toadd.pop(); -// LOG(dout_con<getDesc() -// << " queuing reliable packet for peer_id: " << c.peer_id -// << " channel: " << (c.channelnum&0xFF) -// << " seqnum: " << readU16(&p.data[BASE_HEADER_SIZE+1]) -// << std::endl) - chan.queued_reliables.push(p); - } - sanity_check(chan.queued_reliables.size() < 0xFFFF); - return true; - } - - u16 packets_available = toadd.size(); - /* we didn't get a single sequence number no need to fill queue */ - if (!have_initial_sequence_number) { - LOG(derr_con << m_connection->getDesc() << "Ran out of sequence numbers!" << std::endl); - return false; - } - - while (!toadd.empty()) { - /* remove packet */ - toadd.pop(); - - bool successfully_put_back_sequence_number - = chan.putBackSequenceNumber( - (initial_sequence_number+toadd.size() % (SEQNUM_MAX+1))); - - FATAL_ERROR_IF(!successfully_put_back_sequence_number, "error"); - } - - u32 n_queued = chan.outgoing_reliables_sent.size(); - - LOG(dout_con<getDesc() - << " Windowsize exceeded on reliable sending " - << c.data.getSize() << " bytes" - << std::endl << "\t\tinitial_sequence_number: " - << initial_sequence_number - << std::endl << "\t\tgot at most : " - << packets_available << " packets" - << std::endl << "\t\tpackets queued : " - << n_queued - << std::endl); - - return false; -} - -void UDPPeer::RunCommandQueues( - unsigned int max_packet_size, - unsigned int maxtransfer) -{ - - for (Channel &channel : channels) { - - if ((!channel.queued_commands.empty()) && - (channel.queued_reliables.size() < maxtransfer)) { - try { - ConnectionCommandPtr c = channel.queued_commands.front(); - - LOG(dout_con << m_connection->getDesc() - << " processing queued reliable command " << std::endl); - - // Packet is processed, remove it from queue - if (processReliableSendCommand(c, max_packet_size)) { - channel.queued_commands.pop_front(); - } else { - LOG(dout_con << m_connection->getDesc() - << " Failed to queue packets for peer_id: " << c->peer_id - << ", delaying sending of " << c->data.getSize() - << " bytes" << std::endl); - } - } - catch (ItemNotFoundException &e) { - // intentionally empty - } - } - } -} - -u16 UDPPeer::getNextSplitSequenceNumber(u8 channel) -{ - assert(channel < CHANNEL_COUNT); // Pre-condition - return channels[channel].readNextSplitSeqNum(); -} - -void UDPPeer::setNextSplitSequenceNumber(u8 channel, u16 seqnum) -{ - assert(channel < CHANNEL_COUNT); // Pre-condition - channels[channel].setNextSplitSeqNum(seqnum); -} - -SharedBuffer UDPPeer::addSplitPacket(u8 channel, BufferedPacketPtr &toadd, - bool reliable) -{ - assert(channel < CHANNEL_COUNT); // Pre-condition - return channels[channel].incoming_splits.insert(toadd, reliable); -} - -/* - ConnectionEvent -*/ - -const char *ConnectionEvent::describe() const -{ - switch(type) { - case CONNEVENT_NONE: - return "CONNEVENT_NONE"; - case CONNEVENT_DATA_RECEIVED: - return "CONNEVENT_DATA_RECEIVED"; - case CONNEVENT_PEER_ADDED: - return "CONNEVENT_PEER_ADDED"; - case CONNEVENT_PEER_REMOVED: - return "CONNEVENT_PEER_REMOVED"; - case CONNEVENT_BIND_FAILED: - return "CONNEVENT_BIND_FAILED"; - } - return "Invalid ConnectionEvent"; -} - - -ConnectionEventPtr ConnectionEvent::create(ConnectionEventType type) -{ - return std::shared_ptr(new ConnectionEvent(type)); -} - -ConnectionEventPtr ConnectionEvent::dataReceived(session_t peer_id, const Buffer &data) -{ - auto e = create(CONNEVENT_DATA_RECEIVED); - e->peer_id = peer_id; - data.copyTo(e->data); - return e; -} - -ConnectionEventPtr ConnectionEvent::peerAdded(session_t peer_id, Address address) -{ - auto e = create(CONNEVENT_PEER_ADDED); - e->peer_id = peer_id; - e->address = address; - return e; -} - -ConnectionEventPtr ConnectionEvent::peerRemoved(session_t peer_id, bool is_timeout, Address address) -{ - auto e = create(CONNEVENT_PEER_REMOVED); - e->peer_id = peer_id; - e->timeout = is_timeout; - e->address = address; - return e; -} - -ConnectionEventPtr ConnectionEvent::bindFailed() -{ - return create(CONNEVENT_BIND_FAILED); -} - -/* - Connection -*/ - -Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout, - bool ipv6, PeerHandler *peerhandler) : - m_udpSocket(ipv6), - m_protocol_id(protocol_id), - m_sendThread(new ConnectionSendThread(max_packet_size, timeout)), - m_receiveThread(new ConnectionReceiveThread()), - m_bc_peerhandler(peerhandler) - -{ - /* Amount of time Receive() will wait for data, this is entirely different - * from the connection timeout */ - m_udpSocket.setTimeoutMs(500); - - m_sendThread->setParent(this); - m_receiveThread->setParent(this); - - m_sendThread->start(); - m_receiveThread->start(); -} - - -Connection::~Connection() -{ - m_shutting_down = true; - // request threads to stop - m_sendThread->stop(); - m_receiveThread->stop(); - - //TODO for some unkonwn reason send/receive threads do not exit as they're - // supposed to be but wait on peer timeout. To speed up shutdown we reduce - // timeout to half a second. - m_sendThread->setPeerTimeout(0.5); - - // wait for threads to finish - m_sendThread->wait(); - m_receiveThread->wait(); - - // Delete peers - for (auto &peer : m_peers) { - delete peer.second; - } -} - -/* Internal stuff */ - -void Connection::putEvent(ConnectionEventPtr e) -{ - assert(e->type != CONNEVENT_NONE); // Pre-condition - m_event_queue.push_back(e); -} - -void Connection::TriggerSend() -{ - m_sendThread->Trigger(); -} - -PeerHelper Connection::getPeerNoEx(session_t peer_id) -{ - MutexAutoLock peerlock(m_peers_mutex); - std::map::iterator node = m_peers.find(peer_id); - - if (node == m_peers.end()) { - return PeerHelper(NULL); - } - - // Error checking - FATAL_ERROR_IF(node->second->id != peer_id, "Invalid peer id"); - - return PeerHelper(node->second); -} - -/* find peer_id for address */ -session_t Connection::lookupPeer(const Address& sender) -{ - MutexAutoLock peerlock(m_peers_mutex); - for (auto &it: m_peers) { - Peer *peer = it.second; - if (peer->isPendingDeletion()) - continue; - - if (peer->getAddress() == sender) - return peer->id; - } - - return PEER_ID_INEXISTENT; -} - -u32 Connection::getActiveCount() -{ - MutexAutoLock peerlock(m_peers_mutex); - u32 count = 0; - for (auto &it : m_peers) { - Peer *peer = it.second; - if (peer->isPendingDeletion()) - continue; - if (peer->isHalfOpen()) - continue; - count++; - } - return count; -} - -bool Connection::deletePeer(session_t peer_id, bool timeout) -{ - Peer *peer = 0; - - /* lock list as short as possible */ - { - MutexAutoLock peerlock(m_peers_mutex); - if (m_peers.find(peer_id) == m_peers.end()) - return false; - peer = m_peers[peer_id]; - m_peers.erase(peer_id); - auto it = std::find(m_peer_ids.begin(), m_peer_ids.end(), peer_id); - m_peer_ids.erase(it); - } - - // Create event - putEvent(ConnectionEvent::peerRemoved(peer_id, timeout, peer->getAddress())); - - peer->Drop(); - return true; -} - -/* Interface */ - -ConnectionEventPtr Connection::waitEvent(u32 timeout_ms) -{ - try { - return m_event_queue.pop_front(timeout_ms); - } catch(ItemNotFoundException &ex) { - return ConnectionEvent::create(CONNEVENT_NONE); - } -} - -void Connection::putCommand(ConnectionCommandPtr c) -{ - if (!m_shutting_down) { - m_command_queue.push_back(c); - m_sendThread->Trigger(); - } -} - -void Connection::Serve(Address bind_addr) -{ - putCommand(ConnectionCommand::serve(bind_addr)); -} - -void Connection::Connect(Address address) -{ - putCommand(ConnectionCommand::connect(address)); -} - -bool Connection::Connected() -{ - MutexAutoLock peerlock(m_peers_mutex); - - if (m_peers.size() != 1) - return false; - - std::map::iterator node = m_peers.find(PEER_ID_SERVER); - if (node == m_peers.end()) - return false; - - if (m_peer_id == PEER_ID_INEXISTENT) - return false; - - return true; -} - -void Connection::Disconnect() -{ - putCommand(ConnectionCommand::disconnect()); -} - -bool Connection::ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms) -{ - /* - Note that this function can potentially wait infinitely if non-data - events keep happening before the timeout expires. - This is not considered to be a problem (is it?) - */ - for(;;) { - ConnectionEventPtr e_ptr = waitEvent(timeout_ms); - const ConnectionEvent &e = *e_ptr; - - if (e.type != CONNEVENT_NONE) { - LOG(dout_con << getDesc() << ": Receive: got event: " - << e.describe() << std::endl); - } - - switch (e.type) { - case CONNEVENT_NONE: - return false; - case CONNEVENT_DATA_RECEIVED: - // Data size is lesser than command size, ignoring packet - if (e.data.getSize() < 2) { - continue; - } - - pkt->putRawPacket(*e.data, e.data.getSize(), e.peer_id); - return true; - case CONNEVENT_PEER_ADDED: { - UDPPeer tmp(e.peer_id, e.address, this); - if (m_bc_peerhandler) - m_bc_peerhandler->peerAdded(&tmp); - continue; - } - case CONNEVENT_PEER_REMOVED: { - UDPPeer tmp(e.peer_id, e.address, this); - if (m_bc_peerhandler) - m_bc_peerhandler->deletingPeer(&tmp, e.timeout); - continue; - } - case CONNEVENT_BIND_FAILED: - throw ConnectionBindFailed("Failed to bind socket " - "(port already in use?)"); - } - } - return false; -} - -void Connection::Receive(NetworkPacket *pkt) -{ - bool any = ReceiveTimeoutMs(pkt, m_bc_receive_timeout); - if (!any) - throw NoIncomingDataException("No incoming data"); -} - -bool Connection::TryReceive(NetworkPacket *pkt) -{ - return ReceiveTimeoutMs(pkt, 0); -} - -void Connection::Send(session_t peer_id, u8 channelnum, - NetworkPacket *pkt, bool reliable) -{ - assert(channelnum < CHANNEL_COUNT); // Pre-condition - - // approximate check similar to UDPPeer::processReliableSendCommand() - // to get nicer errors / backtraces if this happens. - if (reliable && pkt->getSize() > MAX_RELIABLE_WINDOW_SIZE*512) { - std::ostringstream oss; - oss << "Packet too big for window, peer_id=" << peer_id - << " command=" << pkt->getCommand() << " size=" << pkt->getSize(); - FATAL_ERROR(oss.str().c_str()); - } - - putCommand(ConnectionCommand::send(peer_id, channelnum, pkt, reliable)); -} - -Address Connection::GetPeerAddress(session_t peer_id) -{ - PeerHelper peer = getPeerNoEx(peer_id); - - if (!peer) - throw PeerNotFoundException("No address for peer found!"); - return peer->getAddress(); -} - -float Connection::getPeerStat(session_t peer_id, rtt_stat_type type) -{ - PeerHelper peer = getPeerNoEx(peer_id); - if (!peer) - return -1; - return peer->getStat(type); -} - -float Connection::getLocalStat(rate_stat_type type) -{ - PeerHelper peer = getPeerNoEx(PEER_ID_SERVER); - - FATAL_ERROR_IF(!peer, "Connection::getLocalStat we couldn't get our own peer? are you serious???"); - - float retval = 0; - - for (Channel &channel : dynamic_cast(&peer)->channels) { - switch(type) { - case CUR_DL_RATE: - retval += channel.getCurrentDownloadRateKB(); - break; - case AVG_DL_RATE: - retval += channel.getAvgDownloadRateKB(); - break; - case CUR_INC_RATE: - retval += channel.getCurrentIncomingRateKB(); - break; - case AVG_INC_RATE: - retval += channel.getAvgIncomingRateKB(); - break; - case AVG_LOSS_RATE: - retval += channel.getAvgLossRateKB(); - break; - case CUR_LOSS_RATE: - retval += channel.getCurrentLossRateKB(); - break; - default: - FATAL_ERROR("Connection::getLocalStat Invalid stat type"); - } - } - return retval; -} - -session_t Connection::createPeer(const Address &sender, int fd) -{ - // Somebody wants to make a new connection - - // Get a unique peer id - const session_t minimum = 2; - const session_t overflow = MAX_UDP_PEERS; - - /* - Find an unused peer id - */ - - MutexAutoLock lock(m_peers_mutex); - session_t peer_id_new; - for (int tries = 0; tries < 100; tries++) { - peer_id_new = myrand_range(minimum, overflow - 1); - if (m_peers.find(peer_id_new) == m_peers.end()) - break; - } - if (m_peers.find(peer_id_new) != m_peers.end()) { - errorstream << getDesc() << " ran out of peer ids" << std::endl; - return PEER_ID_INEXISTENT; - } - - // Create a peer - Peer *peer = 0; - peer = new UDPPeer(peer_id_new, sender, this); - - m_peers[peer->id] = peer; - m_peer_ids.push_back(peer->id); - - LOG(dout_con << getDesc() - << "createPeer(): giving peer_id=" << peer_id_new << std::endl); - - { - Buffer reply(4); - writeU8(&reply[0], PACKET_TYPE_CONTROL); - writeU8(&reply[1], CONTROLTYPE_SET_PEER_ID); - writeU16(&reply[2], peer_id_new); - putCommand(ConnectionCommand::createPeer(peer_id_new, reply)); - } - - // Create peer addition event - putEvent(ConnectionEvent::peerAdded(peer_id_new, sender)); - - // We're now talking to a valid peer_id - return peer_id_new; -} - -const std::string Connection::getDesc() -{ - MutexAutoLock _(m_info_mutex); - return std::string("con(")+ - itos(m_udpSocket.GetHandle())+"/"+itos(m_peer_id)+")"; -} - -void Connection::DisconnectPeer(session_t peer_id) -{ - putCommand(ConnectionCommand::disconnect_peer(peer_id)); -} - -void Connection::doResendOne(session_t peer_id) -{ - assert(peer_id != PEER_ID_INEXISTENT); - putCommand(ConnectionCommand::resend_one(peer_id)); -} - -void Connection::sendAck(session_t peer_id, u8 channelnum, u16 seqnum) -{ - assert(channelnum < CHANNEL_COUNT); // Pre-condition - - LOG(dout_con< ack(4); - writeU8(&ack[0], PACKET_TYPE_CONTROL); - writeU8(&ack[1], CONTROLTYPE_ACK); - writeU16(&ack[2], seqnum); - - putCommand(ConnectionCommand::ack(peer_id, channelnum, ack)); - m_sendThread->Trigger(); -} - -UDPPeer* Connection::createServerPeer(const Address &address) -{ - if (ConnectedToServer()) - throw ConnectionException("Already connected to a server"); - - UDPPeer *peer = new UDPPeer(PEER_ID_SERVER, address, this); - peer->SetFullyOpen(); - - { - MutexAutoLock lock(m_peers_mutex); - m_peers[peer->id] = peer; - m_peer_ids.push_back(peer->id); - } - - return peer; -} - -} // namespace diff --git a/src/network/connection.h b/src/network/connection.h index a9e5a18c5..a3665566e 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -1,44 +1,26 @@ -/* -Minetest -Copyright (C) 2013 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later #pragma once #include "irrlichttypes.h" -#include "peerhandler.h" #include "socket.h" -#include "constants.h" -#include "util/pointer.h" -#include "util/container.h" -#include "util/numeric.h" -#include "porting.h" -#include "networkprotocol.h" -#include -#include -#include +#include "networkprotocol.h" // session_t class NetworkPacket; +class PeerHandler; namespace con { -class ConnectionReceiveThread; -class ConnectionSendThread; +enum rtt_stat_type { + MIN_RTT, + MAX_RTT, + AVG_RTT, + MIN_JITTER, + MAX_JITTER, + AVG_JITTER +}; enum rate_stat_type { CUR_DL_RATE, @@ -49,295 +31,45 @@ enum rate_stat_type { AVG_LOSS_RATE, }; -class Peer; - -// FIXME: Peer refcounting should generally be replaced by std::shared_ptr -class PeerHelper -{ +class IPeer { public: - PeerHelper() = default; - inline PeerHelper(Peer *peer) { *this = peer; } - ~PeerHelper(); + // Unique id of the peer + const session_t id; - PeerHelper& operator=(Peer *peer); - inline Peer* operator->() const { return m_peer; } - inline Peer* operator&() const { return m_peer; } - - inline bool operator!() { return !m_peer; } - inline bool operator!=(std::nullptr_t) { return !!m_peer; } - -private: - Peer *m_peer = nullptr; -}; - -/* - Connection -*/ - -enum ConnectionEventType { - CONNEVENT_NONE, - CONNEVENT_DATA_RECEIVED, - CONNEVENT_PEER_ADDED, - CONNEVENT_PEER_REMOVED, - CONNEVENT_BIND_FAILED, -}; - -struct ConnectionEvent; -typedef std::shared_ptr ConnectionEventPtr; - -// This is very similar to ConnectionCommand -struct ConnectionEvent -{ - const ConnectionEventType type; - session_t peer_id = 0; - Buffer data; - bool timeout = false; - Address address; - - // We don't want to copy "data" - DISABLE_CLASS_COPY(ConnectionEvent); - - static ConnectionEventPtr create(ConnectionEventType type); - static ConnectionEventPtr dataReceived(session_t peer_id, const Buffer &data); - static ConnectionEventPtr peerAdded(session_t peer_id, Address address); - static ConnectionEventPtr peerRemoved(session_t peer_id, bool is_timeout, Address address); - static ConnectionEventPtr bindFailed(); - - const char *describe() const; - -private: - ConnectionEvent(ConnectionEventType type_) : - type(type_) {} -}; - -struct ConnectionCommand; -typedef std::shared_ptr ConnectionCommandPtr; - -struct BufferedPacket; -typedef std::shared_ptr BufferedPacketPtr; - -class Connection; -class PeerHandler; - -class Peer { - public: - friend class PeerHelper; - - virtual ~Peer() { - MutexAutoLock usage_lock(m_exclusive_access_mutex); - FATAL_ERROR_IF(m_usage != 0, "Reference counting failure"); - } - - // Unique id of the peer - const session_t id; - - void Drop(); - - virtual void PutReliableSendCommand(ConnectionCommandPtr &c, - unsigned int max_packet_size) {}; - - virtual const Address &getAddress() const = 0; - - bool isPendingDeletion() const { - MutexAutoLock lock(m_exclusive_access_mutex); - return m_pending_deletion; - } - void ResetTimeout() { - MutexAutoLock lock(m_exclusive_access_mutex); - m_timeout_counter = 0; - } - - bool isHalfOpen() const { - MutexAutoLock lock(m_exclusive_access_mutex); - return m_half_open; - } - void SetFullyOpen() { - MutexAutoLock lock(m_exclusive_access_mutex); - m_half_open = false; - } - - virtual bool isTimedOut(float timeout, std::string &reason); - - unsigned int m_increment_packets_remaining = 0; - - virtual u16 getNextSplitSequenceNumber(u8 channel) { return 0; }; - virtual void setNextSplitSequenceNumber(u8 channel, u16 seqnum) {}; - virtual SharedBuffer addSplitPacket(u8 channel, BufferedPacketPtr &toadd, - bool reliable) - { - FATAL_ERROR("unimplemented in abstract class"); - } - - virtual bool Ping(float dtime, SharedBuffer& data) { return false; }; - - virtual float getStat(rtt_stat_type type) const { - switch (type) { - case MIN_RTT: - return m_rtt.min_rtt; - case MAX_RTT: - return m_rtt.max_rtt; - case AVG_RTT: - return m_rtt.avg_rtt; - case MIN_JITTER: - return m_rtt.jitter_min; - case MAX_JITTER: - return m_rtt.jitter_max; - case AVG_JITTER: - return m_rtt.jitter_avg; - } - return -1; - } - - protected: - Peer(session_t id, const Address &address, Connection *connection) : - id(id), - m_connection(connection), - address(address), - m_last_timeout_check(porting::getTimeMs()) - { - } - - virtual void reportRTT(float rtt) {}; - - void RTTStatistics(float rtt, - const std::string &profiler_id = "", - unsigned int num_samples = 1000); - - bool IncUseCount(); - void DecUseCount(); - - mutable std::mutex m_exclusive_access_mutex; - - bool m_pending_deletion = false; - - Connection *m_connection; - - // Address of the peer - Address address; - - // Ping timer - float m_ping_timer = 0.0f; - - private: - struct rttstats { - float jitter_min = FLT_MAX; - float jitter_max = 0.0f; - float jitter_avg = -1.0f; - float min_rtt = FLT_MAX; - float max_rtt = 0.0f; - float avg_rtt = -1.0f; - }; - - rttstats m_rtt; - float m_last_rtt = -1.0f; - - /* - Until the peer has communicated with us using their assigned peer id - the connection is considered half-open. - During this time we inhibit re-sending any reliables or pings. This - is to avoid spending too many resources on a potential DoS attack - and to make sure Minetest servers are not useful for UDP amplificiation. - */ - bool m_half_open = true; - - // current usage count - unsigned int m_usage = 0; - - // Seconds from last receive - float m_timeout_counter = 0.0f; - - u64 m_last_timeout_check; -}; - -class UDPPeer; - -class Connection -{ -public: - friend class ConnectionSendThread; - friend class ConnectionReceiveThread; - - Connection(u32 protocol_id, u32 max_packet_size, float timeout, bool ipv6, - PeerHandler *peerhandler); - ~Connection(); - - /* Interface */ - ConnectionEventPtr waitEvent(u32 timeout_ms); - - void putCommand(ConnectionCommandPtr c); - - void SetTimeoutMs(u32 timeout) { m_bc_receive_timeout = timeout; } - void Serve(Address bind_addr); - void Connect(Address address); - bool Connected(); - void Disconnect(); - bool ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms); - void Receive(NetworkPacket *pkt); - bool TryReceive(NetworkPacket *pkt); - void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable); - session_t GetPeerID() const { return m_peer_id; } - Address GetPeerAddress(session_t peer_id); - float getPeerStat(session_t peer_id, rtt_stat_type type); - float getLocalStat(rate_stat_type type); - u32 GetProtocolID() const { return m_protocol_id; }; - const std::string getDesc(); - void DisconnectPeer(session_t peer_id); + virtual const Address &getAddress() const = 0; protected: - PeerHelper getPeerNoEx(session_t peer_id); - session_t lookupPeer(const Address& sender); - - session_t createPeer(const Address& sender, int fd); - UDPPeer* createServerPeer(const Address& sender); - bool deletePeer(session_t peer_id, bool timeout); - - void SetPeerID(session_t id) { m_peer_id = id; } - - void doResendOne(session_t peer_id); - - void sendAck(session_t peer_id, u8 channelnum, u16 seqnum); - - std::vector getPeerIDs() - { - MutexAutoLock peerlock(m_peers_mutex); - return m_peer_ids; - } - - u32 getActiveCount(); - - UDPSocket m_udpSocket; - // Command queue: user -> SendThread - MutexedQueue m_command_queue; - - void putEvent(ConnectionEventPtr e); - - void TriggerSend(); - - bool ConnectedToServer() - { - return getPeerNoEx(PEER_ID_SERVER) != nullptr; - } -private: - // Event queue: ReceiveThread -> user - MutexedQueue m_event_queue; - - session_t m_peer_id = 0; - u32 m_protocol_id; - - std::map m_peers; - std::vector m_peer_ids; - std::mutex m_peers_mutex; - - std::unique_ptr m_sendThread; - std::unique_ptr m_receiveThread; - - mutable std::mutex m_info_mutex; - - // Backwards compatibility - PeerHandler *m_bc_peerhandler; - u32 m_bc_receive_timeout = 0; - - bool m_shutting_down = false; + IPeer(session_t id) : id(id) {} + ~IPeer() {} }; +class IConnection +{ +public: + virtual ~IConnection() = default; + + virtual void SetTimeoutMs(u32 timeout) = 0; + virtual void Serve(Address bind_addr) = 0; + virtual void Connect(Address address) = 0; + virtual bool Connected() = 0; + virtual void Disconnect() = 0; + virtual void DisconnectPeer(session_t peer_id) = 0; + + virtual bool ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms) = 0; + virtual void Receive(NetworkPacket *pkt) = 0; + bool TryReceive(NetworkPacket *pkt) { + return ReceiveTimeoutMs(pkt, 0); + } + + virtual void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable) = 0; + + virtual session_t GetPeerID() const = 0; + virtual Address GetPeerAddress(session_t peer_id) = 0; + virtual float getPeerStat(session_t peer_id, rtt_stat_type type) = 0; + virtual float getLocalStat(rate_stat_type type) = 0; +}; + +// MTP = Minetest Protocol +IConnection *createMTP(float timeout, bool ipv6, PeerHandler *handler); + } // namespace diff --git a/src/network/mtp/impl.cpp b/src/network/mtp/impl.cpp new file mode 100644 index 000000000..2d4a99119 --- /dev/null +++ b/src/network/mtp/impl.cpp @@ -0,0 +1,1667 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include +#include +#include +#include +#include "network/mtp/internal.h" +#include "serialization.h" +#include "log.h" +#include "porting.h" +#include "network/mtp/threads.h" +#include "network/peerhandler.h" +#include "network/networkpacket.h" +#include "util/serialize.h" +#include "util/numeric.h" +#include "util/string.h" +#include "settings.h" +#include "profiler.h" + +namespace con +{ + +/******************************************************************************/ +/* defines used for debugging and profiling */ +/******************************************************************************/ +#ifdef NDEBUG + #define PROFILE(a) +#else + #define PROFILE(a) a +#endif + +// TODO: Clean this up. +#define LOG(a) a + +#define PING_TIMEOUT 5.0f + +// exponent base +#define RESEND_SCALE_BASE 1.5f + +// since spacing is exponential the numbers here shouldn't be too high +// (it's okay to start out quick) +#define RESEND_TIMEOUT_MIN 0.1f +#define RESEND_TIMEOUT_MAX 2.0f +#define RESEND_TIMEOUT_FACTOR 2 + +u16 BufferedPacket::getSeqnum() const +{ + if (size() < BASE_HEADER_SIZE + 3) + return 0; // should never happen + + return readU16(&data[BASE_HEADER_SIZE + 1]); +} + +BufferedPacketPtr makePacket(const Address &address, const SharedBuffer &data, + u32 protocol_id, session_t sender_peer_id, u8 channel) +{ + u32 packet_size = data.getSize() + BASE_HEADER_SIZE; + + auto p = std::make_shared(packet_size); + p->address = address; + + writeU32(&p->data[0], protocol_id); + writeU16(&p->data[4], sender_peer_id); + writeU8(&p->data[6], channel); + + memcpy(&p->data[BASE_HEADER_SIZE], *data, data.getSize()); + + return p; +} + +SharedBuffer makeOriginalPacket(const SharedBuffer &data) +{ + u32 header_size = 1; + u32 packet_size = data.getSize() + header_size; + SharedBuffer b(packet_size); + + writeU8(&(b[0]), PACKET_TYPE_ORIGINAL); + if (data.getSize() > 0) { + memcpy(&(b[header_size]), *data, data.getSize()); + } + return b; +} + +// Split data in chunks and add TYPE_SPLIT headers to them +void makeSplitPacket(const SharedBuffer &data, u32 chunksize_max, u16 seqnum, + std::list> *chunks) +{ + // Chunk packets, containing the TYPE_SPLIT header + const u32 chunk_header_size = 7; + const u32 maximum_data_size = chunksize_max - chunk_header_size; + u32 start = 0, end = 0; + u16 chunk_num = 0; + do { + end = start + maximum_data_size - 1; + if (end > data.getSize() - 1) + end = data.getSize() - 1; + + u32 payload_size = end - start + 1; + u32 packet_size = chunk_header_size + payload_size; + + SharedBuffer chunk(packet_size); + + writeU8(&chunk[0], PACKET_TYPE_SPLIT); + writeU16(&chunk[1], seqnum); + // [3] u16 chunk_count is written at next stage + writeU16(&chunk[5], chunk_num); + memcpy(&chunk[chunk_header_size], &data[start], payload_size); + + chunks->push_back(chunk); + + start = end + 1; + sanity_check(chunk_num < 0xFFFF); // overflow + chunk_num++; + } + while (end != data.getSize() - 1); + + for (auto &chunk : *chunks) { + // Write chunk_count + writeU16(&chunk[3], chunk_num); + } +} + +void makeAutoSplitPacket(const SharedBuffer &data, u32 chunksize_max, + u16 &split_seqnum, std::list> *list) +{ + u32 original_header_size = 1; + + if (data.getSize() + original_header_size > chunksize_max) { + makeSplitPacket(data, chunksize_max, split_seqnum, list); + split_seqnum++; + return; + } + + list->push_back(makeOriginalPacket(data)); +} + +SharedBuffer makeReliablePacket(const SharedBuffer &data, u16 seqnum) +{ + u32 header_size = 3; + u32 packet_size = data.getSize() + header_size; + SharedBuffer b(packet_size); + + writeU8(&b[0], PACKET_TYPE_RELIABLE); + writeU16(&b[1], seqnum); + + memcpy(&b[header_size], *data, data.getSize()); + + return b; +} + +/* + ReliablePacketBuffer +*/ + +void ReliablePacketBuffer::print() +{ + MutexAutoLock listlock(m_list_mutex); + LOG(dout_con<<"Dump of ReliablePacketBuffer:" << std::endl); + unsigned int index = 0; + for (BufferedPacketPtr &packet : m_list) { + LOG(dout_con<getSeqnum() << std::endl); + index++; + } +} + +bool ReliablePacketBuffer::empty() +{ + MutexAutoLock listlock(m_list_mutex); + return m_list.empty(); +} + +u32 ReliablePacketBuffer::size() +{ + MutexAutoLock listlock(m_list_mutex); + return m_list.size(); +} + +ReliablePacketBuffer::FindResult ReliablePacketBuffer::findPacketNoLock(u16 seqnum) +{ + for (auto it = m_list.begin(); it != m_list.end(); ++it) { + if ((*it)->getSeqnum() == seqnum) + return it; + } + return m_list.end(); +} + +bool ReliablePacketBuffer::getFirstSeqnum(u16& result) +{ + MutexAutoLock listlock(m_list_mutex); + if (m_list.empty()) + return false; + result = m_list.front()->getSeqnum(); + return true; +} + +BufferedPacketPtr ReliablePacketBuffer::popFirst() +{ + MutexAutoLock listlock(m_list_mutex); + if (m_list.empty()) + throw NotFoundException("Buffer is empty"); + + BufferedPacketPtr p(m_list.front()); + m_list.pop_front(); + + if (m_list.empty()) { + m_oldest_non_answered_ack = 0; + } else { + m_oldest_non_answered_ack = m_list.front()->getSeqnum(); + } + return p; +} + +BufferedPacketPtr ReliablePacketBuffer::popSeqnum(u16 seqnum) +{ + MutexAutoLock listlock(m_list_mutex); + auto r = findPacketNoLock(seqnum); + if (r == m_list.end()) { + LOG(dout_con<<"Sequence number: " << seqnum + << " not found in reliable buffer"<getSeqnum(); + } + return p; +} + +void ReliablePacketBuffer::insert(BufferedPacketPtr &p_ptr, u16 next_expected) +{ + MutexAutoLock listlock(m_list_mutex); + const BufferedPacket &p = *p_ptr; + + if (p.size() < BASE_HEADER_SIZE + 3) { + errorstream << "ReliablePacketBuffer::insert(): Invalid data size for " + "reliable packet" << std::endl; + return; + } + u8 type = readU8(&p.data[BASE_HEADER_SIZE + 0]); + if (type != PACKET_TYPE_RELIABLE) { + errorstream << "ReliablePacketBuffer::insert(): type is not reliable" + << std::endl; + return; + } + const u16 seqnum = p.getSeqnum(); + + if (!seqnum_in_window(seqnum, next_expected, MAX_RELIABLE_WINDOW_SIZE)) { + errorstream << "ReliablePacketBuffer::insert(): seqnum is outside of " + "expected window " << std::endl; + return; + } + if (seqnum == next_expected) { + errorstream << "ReliablePacketBuffer::insert(): seqnum is next expected" + << std::endl; + return; + } + + sanity_check(m_list.size() <= SEQNUM_MAX); // FIXME: Handle the error? + + // Find the right place for the packet and insert it there + // If list is empty, just add it + if (m_list.empty()) { + m_list.push_back(p_ptr); + m_oldest_non_answered_ack = seqnum; + // Done. + return; + } + + // Otherwise find the right place + auto it = m_list.begin(); + // Find the first packet in the list which has a higher seqnum + u16 s = (*it)->getSeqnum(); + + /* case seqnum is smaller then next_expected seqnum */ + /* this is true e.g. on wrap around */ + if (seqnum < next_expected) { + while(((s < seqnum) || (s >= next_expected)) && (it != m_list.end())) { + ++it; + if (it != m_list.end()) + s = (*it)->getSeqnum(); + } + } + /* non wrap around case (at least for incoming and next_expected */ + else + { + while(((s < seqnum) && (s >= next_expected)) && (it != m_list.end())) { + ++it; + if (it != m_list.end()) + s = (*it)->getSeqnum(); + } + } + + if (s == seqnum) { + /* nothing to do this seems to be a resent packet */ + /* for paranoia reason data should be compared */ + auto &i = *it; + if ( + (i->getSeqnum() != seqnum) || + (i->size() != p.size()) || + (i->address != p.address) + ) + { + /* if this happens your maximum transfer window may be to big */ + char buf[200]; + snprintf(buf, sizeof(buf), + "Duplicated seqnum %d non matching packet detected:\n", + seqnum); + warningstream << buf; + snprintf(buf, sizeof(buf), + "Old: seqnum: %05d size: %04zu, address: %s\n", + i->getSeqnum(), i->size(), + i->address.serializeString().c_str()); + warningstream << buf; + snprintf(buf, sizeof(buf), + "New: seqnum: %05d size: %04zu, address: %s\n", + p.getSeqnum(), p.size(), + p.address.serializeString().c_str()); + warningstream << buf << std::flush; + throw IncomingDataCorruption("duplicated packet isn't same as original one"); + } + } + /* insert or push back */ + else if (it != m_list.end()) { + m_list.insert(it, p_ptr); + } else { + m_list.push_back(p_ptr); + } + + /* update last packet number */ + m_oldest_non_answered_ack = m_list.front()->getSeqnum(); +} + +void ReliablePacketBuffer::incrementTimeouts(float dtime) +{ + MutexAutoLock listlock(m_list_mutex); + for (auto &packet : m_list) { + packet->time += dtime; + packet->totaltime += dtime; + } +} + +u32 ReliablePacketBuffer::getTimedOuts(float timeout) +{ + MutexAutoLock listlock(m_list_mutex); + u32 count = 0; + for (auto &packet : m_list) { + if (packet->totaltime >= timeout) + count++; + } + return count; +} + +std::vector> + ReliablePacketBuffer::getResend(float timeout, u32 max_packets) +{ + MutexAutoLock listlock(m_list_mutex); + std::vector> timed_outs; + for (auto &packet : m_list) { + // resend time scales exponentially with each cycle + const float pkt_timeout = timeout * powf(RESEND_SCALE_BASE, packet->resend_count); + + if (packet->time < pkt_timeout) + continue; + + // caller will resend packet so reset time and increase counter + packet->time = 0.0f; + packet->resend_count++; + + timed_outs.emplace_back(packet); + + if (timed_outs.size() >= max_packets) + break; + } + return timed_outs; +} + +/* + IncomingSplitPacket +*/ + +bool IncomingSplitPacket::insert(u32 chunk_num, SharedBuffer &chunkdata) +{ + sanity_check(chunk_num < chunk_count); + + // If chunk already exists, ignore it. + // Sometimes two identical packets may arrive when there is network + // lag and the server re-sends stuff. + if (chunks.find(chunk_num) != chunks.end()) + return false; + + // Set chunk data in buffer + chunks[chunk_num] = chunkdata; + + return true; +} + +SharedBuffer IncomingSplitPacket::reassemble() +{ + sanity_check(allReceived()); + + // Calculate total size + u32 totalsize = 0; + for (const auto &chunk : chunks) + totalsize += chunk.second.getSize(); + + SharedBuffer fulldata(totalsize); + + // Copy chunks to data buffer + u32 start = 0; + for (u32 chunk_i = 0; chunk_i < chunk_count; chunk_i++) { + const SharedBuffer &buf = chunks[chunk_i]; + memcpy(&fulldata[start], *buf, buf.getSize()); + start += buf.getSize(); + } + + return fulldata; +} + +/* + IncomingSplitBuffer +*/ + +IncomingSplitBuffer::~IncomingSplitBuffer() +{ + MutexAutoLock listlock(m_map_mutex); + for (auto &i : m_buf) { + delete i.second; + } +} + +SharedBuffer IncomingSplitBuffer::insert(BufferedPacketPtr &p_ptr, bool reliable) +{ + MutexAutoLock listlock(m_map_mutex); + const BufferedPacket &p = *p_ptr; + + u32 headersize = BASE_HEADER_SIZE + 7; + if (p.size() < headersize) { + errorstream << "Invalid data size for split packet" << std::endl; + return SharedBuffer(); + } + u8 type = readU8(&p.data[BASE_HEADER_SIZE+0]); + u16 seqnum = readU16(&p.data[BASE_HEADER_SIZE+1]); + u16 chunk_count = readU16(&p.data[BASE_HEADER_SIZE+3]); + u16 chunk_num = readU16(&p.data[BASE_HEADER_SIZE+5]); + + if (type != PACKET_TYPE_SPLIT) { + errorstream << "IncomingSplitBuffer::insert(): type is not split" + << std::endl; + return SharedBuffer(); + } + if (chunk_num >= chunk_count) { + errorstream << "IncomingSplitBuffer::insert(): chunk_num=" << chunk_num + << " >= chunk_count=" << chunk_count << std::endl; + return SharedBuffer(); + } + + // Add if doesn't exist + IncomingSplitPacket *sp; + if (m_buf.find(seqnum) == m_buf.end()) { + sp = new IncomingSplitPacket(chunk_count, reliable); + m_buf[seqnum] = sp; + } else { + sp = m_buf[seqnum]; + } + + if (chunk_count != sp->chunk_count) { + errorstream << "IncomingSplitBuffer::insert(): chunk_count=" + << chunk_count << " != sp->chunk_count=" << sp->chunk_count + << std::endl; + return SharedBuffer(); + } + if (reliable != sp->reliable) + LOG(derr_con<<"Connection: WARNING: reliable="<reliable="<reliable + < chunkdata(chunkdatasize); + memcpy(*chunkdata, &(p.data[headersize]), chunkdatasize); + + if (!sp->insert(chunk_num, chunkdata)) + return SharedBuffer(); + + // If not all chunks are received, return empty buffer + if (!sp->allReceived()) + return SharedBuffer(); + + SharedBuffer fulldata = sp->reassemble(); + + // Remove sp from buffer + m_buf.erase(seqnum); + delete sp; + + return fulldata; +} + +void IncomingSplitBuffer::removeUnreliableTimedOuts(float dtime, float timeout) +{ + MutexAutoLock listlock(m_map_mutex); + std::vector remove_queue; + { + for (const auto &i : m_buf) { + IncomingSplitPacket *p = i.second; + // Reliable ones are not removed by timeout + if (p->reliable) + continue; + p->time += dtime; + if (p->time >= timeout) + remove_queue.push_back(i.first); + } + } + for (u16 j : remove_queue) { + LOG(dout_con<<"NOTE: Removing timed out unreliable split packet"<second; + m_buf.erase(it); + } +} + +/* + ConnectionCommand + */ + +ConnectionCommandPtr ConnectionCommand::create(ConnectionCommandType type) +{ + return ConnectionCommandPtr(new ConnectionCommand(type)); +} + +ConnectionCommandPtr ConnectionCommand::serve(Address address) +{ + auto c = create(CONNCMD_SERVE); + c->address = address; + return c; +} + +ConnectionCommandPtr ConnectionCommand::connect(Address address) +{ + auto c = create(CONNCMD_CONNECT); + c->address = address; + return c; +} + +ConnectionCommandPtr ConnectionCommand::disconnect() +{ + return create(CONNCMD_DISCONNECT); +} + +ConnectionCommandPtr ConnectionCommand::disconnect_peer(session_t peer_id) +{ + auto c = create(CONNCMD_DISCONNECT_PEER); + c->peer_id = peer_id; + return c; +} + +ConnectionCommandPtr ConnectionCommand::resend_one(session_t peer_id) +{ + auto c = create(CONNCMD_RESEND_ONE); + c->peer_id = peer_id; + c->channelnum = 0; // must be same as createPeer + c->reliable = true; + return c; +} + +ConnectionCommandPtr ConnectionCommand::send(session_t peer_id, u8 channelnum, + NetworkPacket *pkt, bool reliable) +{ + auto c = create(CONNCMD_SEND); + c->peer_id = peer_id; + c->channelnum = channelnum; + c->reliable = reliable; + c->data = pkt->oldForgePacket(); + return c; +} + +ConnectionCommandPtr ConnectionCommand::ack(session_t peer_id, u8 channelnum, const Buffer &data) +{ + auto c = create(CONCMD_ACK); + c->peer_id = peer_id; + c->channelnum = channelnum; + c->reliable = false; + data.copyTo(c->data); + return c; +} + +ConnectionCommandPtr ConnectionCommand::createPeer(session_t peer_id, const Buffer &data) +{ + auto c = create(CONCMD_CREATE_PEER); + c->peer_id = peer_id; + c->channelnum = 0; + c->reliable = true; + c->raw = true; + data.copyTo(c->data); + return c; +} + +/* + Channel +*/ + +u16 Channel::readNextIncomingSeqNum() +{ + MutexAutoLock internal(m_internal_mutex); + return next_incoming_seqnum; +} + +u16 Channel::incNextIncomingSeqNum() +{ + MutexAutoLock internal(m_internal_mutex); + u16 retval = next_incoming_seqnum; + next_incoming_seqnum++; + return retval; +} + +u16 Channel::readNextSplitSeqNum() +{ + MutexAutoLock internal(m_internal_mutex); + return next_outgoing_split_seqnum; +} +void Channel::setNextSplitSeqNum(u16 seqnum) +{ + MutexAutoLock internal(m_internal_mutex); + next_outgoing_split_seqnum = seqnum; +} + +u16 Channel::getOutgoingSequenceNumber(bool& successful) +{ + MutexAutoLock internal(m_internal_mutex); + + u16 retval = next_outgoing_seqnum; + successful = false; + + /* shortcut if there ain't any packet in outgoing list */ + if (outgoing_reliables_sent.empty()) { + successful = true; + next_outgoing_seqnum++; + return retval; + } + + u16 lowest_unacked_seqnumber; + if (outgoing_reliables_sent.getFirstSeqnum(lowest_unacked_seqnumber)) { + if (lowest_unacked_seqnumber < next_outgoing_seqnum) { + // ugly cast but this one is required in order to tell compiler we + // know about difference of two unsigned may be negative in general + // but we already made sure it won't happen in this case + if (((u16)(next_outgoing_seqnum - lowest_unacked_seqnumber)) > m_window_size) { + return 0; + } + } else { + // ugly cast but this one is required in order to tell compiler we + // know about difference of two unsigned may be negative in general + // but we already made sure it won't happen in this case + if ((next_outgoing_seqnum + (u16)(SEQNUM_MAX - lowest_unacked_seqnumber)) > + m_window_size) { + return 0; + } + } + } + + successful = true; + next_outgoing_seqnum++; + return retval; +} + +u16 Channel::readOutgoingSequenceNumber() +{ + MutexAutoLock internal(m_internal_mutex); + return next_outgoing_seqnum; +} + +bool Channel::putBackSequenceNumber(u16 seqnum) +{ + if (((seqnum + 1) % (SEQNUM_MAX+1)) == next_outgoing_seqnum) { + + next_outgoing_seqnum = seqnum; + return true; + } + return false; +} + +void Channel::UpdateBytesSent(unsigned int bytes, unsigned int packets) +{ + MutexAutoLock internal(m_internal_mutex); + current_bytes_transfered += bytes; + current_packet_successful += packets; +} + +void Channel::UpdateBytesReceived(unsigned int bytes) { + MutexAutoLock internal(m_internal_mutex); + current_bytes_received += bytes; +} + +void Channel::UpdateBytesLost(unsigned int bytes) +{ + MutexAutoLock internal(m_internal_mutex); + current_bytes_lost += bytes; +} + + +void Channel::UpdatePacketLossCounter(unsigned int count) +{ + MutexAutoLock internal(m_internal_mutex); + current_packet_loss += count; +} + +void Channel::UpdatePacketTooLateCounter() +{ + MutexAutoLock internal(m_internal_mutex); + current_packet_too_late++; +} + +void Channel::UpdateTimers(float dtime) +{ + bpm_counter += dtime; + packet_loss_counter += dtime; + + if (packet_loss_counter > 1.0f) { + packet_loss_counter -= 1.0f; + + unsigned int packet_loss = 11; /* use a neutral value for initialization */ + unsigned int packets_successful = 0; + //unsigned int packet_too_late = 0; + + bool reasonable_amount_of_data_transmitted = false; + + { + MutexAutoLock internal(m_internal_mutex); + packet_loss = current_packet_loss; + //packet_too_late = current_packet_too_late; + packets_successful = current_packet_successful; + + if (current_bytes_transfered > (unsigned int) (m_window_size*512/2)) { + reasonable_amount_of_data_transmitted = true; + } + current_packet_loss = 0; + current_packet_too_late = 0; + current_packet_successful = 0; + } + + /* dynamic window size */ + float successful_to_lost_ratio = 0.0f; + bool done = false; + + if (packets_successful > 0) { + successful_to_lost_ratio = packet_loss/packets_successful; + } else if (packet_loss > 0) { + setWindowSize(m_window_size - 10); + done = true; + } + + if (!done) { + if (successful_to_lost_ratio < 0.01f) { + /* don't even think about increasing if we didn't even + * use major parts of our window */ + if (reasonable_amount_of_data_transmitted) + setWindowSize(m_window_size + 100); + } else if (successful_to_lost_ratio < 0.05f) { + /* don't even think about increasing if we didn't even + * use major parts of our window */ + if (reasonable_amount_of_data_transmitted) + setWindowSize(m_window_size + 50); + } else if (successful_to_lost_ratio > 0.15f) { + setWindowSize(m_window_size - 100); + } else if (successful_to_lost_ratio > 0.1f) { + setWindowSize(m_window_size - 50); + } + } + } + + if (bpm_counter > 10.0f) { + { + MutexAutoLock internal(m_internal_mutex); + cur_kbps = + (((float) current_bytes_transfered)/bpm_counter)/1024.0f; + current_bytes_transfered = 0; + cur_kbps_lost = + (((float) current_bytes_lost)/bpm_counter)/1024.0f; + current_bytes_lost = 0; + cur_incoming_kbps = + (((float) current_bytes_received)/bpm_counter)/1024.0f; + current_bytes_received = 0; + bpm_counter = 0.0f; + } + + if (cur_kbps > max_kbps) { + max_kbps = cur_kbps; + } + + if (cur_kbps_lost > max_kbps_lost) { + max_kbps_lost = cur_kbps_lost; + } + + if (cur_incoming_kbps > max_incoming_kbps) { + max_incoming_kbps = cur_incoming_kbps; + } + + rate_samples = MYMIN(rate_samples+1,10); + float old_fraction = ((float) (rate_samples-1) )/( (float) rate_samples); + avg_kbps = avg_kbps * old_fraction + + cur_kbps * (1.0 - old_fraction); + avg_kbps_lost = avg_kbps_lost * old_fraction + + cur_kbps_lost * (1.0 - old_fraction); + avg_incoming_kbps = avg_incoming_kbps * old_fraction + + cur_incoming_kbps * (1.0 - old_fraction); + } +} + + +/* + Peer +*/ + +PeerHelper::~PeerHelper() +{ + if (m_peer) + m_peer->DecUseCount(); + + m_peer = nullptr; +} + +PeerHelper& PeerHelper::operator=(Peer* peer) +{ + if (m_peer) + m_peer->DecUseCount(); + m_peer = peer; + if (peer && !peer->IncUseCount()) + m_peer = nullptr; + return *this; +} + +bool Peer::IncUseCount() +{ + MutexAutoLock lock(m_exclusive_access_mutex); + + if (!m_pending_deletion) { + this->m_usage++; + return true; + } + + return false; +} + +void Peer::DecUseCount() +{ + { + MutexAutoLock lock(m_exclusive_access_mutex); + sanity_check(m_usage > 0); + m_usage--; + + if (!((m_pending_deletion) && (m_usage == 0))) + return; + } + delete this; +} + +void Peer::RTTStatistics(float rtt, const std::string &profiler_id, + unsigned int num_samples) { + + if (m_last_rtt > 0) { + /* set min max values */ + if (rtt < m_rtt.min_rtt) + m_rtt.min_rtt = rtt; + if (rtt >= m_rtt.max_rtt) + m_rtt.max_rtt = rtt; + + /* do average calculation */ + if (m_rtt.avg_rtt < 0.0) + m_rtt.avg_rtt = rtt; + else + m_rtt.avg_rtt = m_rtt.avg_rtt * (num_samples/(num_samples-1)) + + rtt * (1/num_samples); + + /* do jitter calculation */ + + //just use some neutral value at beginning + float jitter = m_rtt.jitter_min; + + if (rtt > m_last_rtt) + jitter = rtt-m_last_rtt; + + if (rtt <= m_last_rtt) + jitter = m_last_rtt - rtt; + + if (jitter < m_rtt.jitter_min) + m_rtt.jitter_min = jitter; + if (jitter >= m_rtt.jitter_max) + m_rtt.jitter_max = jitter; + + if (m_rtt.jitter_avg < 0.0) + m_rtt.jitter_avg = jitter; + else + m_rtt.jitter_avg = m_rtt.jitter_avg * (num_samples/(num_samples-1)) + + jitter * (1/num_samples); + + if (!profiler_id.empty()) { + g_profiler->graphAdd(profiler_id + " RTT [ms]", rtt * 1000.f); + g_profiler->graphAdd(profiler_id + " jitter [ms]", jitter * 1000.f); + } + } + /* save values required for next loop */ + m_last_rtt = rtt; +} + +bool Peer::isTimedOut(float timeout, std::string &reason) +{ + MutexAutoLock lock(m_exclusive_access_mutex); + + { + u64 current_time = porting::getTimeMs(); + float dtime = CALC_DTIME(m_last_timeout_check, current_time); + m_last_timeout_check = current_time; + m_timeout_counter += dtime; + } + if (m_timeout_counter > timeout) { + reason = "timeout counter"; + return true; + } + + return false; +} + +void Peer::Drop() +{ + { + MutexAutoLock usage_lock(m_exclusive_access_mutex); + m_pending_deletion = true; + if (m_usage != 0) + return; + } + + PROFILE(std::stringstream peerIdentifier1); + PROFILE(peerIdentifier1 << "runTimeouts[" << m_connection->getDesc() + << ";" << id << ";RELIABLE]"); + PROFILE(g_profiler->remove(peerIdentifier1.str())); + PROFILE(std::stringstream peerIdentifier2); + PROFILE(peerIdentifier2 << "sendPackets[" << m_connection->getDesc() + << ";" << id << ";RELIABLE]"); + PROFILE(ScopeProfiler peerprofiler(g_profiler, peerIdentifier2.str(), SPT_AVG)); + + delete this; +} + +UDPPeer::UDPPeer(session_t id, const Address &address, Connection *connection) : + Peer(id, address, connection) +{ + for (Channel &channel : channels) + channel.setWindowSize(START_RELIABLE_WINDOW_SIZE); +} + +bool UDPPeer::isTimedOut(float timeout, std::string &reason) +{ + if (Peer::isTimedOut(timeout, reason)) + return true; + + MutexAutoLock lock(m_exclusive_access_mutex); + + for (int i = 0; i < CHANNEL_COUNT; i++) { + Channel &channel = channels[i]; + if (channel.outgoing_reliables_sent.getTimedOuts(timeout) > 0) { + reason = "outgoing reliables channel=" + itos(i); + return true; + } + } + + return false; +} + +void UDPPeer::reportRTT(float rtt) +{ + if (rtt < 0.0) { + return; + } + RTTStatistics(rtt,"rudp",MAX_RELIABLE_WINDOW_SIZE*10); + + // use this value to decide the resend timeout + float timeout = getStat(AVG_RTT) * RESEND_TIMEOUT_FACTOR; + if (timeout < RESEND_TIMEOUT_MIN) + timeout = RESEND_TIMEOUT_MIN; + if (timeout > RESEND_TIMEOUT_MAX) + timeout = RESEND_TIMEOUT_MAX; + + setResendTimeout(timeout); +} + +bool UDPPeer::Ping(float dtime,SharedBuffer& data) +{ + m_ping_timer += dtime; + if (!isHalfOpen() && m_ping_timer >= PING_TIMEOUT) + { + // Create and send PING packet + writeU8(&data[0], PACKET_TYPE_CONTROL); + writeU8(&data[1], CONTROLTYPE_PING); + m_ping_timer = 0.0f; + return true; + } + return false; +} + +void UDPPeer::PutReliableSendCommand(ConnectionCommandPtr &c, + unsigned int max_packet_size) +{ + if (m_pending_disconnect) + return; + + Channel &chan = channels[c->channelnum]; + + if (chan.queued_commands.empty() && + /* don't queue more packets then window size */ + (chan.queued_reliables.size() + 1 < chan.getWindowSize() / 2)) { + LOG(dout_con<getDesc() + <<" processing reliable command for peer id: " << c->peer_id + <<" data size: " << c->data.getSize() << std::endl); + if (processReliableSendCommand(c, max_packet_size)) + return; + } else { + LOG(dout_con<getDesc() + <<" Queueing reliable command for peer id: " << c->peer_id + <<" data size: " << c->data.getSize() <= chan.getWindowSize() / 2) { + LOG(derr_con << m_connection->getDesc() + << "Possible packet stall to peer id: " << c->peer_id + << " queued_commands=" << chan.queued_commands.size() + << std::endl); + } + } + chan.queued_commands.push_back(c); +} + +bool UDPPeer::processReliableSendCommand( + ConnectionCommandPtr &c_ptr, + unsigned int max_packet_size) +{ + if (m_pending_disconnect) + return true; + + const auto &c = *c_ptr; + Channel &chan = channels[c.channelnum]; + + const u32 chunksize_max = max_packet_size + - BASE_HEADER_SIZE + - RELIABLE_HEADER_SIZE; + + std::list> originals; + + if (c.raw) { + originals.emplace_back(c.data); + } else { + u16 split_seqnum = chan.readNextSplitSeqNum(); + makeAutoSplitPacket(c.data, chunksize_max, split_seqnum, &originals); + chan.setNextSplitSeqNum(split_seqnum); + } + + sanity_check(originals.size() < MAX_RELIABLE_WINDOW_SIZE); + + bool have_sequence_number = false; + bool have_initial_sequence_number = false; + std::queue toadd; + u16 initial_sequence_number = 0; + + for (SharedBuffer &original : originals) { + u16 seqnum = chan.getOutgoingSequenceNumber(have_sequence_number); + + /* oops, we don't have enough sequence numbers to send this packet */ + if (!have_sequence_number) + break; + + if (!have_initial_sequence_number) + { + initial_sequence_number = seqnum; + have_initial_sequence_number = true; + } + + SharedBuffer reliable = makeReliablePacket(original, seqnum); + + // Add base headers and make a packet + BufferedPacketPtr p = con::makePacket(address, reliable, + m_connection->GetProtocolID(), m_connection->GetPeerID(), + c.channelnum); + + toadd.push(p); + } + + if (have_sequence_number) { + while (!toadd.empty()) { + BufferedPacketPtr p = toadd.front(); + toadd.pop(); +// LOG(dout_con<getDesc() +// << " queuing reliable packet for peer_id: " << c.peer_id +// << " channel: " << (c.channelnum&0xFF) +// << " seqnum: " << readU16(&p.data[BASE_HEADER_SIZE+1]) +// << std::endl) + chan.queued_reliables.push(p); + } + sanity_check(chan.queued_reliables.size() < 0xFFFF); + return true; + } + + u16 packets_available = toadd.size(); + /* we didn't get a single sequence number no need to fill queue */ + if (!have_initial_sequence_number) { + LOG(derr_con << m_connection->getDesc() << "Ran out of sequence numbers!" << std::endl); + return false; + } + + while (!toadd.empty()) { + /* remove packet */ + toadd.pop(); + + bool successfully_put_back_sequence_number + = chan.putBackSequenceNumber( + (initial_sequence_number+toadd.size() % (SEQNUM_MAX+1))); + + FATAL_ERROR_IF(!successfully_put_back_sequence_number, "error"); + } + + u32 n_queued = chan.outgoing_reliables_sent.size(); + + LOG(dout_con<getDesc() + << " Windowsize exceeded on reliable sending " + << c.data.getSize() << " bytes" + << std::endl << "\t\tinitial_sequence_number: " + << initial_sequence_number + << std::endl << "\t\tgot at most : " + << packets_available << " packets" + << std::endl << "\t\tpackets queued : " + << n_queued + << std::endl); + + return false; +} + +void UDPPeer::RunCommandQueues( + unsigned int max_packet_size, + unsigned int maxtransfer) +{ + + for (Channel &channel : channels) { + + if ((!channel.queued_commands.empty()) && + (channel.queued_reliables.size() < maxtransfer)) { + try { + ConnectionCommandPtr c = channel.queued_commands.front(); + + LOG(dout_con << m_connection->getDesc() + << " processing queued reliable command " << std::endl); + + // Packet is processed, remove it from queue + if (processReliableSendCommand(c, max_packet_size)) { + channel.queued_commands.pop_front(); + } else { + LOG(dout_con << m_connection->getDesc() + << " Failed to queue packets for peer_id: " << c->peer_id + << ", delaying sending of " << c->data.getSize() + << " bytes" << std::endl); + } + } + catch (ItemNotFoundException &e) { + // intentionally empty + } + } + } +} + +u16 UDPPeer::getNextSplitSequenceNumber(u8 channel) +{ + assert(channel < CHANNEL_COUNT); // Pre-condition + return channels[channel].readNextSplitSeqNum(); +} + +void UDPPeer::setNextSplitSequenceNumber(u8 channel, u16 seqnum) +{ + assert(channel < CHANNEL_COUNT); // Pre-condition + channels[channel].setNextSplitSeqNum(seqnum); +} + +SharedBuffer UDPPeer::addSplitPacket(u8 channel, BufferedPacketPtr &toadd, + bool reliable) +{ + assert(channel < CHANNEL_COUNT); // Pre-condition + return channels[channel].incoming_splits.insert(toadd, reliable); +} + +/* + ConnectionEvent +*/ + +const char *ConnectionEvent::describe() const +{ + switch(type) { + case CONNEVENT_NONE: + return "CONNEVENT_NONE"; + case CONNEVENT_DATA_RECEIVED: + return "CONNEVENT_DATA_RECEIVED"; + case CONNEVENT_PEER_ADDED: + return "CONNEVENT_PEER_ADDED"; + case CONNEVENT_PEER_REMOVED: + return "CONNEVENT_PEER_REMOVED"; + case CONNEVENT_BIND_FAILED: + return "CONNEVENT_BIND_FAILED"; + } + return "Invalid ConnectionEvent"; +} + + +ConnectionEventPtr ConnectionEvent::create(ConnectionEventType type) +{ + return std::shared_ptr(new ConnectionEvent(type)); +} + +ConnectionEventPtr ConnectionEvent::dataReceived(session_t peer_id, const Buffer &data) +{ + auto e = create(CONNEVENT_DATA_RECEIVED); + e->peer_id = peer_id; + data.copyTo(e->data); + return e; +} + +ConnectionEventPtr ConnectionEvent::peerAdded(session_t peer_id, Address address) +{ + auto e = create(CONNEVENT_PEER_ADDED); + e->peer_id = peer_id; + e->address = address; + return e; +} + +ConnectionEventPtr ConnectionEvent::peerRemoved(session_t peer_id, bool is_timeout, Address address) +{ + auto e = create(CONNEVENT_PEER_REMOVED); + e->peer_id = peer_id; + e->timeout = is_timeout; + e->address = address; + return e; +} + +ConnectionEventPtr ConnectionEvent::bindFailed() +{ + return create(CONNEVENT_BIND_FAILED); +} + +/* + Connection +*/ + +Connection::Connection(u32 max_packet_size, float timeout, + bool ipv6, PeerHandler *peerhandler) : + m_udpSocket(ipv6), + m_protocol_id(PROTOCOL_ID), + m_sendThread(new ConnectionSendThread(max_packet_size, timeout)), + m_receiveThread(new ConnectionReceiveThread()), + m_bc_peerhandler(peerhandler) + +{ + /* Amount of time Receive() will wait for data, this is entirely different + * from the connection timeout */ + m_udpSocket.setTimeoutMs(500); + + m_sendThread->setParent(this); + m_receiveThread->setParent(this); + + m_sendThread->start(); + m_receiveThread->start(); +} + + +Connection::~Connection() +{ + m_shutting_down = true; + // request threads to stop + m_sendThread->stop(); + m_receiveThread->stop(); + + //TODO for some unkonwn reason send/receive threads do not exit as they're + // supposed to be but wait on peer timeout. To speed up shutdown we reduce + // timeout to half a second. + m_sendThread->setPeerTimeout(0.5); + + // wait for threads to finish + m_sendThread->wait(); + m_receiveThread->wait(); + + // Delete peers + for (auto &peer : m_peers) { + delete peer.second; + } +} + +/* Internal stuff */ + +void Connection::putEvent(ConnectionEventPtr e) +{ + assert(e->type != CONNEVENT_NONE); // Pre-condition + m_event_queue.push_back(e); +} + +void Connection::TriggerSend() +{ + m_sendThread->Trigger(); +} + +PeerHelper Connection::getPeerNoEx(session_t peer_id) +{ + MutexAutoLock peerlock(m_peers_mutex); + std::map::iterator node = m_peers.find(peer_id); + + if (node == m_peers.end()) { + return PeerHelper(NULL); + } + + // Error checking + FATAL_ERROR_IF(node->second->id != peer_id, "Invalid peer id"); + + return PeerHelper(node->second); +} + +/* find peer_id for address */ +session_t Connection::lookupPeer(const Address& sender) +{ + MutexAutoLock peerlock(m_peers_mutex); + for (auto &it: m_peers) { + Peer *peer = it.second; + if (peer->isPendingDeletion()) + continue; + + if (peer->getAddress() == sender) + return peer->id; + } + + return PEER_ID_INEXISTENT; +} + +u32 Connection::getActiveCount() +{ + MutexAutoLock peerlock(m_peers_mutex); + u32 count = 0; + for (auto &it : m_peers) { + Peer *peer = it.second; + if (peer->isPendingDeletion()) + continue; + if (peer->isHalfOpen()) + continue; + count++; + } + return count; +} + +bool Connection::deletePeer(session_t peer_id, bool timeout) +{ + Peer *peer = 0; + + /* lock list as short as possible */ + { + MutexAutoLock peerlock(m_peers_mutex); + if (m_peers.find(peer_id) == m_peers.end()) + return false; + peer = m_peers[peer_id]; + m_peers.erase(peer_id); + auto it = std::find(m_peer_ids.begin(), m_peer_ids.end(), peer_id); + m_peer_ids.erase(it); + } + + // Create event + putEvent(ConnectionEvent::peerRemoved(peer_id, timeout, peer->getAddress())); + + peer->Drop(); + return true; +} + +/* Interface */ + +ConnectionEventPtr Connection::waitEvent(u32 timeout_ms) +{ + try { + return m_event_queue.pop_front(timeout_ms); + } catch(ItemNotFoundException &ex) { + return ConnectionEvent::create(CONNEVENT_NONE); + } +} + +void Connection::putCommand(ConnectionCommandPtr c) +{ + if (!m_shutting_down) { + m_command_queue.push_back(c); + m_sendThread->Trigger(); + } +} + +void Connection::Serve(Address bind_addr) +{ + putCommand(ConnectionCommand::serve(bind_addr)); +} + +void Connection::Connect(Address address) +{ + putCommand(ConnectionCommand::connect(address)); +} + +bool Connection::Connected() +{ + MutexAutoLock peerlock(m_peers_mutex); + + if (m_peers.size() != 1) + return false; + + std::map::iterator node = m_peers.find(PEER_ID_SERVER); + if (node == m_peers.end()) + return false; + + if (m_peer_id == PEER_ID_INEXISTENT) + return false; + + return true; +} + +void Connection::Disconnect() +{ + putCommand(ConnectionCommand::disconnect()); +} + +bool Connection::ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms) +{ + /* + Note that this function can potentially wait infinitely if non-data + events keep happening before the timeout expires. + This is not considered to be a problem (is it?) + */ + for(;;) { + ConnectionEventPtr e_ptr = waitEvent(timeout_ms); + const ConnectionEvent &e = *e_ptr; + + if (e.type != CONNEVENT_NONE) { + LOG(dout_con << getDesc() << ": Receive: got event: " + << e.describe() << std::endl); + } + + switch (e.type) { + case CONNEVENT_NONE: + return false; + case CONNEVENT_DATA_RECEIVED: + // Data size is lesser than command size, ignoring packet + if (e.data.getSize() < 2) { + continue; + } + + pkt->putRawPacket(*e.data, e.data.getSize(), e.peer_id); + return true; + case CONNEVENT_PEER_ADDED: { + UDPPeer tmp(e.peer_id, e.address, this); + if (m_bc_peerhandler) + m_bc_peerhandler->peerAdded(&tmp); + continue; + } + case CONNEVENT_PEER_REMOVED: { + UDPPeer tmp(e.peer_id, e.address, this); + if (m_bc_peerhandler) + m_bc_peerhandler->deletingPeer(&tmp, e.timeout); + continue; + } + case CONNEVENT_BIND_FAILED: + throw ConnectionBindFailed("Failed to bind socket " + "(port already in use?)"); + } + } + return false; +} + +void Connection::Receive(NetworkPacket *pkt) +{ + bool any = ReceiveTimeoutMs(pkt, m_bc_receive_timeout); + if (!any) + throw NoIncomingDataException("No incoming data"); +} + +void Connection::Send(session_t peer_id, u8 channelnum, + NetworkPacket *pkt, bool reliable) +{ + assert(channelnum < CHANNEL_COUNT); // Pre-condition + + // approximate check similar to UDPPeer::processReliableSendCommand() + // to get nicer errors / backtraces if this happens. + if (reliable && pkt->getSize() > MAX_RELIABLE_WINDOW_SIZE*512) { + std::ostringstream oss; + oss << "Packet too big for window, peer_id=" << peer_id + << " command=" << pkt->getCommand() << " size=" << pkt->getSize(); + FATAL_ERROR(oss.str().c_str()); + } + + putCommand(ConnectionCommand::send(peer_id, channelnum, pkt, reliable)); +} + +Address Connection::GetPeerAddress(session_t peer_id) +{ + PeerHelper peer = getPeerNoEx(peer_id); + + if (!peer) + throw PeerNotFoundException("No address for peer found!"); + return peer->getAddress(); +} + +float Connection::getPeerStat(session_t peer_id, rtt_stat_type type) +{ + PeerHelper peer = getPeerNoEx(peer_id); + if (!peer) + return -1; + return peer->getStat(type); +} + +float Connection::getLocalStat(rate_stat_type type) +{ + PeerHelper peer = getPeerNoEx(PEER_ID_SERVER); + + FATAL_ERROR_IF(!peer, "Connection::getLocalStat we couldn't get our own peer? are you serious???"); + + float retval = 0; + + for (Channel &channel : dynamic_cast(&peer)->channels) { + switch(type) { + case CUR_DL_RATE: + retval += channel.getCurrentDownloadRateKB(); + break; + case AVG_DL_RATE: + retval += channel.getAvgDownloadRateKB(); + break; + case CUR_INC_RATE: + retval += channel.getCurrentIncomingRateKB(); + break; + case AVG_INC_RATE: + retval += channel.getAvgIncomingRateKB(); + break; + case AVG_LOSS_RATE: + retval += channel.getAvgLossRateKB(); + break; + case CUR_LOSS_RATE: + retval += channel.getCurrentLossRateKB(); + break; + default: + FATAL_ERROR("Connection::getLocalStat Invalid stat type"); + } + } + return retval; +} + +session_t Connection::createPeer(const Address &sender, int fd) +{ + // Somebody wants to make a new connection + + // Get a unique peer id + const session_t minimum = 2; + const session_t overflow = MAX_UDP_PEERS; + + /* + Find an unused peer id + */ + + MutexAutoLock lock(m_peers_mutex); + session_t peer_id_new; + for (int tries = 0; tries < 100; tries++) { + peer_id_new = myrand_range(minimum, overflow - 1); + if (m_peers.find(peer_id_new) == m_peers.end()) + break; + } + if (m_peers.find(peer_id_new) != m_peers.end()) { + errorstream << getDesc() << " ran out of peer ids" << std::endl; + return PEER_ID_INEXISTENT; + } + + // Create a peer + Peer *peer = 0; + peer = new UDPPeer(peer_id_new, sender, this); + + m_peers[peer->id] = peer; + m_peer_ids.push_back(peer->id); + + LOG(dout_con << getDesc() + << "createPeer(): giving peer_id=" << peer_id_new << std::endl); + + { + Buffer reply(4); + writeU8(&reply[0], PACKET_TYPE_CONTROL); + writeU8(&reply[1], CONTROLTYPE_SET_PEER_ID); + writeU16(&reply[2], peer_id_new); + putCommand(ConnectionCommand::createPeer(peer_id_new, reply)); + } + + // Create peer addition event + putEvent(ConnectionEvent::peerAdded(peer_id_new, sender)); + + // We're now talking to a valid peer_id + return peer_id_new; +} + +const std::string Connection::getDesc() +{ + MutexAutoLock _(m_info_mutex); + return std::string("con(")+ + itos(m_udpSocket.GetHandle())+"/"+itos(m_peer_id)+")"; +} + +void Connection::DisconnectPeer(session_t peer_id) +{ + putCommand(ConnectionCommand::disconnect_peer(peer_id)); +} + +void Connection::doResendOne(session_t peer_id) +{ + assert(peer_id != PEER_ID_INEXISTENT); + putCommand(ConnectionCommand::resend_one(peer_id)); +} + +void Connection::sendAck(session_t peer_id, u8 channelnum, u16 seqnum) +{ + assert(channelnum < CHANNEL_COUNT); // Pre-condition + + LOG(dout_con< ack(4); + writeU8(&ack[0], PACKET_TYPE_CONTROL); + writeU8(&ack[1], CONTROLTYPE_ACK); + writeU16(&ack[2], seqnum); + + putCommand(ConnectionCommand::ack(peer_id, channelnum, ack)); + m_sendThread->Trigger(); +} + +UDPPeer* Connection::createServerPeer(const Address &address) +{ + if (ConnectedToServer()) + throw ConnectionException("Already connected to a server"); + + UDPPeer *peer = new UDPPeer(PEER_ID_SERVER, address, this); + peer->SetFullyOpen(); + + { + MutexAutoLock lock(m_peers_mutex); + m_peers[peer->id] = peer; + m_peer_ids.push_back(peer->id); + } + + return peer; +} + +} // namespace diff --git a/src/network/mtp/impl.h b/src/network/mtp/impl.h new file mode 100644 index 000000000..0232ebc0d --- /dev/null +++ b/src/network/mtp/impl.h @@ -0,0 +1,325 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "network/connection.h" +#include "network/socket.h" +#include "constants.h" +#include "util/pointer.h" +#include "util/container.h" +#include "util/numeric.h" +#include "porting.h" +#include "network/networkprotocol.h" +#include +#include +#include + +namespace con +{ + +class ConnectionReceiveThread; +class ConnectionSendThread; + +class Peer; + +// FIXME: Peer refcounting should generally be replaced by std::shared_ptr +class PeerHelper +{ +public: + PeerHelper() = default; + inline PeerHelper(Peer *peer) { *this = peer; } + ~PeerHelper(); + + PeerHelper& operator=(Peer *peer); + inline Peer* operator->() const { return m_peer; } + inline Peer* operator&() const { return m_peer; } + + inline bool operator!() { return !m_peer; } + inline bool operator!=(std::nullptr_t) { return !!m_peer; } + +private: + Peer *m_peer = nullptr; +}; + +/* + Connection +*/ + +enum ConnectionEventType { + CONNEVENT_NONE, + CONNEVENT_DATA_RECEIVED, + CONNEVENT_PEER_ADDED, + CONNEVENT_PEER_REMOVED, + CONNEVENT_BIND_FAILED, +}; + +struct ConnectionEvent; +typedef std::shared_ptr ConnectionEventPtr; + +// This is very similar to ConnectionCommand +struct ConnectionEvent +{ + const ConnectionEventType type; + session_t peer_id = 0; + Buffer data; + bool timeout = false; + Address address; + + // We don't want to copy "data" + DISABLE_CLASS_COPY(ConnectionEvent); + + static ConnectionEventPtr create(ConnectionEventType type); + static ConnectionEventPtr dataReceived(session_t peer_id, const Buffer &data); + static ConnectionEventPtr peerAdded(session_t peer_id, Address address); + static ConnectionEventPtr peerRemoved(session_t peer_id, bool is_timeout, Address address); + static ConnectionEventPtr bindFailed(); + + const char *describe() const; + +private: + ConnectionEvent(ConnectionEventType type_) : + type(type_) {} +}; + +struct ConnectionCommand; +typedef std::shared_ptr ConnectionCommandPtr; + +struct BufferedPacket; +typedef std::shared_ptr BufferedPacketPtr; + +class Connection; +class PeerHandler; + +class Peer : public IPeer { + public: + friend class PeerHelper; + + virtual ~Peer() { + MutexAutoLock usage_lock(m_exclusive_access_mutex); + FATAL_ERROR_IF(m_usage != 0, "Reference counting failure"); + } + + void Drop(); + + virtual void PutReliableSendCommand(ConnectionCommandPtr &c, + unsigned int max_packet_size) {}; + + bool isPendingDeletion() const { + MutexAutoLock lock(m_exclusive_access_mutex); + return m_pending_deletion; + } + void ResetTimeout() { + MutexAutoLock lock(m_exclusive_access_mutex); + m_timeout_counter = 0; + } + + bool isHalfOpen() const { + MutexAutoLock lock(m_exclusive_access_mutex); + return m_half_open; + } + void SetFullyOpen() { + MutexAutoLock lock(m_exclusive_access_mutex); + m_half_open = false; + } + + virtual bool isTimedOut(float timeout, std::string &reason); + + unsigned int m_increment_packets_remaining = 0; + + virtual u16 getNextSplitSequenceNumber(u8 channel) { return 0; }; + virtual void setNextSplitSequenceNumber(u8 channel, u16 seqnum) {}; + virtual SharedBuffer addSplitPacket(u8 channel, BufferedPacketPtr &toadd, + bool reliable) + { + FATAL_ERROR("unimplemented in abstract class"); + } + + virtual bool Ping(float dtime, SharedBuffer& data) { return false; }; + + virtual float getStat(rtt_stat_type type) const { + switch (type) { + case MIN_RTT: + return m_rtt.min_rtt; + case MAX_RTT: + return m_rtt.max_rtt; + case AVG_RTT: + return m_rtt.avg_rtt; + case MIN_JITTER: + return m_rtt.jitter_min; + case MAX_JITTER: + return m_rtt.jitter_max; + case AVG_JITTER: + return m_rtt.jitter_avg; + } + return -1; + } + + protected: + Peer(session_t id, const Address &address, Connection *connection) : + IPeer(id), + m_connection(connection), + address(address), + m_last_timeout_check(porting::getTimeMs()) + { + } + + virtual void reportRTT(float rtt) {}; + + void RTTStatistics(float rtt, + const std::string &profiler_id = "", + unsigned int num_samples = 1000); + + bool IncUseCount(); + void DecUseCount(); + + mutable std::mutex m_exclusive_access_mutex; + + bool m_pending_deletion = false; + + Connection *m_connection; + + // Address of the peer + Address address; + + // Ping timer + float m_ping_timer = 0.0f; + + private: + struct rttstats { + float jitter_min = FLT_MAX; + float jitter_max = 0.0f; + float jitter_avg = -1.0f; + float min_rtt = FLT_MAX; + float max_rtt = 0.0f; + float avg_rtt = -1.0f; + }; + + rttstats m_rtt; + float m_last_rtt = -1.0f; + + /* + Until the peer has communicated with us using their assigned peer id + the connection is considered half-open. + During this time we inhibit re-sending any reliables or pings. This + is to avoid spending too many resources on a potential DoS attack + and to make sure Minetest servers are not useful for UDP amplificiation. + */ + bool m_half_open = true; + + // current usage count + unsigned int m_usage = 0; + + // Seconds from last receive + float m_timeout_counter = 0.0f; + + u64 m_last_timeout_check; +}; + +class UDPPeer; + +class Connection : public IConnection +{ +public: + friend class ConnectionSendThread; + friend class ConnectionReceiveThread; + + Connection(u32 max_packet_size, float timeout, bool ipv6, + PeerHandler *peerhandler); + ~Connection(); + + /* Interface */ + ConnectionEventPtr waitEvent(u32 timeout_ms); + + void putCommand(ConnectionCommandPtr c); + + void SetTimeoutMs(u32 timeout) { m_bc_receive_timeout = timeout; } + void Serve(Address bind_addr); + void Connect(Address address); + bool Connected(); + void Disconnect(); + bool ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms); + void Receive(NetworkPacket *pkt); + void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable); + session_t GetPeerID() const { return m_peer_id; } + Address GetPeerAddress(session_t peer_id); + float getPeerStat(session_t peer_id, rtt_stat_type type); + float getLocalStat(rate_stat_type type); + u32 GetProtocolID() const { return m_protocol_id; }; + const std::string getDesc(); + void DisconnectPeer(session_t peer_id); + +protected: + PeerHelper getPeerNoEx(session_t peer_id); + session_t lookupPeer(const Address& sender); + + session_t createPeer(const Address& sender, int fd); + UDPPeer* createServerPeer(const Address& sender); + bool deletePeer(session_t peer_id, bool timeout); + + void SetPeerID(session_t id) { m_peer_id = id; } + + void doResendOne(session_t peer_id); + + void sendAck(session_t peer_id, u8 channelnum, u16 seqnum); + + std::vector getPeerIDs() + { + MutexAutoLock peerlock(m_peers_mutex); + return m_peer_ids; + } + + u32 getActiveCount(); + + UDPSocket m_udpSocket; + // Command queue: user -> SendThread + MutexedQueue m_command_queue; + + void putEvent(ConnectionEventPtr e); + + void TriggerSend(); + + bool ConnectedToServer() + { + return getPeerNoEx(PEER_ID_SERVER) != nullptr; + } +private: + // Event queue: ReceiveThread -> user + MutexedQueue m_event_queue; + + session_t m_peer_id = 0; + u32 m_protocol_id; + + std::map m_peers; + std::vector m_peer_ids; + std::mutex m_peers_mutex; + + std::unique_ptr m_sendThread; + std::unique_ptr m_receiveThread; + + mutable std::mutex m_info_mutex; + + // Backwards compatibility + PeerHandler *m_bc_peerhandler; + u32 m_bc_receive_timeout = 0; + + bool m_shutting_down = false; +}; + +} // namespace diff --git a/src/network/connection_internal.h b/src/network/mtp/internal.h similarity index 95% rename from src/network/connection_internal.h rename to src/network/mtp/internal.h index a528f3fef..f6ab1f159 100644 --- a/src/network/connection_internal.h +++ b/src/network/mtp/internal.h @@ -19,11 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -/********************************************/ -/* may only be included from in src/network */ -/********************************************/ +#include "network/mtp/impl.h" -#include "connection.h" +// Constant that differentiates the protocol from random data and other protocols +#define PROTOCOL_ID 0x4f457403 #define MAX_UDP_PEERS 65535 @@ -161,6 +160,32 @@ inline float CALC_DTIME(u64 lasttime, u64 curtime) return MYMAX(MYMIN(value, 0.1f), 0.0f); } +/* Exceptions */ + +class NotFoundException : public BaseException +{ +public: + NotFoundException(const char *s) : BaseException(s) {} +}; + +class ProcessedSilentlyException : public BaseException +{ +public: + ProcessedSilentlyException(const char *s) : BaseException(s) {} +}; + +class ProcessedQueued : public BaseException +{ +public: + ProcessedQueued(const char *s) : BaseException(s) {} +}; + +class IncomingDataCorruption : public BaseException +{ +public: + IncomingDataCorruption(const char *s) : BaseException(s) {} +}; + /* Struct for all kinds of packets. Includes following data: diff --git a/src/network/connectionthreads.cpp b/src/network/mtp/threads.cpp similarity index 99% rename from src/network/connectionthreads.cpp rename to src/network/mtp/threads.cpp index d5c9a39ed..c7c728c9d 100644 --- a/src/network/connectionthreads.cpp +++ b/src/network/mtp/threads.cpp @@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "connectionthreads.h" +#include "network/mtp/threads.h" #include "log.h" #include "profiler.h" #include "settings.h" diff --git a/src/network/connectionthreads.h b/src/network/mtp/threads.h similarity index 99% rename from src/network/connectionthreads.h rename to src/network/mtp/threads.h index fff71f657..b41307fa6 100644 --- a/src/network/connectionthreads.h +++ b/src/network/mtp/threads.h @@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "threading/thread.h" -#include "connection_internal.h" +#include "network/mtp/internal.h" namespace con { diff --git a/src/network/networkexceptions.h b/src/network/networkexceptions.h index 58a3bb490..2e9c2a6e8 100644 --- a/src/network/networkexceptions.h +++ b/src/network/networkexceptions.h @@ -26,11 +26,6 @@ namespace con /* Exceptions */ -class NotFoundException : public BaseException -{ -public: - NotFoundException(const char *s) : BaseException(s) {} -}; class PeerNotFoundException : public BaseException { @@ -62,23 +57,6 @@ public: NoIncomingDataException(const char *s) : BaseException(s) {} }; -class ProcessedSilentlyException : public BaseException -{ -public: - ProcessedSilentlyException(const char *s) : BaseException(s) {} -}; - -class ProcessedQueued : public BaseException -{ -public: - ProcessedQueued(const char *s) : BaseException(s) {} -}; - -class IncomingDataCorruption : public BaseException -{ -public: - IncomingDataCorruption(const char *s) : BaseException(s) {} -}; } class SocketException : public BaseException diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index d84c735b8..743aac831 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -237,9 +237,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CLIENT_PROTOCOL_VERSION_MIN 37 #define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION -// Constant that differentiates the protocol from random data and other protocols -#define PROTOCOL_ID 0x4f457403 - #define PASSWORD_SIZE 28 // Maximum password length. Allows for // base64-encoded SHA-1 (27+\0). diff --git a/src/network/peerhandler.h b/src/network/peerhandler.h index da65483ef..1e00249ca 100644 --- a/src/network/peerhandler.h +++ b/src/network/peerhandler.h @@ -24,17 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., namespace con { -typedef enum -{ - MIN_RTT, - MAX_RTT, - AVG_RTT, - MIN_JITTER, - MAX_JITTER, - AVG_JITTER -} rtt_stat_type; - -class Peer; +class IPeer; class PeerHandler { @@ -47,13 +37,13 @@ public: This is called after the Peer has been inserted into the Connection's peer container. */ - virtual void peerAdded(Peer *peer) = 0; + virtual void peerAdded(IPeer *peer) = 0; /* This is called before the Peer has been removed from the Connection's peer container. */ - virtual void deletingPeer(Peer *peer, bool timeout) = 0; + virtual void deletingPeer(IPeer *peer, bool timeout) = 0; }; enum PeerChangeType : u8 diff --git a/src/server.cpp b/src/server.cpp index 1d98e4634..a78244e99 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -258,11 +258,7 @@ Server::Server( m_simple_singleplayer_mode(simple_singleplayer_mode), m_dedicated(dedicated), m_async_fatal_error(""), - m_con(std::make_shared(PROTOCOL_ID, - 512, - CONNECTION_TIMEOUT, - m_bind_addr.isIPv6(), - this)), + m_con(con::createMTP(CONNECTION_TIMEOUT, m_bind_addr.isIPv6(), this)), m_itemdef(createItemDefManager()), m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), @@ -1258,7 +1254,7 @@ void Server::onMapEditEvent(const MapEditEvent &event) m_unsent_map_edit_queue.push(new MapEditEvent(event)); } -void Server::peerAdded(con::Peer *peer) +void Server::peerAdded(con::IPeer *peer) { verbosestream<<"Server::peerAdded(): peer->id=" <id<id, false)); } -void Server::deletingPeer(con::Peer *peer, bool timeout) +void Server::deletingPeer(con::IPeer *peer, bool timeout) { verbosestream<<"Server::deletingPeer(): peer->id=" <id<<", timeout="< m_con; + std::shared_ptr m_con; // Ban checking BanManager *m_banmanager = nullptr; diff --git a/src/server/clientiface.cpp b/src/server/clientiface.cpp index e5c07b3d8..0e5140f00 100644 --- a/src/server/clientiface.cpp +++ b/src/server/clientiface.cpp @@ -648,13 +648,14 @@ void RemoteClient::setLangCode(const std::string &code) m_lang_code = string_sanitize_ascii(code, 12); } -ClientInterface::ClientInterface(const std::shared_ptr & con) +ClientInterface::ClientInterface(const std::shared_ptr &con) : m_con(con), m_env(nullptr) { } + ClientInterface::~ClientInterface() { /* diff --git a/src/server/clientiface.h b/src/server/clientiface.h index ac41b00ca..d4d21ceb2 100644 --- a/src/server/clientiface.h +++ b/src/server/clientiface.h @@ -168,7 +168,7 @@ class EmergeManager; */ namespace con { - class Connection; + class IConnection; } @@ -464,7 +464,7 @@ public: friend class Server; - ClientInterface(const std::shared_ptr &con); + ClientInterface(const std::shared_ptr &con); ~ClientInterface(); /* run sync step */ @@ -543,7 +543,7 @@ private: void UpdatePlayerList(); // Connection - std::shared_ptr m_con; + std::shared_ptr m_con; std::recursive_mutex m_clients_mutex; // Connected clients (behind the con mutex) RemoteClientMap m_clients; diff --git a/src/unittest/test_connection.cpp b/src/unittest/test_connection.cpp index 4adbc9039..3c0043389 100644 --- a/src/unittest/test_connection.cpp +++ b/src/unittest/test_connection.cpp @@ -23,7 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "settings.h" #include "util/serialize.h" -#include "network/connection_internal.h" +#include "network/peerhandler.h" +#include "network/mtp/internal.h" #include "network/networkpacket.h" #include "network/socket.h" @@ -59,7 +60,7 @@ struct Handler : public con::PeerHandler { Handler(const char *a_name) : name(a_name) {} - void peerAdded(con::Peer *peer) + void peerAdded(con::IPeer *peer) { infostream << "Handler(" << name << ")::peerAdded(): " "id=" << peer->id << std::endl; @@ -67,7 +68,7 @@ struct Handler : public con::PeerHandler count++; } - void deletingPeer(con::Peer *peer, bool timeout) + void deletingPeer(con::IPeer *peer, bool timeout) { infostream << "Handler(" << name << ")::deletingPeer(): " "id=" << peer->id << ", timeout=" << timeout << std::endl; @@ -165,8 +166,6 @@ void TestConnection::testConnectSendReceive() NOTE: This mostly tests the legacy interface. */ - u32 proto_id = 0xad26846a; - Handler hand_server("server"); Handler hand_client("client"); @@ -187,11 +186,11 @@ void TestConnection::testConnectSendReceive() } infostream << "** Creating server Connection" << std::endl; - con::Connection server(proto_id, 512, 5.0, false, &hand_server); + con::Connection server(512, 5.0f, false, &hand_server); server.Serve(address); infostream << "** Creating client Connection" << std::endl; - con::Connection client(proto_id, 512, 5.0, false, &hand_client); + con::Connection client(512, 5.0f, false, &hand_client); UASSERT(hand_server.count == 0); UASSERT(hand_client.count == 0); From 274c223d00519c731fea7de960aac9879273ad8a Mon Sep 17 00:00:00 2001 From: Zemtzov7 <72821250+zmv7@users.noreply.github.com> Date: Fri, 23 Aug 2024 02:15:55 +0500 Subject: [PATCH 23/75] Fix CSM help form using "/" instead of "." (#15034) when copying commands to chat --- builtin/common/information_formspecs.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/common/information_formspecs.lua b/builtin/common/information_formspecs.lua index 3fa397d25..270631fc9 100644 --- a/builtin/common/information_formspecs.lua +++ b/builtin/common/information_formspecs.lua @@ -69,7 +69,7 @@ local function build_chatcommands_formspec(name, sel, copy) description = cmds[2].description if copy then local msg = S("Command: @1 @2", - core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params) + core.colorize("#0FF", (INIT == "client" and "." or "/") .. cmds[1]), cmds[2].params) if INIT == "client" then core.display_chat_message(msg) else From 56123b2fbe1ad4e7878556f8d51c551d84fb92e7 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 23 Aug 2024 18:46:12 +0200 Subject: [PATCH 24/75] Fix bounding box of clouds fixes #15031 --- src/client/clouds.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/clouds.h b/src/client/clouds.h index d93fa9b43..e288c5e8a 100644 --- a/src/client/clouds.h +++ b/src/client/clouds.h @@ -133,8 +133,8 @@ private: { float height_bs = m_params.height * BS; float thickness_bs = m_params.thickness * BS; - m_box = aabb3f(-BS * 1000000.0f, height_bs - BS * m_camera_offset.Y, -BS * 1000000.0f, - BS * 1000000.0f, height_bs + thickness_bs - BS * m_camera_offset.Y, BS * 1000000.0f); + m_box = aabb3f(-BS * 1000000.0f, height_bs, -BS * 1000000.0f, + BS * 1000000.0f, height_bs + thickness_bs, BS * 1000000.0f); } void updateMesh(); From df8afe3dc418534111c0f2d7f0c67bf1caf6cda9 Mon Sep 17 00:00:00 2001 From: wsor4035 <24964441+wsor4035@users.noreply.github.com> Date: Mon, 26 Aug 2024 12:32:42 -0400 Subject: [PATCH 25/75] Reword CMake message for LuaJIT detection --- cmake/Modules/FindLua.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/Modules/FindLua.cmake b/cmake/Modules/FindLua.cmake index be5d92d8c..a239046ac 100644 --- a/cmake/Modules/FindLua.cmake +++ b/cmake/Modules/FindLua.cmake @@ -11,7 +11,7 @@ if(ENABLE_LUAJIT) find_package(LuaJIT) if(LUAJIT_FOUND) set(USE_LUAJIT TRUE) - message (STATUS "Using LuaJIT provided by system.") + message (STATUS "Using LuaJIT") elseif(REQUIRE_LUAJIT) message(FATAL_ERROR "LuaJIT not found whereas REQUIRE_LUAJIT=\"TRUE\" is used.\n" "To continue, either install LuaJIT or do not use REQUIRE_LUAJIT=\"TRUE\".") From da1fc9a5366b9ceace6d829783a9a56545616158 Mon Sep 17 00:00:00 2001 From: Desour Date: Sun, 25 Aug 2024 10:21:50 +0200 Subject: [PATCH 26/75] Meshgen: Don't get lights for not drawn solid faces `drawCuboid()` doesn't call the face lighter function for masked faces, so we don't need these values. This is for performance. --- src/client/content_mapblock.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index c351c4b80..1de36c9c5 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -463,6 +463,8 @@ void MapblockMeshGenerator::drawSolidNode() if (data->m_smooth_lighting) { LightPair lights[6][4]; for (int face = 0; face < 6; ++face) { + if (mask & (1 << face)) + continue; for (int k = 0; k < 4; k++) { v3s16 corner = light_dirs[light_indices[face][k]]; lights[face][k] = LightPair(getSmoothLightSolid( From 5583831c40d51e5f63540ccfcda71b678564c21b Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 24 Aug 2024 10:50:44 +0200 Subject: [PATCH 27/75] zstd: Fix minetest.decompress lockup when data ends too early --- src/serialization.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/serialization.cpp b/src/serialization.cpp index 4134126ca..0319b0159 100644 --- a/src/serialization.cpp +++ b/src/serialization.cpp @@ -262,6 +262,8 @@ void decompressZstd(std::istream &is, std::ostream &os) is.read(input_buffer, bufsize); input.size = is.gcount(); input.pos = 0; + if (input.size == 0) + throw SerializationError("decompressZstd: data ended too early"); } ret = ZSTD_decompressStream(stream.get(), &output, &input); From 21ed680b10aef84f58222a427aebffab48efa171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Mon, 26 Aug 2024 21:22:38 +0200 Subject: [PATCH 28/75] Make getting bone overrides return the "same" euler angles (#15007) --- games/devtest/mods/unittests/entity.lua | 20 ++++++++++++++++++++ src/activeobject.h | 3 +++ src/script/lua_api/l_object.cpp | 24 ++++++++++++++---------- 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/games/devtest/mods/unittests/entity.lua b/games/devtest/mods/unittests/entity.lua index 8e8bd1c48..af91a2a94 100644 --- a/games/devtest/mods/unittests/entity.lua +++ b/games/devtest/mods/unittests/entity.lua @@ -214,3 +214,23 @@ unittests.register("test_objects_in_area", function(_, pos) return core.objects_in_area(pos:offset(-1, -1, -1), pos:offset(1, 1, 1)) end) end, {map=true}) + +-- Tests that bone rotation euler angles are preserved (see #14992) +local function test_get_bone_rot(_, pos) + local obj = core.add_entity(pos, "unittests:dummy") + for _ = 1, 100 do + local function assert_similar(euler_angles) + local _, rot = obj:get_bone_position("bonename") + assert(euler_angles:distance(rot) < 1e-3) + local override = obj:get_bone_override("bonename") + assert(euler_angles:distance(override.rotation.vec:apply(math.deg)) < 1e-3) + end + local deg = 1e3 * vector.new(math.random(), math.random(), math.random()) + obj:set_bone_position("bonename", vector.zero(), deg) + assert_similar(deg) + local rad = 3 * math.pi * vector.new(math.random(), math.random(), math.random()) + obj:set_bone_override("bonename", {rotation = {vec = rad}}) + assert_similar(rad:apply(math.deg)) + end +end +unittests.register("test_get_bone_rot", test_get_bone_rot, {map=true}) diff --git a/src/activeobject.h b/src/activeobject.h index 989b48e91..cb868346a 100644 --- a/src/activeobject.h +++ b/src/activeobject.h @@ -96,6 +96,9 @@ struct BoneOverride { core::quaternion previous; core::quaternion next; + // Redundantly store the euler angles serverside + // so that we can return them in the appropriate getters + v3f next_radians; bool absolute = false; f32 interp_timer = 0; } rotation; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index eb0a375d4..db7212897 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -562,8 +562,10 @@ int ObjectRef::l_set_bone_position(lua_State *L) BoneOverride props; if (!lua_isnoneornil(L, 3)) props.position.vector = check_v3f(L, 3); - if (!lua_isnoneornil(L, 4)) - props.rotation.next = core::quaternion(check_v3f(L, 4) * core::DEGTORAD); + if (!lua_isnoneornil(L, 4)) { + props.rotation.next_radians = check_v3f(L, 4) * core::DEGTORAD; + props.rotation.next = core::quaternion(props.rotation.next_radians); + } props.position.absolute = true; props.rotation.absolute = true; sao->setBoneOverride(bone, props); @@ -585,9 +587,9 @@ int ObjectRef::l_get_bone_position(lua_State *L) std::string bone = readParam(L, 2, ""); BoneOverride props = sao->getBoneOverride(bone); push_v3f(L, props.position.vector); - v3f euler_rot; - props.rotation.next.toEuler(euler_rot); - push_v3f(L, euler_rot * core::RADTODEG); + // In order to give modders back the euler angles they passed in, + // this **must not** compute equivalent euler angles from the quaternion + push_v3f(L, props.rotation.next_radians * core::RADTODEG); return 2; } @@ -633,8 +635,10 @@ int ObjectRef::l_set_bone_override(lua_State *L) lua_getfield(L, 3, "rotation"); if (!lua_isnil(L, -1)) { lua_getfield(L, -1, "vec"); - if (!lua_isnil(L, -1)) - props.rotation.next = core::quaternion(check_v3f(L, -1)); + if (!lua_isnil(L, -1)) { + props.rotation.next_radians = check_v3f(L, -1); + props.rotation.next = core::quaternion(props.rotation.next_radians); + } lua_pop(L, 1); read_prop_attrs(props.rotation); @@ -672,9 +676,9 @@ static void push_bone_override(lua_State *L, const BoneOverride &props) push_prop("position", props.position, props.position.vector); - v3f euler_rot; - props.rotation.next.toEuler(euler_rot); - push_prop("rotation", props.rotation, euler_rot); + // In order to give modders back the euler angles they passed in, + // this **must not** compute equivalent euler angles from the quaternion + push_prop("rotation", props.rotation, props.rotation.next_radians); push_prop("scale", props.scale, props.scale.vector); From 8109563a02cbe4c6c3bff224d808e1061f3e4386 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 26 Aug 2024 21:23:12 +0200 Subject: [PATCH 29/75] LocalPlayer: Restore 2u height sneak jump (#15015) Fix 1: Do not consider LocalPlayer's CAO in the collision data. Fix 2: work around the "aabbox3d::intersectsWithBox" edge-case. --- src/client/localplayer.cpp | 8 ++++---- src/collision.cpp | 16 +++++++++------- src/collision.h | 9 +++++++-- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/client/localplayer.cpp b/src/client/localplayer.cpp index aa335e90e..75f755578 100644 --- a/src/client/localplayer.cpp +++ b/src/client/localplayer.cpp @@ -360,7 +360,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, collisionMoveResult result = collisionMoveSimple(env, m_client, pos_max_d, m_collisionbox, player_stepheight, dtime, - &position, &m_speed, accel_f); + &position, &m_speed, accel_f, m_cao); bool could_sneak = control.sneak && !free_move && !in_liquid && !is_climbing && physics_override.sneak; @@ -444,7 +444,7 @@ void LocalPlayer::move(f32 dtime, Environment *env, f32 pos_max_d, v3f check_pos = position; check_pos.Y += y_diff * dtime * 22.0f + BS * 0.01f; if (y_diff < BS * 0.6f || (physics_override.sneak_glitch - && !collision_check_intersection(env, m_client, m_collisionbox, check_pos))) { + && !collision_check_intersection(env, m_client, m_collisionbox, check_pos, m_cao))) { // Smoothen the movement (based on 'position.Y = bmax.Y') position.Y = std::min(check_pos.Y, bmax.Y); m_speed.Y = 0.0f; @@ -990,7 +990,7 @@ void LocalPlayer::old_move(f32 dtime, Environment *env, f32 pos_max_d, collisionMoveResult result = collisionMoveSimple(env, m_client, pos_max_d, m_collisionbox, player_stepheight, dtime, - &position, &m_speed, accel_f); + &position, &m_speed, accel_f, m_cao); // Position was slightly changed; update standing node pos if (touching_ground) @@ -1254,7 +1254,7 @@ void LocalPlayer::handleAutojump(f32 dtime, Environment *env, // try at peak of jump, zero step height collisionMoveResult jump_result = collisionMoveSimple(env, m_client, pos_max_d, - m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f)); + m_collisionbox, 0.0f, dtime, &jump_pos, &jump_speed, v3f(0.0f), m_cao); // see if we can get a little bit farther horizontally if we had // jumped diff --git a/src/collision.cpp b/src/collision.cpp index d804f7f0d..f554dac80 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -273,7 +273,7 @@ static void add_object_boxes(Environment *env, const v3f pos_f, const v3f speed_f, ActiveObject *self, std::vector &cinfo) { - auto process_object = [&] (ActiveObject *object) { + auto process_object = [&cinfo] (ActiveObject *object) { if (object && object->collideWithObjects()) { aabb3f box; if (object->getCollisionBox(&box)) @@ -292,7 +292,7 @@ static void add_object_boxes(Environment *env, c_env->getActiveObjects(pos_f, distance, clientobjects); for (auto &clientobject : clientobjects) { - // Do collide with everything but itself and the parent CAO + // Do collide with everything but itself and children if (!self || (self != clientobject.obj && self != clientobject.obj->getParent())) { process_object(clientobject.obj); @@ -301,12 +301,12 @@ static void add_object_boxes(Environment *env, // add collision with local player LocalPlayer *lplayer = c_env->getLocalPlayer(); - if (lplayer->getParent() == nullptr) { + auto *obj = (ClientActiveObject*) lplayer->getCAO(); + if (!self || (self != obj && self != obj->getParent())) { aabb3f lplayer_collisionbox = lplayer->getCollisionbox(); v3f lplayer_pos = lplayer->getPosition(); lplayer_collisionbox.MinEdge += lplayer_pos; lplayer_collisionbox.MaxEdge += lplayer_pos; - auto *obj = (ActiveObject*) lplayer->getCAO(); cinfo.emplace_back(obj, 0, lplayer_collisionbox); } } @@ -315,7 +315,7 @@ static void add_object_boxes(Environment *env, { ServerEnvironment *s_env = dynamic_cast(env); if (s_env) { - // search for objects which are not us, or we are not its parent. + // search for objects which are not us and not our children. // we directly process the object in this callback to avoid useless // looping afterwards. auto include_obj_cb = [self, &process_object] (ServerActiveObject *obj) { @@ -623,8 +623,10 @@ bool collision_check_intersection(Environment *env, IGameDef *gamedef, Collision detection */ aabb3f checkbox = box_0; - checkbox.MinEdge += pos_f; - checkbox.MaxEdge += pos_f; + // aabbox3d::intersectsWithBox(box) returns true when the faces are touching perfectly. + // However, we do not want want a true-ish return value in that case. Add some tolerance. + checkbox.MinEdge += pos_f + (0.1f * BS); + checkbox.MaxEdge += pos_f - (0.1f * BS); /* Go through every node and object box diff --git a/src/collision.h b/src/collision.h index b29a222c3..a698e6328 100644 --- a/src/collision.h +++ b/src/collision.h @@ -65,7 +65,8 @@ struct collisionMoveResult std::vector collisions; }; -// Moves using a single iteration; speed should not exceed pos_max_d/dtime +/// @brief Moves using a single iteration; speed should not exceed pos_max_d/dtime +/// @param self (optional) ActiveObject to ignore in the collision detection. collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef, f32 pos_max_d, const aabb3f &box_0, f32 stepheight, f32 dtime, @@ -73,7 +74,11 @@ collisionMoveResult collisionMoveSimple(Environment *env,IGameDef *gamedef, v3f accel_f, ActiveObject *self=NULL, bool collide_with_objects=true); -// check if box is in collision on actual position +/// @brief A simpler version of "collisionMoveSimple" that only checks whether +/// a collision occurs at the given position. +/// @param self (optional) ActiveObject to ignore in the collision detection. +/// @returns `true` when `box_0` truly intersects with a node or object. +/// Touching faces are not counted as intersection. bool collision_check_intersection(Environment *env, IGameDef *gamedef, const aabb3f &box_0, const v3f &pos_f, ActiveObject *self = nullptr, bool collide_with_objects = true); From 5d18b6fcd0960fa495164bd8ac91b39e2d3b4b2e Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Tue, 27 Aug 2024 16:33:50 +0200 Subject: [PATCH 30/75] Fix incorrect documentation of new-style particlespawner size property --- 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 a43f517a0..ae046dd3a 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -10999,7 +10999,7 @@ Types used are defined in the previous section. * vec3 range `acc`: the direction and speed with which the particle accelerates -* vec3 range `size`: scales the visual size of the particle texture. +* float range `size`: scales the visual size of the particle texture. if `node` is set, this can be set to 0 to spawn randomly-sized particles (just like actual node dig particles). From c893e0b72b718a851dde39ef1e5c5a1cb0b1c780 Mon Sep 17 00:00:00 2001 From: JosiahWI <41302989+JosiahWI@users.noreply.github.com> Date: Wed, 28 Aug 2024 08:36:02 -0500 Subject: [PATCH 31/75] Convert nodedef tests to Catch2 (#15045) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com> --- src/unittest/test_nodedef.cpp | 36 +++++++++-------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/src/unittest/test_nodedef.cpp b/src/unittest/test_nodedef.cpp index acf669783..85dadd17d 100644 --- a/src/unittest/test_nodedef.cpp +++ b/src/unittest/test_nodedef.cpp @@ -17,38 +17,22 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "test.h" - -#include - #include "gamedef.h" #include "nodedef.h" #include "network/networkprotocol.h" -class TestNodeDef : public TestBase -{ -public: - TestNodeDef() { TestManager::registerTestModule(this); } - const char *getName() { return "TestNodeDef"; } +#include - void runTests(IGameDef *gamedef); +#include +#include - void testContentFeaturesSerialization(); -}; -static TestNodeDef g_test_instance; - -void TestNodeDef::runTests(IGameDef *gamedef) -{ - TEST(testContentFeaturesSerialization); -} - -//////////////////////////////////////////////////////////////////////////////// - -void TestNodeDef::testContentFeaturesSerialization() +TEST_CASE("Given a node definition, " + "when we serialize and then deserialize it, " + "then the deserialized one should be equal to the original.", + "[nodedef]") { ContentFeatures f; - f.name = "default:stone"; for (TileDef &tiledef : f.tiledef) tiledef.name = "default_stone.png"; @@ -56,12 +40,10 @@ void TestNodeDef::testContentFeaturesSerialization() std::ostringstream os(std::ios::binary); f.serialize(os, LATEST_PROTOCOL_VERSION); - // verbosestream<<"Test ContentFeatures size: "< Date: Wed, 28 Aug 2024 15:37:43 +0200 Subject: [PATCH 32/75] Menu docs: clarify that image paths must be escaped to correctly render on Windows (#15072) --- doc/menu_lua_api.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index 8f945052c..df14b859d 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -8,6 +8,15 @@ The main menu is defined as a formspec by Lua in `builtin/mainmenu/` Description of formspec language to show your menu is in `lua_api.md` +Images and 3D models +------ + +Directory delimiters change according to the OS (e.g. on Unix-like systems +is `/`, on Windows is `\`). When putting an image or a 3D model inside a formspec, +be sure to sanitize it first with `core.formspec_escape(img)`; otherwise, +any resource located in a subpath won't be displayed on OSs using `\` as delimiter. + + Callbacks --------- @@ -62,6 +71,12 @@ Functions Filesystem ---------- +To access specific subpaths, use `DIR_DELIM` as a directory delimiter instead +of manually putting one, as different OSs use different delimiters. E.g. +```lua +"my" .. DIR_DELIM .. "custom" .. DIR_DELIM .. "path" -- and not my/custom/path +``` + * `core.get_builtin_path()` * returns path to builtin root * `core.create_dir(absolute_path)` (possible in async calls) From 04f0a4a1c6e82d2a649790f9958a1b54426cd45e Mon Sep 17 00:00:00 2001 From: Desour Date: Sat, 24 Aug 2024 13:29:45 +0200 Subject: [PATCH 33/75] Fix MeshGrid::isMeshPos() `(1 + 1 + 0) % 2 = 0`, for example, so it had false positives. Only minimap generation uses this function. It did useless work. --- src/util/numeric.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/numeric.h b/src/util/numeric.h index 73b4fb5cd..cfe0317a1 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -179,7 +179,9 @@ struct MeshGrid { /// @brief Returns true if p is an origin of a cell in the grid. bool isMeshPos(v3s16 &p) const { - return ((p.X + p.Y + p.Z) % cell_size) == 0; + return p.X % cell_size == 0 + && p.Y % cell_size == 0 + && p.Z % cell_size == 0; } /// @brief Returns index of the given offset in a grid cell From 2e883189c102b07ad5b051878949ec77ef530bf0 Mon Sep 17 00:00:00 2001 From: Desour Date: Sat, 24 Aug 2024 13:32:39 +0200 Subject: [PATCH 34/75] Improve block bounds HUD feature * Use different material than selection box, so it doesn't break for non-default `node_highlighting` values. * Add `show_block_bounds_radius_near` setting. * Draw mesh chunk edges in a different color (red vs yellow). --- builtin/settingtypes.txt | 9 ++++-- src/client/hud.cpp | 68 +++++++++++++++++++++++++++++++--------- src/client/hud.h | 1 + src/defaultsettings.cpp | 1 + 4 files changed, 61 insertions(+), 18 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 8828fc1df..229d920d5 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -735,6 +735,12 @@ hud_scaling (HUD scaling) float 1.0 0.5 20 # Mods may still set a background. show_nametag_backgrounds (Show name tag backgrounds by default) bool true +# Whether to show the client debug info (has the same effect as hitting F5). +show_debug (Show debug info) bool false + +# Radius to use when the block bounds HUD feature is set to near blocks. +show_block_bounds_radius_near (Block bounds HUD radius) int 4 0 1000 + [**Chat] # Maximum number of recent chat messages to show @@ -2009,9 +2015,6 @@ client_unload_unused_data_timeout (Mapblock unload timeout) float 600.0 0.0 # Set to -1 for unlimited amount. client_mapblock_limit (Mapblock limit) int 7500 -1 2147483647 -# Whether to show the client debug info (has the same effect as hitting F5). -show_debug (Show debug info) bool false - # Maximum number of blocks that are simultaneously sent per client. # The maximum total count is calculated dynamically: # max_total = ceil((#clients + max_users) * per_client / 4) diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 962818b8f..3f9672f3d 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -97,6 +97,8 @@ Hud::Hud(Client *client, LocalPlayer *player, m_mode = HIGHLIGHT_BOX; } + // Initialize m_selection_material + m_selection_material.Lighting = false; if (g_settings->getBool("enable_shaders")) { @@ -118,6 +120,18 @@ Hud::Hud(Client *client, LocalPlayer *player, m_selection_material.MaterialType = video::EMT_SOLID; } + // Initialize m_block_bounds_material + m_block_bounds_material.Lighting = false; + if (g_settings->getBool("enable_shaders")) { + IShaderSource *shdrsrc = client->getShaderSource(); + auto shader_id = shdrsrc->getShader("default_shader", TILE_MATERIAL_ALPHA); + m_block_bounds_material.MaterialType = shdrsrc->getShaderInfo(shader_id).material; + } else { + m_block_bounds_material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + } + m_block_bounds_material.Thickness = + rangelim(g_settings->getS16("selectionbox_width"), 1, 5); + // Prepare mesh for compass drawing auto &b = m_rotation_mesh_buffer; b.Vertices.resize(4); @@ -934,33 +948,57 @@ void Hud::drawBlockBounds() } video::SMaterial old_material = driver->getMaterial2D(); - driver->setMaterial(m_selection_material); + driver->setMaterial(m_block_bounds_material); + + u16 mesh_chunk_size = std::max(1, g_settings->getU16("client_mesh_chunk")); v3s16 pos = player->getStandingNodePos(); - - v3s16 blockPos( + v3s16 block_pos( floorf((float) pos.X / MAP_BLOCKSIZE), floorf((float) pos.Y / MAP_BLOCKSIZE), floorf((float) pos.Z / MAP_BLOCKSIZE) ); - v3f offset = intToFloat(client->getCamera()->getOffset(), BS); + v3f cam_offset = intToFloat(client->getCamera()->getOffset(), BS); - s8 radius = m_block_bounds_mode == BLOCK_BOUNDS_NEAR ? 2 : 0; + v3f half_node = v3f(BS, BS, BS) / 2.0f; + v3f base_corner = intToFloat(block_pos * MAP_BLOCKSIZE, BS) - cam_offset - half_node; - v3f halfNode = v3f(BS, BS, BS) / 2.0f; + s16 radius = m_block_bounds_mode == BLOCK_BOUNDS_NEAR ? + rangelim(g_settings->getU16("show_block_bounds_radius_near"), 0, 1000) : 0; - for (s8 x = -radius; x <= radius; x++) - for (s8 y = -radius; y <= radius; y++) - for (s8 z = -radius; z <= radius; z++) { - v3s16 blockOffset(x, y, z); + for (s16 x = -radius; x <= radius + 1; x++) + for (s16 y = -radius; y <= radius + 1; y++) { + // Red for mesh chunk edges, yellow for other block edges. + auto choose_color = [&](s16 x_base, s16 y_base) { + // See also MeshGrid::isMeshPos(). + // If the block is mesh pos, it means it's at the (-,-,-) corner of + // the mesh. And we're drawing a (-,-) edge of this block. Hence, + // it is an edge of the mesh grid. + return (x + x_base) % mesh_chunk_size == 0 + && (y + y_base) % mesh_chunk_size == 0 ? + video::SColor(255, 255, 0, 0) : + video::SColor(255, 255, 255, 0); + }; - aabb3f box( - intToFloat((blockPos + blockOffset) * MAP_BLOCKSIZE, BS) - offset - halfNode, - intToFloat(((blockPos + blockOffset) * MAP_BLOCKSIZE) + (MAP_BLOCKSIZE - 1), BS) - offset + halfNode + v3f pmin = v3f(x, y, -radius) * MAP_BLOCKSIZE * BS; + v3f pmax = v3f(x, y, 1 + radius) * MAP_BLOCKSIZE * BS; + + driver->draw3DLine( + base_corner + v3f(pmin.X, pmin.Y, pmin.Z), + base_corner + v3f(pmax.X, pmax.Y, pmax.Z), + choose_color(block_pos.X, block_pos.Y) + ); + driver->draw3DLine( + base_corner + v3f(pmin.X, pmin.Z, pmin.Y), + base_corner + v3f(pmax.X, pmax.Z, pmax.Y), + choose_color(block_pos.X, block_pos.Z) + ); + driver->draw3DLine( + base_corner + v3f(pmin.Z, pmin.X, pmin.Y), + base_corner + v3f(pmax.Z, pmax.X, pmax.Y), + choose_color(block_pos.Y, block_pos.Z) ); - - driver->draw3DBox(box, video::SColor(255, 255, 0, 0)); } driver->setMaterial(old_material); diff --git a/src/client/hud.h b/src/client/hud.h index b2b5dd09c..3a9d27022 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -137,6 +137,7 @@ private: v3f m_selected_face_normal; video::SMaterial m_selection_material; + video::SMaterial m_block_bounds_material; scene::SMeshBuffer m_rotation_mesh_buffer; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 7d3273271..ccfb12971 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -302,6 +302,7 @@ void set_default_settings() settings->setDefault("enable_particles", "true"); settings->setDefault("arm_inertia", "true"); settings->setDefault("show_nametag_backgrounds", "true"); + settings->setDefault("show_block_bounds_radius_near", "4"); settings->setDefault("transparency_sorting_distance", "16"); settings->setDefault("enable_minimap", "true"); From 0f7ee126ded7164cba688d59348e8cc82b33d512 Mon Sep 17 00:00:00 2001 From: Desour Date: Sat, 24 Aug 2024 11:31:36 +0200 Subject: [PATCH 35/75] Fix transparency sorting and animation faraway check not using mesh chunk bounding sphere --- src/client/clientmap.cpp | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 45995c0ea..4c02879cc 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -767,15 +767,12 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) if (is_frustum_culled(mesh_sphere_center, mesh_sphere_radius)) continue; - v3f block_pos_r = intToFloat(block->getPosRelative() + MAP_BLOCKSIZE / 2, BS); - - float d = camera_position.getDistanceFrom(block_pos_r); - d = MYMAX(0,d - BLOCK_MAX_RADIUS); - // Mesh animation if (pass == scene::ESNRP_SOLID) { - // Pretty random but this should work somewhat nicely - bool faraway = d >= BS * 50; + // 50 nodes is pretty arbitrary but it should work somewhat nicely + float distance_sq = camera_position.getDistanceFromSQ(mesh_sphere_center); + bool faraway = distance_sq >= std::pow(BS * 50 + mesh_sphere_radius, 2.0f); + if (block_mesh->isAnimationForced() || !faraway || mesh_animate_count < (m_control.range_all ? 200 : 50)) { @@ -1305,27 +1302,29 @@ void ClientMap::updateTransparentMeshBuffers() ScopeProfiler sp(g_profiler, "CM::updateTransparentMeshBuffers", SPT_AVG); u32 sorted_blocks = 0; u32 unsorted_blocks = 0; - f32 sorting_distance_sq = std::pow(m_cache_transparency_sorting_distance * BS, 2.0f); + f32 sorting_distance = m_cache_transparency_sorting_distance * BS; // Update the order of transparent mesh buffers in each mesh for (auto it = m_drawlist.begin(); it != m_drawlist.end(); it++) { - MapBlock* block = it->second; - if (!block->mesh) + MapBlock *block = it->second; + MapBlockMesh *blockmesh = block->mesh; + if (!blockmesh) continue; if (m_needs_update_transparent_meshes || - block->mesh->getTransparentBuffers().size() == 0) { + blockmesh->getTransparentBuffers().size() == 0) { - v3s16 block_pos = block->getPos(); - v3f block_pos_f = intToFloat(block_pos * MAP_BLOCKSIZE + MAP_BLOCKSIZE / 2, BS); - f32 distance = m_camera_position.getDistanceFromSQ(block_pos_f); - if (distance <= sorting_distance_sq) { - block->mesh->updateTransparentBuffers(m_camera_position, block_pos); + v3f mesh_sphere_center = intToFloat(block->getPosRelative(), BS) + + blockmesh->getBoundingSphereCenter(); + f32 mesh_sphere_radius = blockmesh->getBoundingRadius(); + f32 distance_sq = m_camera_position.getDistanceFromSQ(mesh_sphere_center); + + if (distance_sq <= std::pow(sorting_distance + mesh_sphere_radius, 2.0f)) { + blockmesh->updateTransparentBuffers(m_camera_position, block->getPos()); ++sorted_blocks; - } - else { - block->mesh->consolidateTransparentBuffers(); + } else { + blockmesh->consolidateTransparentBuffers(); ++unsorted_blocks; } } From c52a4369eb89b2e0ecae02d021b55447245485aa Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 26 Aug 2024 23:51:01 +0200 Subject: [PATCH 36/75] Fix vertex count accounting in ClientMap --- src/client/clientmap.cpp | 23 ++++++++++------------- src/client/clientmap.h | 5 +++-- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 4c02879cc..eb5076e7f 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -841,10 +841,8 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) drawcall_count += draw_order.size(); for (auto &descriptor : draw_order) { - scene::IMeshBuffer *buf = descriptor.getBuffer(); - if (!descriptor.m_reuse_material) { - auto &material = buf->getMaterial(); + auto &material = descriptor.getMaterial(); // Apply filter settings material.forEachTexture([this] (auto &tex) { @@ -876,8 +874,7 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) m.setTranslation(block_wpos - offset); driver->setTransform(video::ETS_WORLD, m); - descriptor.draw(driver); - vertex_count += buf->getIndexCount(); + vertex_count += descriptor.draw(driver); } g_profiler->avg(prefix + "draw meshes [ms]", draw.stop(true)); @@ -1195,11 +1192,9 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, drawcall_count += draw_order.size(); for (auto &descriptor : draw_order) { - scene::IMeshBuffer *buf = descriptor.getBuffer(); - if (!descriptor.m_reuse_material) { // override some material properties - video::SMaterial local_material = buf->getMaterial(); + video::SMaterial local_material = descriptor.getMaterial(); local_material.MaterialType = material.MaterialType; // do not override culling if the original material renders both back // and front faces in solid mode (e.g. plantlike) @@ -1219,8 +1214,7 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, m.setTranslation(block_wpos - offset); driver->setTransform(video::ETS_WORLD, m); - descriptor.draw(driver); - vertex_count += buf->getIndexCount(); + vertex_count += descriptor.draw(driver); } // restore the driver material state @@ -1335,19 +1329,22 @@ void ClientMap::updateTransparentMeshBuffers() m_needs_update_transparent_meshes = false; } -scene::IMeshBuffer* ClientMap::DrawDescriptor::getBuffer() +video::SMaterial &ClientMap::DrawDescriptor::getMaterial() { - return m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer; + return (m_use_partial_buffer ? m_partial_buffer->getBuffer() : m_buffer)->getMaterial(); } -void ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver) +u32 ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver) { if (m_use_partial_buffer) { m_partial_buffer->beforeDraw(); driver->drawMeshBuffer(m_partial_buffer->getBuffer()); + auto count = m_partial_buffer->getBuffer()->getVertexCount(); m_partial_buffer->afterDraw(); + return count; } else { driver->drawMeshBuffer(m_buffer); + return m_buffer->getVertexCount(); } } diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 05c33d67c..6c424069a 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -162,8 +162,9 @@ private: m_pos(pos), m_partial_buffer(buffer), m_reuse_material(false), m_use_partial_buffer(true) {} - scene::IMeshBuffer* getBuffer(); - void draw(video::IVideoDriver* driver); + video::SMaterial &getMaterial(); + /// @return index count + u32 draw(video::IVideoDriver* driver); }; Client *m_client; From c00fed20b7e770b2ad360f8a4a3db31ce7b466f8 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 27 Aug 2024 09:14:31 +0200 Subject: [PATCH 37/75] Fix re-loading of settings in ClientMap --- src/client/clientmap.cpp | 55 ++++++++++++++++++++++------------------ src/client/clientmap.h | 2 +- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index eb5076e7f..cdad3146c 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -73,11 +73,23 @@ namespace { }; } +/* + ClientMap +*/ + static void on_settings_changed(const std::string &name, void *data) { - static_cast(data)->onSettingChanged(name); + static_cast(data)->onSettingChanged(name, false); } -// ClientMap + +static const std::string ClientMap_settings[] = { + "trilinear_filter", + "bilinear_filter", + "anisotropic_filter", + "transparency_sorting_distance", + "occlusion_culler", + "enable_raytraced_culling", +}; ClientMap::ClientMap( Client *client, @@ -102,37 +114,32 @@ ClientMap::ClientMap( m_box = aabb3f(-BS*1000000,-BS*1000000,-BS*1000000, BS*1000000,BS*1000000,BS*1000000); - /* TODO: Add a callback function so these can be updated when a setting - * changes. At this point in time it doesn't matter (e.g. /set - * is documented to change server settings only) - * - * TODO: Local caching of settings is not optimal and should at some stage - * be updated to use a global settings object for getting thse values - * (as opposed to the this local caching). This can be addressed in - * a later release. - */ - m_cache_trilinear_filter = g_settings->getBool("trilinear_filter"); - m_cache_bilinear_filter = g_settings->getBool("bilinear_filter"); - m_cache_anistropic_filter = g_settings->getBool("anisotropic_filter"); - m_cache_transparency_sorting_distance = g_settings->getU16("transparency_sorting_distance"); - m_loops_occlusion_culler = g_settings->get("occlusion_culler") == "loops"; - g_settings->registerChangedCallback("occlusion_culler", on_settings_changed, this); - m_enable_raytraced_culling = g_settings->getBool("enable_raytraced_culling"); - g_settings->registerChangedCallback("enable_raytraced_culling", on_settings_changed, this); + for (const auto &name : ClientMap_settings) + g_settings->registerChangedCallback(name, on_settings_changed, this); + // load all settings at once + onSettingChanged("", true); } -void ClientMap::onSettingChanged(const std::string &name) +void ClientMap::onSettingChanged(std::string_view name, bool all) { - if (name == "occlusion_culler") + if (all || name == "trilinear_filter") + m_cache_trilinear_filter = g_settings->getBool("trilinear_filter"); + if (all || name == "bilinear_filter") + 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_distance") + m_cache_transparency_sorting_distance = g_settings->getU16("transparency_sorting_distance"); + if (all || name == "occlusion_culler") m_loops_occlusion_culler = g_settings->get("occlusion_culler") == "loops"; - if (name == "enable_raytraced_culling") + if (all || name == "enable_raytraced_culling") m_enable_raytraced_culling = g_settings->getBool("enable_raytraced_culling"); } ClientMap::~ClientMap() { - g_settings->deregisterChangedCallback("occlusion_culler", on_settings_changed, this); - g_settings->deregisterChangedCallback("enable_raytraced_culling", on_settings_changed, this); + for (const auto &name : ClientMap_settings) + g_settings->deregisterChangedCallback(name, on_settings_changed, this); } void ClientMap::updateCamera(v3f pos, v3f dir, f32 fov, v3s16 offset, video::SColor light_color) diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 6c424069a..7456902c8 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -112,7 +112,7 @@ public: f32 getWantedRange() const { return m_control.wanted_range; } f32 getCameraFov() const { return m_camera_fov; } - void onSettingChanged(const std::string &name); + void onSettingChanged(std::string_view name, bool all); protected: // use drop() instead From 39970fed38ef2f7353a4cd2ba9dc1e4c6a9baa22 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 27 Aug 2024 10:03:13 +0200 Subject: [PATCH 38/75] Consolidate transparent buffers lazily --- src/client/mapblock_mesh.cpp | 5 +++++ src/client/mapblock_mesh.h | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 9b5612148..04c50ad99 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -906,6 +906,7 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) m_bsp_tree.traverse(rel_camera_pos, triangle_refs); // arrange index sequences into partial buffers + m_transparent_buffers_consolidated = false; m_transparent_buffers.clear(); scene::SMeshBuffer *current_buffer = nullptr; @@ -930,6 +931,8 @@ void MapBlockMesh::updateTransparentBuffers(v3f camera_pos, v3s16 block_pos) void MapBlockMesh::consolidateTransparentBuffers() { + if (m_transparent_buffers_consolidated) + return; m_transparent_buffers.clear(); scene::SMeshBuffer *current_buffer = nullptr; @@ -952,6 +955,8 @@ void MapBlockMesh::consolidateTransparentBuffers() if (!current_strain.empty()) { this->m_transparent_buffers.emplace_back(current_buffer, std::move(current_strain)); } + + m_transparent_buffers_consolidated = true; } video::SColor encode_light(u16 light, u8 emissive_light) diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 7cd368762..6ddd988aa 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -282,6 +282,8 @@ private: MapBlockBspTree m_bsp_tree; // Ordered list of references to parts of transparent buffers to draw std::vector m_transparent_buffers; + // Is m_transparent_buffers currently in consolidated form? + bool m_transparent_buffers_consolidated = false; }; /*! From fa4529b4f166a8ca160520c8867320fad7b5357b Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 27 Aug 2024 12:09:08 +0200 Subject: [PATCH 39/75] Keep stats on hw buffer uploads --- irr/include/IVideoDriver.h | 23 +++++++++++------------ irr/src/CFPSCounter.cpp | 35 ++--------------------------------- irr/src/CFPSCounter.h | 18 ++---------------- irr/src/CNullDriver.cpp | 17 +++++++---------- irr/src/CNullDriver.h | 14 ++++++++------ irr/src/COpenGLDriver.cpp | 4 ++++ irr/src/OpenGL/Driver.cpp | 25 ++++++++++++++----------- src/client/game.cpp | 5 +++++ 8 files changed, 53 insertions(+), 88 deletions(-) diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index 888e4a9f7..7b15fdd1b 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -114,6 +114,15 @@ const c8 *const FogTypeNames[] = { 0, }; +struct SFrameStats { + //! Count of primitives drawn + u32 PrimitivesDrawn = 0; + //! Number of hardware buffers uploaded (new or updated) + u32 HWBuffersUploaded = 0; + //! Sum of uploaded hardware buffer size + u32 HWBuffersUploadedSize = 0; +}; + //! Interface to driver which is able to perform 2d and 3d graphics functions. /** This interface is one of the most important interfaces of the Irrlicht Engine: All rendering and texture manipulation is done with @@ -194,12 +203,6 @@ public: */ virtual const io::IAttributes &getDriverAttributes() const = 0; - //! Check if the driver was recently reset. - /** For d3d devices you will need to recreate the RTTs if the - driver was reset. Should be queried right after beginScene(). - */ - virtual bool checkDriverReset() = 0; - //! Sets transformation matrices. /** \param state Transformation type to be set, e.g. view, world, or projection. @@ -855,12 +858,8 @@ public: \return Approximate amount of frames per second drawn. */ virtual s32 getFPS() const = 0; - //! Returns amount of primitives (mostly triangles) which were drawn in the last frame. - /** Together with getFPS() very useful method for statistics. - \param mode Defines if the primitives drawn are accumulated or - counted per frame. - \return Amount of primitives drawn in the last frame. */ - virtual u32 getPrimitiveCountDrawn(u32 mode = 0) const = 0; + //! Return some statistics about the last frame + virtual SFrameStats getFrameStats() const = 0; //! Gets name of this video driver. /** \return Returns the name of the video driver, e.g. in case diff --git a/irr/src/CFPSCounter.cpp b/irr/src/CFPSCounter.cpp index 6717df5c9..0542fc5cb 100644 --- a/irr/src/CFPSCounter.cpp +++ b/irr/src/CFPSCounter.cpp @@ -11,53 +11,22 @@ namespace video { CFPSCounter::CFPSCounter() : - FPS(60), Primitive(0), StartTime(0), FramesCounted(0), - PrimitivesCounted(0), PrimitiveAverage(0), PrimitiveTotal(0) + FPS(0), StartTime(0), FramesCounted(0) { } -//! returns current fps -s32 CFPSCounter::getFPS() const -{ - return FPS; -} - -//! returns current primitive count -u32 CFPSCounter::getPrimitive() const -{ - return Primitive; -} - -//! returns average primitive count of last period -u32 CFPSCounter::getPrimitiveAverage() const -{ - return PrimitiveAverage; -} - -//! returns accumulated primitive count since start -u32 CFPSCounter::getPrimitiveTotal() const -{ - return PrimitiveTotal; -} - //! to be called every frame -void CFPSCounter::registerFrame(u32 now, u32 primitivesDrawn) +void CFPSCounter::registerFrame(u32 now) { ++FramesCounted; - PrimitiveTotal += primitivesDrawn; - PrimitivesCounted += primitivesDrawn; - Primitive = primitivesDrawn; const u32 milliseconds = now - StartTime; - if (milliseconds >= 1500) { const f32 invMilli = core::reciprocal((f32)milliseconds); FPS = core::ceil32((1000 * FramesCounted) * invMilli); - PrimitiveAverage = core::ceil32((1000 * PrimitivesCounted) * invMilli); FramesCounted = 0; - PrimitivesCounted = 0; StartTime = now; } } diff --git a/irr/src/CFPSCounter.h b/irr/src/CFPSCounter.h index e73afb049..692aef6bd 100644 --- a/irr/src/CFPSCounter.h +++ b/irr/src/CFPSCounter.h @@ -17,29 +17,15 @@ public: CFPSCounter(); //! returns current fps - s32 getFPS() const; - - //! returns primitive count - u32 getPrimitive() const; - - //! returns average primitive count of last period - u32 getPrimitiveAverage() const; - - //! returns accumulated primitive count since start - u32 getPrimitiveTotal() const; + s32 getFPS() const { return FPS; } //! to be called every frame - void registerFrame(u32 now, u32 primitive); + void registerFrame(u32 now); private: s32 FPS; - u32 Primitive; u32 StartTime; - u32 FramesCounted; - u32 PrimitivesCounted; - u32 PrimitiveAverage; - u32 PrimitiveTotal; }; } // end namespace video diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index c1cecbe8c..054267711 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -53,7 +53,7 @@ public: //! constructor CNullDriver::CNullDriver(io::IFileSystem *io, const core::dimension2d &screenSize) : SharedRenderTarget(0), CurrentRenderTarget(0), CurrentRenderTargetSize(0, 0), FileSystem(io), MeshManipulator(0), - ViewPort(0, 0, 0, 0), ScreenSize(screenSize), PrimitivesDrawn(0), MinVertexCountForVBO(500), + ViewPort(0, 0, 0, 0), ScreenSize(screenSize), MinVertexCountForVBO(500), TextureCreationFlags(0), OverrideMaterial2DEnabled(false), AllowZWriteOnTransparent(false) { #ifdef _DEBUG @@ -222,13 +222,13 @@ void CNullDriver::deleteAllTextures() bool CNullDriver::beginScene(u16 clearFlag, SColor clearColor, f32 clearDepth, u8 clearStencil, const SExposedVideoData &videoData, core::rect *sourceRect) { - PrimitivesDrawn = 0; + FrameStats = {}; return true; } bool CNullDriver::endScene() { - FPSCounter.registerFrame(os::Timer::getRealTime(), PrimitivesDrawn); + FPSCounter.registerFrame(os::Timer::getRealTime()); updateAllHardwareBuffers(); updateAllOcclusionQueries(); return true; @@ -605,7 +605,7 @@ void CNullDriver::drawVertexPrimitiveList(const void *vertices, u32 vertexCount, { if ((iType == EIT_16BIT) && (vertexCount > 65536)) os::Printer::log("Too many vertices for 16bit index type, render artifacts may occur."); - PrimitivesDrawn += primitiveCount; + FrameStats.PrimitivesDrawn += primitiveCount; } //! draws a vertex primitive list in 2d @@ -613,7 +613,7 @@ void CNullDriver::draw2DVertexPrimitiveList(const void *vertices, u32 vertexCoun { if ((iType == EIT_16BIT) && (vertexCount > 65536)) os::Printer::log("Too many vertices for 16bit index type, render artifacts may occur."); - PrimitivesDrawn += primitiveCount; + FrameStats.PrimitivesDrawn += primitiveCount; } //! Draws a 3d line. @@ -743,12 +743,9 @@ s32 CNullDriver::getFPS() const return FPSCounter.getFPS(); } -//! returns amount of primitives (mostly triangles) were drawn in the last frame. -//! very useful method for statistics. -u32 CNullDriver::getPrimitiveCountDrawn(u32 param) const +SFrameStats CNullDriver::getFrameStats() const { - return (0 == param) ? FPSCounter.getPrimitive() : (1 == param) ? FPSCounter.getPrimitiveAverage() - : FPSCounter.getPrimitiveTotal(); + return FrameStats; } //! Sets the dynamic ambient light color. The default color is diff --git a/irr/src/CNullDriver.h b/irr/src/CNullDriver.h index 30911b7a6..302108a1b 100644 --- a/irr/src/CNullDriver.h +++ b/irr/src/CNullDriver.h @@ -195,9 +195,7 @@ public: // get current frames per second value s32 getFPS() const override; - //! returns amount of primitives (mostly triangles) were drawn in the last frame. - //! very useful method for statistics. - u32 getPrimitiveCountDrawn(u32 param = 0) const override; + SFrameStats getFrameStats() const override; //! \return Returns the name of the video driver. Example: In case of the DIRECT3D8 //! driver, it would return "Direct3D8.1". @@ -538,8 +536,6 @@ public: virtual void convertColor(const void *sP, ECOLOR_FORMAT sF, s32 sN, void *dP, ECOLOR_FORMAT dF) const override; - bool checkDriverReset() override { return false; } - protected: //! deletes all textures void deleteAllTextures(); @@ -570,6 +566,12 @@ protected: // prints renderer version void printVersion(); + inline void accountHWBufferUpload(u32 size) + { + FrameStats.HWBuffersUploaded++; + FrameStats.HWBuffersUploadedSize += size; + } + inline bool getWriteZBuffer(const SMaterial &material) const { switch (material.ZWriteEnable) { @@ -696,8 +698,8 @@ protected: core::matrix4 TransformationMatrix; CFPSCounter FPSCounter; + SFrameStats FrameStats; - u32 PrimitivesDrawn; u32 MinVertexCountForVBO; u32 TextureCreationFlags; diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index 1c6342090..875534a4e 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -266,6 +266,8 @@ bool COpenGLDriver::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) const E_VERTEX_TYPE vType = mb->getVertexType(); const u32 vertexSize = getVertexPitchFromType(vType); + accountHWBufferUpload(vertexSize * vertexCount); + const c8 *vbuf = static_cast(vertices); core::array buffer; if (!FeatureAvailable[IRR_ARB_vertex_array_bgra] && !FeatureAvailable[IRR_EXT_vertex_array_bgra]) { @@ -367,6 +369,8 @@ bool COpenGLDriver::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) } } + accountHWBufferUpload(indexCount * indexSize); + // get or create buffer bool newBuffer = false; if (!HWBuffer->vbo_indicesID) { diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index 1defa9abc..f681a3de9 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -255,10 +255,7 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d &screenS 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", MaxMultipleRenderTargets); DriverAttributes->setAttribute("MaxIndices", (s32)MaxIndices); DriverAttributes->setAttribute("MaxTextureSize", (s32)MaxTextureSize); DriverAttributes->setAttribute("MaxTextureLODBias", MaxTextureLODBias); @@ -493,6 +490,7 @@ bool COpenGL3DriverBase::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuff const void *buffer = vertices; size_t bufferSize = vertexSize * vertexCount; + accountHWBufferUpload(bufferSize); // get or create buffer bool newBuffer = false; @@ -551,6 +549,9 @@ bool COpenGL3DriverBase::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffe } } + const size_t bufferSize = indexCount * indexSize; + accountHWBufferUpload(bufferSize); + // get or create buffer bool newBuffer = false; if (!HWBuffer->vbo_indicesID) { @@ -558,7 +559,7 @@ bool COpenGL3DriverBase::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffe if (!HWBuffer->vbo_indicesID) return false; newBuffer = true; - } else if (HWBuffer->vbo_indicesSize < indexCount * indexSize) { + } else if (HWBuffer->vbo_indicesSize < bufferSize) { newBuffer = true; } @@ -566,16 +567,16 @@ bool COpenGL3DriverBase::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffe // copy data to graphics card if (!newBuffer) - GL.BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexCount * indexSize, indices); + GL.BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, bufferSize, indices); else { - HWBuffer->vbo_indicesSize = indexCount * indexSize; + HWBuffer->vbo_indicesSize = bufferSize; GLenum usage = GL_STATIC_DRAW; if (HWBuffer->Mapped_Index == scene::EHM_STREAM) usage = GL_STREAM_DRAW; else if (HWBuffer->Mapped_Index == scene::EHM_DYNAMIC) usage = GL_DYNAMIC_DRAW; - GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, usage); + GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, bufferSize, indices, usage); } GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -589,22 +590,24 @@ bool COpenGL3DriverBase::updateHardwareBuffer(SHWBufferLink *HWBuffer) if (!HWBuffer) return false; + auto *b = static_cast(HWBuffer); + if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) { - if (HWBuffer->ChangedID_Vertex != HWBuffer->MeshBuffer->getChangedID_Vertex() || !static_cast(HWBuffer)->vbo_verticesID) { + if (HWBuffer->ChangedID_Vertex != HWBuffer->MeshBuffer->getChangedID_Vertex() || !b->vbo_verticesID) { HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex(); - if (!updateVertexHardwareBuffer(static_cast(HWBuffer))) + if (!updateVertexHardwareBuffer(b)) return false; } } if (HWBuffer->Mapped_Index != scene::EHM_NEVER) { - if (HWBuffer->ChangedID_Index != HWBuffer->MeshBuffer->getChangedID_Index() || !static_cast(HWBuffer)->vbo_indicesID) { + if (HWBuffer->ChangedID_Index != HWBuffer->MeshBuffer->getChangedID_Index() || !b->vbo_indicesID) { HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index(); - if (!updateIndexHardwareBuffer((SHWBufferLink_opengl *)HWBuffer)) + if (!updateIndexHardwareBuffer(b)) return false; } } diff --git a/src/client/game.cpp b/src/client/game.cpp index 41c7972cb..1267b32a9 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1953,6 +1953,11 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, g_profiler->graphAdd("Sleep [us]", draw_times.sleep_time); g_profiler->graphSet("FPS", 1.0f / dtime); + + auto stats2 = driver->getFrameStats(); + g_profiler->avg("Irr: primitives drawn", stats2.PrimitivesDrawn); + g_profiler->avg("Irr: buffers uploaded", stats2.HWBuffersUploaded); + g_profiler->avg("Irr: buffers uploaded (bytes)", stats2.HWBuffersUploadedSize); } void Game::updateStats(RunStats *stats, const FpsControl &draw_times, From 19a58745c9db8b984d72f6634a1307e2f2d39c00 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 27 Aug 2024 13:51:02 +0200 Subject: [PATCH 40/75] Avoid copies when working with EnrichedString --- src/irrlicht_changes/CGUITTFont.cpp | 7 +++-- src/irrlicht_changes/static_text.h | 4 +-- src/util/enriched_string.cpp | 24 +++++----------- src/util/enriched_string.h | 23 +++++++++------ src/util/string.cpp | 43 ++++++++++++++--------------- src/util/string.h | 6 ++-- 6 files changed, 53 insertions(+), 54 deletions(-) diff --git a/src/irrlicht_changes/CGUITTFont.cpp b/src/irrlicht_changes/CGUITTFont.cpp index 40d2bb405..4f5f52b4e 100644 --- a/src/irrlicht_changes/CGUITTFont.cpp +++ b/src/irrlicht_changes/CGUITTFont.cpp @@ -513,12 +513,15 @@ void CGUITTFont::setFontHinting(const bool enable, const bool enable_auto_hintin void CGUITTFont::draw(const core::stringw& text, const core::rect& position, video::SColor color, bool hcenter, bool vcenter, const core::rect* clip) { - draw(EnrichedString(std::wstring(text.c_str()), color), position, hcenter, vcenter, clip); + // Allow colors to work for strings that have passed through irrlicht by catching + // them here and converting them to enriched just before drawing. + EnrichedString s(text.c_str(), color); + draw(s, position, hcenter, vcenter, clip); } void CGUITTFont::draw(const EnrichedString &text, const core::rect& position, bool hcenter, bool vcenter, const core::rect* clip) { - const std::vector &colors = text.getColors(); + const auto &colors = text.getColors(); if (!Driver) return; diff --git a/src/irrlicht_changes/static_text.h b/src/irrlicht_changes/static_text.h index 636760f6c..15a976c74 100644 --- a/src/irrlicht_changes/static_text.h +++ b/src/irrlicht_changes/static_text.h @@ -61,7 +61,7 @@ namespace gui static irr::gui::IGUIStaticText *add( irr::gui::IGUIEnvironment *guienv, - const wchar_t *text, + std::wstring_view text, const core::rect< s32 > &rectangle, bool border = false, bool wordWrap = true, @@ -204,7 +204,7 @@ inline void setStaticText(irr::gui::IGUIStaticText *static_text, const EnrichedS } } -inline void setStaticText(irr::gui::IGUIStaticText *static_text, const wchar_t *text) +inline void setStaticText(irr::gui::IGUIStaticText *static_text, std::wstring_view text) { setStaticText(static_text, EnrichedString(text, static_text->getOverrideColor())); } diff --git a/src/util/enriched_string.cpp b/src/util/enriched_string.cpp index 04c5ef806..09baeac29 100644 --- a/src/util/enriched_string.cpp +++ b/src/util/enriched_string.cpp @@ -29,7 +29,7 @@ EnrichedString::EnrichedString() clear(); } -EnrichedString::EnrichedString(const std::wstring &string, +EnrichedString::EnrichedString(std::wstring_view string, const std::vector &colors) { clear(); @@ -37,18 +37,12 @@ EnrichedString::EnrichedString(const std::wstring &string, m_colors = colors; } -EnrichedString::EnrichedString(const std::wstring &s, const SColor &color) +EnrichedString::EnrichedString(std::wstring_view s, const SColor &color) { clear(); addAtEnd(translate_string(s), color); } -EnrichedString::EnrichedString(const wchar_t *str, const SColor &color) -{ - clear(); - addAtEnd(translate_string(std::wstring(str)), color); -} - void EnrichedString::clear() { m_string.clear(); @@ -59,19 +53,20 @@ void EnrichedString::clear() m_background = irr::video::SColor(0, 0, 0, 0); } -EnrichedString &EnrichedString::operator=(const wchar_t *str) +EnrichedString &EnrichedString::operator=(std::wstring_view str) { clear(); - addAtEnd(translate_string(std::wstring(str)), m_default_color); + addAtEnd(translate_string(str), m_default_color); return *this; } -void EnrichedString::addAtEnd(const std::wstring &s, SColor initial_color) +void EnrichedString::addAtEnd(std::wstring_view s, SColor initial_color) { SColor color(initial_color); bool use_default = (m_default_length == m_string.size() && color == m_default_color); + m_string.reserve(m_string.size() + s.size()); m_colors.reserve(m_colors.size() + s.size()); size_t i = 0; @@ -142,7 +137,7 @@ void EnrichedString::addCharNoColor(wchar_t c) if (m_colors.empty()) { m_colors.emplace_back(m_default_color); } else { - m_colors.push_back(m_colors[m_colors.size() - 1]); + m_colors.push_back(m_colors.back()); } } @@ -203,11 +198,6 @@ EnrichedString EnrichedString::substr(size_t pos, size_t len) const return str; } -const wchar_t *EnrichedString::c_str() const -{ - return m_string.c_str(); -} - const std::vector &EnrichedString::getColors() const { return m_colors; diff --git a/src/util/enriched_string.h b/src/util/enriched_string.h index 3d19eaed5..18fd967bc 100644 --- a/src/util/enriched_string.h +++ b/src/util/enriched_string.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include +#include #include #include @@ -28,17 +29,15 @@ using namespace irr; class EnrichedString { public: EnrichedString(); - EnrichedString(const std::wstring &s, + EnrichedString(std::wstring_view s, const video::SColor &color = video::SColor(255, 255, 255, 255)); - EnrichedString(const wchar_t *str, - const video::SColor &color = video::SColor(255, 255, 255, 255)); - EnrichedString(const std::wstring &string, + EnrichedString(std::wstring_view string, const std::vector &colors); - EnrichedString &operator=(const wchar_t *str); + EnrichedString &operator=(std::wstring_view s); void clear(); - void addAtEnd(const std::wstring &s, video::SColor color); + void addAtEnd(std::wstring_view s, video::SColor color); // Adds the character source[i] at the end. // An EnrichedString should always be able to be copied @@ -51,9 +50,18 @@ public: EnrichedString getNextLine(size_t *pos) const; EnrichedString substr(size_t pos = 0, size_t len = std::string::npos) const; + EnrichedString operator+(const EnrichedString &other) const; void operator+=(const EnrichedString &other); - const wchar_t *c_str() const; + void operator+=(std::wstring_view other) + { + *this += EnrichedString(other); + } + + const wchar_t *c_str() const + { + return getString().c_str(); + } const std::vector &getColors() const; const std::wstring &getString() const; @@ -106,6 +114,5 @@ private: video::SColor m_default_color; video::SColor m_background; // This variable defines the length of the default-colored text. - // Change this to a std::vector if an "end coloring" tag is wanted. size_t m_default_length = 0; }; diff --git a/src/util/string.cpp b/src/util/string.cpp index 73d1d6907..74a360266 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -670,23 +670,26 @@ std::string wrap_rows(std::string_view from, unsigned row_len, bool has_color_co * before filling it again. */ -static void translate_all(const std::wstring &s, size_t &i, +static void translate_all(std::wstring_view s, size_t &i, Translations *translations, std::wstring &res); -static void translate_string(const std::wstring &s, Translations *translations, +static void translate_string(std::wstring_view s, Translations *translations, const std::wstring &textdomain, size_t &i, std::wstring &res) { - std::wostringstream output; std::vector args; int arg_number = 1; + + // Re-assemble the template. + std::wstring output; + output.reserve(s.length()); while (i < s.length()) { // Not an escape sequence: just add the character. if (s[i] != '\x1b') { - output.put(s[i]); + output += s[i]; // The character is a literal '@'; add it twice // so that it is not mistaken for an argument. if (s[i] == L'@') - output.put(L'@'); + output += L'@'; ++i; continue; } @@ -733,12 +736,12 @@ static void translate_string(const std::wstring &s, Translations *translations, args.push_back(arg); continue; } - output.put(L'@'); - output << arg_number; + output += L'@'; + output += std::to_wstring(arg_number); ++arg_number; std::wstring arg; translate_all(s, i, translations, arg); - args.push_back(arg); + args.push_back(std::move(arg)); } else { // This is an escape sequence *inside* the template string to translate itself. // This should not happen, show an error message. @@ -747,21 +750,18 @@ static void translate_string(const std::wstring &s, Translations *translations, } } - std::wstring toutput; // Translate the template. - if (translations != nullptr) - toutput = translations->getTranslation( - textdomain, output.str()); - else - toutput = output.str(); + const std::wstring &toutput = translations ? + translations->getTranslation(textdomain, output) : output; // Put back the arguments in the translated template. - std::wostringstream result; size_t j = 0; + res.clear(); + res.reserve(toutput.length()); while (j < toutput.length()) { // Normal character, add it to output and continue. if (toutput[j] != L'@' || j == toutput.length() - 1) { - result.put(toutput[j]); + res += toutput[j]; ++j; continue; } @@ -769,7 +769,7 @@ static void translate_string(const std::wstring &s, Translations *translations, ++j; // Literal escape for '@'. if (toutput[j] == L'@') { - result.put(L'@'); + res += L'@'; ++j; continue; } @@ -778,16 +778,15 @@ static void translate_string(const std::wstring &s, Translations *translations, int arg_index = toutput[j] - L'1'; ++j; if (0 <= arg_index && (size_t)arg_index < args.size()) { - result << args[arg_index]; + res += args[arg_index]; } else { // This is not allowed: show an error message errorstream << "Ignoring out-of-bounds argument escape sequence in translation" << std::endl; } } - res = result.str(); } -static void translate_all(const std::wstring &s, size_t &i, +static void translate_all(std::wstring_view s, size_t &i, Translations *translations, std::wstring &res) { res.clear(); @@ -849,7 +848,7 @@ static void translate_all(const std::wstring &s, size_t &i, } // Translate string server side -std::wstring translate_string(const std::wstring &s, Translations *translations) +std::wstring translate_string(std::wstring_view s, Translations *translations) { size_t i = 0; std::wstring res; @@ -858,7 +857,7 @@ std::wstring translate_string(const std::wstring &s, Translations *translations) } // Translate string client side -std::wstring translate_string(const std::wstring &s) +std::wstring translate_string(std::wstring_view s) { #ifdef SERVER return translate_string(s, nullptr); diff --git a/src/util/string.h b/src/util/string.h index ad3d09818..aae1167b6 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -631,11 +631,11 @@ std::vector > split(const std::basic_string &s, T delim) return tokens; } -std::wstring translate_string(const std::wstring &s, Translations *translations); +std::wstring translate_string(std::wstring_view s, Translations *translations); -std::wstring translate_string(const std::wstring &s); +std::wstring translate_string(std::wstring_view s); -inline std::wstring unescape_translate(const std::wstring &s) { +inline std::wstring unescape_translate(std::wstring_view s) { return unescape_enriched(translate_string(s)); } From 1298d6c0206a964f4f5cb5a94b8463340c7dbbf4 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 28 Aug 2024 14:56:10 +0200 Subject: [PATCH 41/75] Fix VBO hint for transparent block parts --- src/client/mapblock_mesh.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 04c50ad99..0638c34c9 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -783,12 +783,15 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs } if (mesh) { - // Use VBO for mesh (this just would set this for ever buffer) + // Use VBO for mesh (this just would set this for every buffer) mesh->setHardwareMappingHint(scene::EHM_STATIC); } } - //std::cout<<"added "<setHardwareMappingHint(scene::EHM_STREAM, scene::EBT_INDEX); + m_bsp_tree.buildTree(&m_transparent_triangles, data->side_length); // Check if animation is required for this mesh From bf4d31227bc08c1940384e8cd00d25609c7ed674 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 28 Aug 2024 20:44:42 +0200 Subject: [PATCH 42/75] Delete OpenGL ES 1.0 driver (#15067) --- builtin/settingtypes.txt | 3 +- irr/README.md | 1 - irr/include/EDriverTypes.h | 3 - irr/src/CEGLManager.cpp | 6 - irr/src/CIrrDeviceLinux.cpp | 22 +- irr/src/CIrrDeviceOSX.mm | 3 +- irr/src/CIrrDeviceSDL.cpp | 8 +- irr/src/CIrrDeviceWin32.cpp | 21 +- irr/src/CMakeLists.txt | 31 - irr/src/COGLESCommon.h | 122 -- irr/src/COGLESDriver.cpp | 2346 ---------------------------- irr/src/COGLESDriver.h | 317 ---- irr/src/COGLESExtensionHandler.cpp | 89 -- irr/src/COGLESExtensionHandler.h | 189 --- irr/src/COGLESMaterialRenderer.h | 286 ---- irr/src/COpenGLCoreCacheHandler.h | 8 +- irr/src/COpenGLCoreTexture.h | 3 +- irr/src/Irrlicht.cpp | 4 - src/client/renderingengine.cpp | 2 - 19 files changed, 11 insertions(+), 3453 deletions(-) delete mode 100644 irr/src/COGLESCommon.h delete mode 100644 irr/src/COGLESDriver.cpp delete mode 100644 irr/src/COGLESDriver.h delete mode 100644 irr/src/COGLESExtensionHandler.cpp delete mode 100644 irr/src/COGLESExtensionHandler.h delete mode 100644 irr/src/COGLESMaterialRenderer.h diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 229d920d5..001eb7288 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1851,8 +1851,7 @@ shader_path (Shader path) path # The rendering back-end. # Note: A restart is required after changing this! # OpenGL is the default for desktop, and OGLES2 for Android. -# Shaders are supported by everything but OGLES1. -video_driver (Video driver) enum ,opengl,opengl3,ogles1,ogles2 +video_driver (Video driver) enum ,opengl,opengl3,ogles2 # Distance in nodes at which transparency depth sorting is enabled # Use this to limit the performance impact of transparency depth sorting diff --git a/irr/README.md b/irr/README.md index 640b82c6f..50e7338a5 100644 --- a/irr/README.md +++ b/irr/README.md @@ -20,7 +20,6 @@ The following libraries are required to be installed: Aside from standard search options (`ZLIB_INCLUDE_DIR`, `ZLIB_LIBRARY`, ...) the following options are available: * `ENABLE_OPENGL` - Enable OpenGL driver * `ENABLE_OPENGL3` (default: `OFF`) - Enable OpenGL 3+ driver -* `ENABLE_GLES1` - Enable OpenGL ES driver, legacy * `ENABLE_GLES2` - Enable OpenGL ES 2+ driver * `USE_SDL2` (default: platform-dependent, usually `ON`) - Use SDL2 instead of older native device code diff --git a/irr/include/EDriverTypes.h b/irr/include/EDriverTypes.h index 33f987889..f19e65ace 100644 --- a/irr/include/EDriverTypes.h +++ b/irr/include/EDriverTypes.h @@ -24,9 +24,6 @@ enum E_DRIVER_TYPE primitives. */ EDT_OPENGL, - //! OpenGL-ES 1.x driver, for embedded and mobile systems - EDT_OGLES1, - //! OpenGL-ES 2.x driver, for embedded and mobile systems /** Supports shaders etc. */ EDT_OGLES2, diff --git a/irr/src/CEGLManager.cpp b/irr/src/CEGLManager.cpp index ffe7a44cf..daaa64fdf 100644 --- a/irr/src/CEGLManager.cpp +++ b/irr/src/CEGLManager.cpp @@ -152,9 +152,6 @@ EGLConfig CEGLManager::chooseConfig(EConfigStyle confStyle) // Find proper OpenGL BIT. EGLint eglOpenGLBIT = 0; switch (Params.DriverType) { - case EDT_OGLES1: - eglOpenGLBIT = EGL_OPENGL_ES_BIT; - break; case EDT_OGLES2: case EDT_WEBGL1: eglOpenGLBIT = EGL_OPENGL_ES2_BIT; @@ -459,9 +456,6 @@ bool CEGLManager::generateContext() EGLint OpenGLESVersion = 0; switch (Params.DriverType) { - case EDT_OGLES1: - OpenGLESVersion = 1; - break; case EDT_OGLES2: case EDT_WEBGL1: OpenGLESVersion = 2; diff --git a/irr/src/CIrrDeviceLinux.cpp b/irr/src/CIrrDeviceLinux.cpp index 5491d2037..5e3afae36 100644 --- a/irr/src/CIrrDeviceLinux.cpp +++ b/irr/src/CIrrDeviceLinux.cpp @@ -33,7 +33,7 @@ #include #endif -#if defined(_IRR_COMPILE_WITH_OGLES1_) || defined(_IRR_COMPILE_WITH_OGLES2_) +#if defined(_IRR_COMPILE_WITH_OGLES2_) #include "CEGLManager.h" #endif @@ -76,10 +76,6 @@ namespace video IVideoDriver *createOpenGLDriver(const irr::SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager); #endif -#ifdef _IRR_COMPILE_WITH_OGLES1_ -IVideoDriver *createOGLES1Driver(const irr::SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager); -#endif - #ifdef _IRR_COMPILE_WITH_OGLES2_ IVideoDriver *createOGLES2Driver(const irr::SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager); #endif @@ -554,22 +550,6 @@ void CIrrDeviceLinux::createDriver() } #else os::Printer::log("No OpenGL support compiled in.", ELL_ERROR); -#endif - break; - case video::EDT_OGLES1: -#ifdef _IRR_COMPILE_WITH_OGLES1_ - { - video::SExposedVideoData data; - data.OpenGLLinux.X11Window = XWindow; - data.OpenGLLinux.X11Display = XDisplay; - - ContextManager = new video::CEGLManager(); - ContextManager->initialize(CreationParams, data); - - VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager); - } -#else - os::Printer::log("No OpenGL-ES1 support compiled in.", ELL_ERROR); #endif break; case video::EDT_OGLES2: diff --git a/irr/src/CIrrDeviceOSX.mm b/irr/src/CIrrDeviceOSX.mm index 67c0ce05c..e335085e4 100644 --- a/irr/src/CIrrDeviceOSX.mm +++ b/irr/src/CIrrDeviceOSX.mm @@ -720,9 +720,8 @@ void CIrrDeviceMacOSX::createDriver() #endif break; - case video::EDT_OGLES1: case video::EDT_OGLES2: - os::Printer::log("This driver is not available in OSX. Try OpenGL or Software renderer.", ELL_ERROR); + os::Printer::log("This driver is not available on OSX.", ELL_ERROR); break; case video::EDT_NULL: diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index ffbd81950..58fba4f25 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -594,18 +594,14 @@ bool CIrrDeviceSDL::createWindowWithContext() SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); break; - case video::EDT_OGLES1: - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - break; case video::EDT_OGLES2: case video::EDT_WEBGL1: SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); break; - default:; + default: + _IRR_DEBUG_BREAK_IF(1); } if (CreationParams.DriverDebug) { diff --git a/irr/src/CIrrDeviceWin32.cpp b/irr/src/CIrrDeviceWin32.cpp index c2876fcce..fe5293988 100644 --- a/irr/src/CIrrDeviceWin32.cpp +++ b/irr/src/CIrrDeviceWin32.cpp @@ -29,7 +29,7 @@ #endif #endif -#if defined(_IRR_COMPILE_WITH_OGLES1_) || defined(_IRR_COMPILE_WITH_OGLES2_) +#if defined(_IRR_COMPILE_WITH_OGLES2_) #include "CEGLManager.h" #endif @@ -45,10 +45,6 @@ namespace video IVideoDriver *createOpenGLDriver(const irr::SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager); #endif -#ifdef _IRR_COMPILE_WITH_OGLES1_ -IVideoDriver *createOGLES1Driver(const irr::SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager); -#endif - #ifdef _IRR_COMPILE_WITH_OGLES2_ IVideoDriver *createOGLES2Driver(const irr::SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager); #endif @@ -890,21 +886,6 @@ void CIrrDeviceWin32::createDriver() os::Printer::log("Could not create OpenGL driver.", ELL_ERROR); #else os::Printer::log("OpenGL driver was not compiled in.", ELL_ERROR); -#endif - break; - case video::EDT_OGLES1: -#ifdef _IRR_COMPILE_WITH_OGLES1_ - switchToFullScreen(); - - ContextManager = new video::CEGLManager(); - ContextManager->initialize(CreationParams, video::SExposedVideoData(HWnd)); - - VideoDriver = video::createOGLES1Driver(CreationParams, FileSystem, ContextManager); - - if (!VideoDriver) - os::Printer::log("Could not create OpenGL-ES1 driver.", ELL_ERROR); -#else - os::Printer::log("OpenGL-ES1 driver was not compiled in.", ELL_ERROR); #endif break; case video::EDT_OGLES2: diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index d5e9d47e7..2bc935b9f 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -130,12 +130,6 @@ else() option(ENABLE_OPENGL "Enable OpenGL" TRUE) endif() -if(USE_SDL2 OR EMSCRIPTEN OR APPLE) - set(ENABLE_GLES1 FALSE) -else() - option(ENABLE_GLES1 "Enable OpenGL ES" FALSE) -endif() - if(APPLE) set(ENABLE_GLES2 FALSE) set(ENABLE_WEBGL1 FALSE) @@ -172,14 +166,6 @@ if(ENABLE_OPENGL3) endif() endif() -if(ENABLE_GLES1) - add_definitions(-D_IRR_COMPILE_WITH_OGLES1_) - set(OPENGLES_DIRECT_LINK TRUE) - if(DEVICE MATCHES "^(WINDOWS|X11)$") - add_definitions(-D_IRR_COMPILE_WITH_EGL_MANAGER_) - endif() -endif() - if(ENABLE_GLES2) add_definitions(-D_IRR_COMPILE_WITH_OGLES2_) if(DEVICE MATCHES "^(WINDOWS|X11)$" OR EMSCRIPTEN) @@ -204,7 +190,6 @@ endif() message(STATUS "Device: ${DEVICE}") message(STATUS "OpenGL: ${ENABLE_OPENGL}") message(STATUS "OpenGL 3: ${ENABLE_OPENGL3}") -message(STATUS "OpenGL ES: ${ENABLE_GLES1}") if (ENABLE_GLES2) message(STATUS "OpenGL ES 2: ON (unified)") else() @@ -220,13 +205,6 @@ find_package(ZLIB REQUIRED) find_package(JPEG REQUIRED) find_package(PNG REQUIRED) -if(ENABLE_GLES1) - # only tested on Android, probably works on Linux (is this needed anywhere else?) - find_library(OPENGLES_LIBRARY NAMES GLESv1_CM REQUIRED) - find_library(EGL_LIBRARY NAMES EGL REQUIRED) - - message(STATUS "Found OpenGLES: ${OPENGLES_LIBRARY}") -endif() if(ENABLE_GLES2) find_package(OpenGLES2 REQUIRED) endif() @@ -360,14 +338,6 @@ if(ENABLE_OPENGL) ) endif() -if(ENABLE_GLES1) - set(IRRDRVROBJ - ${IRRDRVROBJ} - COGLESDriver.cpp - COGLESExtensionHandler.cpp - ) -endif() - # the unified drivers if(ENABLE_OPENGL3 OR ENABLE_GLES2) @@ -509,7 +479,6 @@ target_link_libraries(IrrlichtMt PRIVATE "$<$:SDL2::SDL2>" "$<$:${OPENGL_LIBRARIES}>" - "$<$:${OPENGLES_LIBRARY}>" ${EGL_LIBRARY} # incl. transitive SDL2 dependencies for static linking diff --git a/irr/src/COGLESCommon.h b/irr/src/COGLESCommon.h deleted file mode 100644 index 1d439546a..000000000 --- a/irr/src/COGLESCommon.h +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright (C) 2015 Patryk Nadrowski -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#pragma once - -#ifdef _IRR_COMPILE_WITH_OGLES1_ - -#if defined(_IRR_COMPILE_WITH_IOS_DEVICE_) -#include -#include -#elif defined(_IRR_OGLES1_USE_KHRONOS_API_HEADERS_) -#include -#include -typedef char GLchar; -#else // or only when defined(_IRR_COMPILE_WITH_ANDROID_DEVICE_) ? -#include -#include -#include -#endif - -#ifndef GL_BGRA -#define GL_BGRA 0x80E1; -#endif - -// Blending definitions. - -#if defined(GL_OES_blend_subtract) -#define GL_FUNC_ADD GL_FUNC_ADD_OES -#else -#define GL_FUNC_ADD 0 -#endif - -// FBO definitions. - -#ifdef GL_OES_framebuffer_object -#define GL_NONE 0 // iOS has missing definition of GL_NONE_OES -#define GL_FRAMEBUFFER GL_FRAMEBUFFER_OES -#define GL_DEPTH_COMPONENT16 GL_DEPTH_COMPONENT16_OES -#define GL_COLOR_ATTACHMENT0 GL_COLOR_ATTACHMENT0_OES -#define GL_DEPTH_ATTACHMENT GL_DEPTH_ATTACHMENT_OES -#define GL_STENCIL_ATTACHMENT GL_STENCIL_ATTACHMENT_OES -#define GL_FRAMEBUFFER_COMPLETE GL_FRAMEBUFFER_COMPLETE_OES -#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 1 -#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 2 -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_OES -#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS GL_FRAMEBUFFER_INCOMPLETE_FORMATS_OES -#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_OES -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_OES -#define GL_FRAMEBUFFER_UNSUPPORTED GL_FRAMEBUFFER_UNSUPPORTED_OES -#else -#define GL_NONE 0 -#define GL_FRAMEBUFFER 0 -#define GL_DEPTH_COMPONENT16 0 -#define GL_COLOR_ATTACHMENT0 0 -#define GL_DEPTH_ATTACHMENT 0 -#define GL_STENCIL_ATTACHMENT 0 -#define GL_FRAMEBUFFER_COMPLETE 0 -#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 1 -#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 2 -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 3 -#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS 4 -#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS 5 -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 6 -#define GL_FRAMEBUFFER_UNSUPPORTED 7 -#endif - -#define GL_DEPTH_COMPONENT 0x1902 - -// Texture definitions. - -#ifdef GL_OES_texture_cube_map -#define GL_TEXTURE_CUBE_MAP GL_TEXTURE_CUBE_MAP_OES -#define GL_TEXTURE_CUBE_MAP_POSITIVE_X GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_OES -#else -#define GL_TEXTURE_CUBE_MAP 0 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0 -#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0 -#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0 -#endif - -// to check if this header is in the current compile unit (different GL implementation used different "GLCommon" headers in Irrlicht -#define IRR_COMPILE_GLES_COMMON - -// macro used with COGLES1Driver -#define TEST_GL_ERROR(cls) (cls)->testGLError(__LINE__) - -namespace irr -{ -namespace video -{ - -// Forward declarations. - -class COpenGLCoreFeature; - -template -class COpenGLCoreTexture; - -template -class COpenGLCoreRenderTarget; - -template -class COpenGLCoreCacheHandler; - -class COGLES1Driver; -typedef COpenGLCoreTexture COGLES1Texture; -typedef COpenGLCoreRenderTarget COGLES1RenderTarget; -typedef COpenGLCoreCacheHandler COGLES1CacheHandler; - -} -} - -#endif diff --git a/irr/src/COGLESDriver.cpp b/irr/src/COGLESDriver.cpp deleted file mode 100644 index d5feda58e..000000000 --- a/irr/src/COGLESDriver.cpp +++ /dev/null @@ -1,2346 +0,0 @@ -// Copyright (C) 2002-2008 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include "COGLESDriver.h" -#include -#include "CNullDriver.h" -#include "IContextManager.h" - -#ifdef _IRR_COMPILE_WITH_OGLES1_ - -#include "COpenGLCoreTexture.h" -#include "COpenGLCoreRenderTarget.h" -#include "COpenGLCoreCacheHandler.h" - -#include "COGLESMaterialRenderer.h" - -#include "EVertexAttributes.h" -#include "CImage.h" -#include "os.h" - -namespace irr -{ -namespace video -{ - -COGLES1Driver::COGLES1Driver(const SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager) : - CNullDriver(io, params.WindowSize), COGLES1ExtensionHandler(), CacheHandler(0), CurrentRenderMode(ERM_NONE), - ResetRenderStates(true), Transformation3DChanged(true), AntiAlias(params.AntiAlias), - ColorFormat(ECF_R8G8B8), Params(params), ContextManager(contextManager) -{ -#ifdef _DEBUG - setDebugName("COGLESDriver"); -#endif - - core::dimension2d windowSize(0, 0); - - if (!ContextManager) - return; - - ContextManager->grab(); - ContextManager->generateSurface(); - ContextManager->generateContext(); - ExposedData = ContextManager->getContext(); - ContextManager->activateContext(ExposedData, false); - - windowSize = params.WindowSize; - - genericDriverInit(windowSize, params.Stencilbuffer); -} - -COGLES1Driver::~COGLES1Driver() -{ - deleteMaterialRenders(); - - CacheHandler->getTextureCache().clear(); - - removeAllRenderTargets(); - deleteAllTextures(); - removeAllOcclusionQueries(); - removeAllHardwareBuffers(); - - delete CacheHandler; - - if (ContextManager) { - ContextManager->destroyContext(); - ContextManager->destroySurface(); - ContextManager->terminate(); - ContextManager->drop(); - } -} - -// ----------------------------------------------------------------------- -// METHODS -// ----------------------------------------------------------------------- - -bool COGLES1Driver::genericDriverInit(const core::dimension2d &screenSize, bool stencilBuffer) -{ - Name = glGetString(GL_VERSION); - printVersion(); - - // print renderer information - VendorName = glGetString(GL_VENDOR); - os::Printer::log(VendorName.c_str(), ELL_INFORMATION); - - // load extensions - initExtensions(); - - // reset cache handler - delete CacheHandler; - CacheHandler = new COGLES1CacheHandler(this); - - StencilBuffer = stencilBuffer; - - DriverAttributes->setAttribute("MaxTextures", (s32)Feature.MaxTextureUnits); - DriverAttributes->setAttribute("MaxSupportedTextures", (s32)Feature.MaxTextureUnits); - DriverAttributes->setAttribute("MaxAnisotropy", MaxAnisotropy); - DriverAttributes->setAttribute("MaxIndices", (s32)MaxIndices); - DriverAttributes->setAttribute("MaxTextureSize", (s32)MaxTextureSize); - DriverAttributes->setAttribute("MaxTextureLODBias", MaxTextureLODBias); - DriverAttributes->setAttribute("Version", Version); - DriverAttributes->setAttribute("AntiAlias", AntiAlias); - - glPixelStorei(GL_PACK_ALIGNMENT, 1); - - for (s32 i = 0; i < ETS_COUNT; ++i) - setTransform(static_cast(i), core::IdentityMatrix); - - setAmbientLight(SColorf(0.0f, 0.0f, 0.0f, 0.0f)); - glClearDepthf(1.0f); - - glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); - glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST); - glDepthFunc(GL_LEQUAL); - glFrontFace(GL_CW); - glAlphaFunc(GL_GREATER, 0.f); - - // create material renderers - createMaterialRenderers(); - - // set the renderstates - setRenderStates3DMode(); - - // set fog mode - setFog(FogColor, FogType, FogStart, FogEnd, FogDensity, PixelFog, RangeFog); - - // create matrix for flipping textures - TextureFlipMatrix.buildTextureTransform(0.0f, core::vector2df(0, 0), core::vector2df(0, 1.0f), core::vector2df(1.0f, -1.0f)); - - // We need to reset once more at the beginning of the first rendering. - // This fixes problems with intermediate changes to the material during texture load. - ResetRenderStates = true; - - testGLError(__LINE__); - - return true; -} - -void COGLES1Driver::createMaterialRenderers() -{ - addAndDropMaterialRenderer(new COGLES1MaterialRenderer_SOLID(this)); - addAndDropMaterialRenderer(new COGLES1MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL(this)); - addAndDropMaterialRenderer(new COGLES1MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF(this)); - addAndDropMaterialRenderer(new COGLES1MaterialRenderer_TRANSPARENT_VERTEX_ALPHA(this)); - addAndDropMaterialRenderer(new COGLES1MaterialRenderer_ONETEXTURE_BLEND(this)); -} - -bool COGLES1Driver::beginScene(u16 clearFlag, SColor clearColor, f32 clearDepth, u8 clearStencil, const SExposedVideoData &videoData, core::rect *sourceRect) -{ - CNullDriver::beginScene(clearFlag, clearColor, clearDepth, clearStencil, videoData, sourceRect); - - if (ContextManager) - ContextManager->activateContext(videoData, true); - - clearBuffers(clearFlag, clearColor, clearDepth, clearStencil); - - return true; -} - -bool COGLES1Driver::endScene() -{ - CNullDriver::endScene(); - - glFlush(); - - if (ContextManager) - return ContextManager->swapBuffers(); - - return false; -} - -//! Returns the transformation set by setTransform -const core::matrix4 &COGLES1Driver::getTransform(E_TRANSFORMATION_STATE state) const -{ - return Matrices[state]; -} - -//! sets transformation -void COGLES1Driver::setTransform(E_TRANSFORMATION_STATE state, const core::matrix4 &mat) -{ - Matrices[state] = mat; - Transformation3DChanged = true; - - switch (state) { - case ETS_VIEW: - case ETS_WORLD: { - // OGLES1 only has a model matrix, view and world is not existent. so lets fake these two. - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf((Matrices[ETS_VIEW] * Matrices[ETS_WORLD]).pointer()); - } break; - case ETS_PROJECTION: { - GLfloat glmat[16]; - getGLMatrix(glmat, mat); - // flip z to compensate OGLES1s right-hand coordinate system - glmat[12] *= -1.0f; - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(glmat); - } break; - default: - break; - } -} - -bool COGLES1Driver::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) -{ - if (!HWBuffer) - return false; - - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; - const void *vertices = mb->getVertices(); - const u32 vertexCount = mb->getVertexCount(); - const E_VERTEX_TYPE vType = mb->getVertexType(); - const u32 vertexSize = getVertexPitchFromType(vType); - - // buffer vertex data, and convert colours... - core::array buffer(vertexSize * vertexCount); - buffer.set_used(vertexSize * vertexCount); - memcpy(buffer.pointer(), vertices, vertexSize * vertexCount); - - // in order to convert the colors into opengl format (RGBA) - switch (vType) { - case EVT_STANDARD: { - S3DVertex *pb = reinterpret_cast(buffer.pointer()); - const S3DVertex *po = static_cast(vertices); - for (u32 i = 0; i < vertexCount; i++) { - po[i].Color.toOpenGLColor((u8 *)&(pb[i].Color.color)); - } - } break; - case EVT_2TCOORDS: { - S3DVertex2TCoords *pb = reinterpret_cast(buffer.pointer()); - const S3DVertex2TCoords *po = static_cast(vertices); - for (u32 i = 0; i < vertexCount; i++) { - po[i].Color.toOpenGLColor((u8 *)&(pb[i].Color.color)); - } - } break; - case EVT_TANGENTS: { - S3DVertexTangents *pb = reinterpret_cast(buffer.pointer()); - const S3DVertexTangents *po = static_cast(vertices); - for (u32 i = 0; i < vertexCount; i++) { - po[i].Color.toOpenGLColor((u8 *)&(pb[i].Color.color)); - } - } break; - default: { - return false; - } - } - - // get or create buffer - bool newBuffer = false; - if (!HWBuffer->vbo_verticesID) { - glGenBuffers(1, &HWBuffer->vbo_verticesID); - if (!HWBuffer->vbo_verticesID) - return false; - newBuffer = true; - } else if (HWBuffer->vbo_verticesSize < vertexCount * vertexSize) { - newBuffer = true; - } - - glBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID); - - // copy data to graphics card - if (!newBuffer) - glBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * vertexSize, buffer.const_pointer()); - else { - HWBuffer->vbo_verticesSize = vertexCount * vertexSize; - - if (HWBuffer->Mapped_Vertex == scene::EHM_STATIC) - glBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_STATIC_DRAW); - else - glBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_DYNAMIC_DRAW); - } - - glBindBuffer(GL_ARRAY_BUFFER, 0); - - return (!testGLError(__LINE__)); -} - -bool COGLES1Driver::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) -{ - if (!HWBuffer) - return false; - - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; - - const void *indices = mb->getIndices(); - u32 indexCount = mb->getIndexCount(); - - GLenum indexSize; - switch (mb->getIndexType()) { - case (EIT_16BIT): { - indexSize = sizeof(u16); - break; - } - case (EIT_32BIT): { - indexSize = sizeof(u32); - break; - } - default: { - return false; - } - } - - // get or create buffer - bool newBuffer = false; - if (!HWBuffer->vbo_indicesID) { - glGenBuffers(1, &HWBuffer->vbo_indicesID); - if (!HWBuffer->vbo_indicesID) - return false; - newBuffer = true; - } else if (HWBuffer->vbo_indicesSize < indexCount * indexSize) { - newBuffer = true; - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID); - - // copy data to graphics card - if (!newBuffer) - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexCount * indexSize, indices); - else { - HWBuffer->vbo_indicesSize = indexCount * indexSize; - - if (HWBuffer->Mapped_Index == scene::EHM_STATIC) - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STATIC_DRAW); - else - glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_DYNAMIC_DRAW); - } - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - return (!testGLError(__LINE__)); -} - -//! updates hardware buffer if needed -bool COGLES1Driver::updateHardwareBuffer(SHWBufferLink *HWBuffer) -{ - if (!HWBuffer) - return false; - - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) { - if (HWBuffer->ChangedID_Vertex != HWBuffer->MeshBuffer->getChangedID_Vertex() || !static_cast(HWBuffer)->vbo_verticesID) { - - HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex(); - - if (!updateVertexHardwareBuffer(static_cast(HWBuffer))) - return false; - } - } - - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) { - if (HWBuffer->ChangedID_Index != HWBuffer->MeshBuffer->getChangedID_Index() || !((SHWBufferLink_opengl *)HWBuffer)->vbo_indicesID) { - - HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index(); - - if (!updateIndexHardwareBuffer(static_cast(HWBuffer))) - return false; - } - } - - return true; -} - -//! Create hardware buffer from meshbuffer -COGLES1Driver::SHWBufferLink *COGLES1Driver::createHardwareBuffer(const scene::IMeshBuffer *mb) -{ - if (!mb || (mb->getHardwareMappingHint_Index() == scene::EHM_NEVER && mb->getHardwareMappingHint_Vertex() == scene::EHM_NEVER)) - return 0; - - SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(mb); - - // add to map - HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer); - - HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex(); - HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index(); - HWBuffer->Mapped_Vertex = mb->getHardwareMappingHint_Vertex(); - HWBuffer->Mapped_Index = mb->getHardwareMappingHint_Index(); - HWBuffer->vbo_verticesID = 0; - HWBuffer->vbo_indicesID = 0; - HWBuffer->vbo_verticesSize = 0; - HWBuffer->vbo_indicesSize = 0; - - if (!updateHardwareBuffer(HWBuffer)) { - deleteHardwareBuffer(HWBuffer); - return 0; - } - - return HWBuffer; -} - -void COGLES1Driver::deleteHardwareBuffer(SHWBufferLink *_HWBuffer) -{ - if (!_HWBuffer) - return; - - SHWBufferLink_opengl *HWBuffer = static_cast(_HWBuffer); - if (HWBuffer->vbo_verticesID) { - glDeleteBuffers(1, &HWBuffer->vbo_verticesID); - HWBuffer->vbo_verticesID = 0; - } - if (HWBuffer->vbo_indicesID) { - glDeleteBuffers(1, &HWBuffer->vbo_indicesID); - HWBuffer->vbo_indicesID = 0; - } - - CNullDriver::deleteHardwareBuffer(_HWBuffer); -} - -//! Draw hardware buffer -void COGLES1Driver::drawHardwareBuffer(SHWBufferLink *_HWBuffer) -{ - if (!_HWBuffer) - return; - - SHWBufferLink_opengl *HWBuffer = static_cast(_HWBuffer); - - updateHardwareBuffer(HWBuffer); // check if update is needed - - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; - const void *vertices = mb->getVertices(); - const void *indexList = mb->getIndices(); - - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) { - glBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID); - vertices = 0; - } - - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID); - indexList = 0; - } - - drawVertexPrimitiveList(vertices, mb->getVertexCount(), indexList, - mb->getPrimitiveCount(), mb->getVertexType(), - mb->getPrimitiveType(), mb->getIndexType()); - - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) - glBindBuffer(GL_ARRAY_BUFFER, 0); - - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); -} - -IRenderTarget *COGLES1Driver::addRenderTarget() -{ - COGLES1RenderTarget *renderTarget = new COGLES1RenderTarget(this); - RenderTargets.push_back(renderTarget); - - return renderTarget; -} - -// small helper function to create vertex buffer object adress offsets -static inline u8 *buffer_offset(const long offset) -{ - return ((u8 *)0 + offset); -} - -//! draws a vertex primitive list -void COGLES1Driver::drawVertexPrimitiveList(const void *vertices, u32 vertexCount, - const void *indexList, u32 primitiveCount, - E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType) -{ - if (!checkPrimitiveCount(primitiveCount)) - return; - - setRenderStates3DMode(); - - drawVertexPrimitiveList2d3d(vertices, vertexCount, (const u16 *)indexList, primitiveCount, vType, pType, iType); -} - -void COGLES1Driver::drawVertexPrimitiveList2d3d(const void *vertices, u32 vertexCount, - const void *indexList, u32 primitiveCount, - E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType, bool threed) -{ - if (!primitiveCount || !vertexCount) - return; - - if (!threed && !checkPrimitiveCount(primitiveCount)) - return; - - CNullDriver::drawVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType); - - if (vertices) { - // convert colors to gl color format. - vertexCount *= 4; // reused as color component count - ColorBuffer.set_used(vertexCount); - u32 i; - - switch (vType) { - case EVT_STANDARD: { - const S3DVertex *p = static_cast(vertices); - for (i = 0; i < vertexCount; i += 4) { - p->Color.toOpenGLColor(&ColorBuffer[i]); - ++p; - } - } break; - case EVT_2TCOORDS: { - const S3DVertex2TCoords *p = static_cast(vertices); - for (i = 0; i < vertexCount; i += 4) { - p->Color.toOpenGLColor(&ColorBuffer[i]); - ++p; - } - } break; - case EVT_TANGENTS: { - const S3DVertexTangents *p = static_cast(vertices); - for (i = 0; i < vertexCount; i += 4) { - p->Color.toOpenGLColor(&ColorBuffer[i]); - ++p; - } - } break; - } - } - - // draw everything - glClientActiveTexture(GL_TEXTURE0); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_VERTEX_ARRAY); - if ((pType != scene::EPT_POINTS) && (pType != scene::EPT_POINT_SPRITES)) - glEnableClientState(GL_TEXTURE_COORD_ARRAY); -#ifdef GL_OES_point_size_array - else if (FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_OES_point_size_array] && (Material.Thickness == 0.0f)) - glEnableClientState(GL_POINT_SIZE_ARRAY_OES); -#endif - if (threed && (pType != scene::EPT_POINTS) && (pType != scene::EPT_POINT_SPRITES)) - glEnableClientState(GL_NORMAL_ARRAY); - - if (vertices) - glColorPointer(4, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); - - switch (vType) { - case EVT_STANDARD: - if (vertices) { - if (threed) - glNormalPointer(GL_FLOAT, sizeof(S3DVertex), &(static_cast(vertices))[0].Normal); - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), &(static_cast(vertices))[0].TCoords); - glVertexPointer((threed ? 3 : 2), GL_FLOAT, sizeof(S3DVertex), &(static_cast(vertices))[0].Pos); - } else { - glNormalPointer(GL_FLOAT, sizeof(S3DVertex), buffer_offset(12)); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex), buffer_offset(24)); - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), buffer_offset(28)); - glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex), 0); - } - - if (Feature.MaxTextureUnits > 0 && CacheHandler->getTextureCache().get(1)) { - glClientActiveTexture(GL_TEXTURE0 + 1); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - if (vertices) - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), &(static_cast(vertices))[0].TCoords); - else - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), buffer_offset(28)); - } - break; - case EVT_2TCOORDS: - if (vertices) { - if (threed) - glNormalPointer(GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast(vertices))[0].Normal); - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast(vertices))[0].TCoords); - glVertexPointer((threed ? 3 : 2), GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast(vertices))[0].Pos); - } else { - glNormalPointer(GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(12)); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), buffer_offset(24)); - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(28)); - glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(0)); - } - - if (Feature.MaxTextureUnits > 0) { - glClientActiveTexture(GL_TEXTURE0 + 1); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - if (vertices) - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast(vertices))[0].TCoords2); - else - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(36)); - } - break; - case EVT_TANGENTS: - if (vertices) { - if (threed) - glNormalPointer(GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].Normal); - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].TCoords); - glVertexPointer((threed ? 3 : 2), GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].Pos); - } else { - glNormalPointer(GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(12)); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), buffer_offset(24)); - glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(28)); - glVertexPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(0)); - } - - if (Feature.MaxTextureUnits > 0) { - glClientActiveTexture(GL_TEXTURE0 + 1); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - if (vertices) - glTexCoordPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].Tangent); - else - glTexCoordPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(36)); - - glClientActiveTexture(GL_TEXTURE0 + 2); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - if (vertices) - glTexCoordPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast(vertices))[0].Binormal); - else - glTexCoordPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(48)); - } - break; - } - - GLenum indexSize = 0; - - switch (iType) { - case (EIT_16BIT): { - indexSize = GL_UNSIGNED_SHORT; - break; - } - case (EIT_32BIT): { -#ifdef GL_OES_element_index_uint -#ifndef GL_UNSIGNED_INT -#define GL_UNSIGNED_INT 0x1405 -#endif - if (FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_OES_element_index_uint]) - indexSize = GL_UNSIGNED_INT; - else -#endif - indexSize = GL_UNSIGNED_SHORT; - break; - } - } - - switch (pType) { - case scene::EPT_POINTS: - case scene::EPT_POINT_SPRITES: { -#ifdef GL_OES_point_sprite - if (pType == scene::EPT_POINT_SPRITES && FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_OES_point_sprite]) - glEnable(GL_POINT_SPRITE_OES); -#endif - // if ==0 we use the point size array - if (Material.Thickness != 0.f) { - float quadratic[] = {0.0f, 0.0f, 10.01f}; - glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, quadratic); - float maxParticleSize = 1.0f; - glGetFloatv(GL_POINT_SIZE_MAX, &maxParticleSize); - // maxParticleSize=maxParticleSize 0) { - if (vType == EVT_TANGENTS) { - glClientActiveTexture(GL_TEXTURE0 + 2); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } - if ((vType != EVT_STANDARD) || CacheHandler->getTextureCache().get(1)) { - glClientActiveTexture(GL_TEXTURE0 + 1); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - } - glClientActiveTexture(GL_TEXTURE0); - } - -#ifdef GL_OES_point_size_array - if (FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_OES_point_size_array] && (Material.Thickness == 0.0f)) - glDisableClientState(GL_POINT_SIZE_ARRAY_OES); -#endif - - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_NORMAL_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -} - -//! draws a 2d image, using a color and the alpha channel of the texture -void COGLES1Driver::draw2DImage(const video::ITexture *texture, - const core::position2d &pos, - const core::rect &sourceRect, - const core::rect *clipRect, SColor color, - bool useAlphaChannelOfTexture) -{ - if (!texture) - return; - - if (!sourceRect.isValid()) - return; - - core::position2d targetPos(pos); - core::position2d sourcePos(sourceRect.UpperLeftCorner); - core::dimension2d sourceSize(sourceRect.getSize()); - if (clipRect) { - if (targetPos.X < clipRect->UpperLeftCorner.X) { - sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X; - if (sourceSize.Width <= 0) - return; - - sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X; - targetPos.X = clipRect->UpperLeftCorner.X; - } - - if (targetPos.X + sourceSize.Width > clipRect->LowerRightCorner.X) { - sourceSize.Width -= (targetPos.X + sourceSize.Width) - clipRect->LowerRightCorner.X; - if (sourceSize.Width <= 0) - return; - } - - if (targetPos.Y < clipRect->UpperLeftCorner.Y) { - sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y; - if (sourceSize.Height <= 0) - return; - - sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y; - targetPos.Y = clipRect->UpperLeftCorner.Y; - } - - if (targetPos.Y + sourceSize.Height > clipRect->LowerRightCorner.Y) { - sourceSize.Height -= (targetPos.Y + sourceSize.Height) - clipRect->LowerRightCorner.Y; - if (sourceSize.Height <= 0) - return; - } - } - - // clip these coordinates - - if (targetPos.X < 0) { - sourceSize.Width += targetPos.X; - if (sourceSize.Width <= 0) - return; - - sourcePos.X -= targetPos.X; - targetPos.X = 0; - } - - const core::dimension2d &renderTargetSize = getCurrentRenderTargetSize(); - - if (targetPos.X + sourceSize.Width > (s32)renderTargetSize.Width) { - sourceSize.Width -= (targetPos.X + sourceSize.Width) - renderTargetSize.Width; - if (sourceSize.Width <= 0) - return; - } - - if (targetPos.Y < 0) { - sourceSize.Height += targetPos.Y; - if (sourceSize.Height <= 0) - return; - - sourcePos.Y -= targetPos.Y; - targetPos.Y = 0; - } - - if (targetPos.Y + sourceSize.Height > (s32)renderTargetSize.Height) { - sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height; - if (sourceSize.Height <= 0) - return; - } - - // ok, we've clipped everything. - // now draw it. - - // texcoords need to be flipped horizontally for RTTs - const bool isRTT = texture->isRenderTarget(); - const core::dimension2d &ss = texture->getOriginalSize(); - const f32 invW = 1.f / static_cast(ss.Width); - const f32 invH = 1.f / static_cast(ss.Height); - const core::rect tcoords( - sourcePos.X * invW, - (isRTT ? (sourcePos.Y + sourceSize.Height) : sourcePos.Y) * invH, - (sourcePos.X + sourceSize.Width) * invW, - (isRTT ? sourcePos.Y : (sourcePos.Y + sourceSize.Height)) * invH); - - const core::rect poss(targetPos, sourceSize); - - if (!CacheHandler->getTextureCache().set(0, texture)) - return; - - setRenderStates2DMode(color.getAlpha() < 255, true, useAlphaChannelOfTexture); - - u16 indices[] = {0, 1, 2, 3}; - S3DVertex vertices[4]; - vertices[0] = S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0, 0, 0, 1, color, tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y); - vertices[1] = S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0, 0, 0, 1, color, tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y); - vertices[2] = S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0, 0, 0, 1, color, tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y); - vertices[3] = S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0, 0, 0, 1, color, tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y); - drawVertexPrimitiveList2d3d(vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false); -} - -//! The same, but with a four element array of colors, one for each vertex -void COGLES1Driver::draw2DImage(const video::ITexture *texture, const core::rect &destRect, - const core::rect &sourceRect, const core::rect *clipRect, - const video::SColor *const colors, bool useAlphaChannelOfTexture) -{ - if (!texture) - return; - - // texcoords need to be flipped horizontally for RTTs - const bool isRTT = texture->isRenderTarget(); - const core::dimension2du &ss = texture->getOriginalSize(); - const f32 invW = 1.f / static_cast(ss.Width); - const f32 invH = 1.f / static_cast(ss.Height); - const core::rect tcoords( - sourceRect.UpperLeftCorner.X * invW, - (isRTT ? sourceRect.LowerRightCorner.Y : sourceRect.UpperLeftCorner.Y) * invH, - sourceRect.LowerRightCorner.X * invW, - (isRTT ? sourceRect.UpperLeftCorner.Y : sourceRect.LowerRightCorner.Y) * invH); - - const video::SColor temp[4] = { - 0xFFFFFFFF, - 0xFFFFFFFF, - 0xFFFFFFFF, - 0xFFFFFFFF, - }; - - const video::SColor *const useColor = colors ? colors : temp; - - if (!CacheHandler->getTextureCache().set(0, texture)) - return; - - setRenderStates2DMode(useColor[0].getAlpha() < 255 || useColor[1].getAlpha() < 255 || - useColor[2].getAlpha() < 255 || useColor[3].getAlpha() < 255, - true, useAlphaChannelOfTexture); - - if (clipRect) { - if (!clipRect->isValid()) - return; - - glEnable(GL_SCISSOR_TEST); - const core::dimension2d &renderTargetSize = getCurrentRenderTargetSize(); - glScissor(clipRect->UpperLeftCorner.X, renderTargetSize.Height - clipRect->LowerRightCorner.Y, - clipRect->getWidth(), clipRect->getHeight()); - } - - u16 indices[] = {0, 1, 2, 3}; - S3DVertex vertices[4]; - vertices[0] = S3DVertex((f32)destRect.UpperLeftCorner.X, (f32)destRect.UpperLeftCorner.Y, 0, 0, 0, 1, useColor[0], tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y); - vertices[1] = S3DVertex((f32)destRect.LowerRightCorner.X, (f32)destRect.UpperLeftCorner.Y, 0, 0, 0, 1, useColor[3], tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y); - vertices[2] = S3DVertex((f32)destRect.LowerRightCorner.X, (f32)destRect.LowerRightCorner.Y, 0, 0, 0, 1, useColor[2], tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y); - vertices[3] = S3DVertex((f32)destRect.UpperLeftCorner.X, (f32)destRect.LowerRightCorner.Y, 0, 0, 0, 1, useColor[1], tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y); - drawVertexPrimitiveList2d3d(vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false); - - if (clipRect) - glDisable(GL_SCISSOR_TEST); -} - -void COGLES1Driver::draw2DImage(const video::ITexture *texture, u32 layer, bool flip) -{ - if (!texture || !CacheHandler->getTextureCache().set(0, texture)) - return; - - setRenderStates2DMode(false, true, true); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - Transformation3DChanged = true; - - u16 indices[] = {0, 1, 2, 3}; - S3DVertex vertices[4]; - - vertices[0].Pos = core::vector3df(-1.f, 1.f, 0.f); - vertices[1].Pos = core::vector3df(1.f, 1.f, 0.f); - vertices[2].Pos = core::vector3df(1.f, -1.f, 0.f); - vertices[3].Pos = core::vector3df(-1.f, -1.f, 0.f); - - f32 modificator = (flip) ? 1.f : 0.f; - - vertices[0].TCoords = core::vector2df(0.f, 0.f + modificator); - vertices[1].TCoords = core::vector2df(1.f, 0.f + modificator); - vertices[2].TCoords = core::vector2df(1.f, 1.f - modificator); - vertices[3].TCoords = core::vector2df(0.f, 1.f - modificator); - - vertices[0].Color = 0xFFFFFFFF; - vertices[1].Color = 0xFFFFFFFF; - vertices[2].Color = 0xFFFFFFFF; - vertices[3].Color = 0xFFFFFFFF; - - drawVertexPrimitiveList2d3d(vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false); -} - -//! draws a set of 2d images, using a color and the alpha channel of the texture if desired. -void COGLES1Driver::draw2DImageBatch(const video::ITexture *texture, - const core::array> &positions, - const core::array> &sourceRects, - const core::rect *clipRect, - SColor color, - bool useAlphaChannelOfTexture) -{ - if (!texture) - return; - - const u32 drawCount = core::min_(positions.size(), sourceRects.size()); - if (!drawCount) - return; - - const core::dimension2d &ss = texture->getOriginalSize(); - if (!ss.Width || !ss.Height) - return; - const f32 invW = 1.f / static_cast(ss.Width); - const f32 invH = 1.f / static_cast(ss.Height); - const core::dimension2d &renderTargetSize = getCurrentRenderTargetSize(); - - if (!CacheHandler->getTextureCache().set(0, texture)) - return; - - setRenderStates2DMode(color.getAlpha() < 255, true, useAlphaChannelOfTexture); - - core::array vertices; - core::array quadIndices; - vertices.reallocate(drawCount * 4); - quadIndices.reallocate(drawCount * 6); - - for (u32 i = 0; i < drawCount; ++i) { - if (!sourceRects[i].isValid()) - continue; - - core::position2d targetPos(positions[i]); - core::position2d sourcePos(sourceRects[i].UpperLeftCorner); - // This needs to be signed as it may go negative. - core::dimension2d sourceSize(sourceRects[i].getSize()); - if (clipRect) { - if (targetPos.X < clipRect->UpperLeftCorner.X) { - sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X; - if (sourceSize.Width <= 0) - continue; - - sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X; - targetPos.X = clipRect->UpperLeftCorner.X; - } - - if (targetPos.X + sourceSize.Width > clipRect->LowerRightCorner.X) { - sourceSize.Width -= (targetPos.X + sourceSize.Width) - clipRect->LowerRightCorner.X; - if (sourceSize.Width <= 0) - continue; - } - - if (targetPos.Y < clipRect->UpperLeftCorner.Y) { - sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y; - if (sourceSize.Height <= 0) - continue; - - sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y; - targetPos.Y = clipRect->UpperLeftCorner.Y; - } - - if (targetPos.Y + sourceSize.Height > clipRect->LowerRightCorner.Y) { - sourceSize.Height -= (targetPos.Y + sourceSize.Height) - clipRect->LowerRightCorner.Y; - if (sourceSize.Height <= 0) - continue; - } - } - - // clip these coordinates - - if (targetPos.X < 0) { - sourceSize.Width += targetPos.X; - if (sourceSize.Width <= 0) - continue; - - sourcePos.X -= targetPos.X; - targetPos.X = 0; - } - - if (targetPos.X + sourceSize.Width > (s32)renderTargetSize.Width) { - sourceSize.Width -= (targetPos.X + sourceSize.Width) - renderTargetSize.Width; - if (sourceSize.Width <= 0) - continue; - } - - if (targetPos.Y < 0) { - sourceSize.Height += targetPos.Y; - if (sourceSize.Height <= 0) - continue; - - sourcePos.Y -= targetPos.Y; - targetPos.Y = 0; - } - - if (targetPos.Y + sourceSize.Height > (s32)renderTargetSize.Height) { - sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height; - if (sourceSize.Height <= 0) - continue; - } - - // ok, we've clipped everything. - - const core::rect tcoords( - sourcePos.X * invW, - sourcePos.Y * invH, - (sourcePos.X + sourceSize.Width) * invW, - (sourcePos.Y + sourceSize.Height) * invH); - - const core::rect poss(targetPos, sourceSize); - - const u32 vstart = vertices.size(); - - vertices.push_back(S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.UpperLeftCorner.Y, 0, 0, 0, 1, color, tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y)); - vertices.push_back(S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.UpperLeftCorner.Y, 0, 0, 0, 1, color, tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y)); - vertices.push_back(S3DVertex((f32)poss.LowerRightCorner.X, (f32)poss.LowerRightCorner.Y, 0, 0, 0, 1, color, tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y)); - vertices.push_back(S3DVertex((f32)poss.UpperLeftCorner.X, (f32)poss.LowerRightCorner.Y, 0, 0, 0, 1, color, tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y)); - - quadIndices.push_back(vstart); - quadIndices.push_back(vstart + 1); - quadIndices.push_back(vstart + 2); - quadIndices.push_back(vstart); - quadIndices.push_back(vstart + 2); - quadIndices.push_back(vstart + 3); - } - if (vertices.size()) - drawVertexPrimitiveList2d3d(vertices.pointer(), vertices.size(), - quadIndices.pointer(), vertices.size() / 2, - video::EVT_STANDARD, scene::EPT_TRIANGLES, - EIT_16BIT, false); -} - -//! draw a 2d rectangle -void COGLES1Driver::draw2DRectangle(SColor color, const core::rect &position, - const core::rect *clip) -{ - setRenderStates2DMode(color.getAlpha() < 255, false, false); - - core::rect pos = position; - - if (clip) - pos.clipAgainst(*clip); - - if (!pos.isValid()) - return; - - u16 indices[] = {0, 1, 2, 3}; - S3DVertex vertices[4]; - vertices[0] = S3DVertex((f32)pos.UpperLeftCorner.X, (f32)pos.UpperLeftCorner.Y, 0, 0, 0, 1, color, 0, 0); - vertices[1] = S3DVertex((f32)pos.LowerRightCorner.X, (f32)pos.UpperLeftCorner.Y, 0, 0, 0, 1, color, 0, 0); - vertices[2] = S3DVertex((f32)pos.LowerRightCorner.X, (f32)pos.LowerRightCorner.Y, 0, 0, 0, 1, color, 0, 0); - vertices[3] = S3DVertex((f32)pos.UpperLeftCorner.X, (f32)pos.LowerRightCorner.Y, 0, 0, 0, 1, color, 0, 0); - drawVertexPrimitiveList2d3d(vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false); -} - -//! draw an 2d rectangle -void COGLES1Driver::draw2DRectangle(const core::rect &position, - SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown, - const core::rect *clip) -{ - core::rect pos = position; - - if (clip) - pos.clipAgainst(*clip); - - if (!pos.isValid()) - return; - - setRenderStates2DMode(colorLeftUp.getAlpha() < 255 || - colorRightUp.getAlpha() < 255 || - colorLeftDown.getAlpha() < 255 || - colorRightDown.getAlpha() < 255, - false, false); - - u16 indices[] = {0, 1, 2, 3}; - S3DVertex vertices[4]; - vertices[0] = S3DVertex((f32)pos.UpperLeftCorner.X, (f32)pos.UpperLeftCorner.Y, 0, 0, 0, 1, colorLeftUp, 0, 0); - vertices[1] = S3DVertex((f32)pos.LowerRightCorner.X, (f32)pos.UpperLeftCorner.Y, 0, 0, 0, 1, colorRightUp, 0, 0); - vertices[2] = S3DVertex((f32)pos.LowerRightCorner.X, (f32)pos.LowerRightCorner.Y, 0, 0, 0, 1, colorRightDown, 0, 0); - vertices[3] = S3DVertex((f32)pos.UpperLeftCorner.X, (f32)pos.LowerRightCorner.Y, 0, 0, 0, 1, colorLeftDown, 0, 0); - drawVertexPrimitiveList2d3d(vertices, 4, indices, 2, video::EVT_STANDARD, scene::EPT_TRIANGLE_FAN, EIT_16BIT, false); -} - -//! Draws a 2d line. -void COGLES1Driver::draw2DLine(const core::position2d &start, - const core::position2d &end, - SColor color) -{ - setRenderStates2DMode(color.getAlpha() < 255, false, false); - - u16 indices[] = {0, 1}; - S3DVertex vertices[2]; - vertices[0] = S3DVertex((f32)start.X, (f32)start.Y, 0, 0, 0, 1, color, 0, 0); - vertices[1] = S3DVertex((f32)end.X, (f32)end.Y, 0, 0, 0, 1, color, 1, 1); - drawVertexPrimitiveList2d3d(vertices, 2, indices, 1, video::EVT_STANDARD, scene::EPT_LINES, EIT_16BIT, false); -} - -//! creates a matrix in supplied GLfloat array to pass to OGLES1 -inline void COGLES1Driver::getGLMatrix(GLfloat gl_matrix[16], const core::matrix4 &m) -{ - memcpy(gl_matrix, m.pointer(), 16 * sizeof(f32)); -} - -//! creates a opengltexturematrix from a D3D style texture matrix -inline void COGLES1Driver::getGLTextureMatrix(GLfloat *o, const core::matrix4 &m) -{ - o[0] = m[0]; - o[1] = m[1]; - o[2] = 0.f; - o[3] = 0.f; - - o[4] = m[4]; - o[5] = m[5]; - o[6] = 0.f; - o[7] = 0.f; - - o[8] = 0.f; - o[9] = 0.f; - o[10] = 1.f; - o[11] = 0.f; - - o[12] = m[8]; - o[13] = m[9]; - o[14] = 0.f; - o[15] = 1.f; -} - -ITexture *COGLES1Driver::createDeviceDependentTexture(const io::path &name, IImage *image) -{ - std::vector tmp { image }; - - COGLES1Texture *texture = new COGLES1Texture(name, tmp, ETT_2D, this); - - return texture; -} - -ITexture *COGLES1Driver::createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) -{ - COGLES1Texture *texture = new COGLES1Texture(name, image, ETT_CUBEMAP, this); - - return texture; -} - -//! Sets a material. All 3d drawing functions draw geometry now using this material. -void COGLES1Driver::setMaterial(const SMaterial &material) -{ - Material = material; - OverrideMaterial.apply(Material); - - for (u32 i = 0; i < Feature.MaxTextureUnits; ++i) - setTransform((E_TRANSFORMATION_STATE)(ETS_TEXTURE_0 + i), material.getTextureMatrix(i)); -} - -//! prints error if an error happened. -bool COGLES1Driver::testGLError(int code) -{ - if (!Params.DriverDebug) - return false; - - GLenum g = glGetError(); - switch (g) { - case GL_NO_ERROR: - return false; - case GL_INVALID_ENUM: - os::Printer::log("GL_INVALID_ENUM", core::stringc(code).c_str(), ELL_ERROR); - break; - case GL_INVALID_VALUE: - os::Printer::log("GL_INVALID_VALUE", core::stringc(code).c_str(), ELL_ERROR); - break; - case GL_INVALID_OPERATION: - os::Printer::log("GL_INVALID_OPERATION", core::stringc(code).c_str(), ELL_ERROR); - break; - case GL_STACK_OVERFLOW: - os::Printer::log("GL_STACK_OVERFLOW", core::stringc(code).c_str(), ELL_ERROR); - break; - case GL_STACK_UNDERFLOW: - os::Printer::log("GL_STACK_UNDERFLOW", core::stringc(code).c_str(), ELL_ERROR); - break; - case GL_OUT_OF_MEMORY: - os::Printer::log("GL_OUT_OF_MEMORY", core::stringc(code).c_str(), ELL_ERROR); - break; - }; - return true; -} - -//! sets the needed renderstates -void COGLES1Driver::setRenderStates3DMode() -{ - if (CurrentRenderMode != ERM_3D) { - // Reset Texture Stages - CacheHandler->setBlend(false); - glDisable(GL_ALPHA_TEST); - CacheHandler->setBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - // switch back the matrices - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf((Matrices[ETS_VIEW] * Matrices[ETS_WORLD]).pointer()); - - GLfloat glmat[16]; - getGLMatrix(glmat, Matrices[ETS_PROJECTION]); - glmat[12] *= -1.0f; - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(glmat); - - ResetRenderStates = true; - } - - if (ResetRenderStates || LastMaterial != Material) { - // unset old material - - if (LastMaterial.MaterialType != Material.MaterialType && - static_cast(LastMaterial.MaterialType) < MaterialRenderers.size()) - MaterialRenderers[LastMaterial.MaterialType].Renderer->OnUnsetMaterial(); - - // set new material. - if (static_cast(Material.MaterialType) < MaterialRenderers.size()) - MaterialRenderers[Material.MaterialType].Renderer->OnSetMaterial( - Material, LastMaterial, ResetRenderStates, this); - - LastMaterial = Material; - CacheHandler->correctCacheMaterial(LastMaterial); - ResetRenderStates = false; - } - - if (static_cast(Material.MaterialType) < MaterialRenderers.size()) - MaterialRenderers[Material.MaterialType].Renderer->OnRender(this, video::EVT_STANDARD); - - CurrentRenderMode = ERM_3D; -} - -GLint COGLES1Driver::getTextureWrapMode(u8 clamp) const -{ - switch (clamp) { - case ETC_CLAMP: - // return GL_CLAMP; not supported in ogl-es - return GL_CLAMP_TO_EDGE; - break; - case ETC_CLAMP_TO_EDGE: - return GL_CLAMP_TO_EDGE; - break; - case ETC_CLAMP_TO_BORDER: - // return GL_CLAMP_TO_BORDER; not supported in ogl-es - return GL_CLAMP_TO_EDGE; - break; - case ETC_MIRROR: -#ifdef GL_OES_texture_mirrored_repeat - if (FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_OES_texture_mirrored_repeat]) - return GL_MIRRORED_REPEAT_OES; - else -#endif - return GL_REPEAT; - break; - // the next three are not yet supported at all - case ETC_MIRROR_CLAMP: - case ETC_MIRROR_CLAMP_TO_EDGE: - case ETC_MIRROR_CLAMP_TO_BORDER: -#ifdef GL_OES_texture_mirrored_repeat - if (FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_OES_texture_mirrored_repeat]) - return GL_MIRRORED_REPEAT_OES; - else -#endif - return GL_CLAMP_TO_EDGE; - break; - case ETC_REPEAT: - default: - return GL_REPEAT; - break; - } -} - -//! Can be called by an IMaterialRenderer to make its work easier. -void COGLES1Driver::setBasicRenderStates(const SMaterial &material, const SMaterial &lastmaterial, - bool resetAllRenderStates) -{ - if (resetAllRenderStates || - lastmaterial.ColorMaterial != material.ColorMaterial) { - // we only have diffuse_and_ambient in ogl-es - if (material.ColorMaterial == ECM_DIFFUSE_AND_AMBIENT) - glEnable(GL_COLOR_MATERIAL); - else - glDisable(GL_COLOR_MATERIAL); - } - - if (resetAllRenderStates || - lastmaterial.AmbientColor != material.AmbientColor || - lastmaterial.DiffuseColor != material.DiffuseColor || - lastmaterial.EmissiveColor != material.EmissiveColor || - lastmaterial.ColorMaterial != material.ColorMaterial) { - GLfloat color[4]; - - const f32 inv = 1.0f / 255.0f; - - if ((material.ColorMaterial != video::ECM_AMBIENT) && - (material.ColorMaterial != video::ECM_DIFFUSE_AND_AMBIENT)) { - color[0] = material.AmbientColor.getRed() * inv; - color[1] = material.AmbientColor.getGreen() * inv; - color[2] = material.AmbientColor.getBlue() * inv; - color[3] = material.AmbientColor.getAlpha() * inv; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color); - } - - if ((material.ColorMaterial != video::ECM_DIFFUSE) && - (material.ColorMaterial != video::ECM_DIFFUSE_AND_AMBIENT)) { - color[0] = material.DiffuseColor.getRed() * inv; - color[1] = material.DiffuseColor.getGreen() * inv; - color[2] = material.DiffuseColor.getBlue() * inv; - color[3] = material.DiffuseColor.getAlpha() * inv; - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color); - } - - if (material.ColorMaterial != video::ECM_EMISSIVE) { - color[0] = material.EmissiveColor.getRed() * inv; - color[1] = material.EmissiveColor.getGreen() * inv; - color[2] = material.EmissiveColor.getBlue() * inv; - color[3] = material.EmissiveColor.getAlpha() * inv; - glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color); - } - } - - if (resetAllRenderStates || - lastmaterial.SpecularColor != material.SpecularColor || - lastmaterial.Shininess != material.Shininess) { - GLfloat color[] = {0.f, 0.f, 0.f, 1.f}; - const f32 inv = 1.0f / 255.0f; - - // disable Specular colors if no shininess is set - if ((material.Shininess != 0.0f) && - (material.ColorMaterial != video::ECM_SPECULAR)) { -#ifdef GL_EXT_separate_specular_color - if (FeatureAvailable[IRR_EXT_separate_specular_color]) - glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); -#endif - glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.Shininess); - color[0] = material.SpecularColor.getRed() * inv; - color[1] = material.SpecularColor.getGreen() * inv; - color[2] = material.SpecularColor.getBlue() * inv; - color[3] = material.SpecularColor.getAlpha() * inv; - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color); - } -#ifdef GL_EXT_separate_specular_color - else if (FeatureAvailable[IRR_EXT_separate_specular_color]) - glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR); -#endif - } - - // TODO ogl-es - // fillmode - // if (resetAllRenderStates || (lastmaterial.Wireframe != material.Wireframe) || (lastmaterial.PointCloud != material.PointCloud)) - // glPolygonMode(GL_FRONT_AND_BACK, material.Wireframe ? GL_LINE : material.PointCloud? GL_POINT : GL_FILL); - - // shademode - if (resetAllRenderStates || (lastmaterial.GouraudShading != material.GouraudShading)) { - if (material.GouraudShading) - glShadeModel(GL_SMOOTH); - else - glShadeModel(GL_FLAT); - } - - // lighting - if (resetAllRenderStates || (lastmaterial.Lighting != material.Lighting)) { - if (material.Lighting) - glEnable(GL_LIGHTING); - else - glDisable(GL_LIGHTING); - } - - // zbuffer - if (resetAllRenderStates || lastmaterial.ZBuffer != material.ZBuffer) { - switch (material.ZBuffer) { - case ECFN_DISABLED: - glDisable(GL_DEPTH_TEST); - break; - case ECFN_LESSEQUAL: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - break; - case ECFN_EQUAL: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_EQUAL); - break; - case ECFN_LESS: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LESS); - break; - case ECFN_NOTEQUAL: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_NOTEQUAL); - break; - case ECFN_GREATEREQUAL: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_GEQUAL); - break; - case ECFN_GREATER: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_GREATER); - break; - case ECFN_ALWAYS: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - break; - case ECFN_NEVER: - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_NEVER); - break; - } - } - - // zwrite - if (getWriteZBuffer(material)) { - glDepthMask(GL_TRUE); - } else { - glDepthMask(GL_FALSE); - } - - // back face culling - if (resetAllRenderStates || (lastmaterial.FrontfaceCulling != material.FrontfaceCulling) || (lastmaterial.BackfaceCulling != material.BackfaceCulling)) { - if ((material.FrontfaceCulling) && (material.BackfaceCulling)) { - glCullFace(GL_FRONT_AND_BACK); - glEnable(GL_CULL_FACE); - } else if (material.BackfaceCulling) { - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); - } else if (material.FrontfaceCulling) { - glCullFace(GL_FRONT); - glEnable(GL_CULL_FACE); - } else - glDisable(GL_CULL_FACE); - } - - // fog - if (resetAllRenderStates || lastmaterial.FogEnable != material.FogEnable) { - if (material.FogEnable) - glEnable(GL_FOG); - else - glDisable(GL_FOG); - } - - // normalization - if (resetAllRenderStates || lastmaterial.NormalizeNormals != material.NormalizeNormals) { - if (material.NormalizeNormals) - glEnable(GL_NORMALIZE); - else - glDisable(GL_NORMALIZE); - } - - // Color Mask - if (resetAllRenderStates || lastmaterial.ColorMask != material.ColorMask) { - glColorMask( - (material.ColorMask & ECP_RED) ? GL_TRUE : GL_FALSE, - (material.ColorMask & ECP_GREEN) ? GL_TRUE : GL_FALSE, - (material.ColorMask & ECP_BLUE) ? GL_TRUE : GL_FALSE, - (material.ColorMask & ECP_ALPHA) ? GL_TRUE : GL_FALSE); - } - - // Blend Equation - if (material.BlendOperation == EBO_NONE) - CacheHandler->setBlend(false); - else { - CacheHandler->setBlend(true); - - if (queryFeature(EVDF_BLEND_OPERATIONS)) { - switch (material.BlendOperation) { - case EBO_ADD: -#if defined(GL_OES_blend_subtract) - CacheHandler->setBlendEquation(GL_FUNC_ADD_OES); -#endif - break; - case EBO_SUBTRACT: -#if defined(GL_OES_blend_subtract) - CacheHandler->setBlendEquation(GL_FUNC_SUBTRACT_OES); -#endif - break; - case EBO_REVSUBTRACT: -#if defined(GL_OES_blend_subtract) - CacheHandler->setBlendEquation(GL_FUNC_REVERSE_SUBTRACT_OES); -#endif - break; - default: - break; - } - } - } - - // Blend Factor - if (IR(material.BlendFactor) & 0xFFFFFFFF // TODO: why the & 0xFFFFFFFF? - && material.MaterialType != EMT_ONETEXTURE_BLEND) { - E_BLEND_FACTOR srcRGBFact = EBF_ZERO; - E_BLEND_FACTOR dstRGBFact = EBF_ZERO; - E_BLEND_FACTOR srcAlphaFact = EBF_ZERO; - E_BLEND_FACTOR dstAlphaFact = EBF_ZERO; - E_MODULATE_FUNC modulo = EMFN_MODULATE_1X; - u32 alphaSource = 0; - - unpack_textureBlendFuncSeparate(srcRGBFact, dstRGBFact, srcAlphaFact, dstAlphaFact, modulo, alphaSource, material.BlendFactor); - - if (queryFeature(EVDF_BLEND_SEPARATE)) { - CacheHandler->setBlendFuncSeparate(getGLBlend(srcRGBFact), getGLBlend(dstRGBFact), - getGLBlend(srcAlphaFact), getGLBlend(dstAlphaFact)); - } else { - CacheHandler->setBlendFunc(getGLBlend(srcRGBFact), getGLBlend(dstRGBFact)); - } - } - - // TODO: Polygon Offset. Not sure if it was left out deliberately or if it won't work with this driver. - - // thickness - if (resetAllRenderStates || lastmaterial.Thickness != material.Thickness) { - if (AntiAlias) { - // glPointSize(core::clamp(static_cast(material.Thickness), DimSmoothedPoint[0], DimSmoothedPoint[1])); - // we don't use point smoothing - glPointSize(core::clamp(static_cast(material.Thickness), DimAliasedPoint[0], DimAliasedPoint[1])); - } else { - glPointSize(core::clamp(static_cast(material.Thickness), DimAliasedPoint[0], DimAliasedPoint[1])); - glLineWidth(core::clamp(static_cast(material.Thickness), DimAliasedLine[0], DimAliasedLine[1])); - } - } - - // Anti aliasing - if (resetAllRenderStates || lastmaterial.AntiAliasing != material.AntiAliasing) { - if (material.AntiAliasing & EAAM_ALPHA_TO_COVERAGE) - glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); - else if (lastmaterial.AntiAliasing & EAAM_ALPHA_TO_COVERAGE) - glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); - - if ((AntiAlias >= 2) && (material.AntiAliasing & (EAAM_SIMPLE | EAAM_QUALITY))) - glEnable(GL_MULTISAMPLE); - else - glDisable(GL_MULTISAMPLE); - } - - // Texture parameters - setTextureRenderStates(material, resetAllRenderStates); -} - -//! Compare in SMaterial doesn't check texture parameters, so we should call this on each OnRender call. -void COGLES1Driver::setTextureRenderStates(const SMaterial &material, bool resetAllRenderstates) -{ - // Set textures to TU/TIU and apply filters to them - - for (s32 i = Feature.MaxTextureUnits - 1; i >= 0; --i) { - CacheHandler->getTextureCache().set(i, material.TextureLayers[i].Texture); - - const COGLES1Texture *tmpTexture = CacheHandler->getTextureCache().get(i); - - if (!tmpTexture) - continue; - - GLenum tmpTextureType = tmpTexture->getOpenGLTextureType(); - - CacheHandler->setActiveTexture(GL_TEXTURE0 + i); - - { - const bool isRTT = tmpTexture->isRenderTarget(); - - glMatrixMode(GL_TEXTURE); - - if (!isRTT && Matrices[ETS_TEXTURE_0 + i].isIdentity()) - glLoadIdentity(); - else { - GLfloat glmat[16]; - if (isRTT) - getGLTextureMatrix(glmat, Matrices[ETS_TEXTURE_0 + i] * TextureFlipMatrix); - else - getGLTextureMatrix(glmat, Matrices[ETS_TEXTURE_0 + i]); - glLoadMatrixf(glmat); - } - } - - COGLES1Texture::SStatesCache &statesCache = tmpTexture->getStatesCache(); - - if (resetAllRenderstates) - statesCache.IsCached = false; - -#if defined(GL_EXT_texture_lod_bias) - if (FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_EXT_texture_lod_bias]) { - if (material.TextureLayers[i].LODBias) { - const float tmp = core::clamp(material.TextureLayers[i].LODBias * 0.125f, -MaxTextureLODBias, MaxTextureLODBias); - glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, tmp); - } else - glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0.f); - } -#endif - - if (!statesCache.IsCached || material.TextureLayers[i].MagFilter != statesCache.MagFilter) { - E_TEXTURE_MAG_FILTER magFilter = material.TextureLayers[i].MagFilter; - glTexParameteri(tmpTextureType, GL_TEXTURE_MAG_FILTER, - magFilter == ETMAGF_NEAREST ? GL_NEAREST : (assert(magFilter == ETMAGF_LINEAR), GL_LINEAR)); - - statesCache.MagFilter = magFilter; - } - - if (material.UseMipMaps && tmpTexture->hasMipMaps()) { - if (!statesCache.IsCached || material.TextureLayers[i].MinFilter != statesCache.MinFilter || - !statesCache.MipMapStatus) { - E_TEXTURE_MIN_FILTER minFilter = material.TextureLayers[i].MinFilter; - glTexParameteri(tmpTextureType, GL_TEXTURE_MIN_FILTER, - minFilter == ETMINF_NEAREST_MIPMAP_NEAREST ? GL_NEAREST_MIPMAP_NEAREST : minFilter == ETMINF_LINEAR_MIPMAP_NEAREST ? GL_LINEAR_MIPMAP_NEAREST - : minFilter == ETMINF_NEAREST_MIPMAP_LINEAR ? GL_NEAREST_MIPMAP_LINEAR - : (assert(minFilter == ETMINF_LINEAR_MIPMAP_LINEAR), GL_LINEAR_MIPMAP_LINEAR)); - - statesCache.MinFilter = minFilter; - statesCache.MipMapStatus = true; - } - } else { - if (!statesCache.IsCached || material.TextureLayers[i].MinFilter != statesCache.MinFilter || - statesCache.MipMapStatus) { - E_TEXTURE_MIN_FILTER minFilter = material.TextureLayers[i].MinFilter; - glTexParameteri(tmpTextureType, GL_TEXTURE_MIN_FILTER, - (minFilter == ETMINF_NEAREST_MIPMAP_NEAREST || minFilter == ETMINF_NEAREST_MIPMAP_LINEAR) ? GL_NEAREST : (assert(minFilter == ETMINF_LINEAR_MIPMAP_NEAREST || minFilter == ETMINF_LINEAR_MIPMAP_LINEAR), GL_LINEAR)); - - statesCache.MinFilter = minFilter; - statesCache.MipMapStatus = false; - } - } - -#ifdef GL_EXT_texture_filter_anisotropic - if (FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_EXT_texture_filter_anisotropic] && - (!statesCache.IsCached || material.TextureLayers[i].AnisotropicFilter != statesCache.AnisotropicFilter)) { - glTexParameteri(tmpTextureType, GL_TEXTURE_MAX_ANISOTROPY_EXT, - material.TextureLayers[i].AnisotropicFilter > 1 ? core::min_(MaxAnisotropy, material.TextureLayers[i].AnisotropicFilter) : 1); - - statesCache.AnisotropicFilter = material.TextureLayers[i].AnisotropicFilter; - } -#endif - - if (!statesCache.IsCached || material.TextureLayers[i].TextureWrapU != statesCache.WrapU) { - glTexParameteri(tmpTextureType, GL_TEXTURE_WRAP_S, getTextureWrapMode(material.TextureLayers[i].TextureWrapU)); - statesCache.WrapU = material.TextureLayers[i].TextureWrapU; - } - - if (!statesCache.IsCached || material.TextureLayers[i].TextureWrapV != statesCache.WrapV) { - glTexParameteri(tmpTextureType, GL_TEXTURE_WRAP_T, getTextureWrapMode(material.TextureLayers[i].TextureWrapV)); - statesCache.WrapV = material.TextureLayers[i].TextureWrapV; - } - - statesCache.IsCached = true; - } - - // be sure to leave in texture stage 0 - CacheHandler->setActiveTexture(GL_TEXTURE0); -} - -//! sets the needed renderstates -void COGLES1Driver::setRenderStates2DMode(bool alpha, bool texture, bool alphaChannel) -{ - if (CurrentRenderMode != ERM_2D || Transformation3DChanged) { - // unset last 3d material - if (CurrentRenderMode == ERM_3D) { - if (static_cast(LastMaterial.MaterialType) < MaterialRenderers.size()) - MaterialRenderers[LastMaterial.MaterialType].Renderer->OnUnsetMaterial(); - } - if (Transformation3DChanged) { - glMatrixMode(GL_PROJECTION); - - const core::dimension2d &renderTargetSize = getCurrentRenderTargetSize(); - core::matrix4 m(core::matrix4::EM4CONST_NOTHING); - m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0f, 1.0f); - m.setTranslation(core::vector3df(-1, 1, 0)); - glLoadMatrixf(m.pointer()); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - Transformation3DChanged = false; - } - } - - Material = (OverrideMaterial2DEnabled) ? OverrideMaterial2D : InitMaterial2D; - Material.Lighting = false; - Material.TextureLayers[0].Texture = (texture) ? const_cast(CacheHandler->getTextureCache().get(0)) : 0; - setTransform(ETS_TEXTURE_0, core::IdentityMatrix); - - setBasicRenderStates(Material, LastMaterial, false); - - LastMaterial = Material; - CacheHandler->correctCacheMaterial(LastMaterial); - - // no alphaChannel without texture - alphaChannel &= texture; - - if (alphaChannel || alpha) { - CacheHandler->setBlend(true); - CacheHandler->setBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - CacheHandler->setBlendEquation(GL_FUNC_ADD); - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.f); - } else { - CacheHandler->setBlend(false); - glDisable(GL_ALPHA_TEST); - } - - if (texture) { - // Due to the transformation change, the previous line would call a reset each frame - // but we can safely reset the variable as it was false before - Transformation3DChanged = false; - - if (alphaChannel) { - // if alpha and alpha texture just modulate, otherwise use only the alpha channel - if (alpha) { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } else { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE); - // rgb always modulates - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR); - } - } else { - if (alpha) { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR); - // rgb always modulates - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR); - } else { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } - } - } - - CurrentRenderMode = ERM_2D; -} - -//! \return Returns the name of the video driver. -const char *COGLES1Driver::getName() const -{ - return Name.c_str(); -} - -//! Sets the dynamic ambient light color. -void COGLES1Driver::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 -void COGLES1Driver::setViewPort(const core::rect &area) -{ - core::rect vp = area; - core::rect rendert(0, 0, getCurrentRenderTargetSize().Width, getCurrentRenderTargetSize().Height); - vp.clipAgainst(rendert); - - if (vp.getHeight() > 0 && vp.getWidth() > 0) - CacheHandler->setViewport(vp.UpperLeftCorner.X, getCurrentRenderTargetSize().Height - vp.UpperLeftCorner.Y - vp.getHeight(), vp.getWidth(), vp.getHeight()); - - ViewPort = vp; -} - -void COGLES1Driver::setViewPortRaw(u32 width, u32 height) -{ - CacheHandler->setViewport(0, 0, width, height); - ViewPort = core::recti(0, 0, width, height); -} - -//! Sets the fog mode. -void COGLES1Driver::setFog(SColor c, E_FOG_TYPE fogType, f32 start, - f32 end, f32 density, bool pixelFog, bool rangeFog) -{ - CNullDriver::setFog(c, fogType, start, end, density, pixelFog, rangeFog); - - glFogf(GL_FOG_MODE, GLfloat((fogType == EFT_FOG_LINEAR) ? GL_LINEAR : (fogType == EFT_FOG_EXP) ? GL_EXP - : GL_EXP2)); - -#ifdef GL_EXT_fog_coord - if (FeatureAvailable[IRR_EXT_fog_coord]) - glFogi(GL_FOG_COORDINATE_SOURCE, GL_FRAGMENT_DEPTH); -#endif - - if (fogType == EFT_FOG_LINEAR) { - glFogf(GL_FOG_START, start); - glFogf(GL_FOG_END, end); - } else - glFogf(GL_FOG_DENSITY, density); - - if (pixelFog) - glHint(GL_FOG_HINT, GL_NICEST); - else - glHint(GL_FOG_HINT, GL_FASTEST); - - SColorf color(c); - GLfloat data[4] = {color.r, color.g, color.b, color.a}; - glFogfv(GL_FOG_COLOR, data); -} - -//! Draws a 3d line. -void COGLES1Driver::draw3DLine(const core::vector3df &start, - const core::vector3df &end, SColor color) -{ - setRenderStates3DMode(); - - u16 indices[] = {0, 1}; - S3DVertex vertices[2]; - vertices[0] = S3DVertex(start.X, start.Y, start.Z, 0, 0, 1, color, 0, 0); - vertices[1] = S3DVertex(end.X, end.Y, end.Z, 0, 0, 1, color, 0, 0); - drawVertexPrimitiveList2d3d(vertices, 2, indices, 1, video::EVT_STANDARD, scene::EPT_LINES); -} - -//! Only used by the internal engine. Used to notify the driver that -//! the window was resized. -void COGLES1Driver::OnResize(const core::dimension2d &size) -{ - CNullDriver::OnResize(size); - CacheHandler->setViewport(0, 0, size.Width, size.Height); - Transformation3DChanged = true; -} - -//! Returns type of video driver -E_DRIVER_TYPE COGLES1Driver::getDriverType() const -{ - return EDT_OGLES1; -} - -//! returns color format -ECOLOR_FORMAT COGLES1Driver::getColorFormat() const -{ - return ColorFormat; -} - -//! Get a vertex shader constant index. -s32 COGLES1Driver::getVertexShaderConstantID(const c8 *name) -{ - return getPixelShaderConstantID(name); -} - -//! Get a pixel shader constant index. -s32 COGLES1Driver::getPixelShaderConstantID(const c8 *name) -{ - os::Printer::log("Error: Please use IMaterialRendererServices from IShaderConstantSetCallBack::OnSetConstants not VideoDriver->getPixelShaderConstantID()."); - return -1; -} - -//! Sets a constant for the vertex shader based on an index. -bool COGLES1Driver::setVertexShaderConstant(s32 index, const f32 *floats, int count) -{ - // pass this along, as in GLSL the same routine is used for both vertex and fragment shaders - return setPixelShaderConstant(index, floats, count); -} - -//! Int interface for the above. -bool COGLES1Driver::setVertexShaderConstant(s32 index, const s32 *ints, int count) -{ - return setPixelShaderConstant(index, ints, count); -} - -bool COGLES1Driver::setVertexShaderConstant(s32 index, const u32 *ints, int count) -{ - return setPixelShaderConstant(index, ints, count); -} - -//! Sets a constant for the pixel shader based on an index. -bool COGLES1Driver::setPixelShaderConstant(s32 index, const f32 *floats, int count) -{ - os::Printer::log("Error: Please use IMaterialRendererServices from IShaderConstantSetCallBack::OnSetConstants not VideoDriver->setPixelShaderConstant()."); - return false; -} - -//! Int interface for the above. -bool COGLES1Driver::setPixelShaderConstant(s32 index, const s32 *ints, int count) -{ - os::Printer::log("Error: Please use IMaterialRendererServices from IShaderConstantSetCallBack::OnSetConstants not VideoDriver->setPixelShaderConstant()."); - return false; -} - -bool COGLES1Driver::setPixelShaderConstant(s32 index, const u32 *ints, int count) -{ - os::Printer::log("Error: Please use IMaterialRendererServices from IShaderConstantSetCallBack::OnSetConstants not VideoDriver->setPixelShaderConstant()."); - return false; -} - -//! Adds a new material renderer to the VideoDriver, using GLSL to render geometry. -s32 COGLES1Driver::addHighLevelShaderMaterial( - const c8 *vertexShaderProgram, - const c8 *vertexShaderEntryPointName, - E_VERTEX_SHADER_TYPE vsCompileTarget, - const c8 *pixelShaderProgram, - const c8 *pixelShaderEntryPointName, - E_PIXEL_SHADER_TYPE psCompileTarget, - const c8 *geometryShaderProgram, - const c8 *geometryShaderEntryPointName, - E_GEOMETRY_SHADER_TYPE gsCompileTarget, - scene::E_PRIMITIVE_TYPE inType, - scene::E_PRIMITIVE_TYPE outType, - u32 verticesOut, - IShaderConstantSetCallBack *callback, - E_MATERIAL_TYPE baseMaterial, - s32 userData) -{ - os::Printer::log("No shader support."); - return -1; -} - -//! Returns a pointer to the IVideoDriver interface. (Implementation for -//! IMaterialRendererServices) -IVideoDriver *COGLES1Driver::getVideoDriver() -{ - return this; -} - -//! Returns pointer to the IGPUProgrammingServices interface. -IGPUProgrammingServices *COGLES1Driver::getGPUProgrammingServices() -{ - return this; -} - -ITexture *COGLES1Driver::addRenderTargetTexture(const core::dimension2d &size, - const io::path &name, const ECOLOR_FORMAT format) -{ - // disable mip-mapping - bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); - setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false); - - bool supportForFBO = (Feature.ColorAttachment > 0); - - core::dimension2du destSize(size); - - if (!supportForFBO) { - destSize = core::dimension2d(core::min_(size.Width, ScreenSize.Width), core::min_(size.Height, ScreenSize.Height)); - destSize = destSize.getOptimalSize((size == size.getOptimalSize()), false, false); - } - - COGLES1Texture *renderTargetTexture = new COGLES1Texture(name, destSize, ETT_2D, format, this); - addTexture(renderTargetTexture); - renderTargetTexture->drop(); - - // restore mip-mapping - setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, generateMipLevels); - - return renderTargetTexture; -} - -ITexture *COGLES1Driver::addRenderTargetTextureCubemap(const irr::u32 sideLen, const io::path &name, const ECOLOR_FORMAT format) -{ - // disable mip-mapping - bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS); - setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false); - - bool supportForFBO = (Feature.ColorAttachment > 0); - - const core::dimension2d size(sideLen, sideLen); - core::dimension2du destSize(size); - - if (!supportForFBO) { - destSize = core::dimension2d(core::min_(size.Width, ScreenSize.Width), core::min_(size.Height, ScreenSize.Height)); - destSize = destSize.getOptimalSize((size == size.getOptimalSize()), false, false); - } - - COGLES1Texture *renderTargetTexture = new COGLES1Texture(name, destSize, ETT_CUBEMAP, format, this); - addTexture(renderTargetTexture); - renderTargetTexture->drop(); - - // restore mip-mapping - setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, generateMipLevels); - - return renderTargetTexture; -} - -//! Returns the maximum amount of primitives -u32 COGLES1Driver::getMaximalPrimitiveCount() const -{ - return 65535; -} - -bool COGLES1Driver::setRenderTargetEx(IRenderTarget *target, u16 clearFlag, SColor clearColor, f32 clearDepth, u8 clearStencil) -{ - if (target && target->getDriverType() != EDT_OGLES1) { - os::Printer::log("Fatal Error: Tried to set a render target not owned by OpenGL driver.", ELL_ERROR); - return false; - } - - bool supportForFBO = (Feature.ColorAttachment > 0); - - core::dimension2d destRenderTargetSize(0, 0); - - if (target) { - COGLES1RenderTarget *renderTarget = static_cast(target); - - if (supportForFBO) { - CacheHandler->setFBO(renderTarget->getBufferID()); - renderTarget->update(); - } - - destRenderTargetSize = renderTarget->getSize(); - - setViewPortRaw(destRenderTargetSize.Width, destRenderTargetSize.Height); - } else { - if (supportForFBO) - CacheHandler->setFBO(0); - else { - COGLES1RenderTarget *prevRenderTarget = static_cast(CurrentRenderTarget); - COGLES1Texture *renderTargetTexture = static_cast(prevRenderTarget->getTexture()); - - if (renderTargetTexture) { - const COGLES1Texture *prevTexture = CacheHandler->getTextureCache().get(0); - - CacheHandler->getTextureCache().set(0, renderTargetTexture); - - const core::dimension2d size = renderTargetTexture->getSize(); - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size.Width, size.Height); - - CacheHandler->getTextureCache().set(0, prevTexture); - } - } - - destRenderTargetSize = core::dimension2d(0, 0); - - setViewPortRaw(ScreenSize.Width, ScreenSize.Height); - } - - if (CurrentRenderTargetSize != destRenderTargetSize) { - CurrentRenderTargetSize = destRenderTargetSize; - - Transformation3DChanged = true; - } - - CurrentRenderTarget = target; - - if (!supportForFBO) { - clearFlag |= ECBF_COLOR; - clearFlag |= ECBF_DEPTH; - } - - clearBuffers(clearFlag, clearColor, clearDepth, clearStencil); - - return true; -} - -void COGLES1Driver::clearBuffers(u16 flag, SColor color, f32 depth, u8 stencil) -{ - GLbitfield mask = 0; - - if (flag & ECBF_COLOR) { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - const f32 inv = 1.0f / 255.0f; - glClearColor(color.getRed() * inv, color.getGreen() * inv, - color.getBlue() * inv, color.getAlpha() * inv); - - mask |= GL_COLOR_BUFFER_BIT; - } - - if (flag & ECBF_DEPTH) { - glDepthMask(GL_TRUE); - glClearDepthf(depth); - mask |= GL_DEPTH_BUFFER_BIT; - } - - if (flag & ECBF_STENCIL) { - glClearStencil(stencil); - mask |= GL_STENCIL_BUFFER_BIT; - } - - if (mask) - glClear(mask); -} - -//! Returns an image created from the last rendered frame. -// We want to read the front buffer to get the latest render finished. -// This is not possible under ogl-es, though, so one has to call this method -// outside of the render loop only. -IImage *COGLES1Driver::createScreenShot(video::ECOLOR_FORMAT format, video::E_RENDER_TARGET target) -{ - if (target == video::ERT_MULTI_RENDER_TEXTURES || target == video::ERT_RENDER_TEXTURE || target == video::ERT_STEREO_BOTH_BUFFERS) - return 0; - GLint internalformat = GL_RGBA; - GLint type = GL_UNSIGNED_BYTE; - if (false && (FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_IMG_read_format] || FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_OES_read_format] || FeatureAvailable[COGLESCoreExtensionHandler::IRR_GL_EXT_read_format_bgra])) { -#ifdef GL_IMPLEMENTATION_COLOR_READ_TYPE_OES - glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES, &internalformat); - glGetIntegerv(GL_IMPLEMENTATION_COLOR_READ_TYPE_OES, &type); -#endif - // there are formats we don't support ATM - if (GL_UNSIGNED_SHORT_4_4_4_4 == type) - type = GL_UNSIGNED_SHORT_5_5_5_1; -#ifdef GL_EXT_read_format_bgra - else if (GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT == type) - type = GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT; -#endif - } - - IImage *newImage = 0; - if ((GL_RGBA == internalformat) -#ifdef GL_EXT_read_format_bgra - || (GL_BGRA_EXT == internalformat) -#endif - ) { - if (GL_UNSIGNED_BYTE == type) - newImage = new CImage(ECF_A8R8G8B8, ScreenSize); - else - newImage = new CImage(ECF_A1R5G5B5, ScreenSize); - } else { - if (GL_UNSIGNED_BYTE == type) - newImage = new CImage(ECF_R8G8B8, ScreenSize); - else - newImage = new CImage(ECF_R5G6B5, ScreenSize); - } - - u8 *pixels = static_cast(newImage->getData()); - if (!pixels) { - newImage->drop(); - return 0; - } - - glReadPixels(0, 0, ScreenSize.Width, ScreenSize.Height, internalformat, type, pixels); - - // opengl images are horizontally flipped, so we have to fix that here. - const s32 pitch = newImage->getPitch(); - u8 *p2 = pixels + (ScreenSize.Height - 1) * pitch; - u8 *tmpBuffer = new u8[pitch]; - for (u32 i = 0; i < ScreenSize.Height; i += 2) { - memcpy(tmpBuffer, pixels, pitch); - memcpy(pixels, p2, pitch); - memcpy(p2, tmpBuffer, pitch); - pixels += pitch; - p2 -= pitch; - } - delete[] tmpBuffer; - - if (testGLError(__LINE__)) { - newImage->drop(); - return 0; - } - - return newImage; -} - -void COGLES1Driver::removeTexture(ITexture *texture) -{ - CacheHandler->getTextureCache().remove(texture); - CNullDriver::removeTexture(texture); -} - -core::dimension2du COGLES1Driver::getMaxTextureSize() const -{ - return core::dimension2du(MaxTextureSize, MaxTextureSize); -} - -GLenum COGLES1Driver::getGLBlend(E_BLEND_FACTOR factor) const -{ - static GLenum const blendTable[] = { - GL_ZERO, - GL_ONE, - GL_DST_COLOR, - GL_ONE_MINUS_DST_COLOR, - GL_SRC_COLOR, - GL_ONE_MINUS_SRC_COLOR, - GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA, - GL_SRC_ALPHA_SATURATE, - }; - - return blendTable[factor]; -} - -GLenum COGLES1Driver::getZBufferBits() const -{ - GLenum bits = 0; - - switch (Params.ZBufferBits) { - case 24: -#if defined(GL_OES_depth24) - if (queryGLESFeature(COGLESCoreExtensionHandler::IRR_GL_OES_depth24)) - bits = GL_DEPTH_COMPONENT24_OES; - else -#endif - bits = GL_DEPTH_COMPONENT16; - break; - case 32: -#if defined(GL_OES_depth32) - if (queryGLESFeature(COGLESCoreExtensionHandler::IRR_GL_OES_depth32)) - bits = GL_DEPTH_COMPONENT32_OES; - else -#endif - bits = GL_DEPTH_COMPONENT16; - break; - default: - bits = GL_DEPTH_COMPONENT16; - break; - } - - return bits; -} - -bool COGLES1Driver::getColorFormatParameters(ECOLOR_FORMAT format, GLint &internalFormat, GLenum &pixelFormat, - GLenum &pixelType, void (**converter)(const void *, s32, void *)) const -{ - bool supported = false; - internalFormat = GL_RGBA; - pixelFormat = GL_RGBA; - pixelType = GL_UNSIGNED_BYTE; - *converter = 0; - - switch (format) { - case ECF_A1R5G5B5: - supported = true; - internalFormat = GL_RGBA; - pixelFormat = GL_RGBA; - pixelType = GL_UNSIGNED_SHORT_5_5_5_1; - *converter = CColorConverter::convert_A1R5G5B5toR5G5B5A1; - break; - case ECF_R5G6B5: - supported = true; - internalFormat = GL_RGB; - pixelFormat = GL_RGB; - pixelType = GL_UNSIGNED_SHORT_5_6_5; - break; - case ECF_R8G8B8: - supported = true; - internalFormat = GL_RGB; - pixelFormat = GL_RGB; - pixelType = GL_UNSIGNED_BYTE; - break; - case ECF_A8R8G8B8: - supported = true; - if (queryGLESFeature(COGLESCoreExtensionHandler::IRR_GL_IMG_texture_format_BGRA8888) || - queryGLESFeature(COGLESCoreExtensionHandler::IRR_GL_EXT_texture_format_BGRA8888) || - queryGLESFeature(COGLESCoreExtensionHandler::IRR_GL_APPLE_texture_format_BGRA8888)) { - internalFormat = GL_BGRA; - pixelFormat = GL_BGRA; - } else { - internalFormat = GL_RGBA; - pixelFormat = GL_RGBA; - *converter = CColorConverter::convert_A8R8G8B8toA8B8G8R8; - } - pixelType = GL_UNSIGNED_BYTE; - break; - case ECF_D16: - supported = true; - internalFormat = GL_DEPTH_COMPONENT16; - pixelFormat = GL_DEPTH_COMPONENT; - pixelType = GL_UNSIGNED_SHORT; - break; - case ECF_D32: -#if defined(GL_OES_depth32) - if (queryGLESFeature(COGLESCoreExtensionHandler::IRR_GL_OES_depth32)) { - supported = true; - internalFormat = GL_DEPTH_COMPONENT32_OES; - pixelFormat = GL_DEPTH_COMPONENT; - pixelType = GL_UNSIGNED_INT; - } -#endif - break; - case ECF_D24S8: -#ifdef GL_OES_packed_depth_stencil - if (queryGLESFeature(COGLESCoreExtensionHandler::IRR_GL_OES_packed_depth_stencil)) { - supported = true; - internalFormat = GL_DEPTH24_STENCIL8_OES; - pixelFormat = GL_DEPTH_STENCIL_OES; - pixelType = GL_UNSIGNED_INT_24_8_OES; - } -#endif - break; - case ECF_R8: - break; - case ECF_R8G8: - break; - case ECF_R16: - break; - case ECF_R16G16: - break; - case ECF_R16F: - break; - case ECF_G16R16F: - break; - case ECF_A16B16G16R16F: - break; - case ECF_R32F: - break; - case ECF_G32R32F: - break; - case ECF_A32B32G32R32F: - break; - default: - break; - } - -#ifdef _IRR_IOS_PLATFORM_ - if (internalFormat == GL_BGRA) - internalFormat = GL_RGBA; -#endif - - return supported; -} - -bool COGLES1Driver::queryTextureFormat(ECOLOR_FORMAT format) const -{ - GLint dummyInternalFormat; - GLenum dummyPixelFormat; - GLenum dummyPixelType; - void (*dummyConverter)(const void *, s32, void *); - return getColorFormatParameters(format, dummyInternalFormat, dummyPixelFormat, dummyPixelType, &dummyConverter); -} - -bool COGLES1Driver::needsTransparentRenderPass(const irr::video::SMaterial &material) const -{ - return CNullDriver::needsTransparentRenderPass(material) || material.isAlphaBlendOperation(); -} - -COGLES1CacheHandler *COGLES1Driver::getCacheHandler() const -{ - return CacheHandler; -} - -} // end namespace -} // end namespace - -#endif // _IRR_COMPILE_WITH_OGLES1_ - -namespace irr -{ -namespace video -{ - -#ifndef _IRR_COMPILE_WITH_OGLES1_ -class IVideoDriver; -class IContextManager; -#endif - -IVideoDriver *createOGLES1Driver(const SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager) -{ -#ifdef _IRR_COMPILE_WITH_OGLES1_ - return new COGLES1Driver(params, io, contextManager); -#else - return 0; -#endif // _IRR_COMPILE_WITH_OGLES1_ -} - -} // end namespace -} // end namespace diff --git a/irr/src/COGLESDriver.h b/irr/src/COGLESDriver.h deleted file mode 100644 index 8b9152cea..000000000 --- a/irr/src/COGLESDriver.h +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright (C) 2002-20014 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 "SIrrCreationParameters.h" - -#ifdef _IRR_COMPILE_WITH_OGLES1_ - -#include "CNullDriver.h" -#include "IMaterialRendererServices.h" -#include "EDriverFeatures.h" -#include "fast_atof.h" -#include "COGLESExtensionHandler.h" -#include "IContextManager.h" - -#define TEST_GL_ERROR(cls) (cls)->testGLError(__LINE__) - -namespace irr -{ -namespace video -{ - -class COGLES1Driver : public CNullDriver, public IMaterialRendererServices, public COGLES1ExtensionHandler -{ - friend class COpenGLCoreTexture; - -public: - //! constructor - COGLES1Driver(const SIrrlichtCreationParameters ¶ms, io::IFileSystem *io, IContextManager *contextManager); - - //! destructor - virtual ~COGLES1Driver(); - - virtual bool beginScene(u16 clearFlag, SColor clearColor = SColor(255, 0, 0, 0), f32 clearDepth = 1.f, u8 clearStencil = 0, - const SExposedVideoData &videoData = SExposedVideoData(), core::rect *sourceRect = 0) override; - - bool endScene() override; - - //! sets transformation - void setTransform(E_TRANSFORMATION_STATE state, const core::matrix4 &mat) override; - - struct SHWBufferLink_opengl : public SHWBufferLink - { - SHWBufferLink_opengl(const scene::IMeshBuffer *_MeshBuffer) : - SHWBufferLink(_MeshBuffer), vbo_verticesID(0), vbo_indicesID(0) {} - - GLuint vbo_verticesID; // tmp - GLuint vbo_indicesID; // tmp - - GLuint vbo_verticesSize; // tmp - GLuint vbo_indicesSize; // tmp - }; - - bool updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer); - bool updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer); - - //! updates hardware buffer if needed - bool updateHardwareBuffer(SHWBufferLink *HWBuffer) override; - - //! Create hardware buffer from mesh - SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer *mb) override; - - //! Delete hardware buffer (only some drivers can) - void deleteHardwareBuffer(SHWBufferLink *HWBuffer) override; - - //! Draw hardware buffer - void drawHardwareBuffer(SHWBufferLink *HWBuffer) override; - - IRenderTarget *addRenderTarget() override; - - //! draws a vertex primitive list - virtual void drawVertexPrimitiveList(const void *vertices, u32 vertexCount, - const void *indexList, u32 primitiveCount, - E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType) override; - - void drawVertexPrimitiveList2d3d(const void *vertices, u32 vertexCount, const void *indexList, u32 primitiveCount, E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType = EIT_16BIT, bool threed = true); - - //! queries the features of the driver, returns true if feature is available - bool queryFeature(E_VIDEO_DRIVER_FEATURE feature) const override - { - // return FeatureEnabled[feature] && COGLES1ExtensionHandler::queryFeature(feature); - return COGLES1ExtensionHandler::queryFeature(feature); - } - - //! Sets a material. - void setMaterial(const SMaterial &material) override; - - virtual void draw2DImage(const video::ITexture *texture, const core::position2d &destPos, - const core::rect &sourceRect, const core::rect *clipRect = 0, - SColor color = SColor(255, 255, 255, 255), bool useAlphaChannelOfTexture = false) override; - - virtual void draw2DImage(const video::ITexture *texture, const core::rect &destRect, - const core::rect &sourceRect, const core::rect *clipRect = 0, - const video::SColor *const colors = 0, bool useAlphaChannelOfTexture = false) override; - - virtual void draw2DImage(const video::ITexture *texture, u32 layer, bool flip); - - //! draws a set of 2d images, using a color and the alpha channel of the texture if desired. - virtual void draw2DImageBatch(const video::ITexture *texture, - const core::array> &positions, - const core::array> &sourceRects, - const core::rect *clipRect = 0, - SColor color = SColor(255, 255, 255, 255), - bool useAlphaChannelOfTexture = false) override; - - //! draw an 2d rectangle - virtual void draw2DRectangle(SColor color, const core::rect &pos, - const core::rect *clip = 0) override; - - //! Draws an 2d rectangle with a gradient. - virtual void draw2DRectangle(const core::rect &pos, - SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown, - const core::rect *clip = 0) override; - - //! Draws a 2d line. - virtual void draw2DLine(const core::position2d &start, - const core::position2d &end, - SColor color = SColor(255, 255, 255, 255)) override; - - //! Draws a 3d line. - virtual void draw3DLine(const core::vector3df &start, - const core::vector3df &end, - SColor color = SColor(255, 255, 255, 255)) override; - - //! Returns the name of the video driver. - const char *getName() const override; - - //! Sets the dynamic ambient light color. - void setAmbientLight(const SColorf &color) override; - - //! sets a viewport - void setViewPort(const core::rect &area) override; - - //! Sets the fog mode. - virtual void setFog(SColor color, E_FOG_TYPE fogType, f32 start, - f32 end, f32 density, bool pixelFog, bool rangeFog) override; - - //! Only used internally by the engine - void OnResize(const core::dimension2d &size) override; - - //! Returns type of video driver - E_DRIVER_TYPE getDriverType() const override; - - //! get color format of the current color buffer - ECOLOR_FORMAT getColorFormat() const override; - - //! Returns the transformation set by setTransform - const core::matrix4 &getTransform(E_TRANSFORMATION_STATE state) const override; - - //! Can be called by an IMaterialRenderer to make its work easier. - virtual void setBasicRenderStates(const SMaterial &material, const SMaterial &lastmaterial, - bool resetAllRenderstates) override; - - //! Compare in SMaterial doesn't check texture parameters, so we should call this on each OnRender call. - virtual void setTextureRenderStates(const SMaterial &material, bool resetAllRenderstates); - - //! Get a vertex shader constant index. - s32 getVertexShaderConstantID(const c8 *name) override; - - //! Get a pixel shader constant index. - s32 getPixelShaderConstantID(const c8 *name) override; - - //! Sets a constant for the vertex shader based on an index. - bool setVertexShaderConstant(s32 index, const f32 *floats, int count) override; - - //! Int interface for the above. - bool setVertexShaderConstant(s32 index, const s32 *ints, int count) override; - - //! Uint interface for the above. - bool setVertexShaderConstant(s32 index, const u32 *ints, int count) override; - - //! Sets a constant for the pixel shader based on an index. - bool setPixelShaderConstant(s32 index, const f32 *floats, int count) override; - - //! Int interface for the above. - bool setPixelShaderConstant(s32 index, const s32 *ints, int count) override; - - //! Uint interface for the above. - bool setPixelShaderConstant(s32 index, const u32 *ints, int count) override; - - //! Adds a new material renderer to the VideoDriver - virtual s32 addHighLevelShaderMaterial(const c8 *vertexShaderProgram, const c8 *vertexShaderEntryPointName, - E_VERTEX_SHADER_TYPE vsCompileTarget, const c8 *pixelShaderProgram, const c8 *pixelShaderEntryPointName, - E_PIXEL_SHADER_TYPE psCompileTarget, const c8 *geometryShaderProgram, const c8 *geometryShaderEntryPointName, - E_GEOMETRY_SHADER_TYPE gsCompileTarget, scene::E_PRIMITIVE_TYPE inType, scene::E_PRIMITIVE_TYPE outType, - u32 verticesOut, IShaderConstantSetCallBack *callback, E_MATERIAL_TYPE baseMaterial, - s32 userData) override; - - //! Returns pointer to the IGPUProgrammingServices interface. - IGPUProgrammingServices *getGPUProgrammingServices() override; - - //! Returns a pointer to the IVideoDriver interface. - IVideoDriver *getVideoDriver() override; - - //! Returns the maximum amount of primitives - u32 getMaximalPrimitiveCount() const override; - - virtual ITexture *addRenderTargetTexture(const core::dimension2d &size, - 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; - - virtual bool setRenderTargetEx(IRenderTarget *target, u16 clearFlag, SColor clearColor = SColor(255, 0, 0, 0), - f32 clearDepth = 1.f, u8 clearStencil = 0) override; - - void clearBuffers(u16 flag, SColor color = SColor(255, 0, 0, 0), f32 depth = 1.f, u8 stencil = 0) override; - - //! Returns an image created from the last rendered frame. - IImage *createScreenShot(video::ECOLOR_FORMAT format = video::ECF_UNKNOWN, video::E_RENDER_TARGET target = video::ERT_FRAME_BUFFER) override; - - //! checks if an OpenGL error has happened and prints it (+ some internal code which is usually the line number) - bool testGLError(int code = 0); - - //! Returns the graphics card vendor name. - core::stringc getVendorInfo() override - { - return VendorName; - } - - //! Get the maximal texture size for this driver - core::dimension2du getMaxTextureSize() const override; - - void removeTexture(ITexture *texture) override; - - //! Check if the driver supports creating textures with the given color format - bool queryTextureFormat(ECOLOR_FORMAT format) const override; - - //! 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; - - //! Convert E_BLEND_FACTOR to OpenGL equivalent - GLenum getGLBlend(E_BLEND_FACTOR factor) const; - - //! Get ZBuffer bits. - GLenum getZBufferBits() const; - - bool getColorFormatParameters(ECOLOR_FORMAT format, GLint &internalFormat, GLenum &pixelFormat, - GLenum &pixelType, void (**converter)(const void *, s32, void *)) const; - - COGLES1CacheHandler *getCacheHandler() const; - -private: - //! inits the opengl-es driver - bool genericDriverInit(const core::dimension2d &screenSize, bool stencilBuffer); - - ITexture *createDeviceDependentTexture(const io::path &name, IImage *image) override; - - ITexture *createDeviceDependentTextureCubemap(const io::path &name, const std::vector &image) override; - - //! creates a transposed matrix in supplied GLfloat array to pass to OGLES1 - inline void getGLMatrix(GLfloat gl_matrix[16], const core::matrix4 &m); - inline void getGLTextureMatrix(GLfloat gl_matrix[16], const core::matrix4 &m); - - //! Set GL pipeline to desired texture wrap modes of the material - void setWrapMode(const SMaterial &material); - - //! Get OpenGL wrap enum from Irrlicht enum - GLint getTextureWrapMode(u8 clamp) const; - - //! sets the needed renderstates - void setRenderStates3DMode(); - - //! sets the needed renderstates - void setRenderStates2DMode(bool alpha, bool texture, bool alphaChannel); - - void createMaterialRenderers(); - - //! Assign a hardware light to the specified requested light, if any - //! free hardware lights exist. - //! \param[in] lightIndex: the index of the requesting light - void assignHardwareLight(u32 lightIndex); - - //! Same as `CacheHandler->setViewport`, but also sets `ViewPort` - virtual void setViewPortRaw(u32 width, u32 height); - - COGLES1CacheHandler *CacheHandler; - - core::stringc Name; - core::matrix4 Matrices[ETS_COUNT]; - core::array ColorBuffer; - - //! enumeration for rendering modes such as 2d and 3d for minimizing the switching of renderStates. - enum E_RENDER_MODE - { - ERM_NONE = 0, // no render state has been set yet. - ERM_2D, // 2d drawing rendermode - ERM_3D // 3d rendering mode - }; - - E_RENDER_MODE CurrentRenderMode; - //! bool to make all renderstates reset if set to true. - bool ResetRenderStates; - bool Transformation3DChanged; - u8 AntiAlias; - - SMaterial Material, LastMaterial; - - core::stringc VendorName; - - core::matrix4 TextureFlipMatrix; - - //! Color buffer format - ECOLOR_FORMAT ColorFormat; - - SIrrlichtCreationParameters Params; - - IContextManager *ContextManager; -}; - -} // end namespace video -} // end namespace irr - -#endif // _IRR_COMPILE_WITH_OGLES1_ diff --git a/irr/src/COGLESExtensionHandler.cpp b/irr/src/COGLESExtensionHandler.cpp deleted file mode 100644 index 866a984d8..000000000 --- a/irr/src/COGLESExtensionHandler.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright (C) 2008 Christian Stehno -// Heavily based on the OpenGL driver implemented by Nikolaus Gebhardt -// 2017 modified by Michael Zeilfelder (unifying extension handlers) -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in Irrlicht.h - -#include "COGLESExtensionHandler.h" - -#ifdef _IRR_COMPILE_WITH_OGLES1_ - -#include "irrString.h" -#include "SMaterial.h" -#include "fast_atof.h" - -#if defined(_IRR_COMPILE_WITH_WINDOWS_DEVICE_) -#include -#else -#include -#endif - -namespace irr -{ -namespace video -{ - -COGLES1ExtensionHandler::COGLES1ExtensionHandler() : - COGLESCoreExtensionHandler(), - MaxLights(0), pGlBlendEquationOES(0), pGlBlendFuncSeparateOES(0), - pGlBindFramebufferOES(0), pGlDeleteFramebuffersOES(0), - pGlGenFramebuffersOES(0), pGlCheckFramebufferStatusOES(0), - pGlFramebufferTexture2DOES(0), pGlGenerateMipmapOES(0) -{ -} - -void COGLES1ExtensionHandler::initExtensions() -{ - getGLVersion(); - - if (Version >= 100) - os::Printer::log("OpenGL ES driver version is 1.1.", ELL_INFORMATION); - else - os::Printer::log("OpenGL ES driver version is 1.0.", ELL_WARNING); - - getGLExtensions(); - - GLint val = 0; - - glGetIntegerv(GL_MAX_LIGHTS, &val); - MaxLights = static_cast(val); - - glGetIntegerv(GL_MAX_TEXTURE_UNITS, &val); - Feature.MaxTextureUnits = static_cast(val); - -#ifdef GL_EXT_texture_filter_anisotropic - if (FeatureAvailable[IRR_GL_EXT_texture_filter_anisotropic]) { - glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &val); - MaxAnisotropy = static_cast(val); - } -#endif -#ifdef GL_MAX_ELEMENTS_INDICES - glGetIntegerv(GL_MAX_ELEMENTS_INDICES, &val); - MaxIndices = val; -#endif - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &val); - MaxTextureSize = static_cast(val); -#ifdef GL_EXT_texture_lod_bias - if (FeatureAvailable[IRR_GL_EXT_texture_lod_bias]) - glGetFloatv(GL_MAX_TEXTURE_LOD_BIAS_EXT, &MaxTextureLODBias); -#endif - glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, DimAliasedLine); - glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, DimAliasedPoint); - - Feature.MaxTextureUnits = core::min_(Feature.MaxTextureUnits, static_cast(MATERIAL_MAX_TEXTURES)); - Feature.ColorAttachment = 1; - - pGlBlendEquationOES = (PFNGLBLENDEQUATIONOESPROC)eglGetProcAddress("glBlendEquationOES"); - pGlBlendFuncSeparateOES = (PFNGLBLENDFUNCSEPARATEOESPROC)eglGetProcAddress("glBlendFuncSeparateOES"); - pGlBindFramebufferOES = (PFNGLBINDFRAMEBUFFEROESPROC)eglGetProcAddress("glBindFramebufferOES"); - pGlDeleteFramebuffersOES = (PFNGLDELETEFRAMEBUFFERSOESPROC)eglGetProcAddress("glDeleteFramebuffersOES"); - pGlGenFramebuffersOES = (PFNGLGENFRAMEBUFFERSOESPROC)eglGetProcAddress("glGenFramebuffersOES"); - pGlCheckFramebufferStatusOES = (PFNGLCHECKFRAMEBUFFERSTATUSOESPROC)eglGetProcAddress("glCheckFramebufferStatusOES"); - pGlFramebufferTexture2DOES = (PFNGLFRAMEBUFFERTEXTURE2DOESPROC)eglGetProcAddress("glFramebufferTexture2DOES"); - pGlGenerateMipmapOES = (PFNGLGENERATEMIPMAPOESPROC)eglGetProcAddress("glGenerateMipmapOES"); -} - -} // end namespace video -} // end namespace irr - -#endif // _IRR_COMPILE_WITH_OGLES2_ diff --git a/irr/src/COGLESExtensionHandler.h b/irr/src/COGLESExtensionHandler.h deleted file mode 100644 index a316afaad..000000000 --- a/irr/src/COGLESExtensionHandler.h +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright (C) 2008 Christian Stehno -// Heavily based on the OpenGL driver implemented by Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in Irrlicht.h - -#pragma once - -#ifdef _IRR_COMPILE_WITH_OGLES1_ - -#include "EDriverFeatures.h" -#include "irrTypes.h" -#include "os.h" - -#include "COGLESCommon.h" - -#include "COGLESCoreExtensionHandler.h" - -namespace irr -{ -namespace video -{ - -class COGLES1ExtensionHandler : public COGLESCoreExtensionHandler -{ -public: - COGLES1ExtensionHandler(); - - void initExtensions(); - - bool queryFeature(video::E_VIDEO_DRIVER_FEATURE feature) const - { - switch (feature) { - case EVDF_RENDER_TO_TARGET: - case EVDF_HARDWARE_TL: - case EVDF_MULTITEXTURE: - case EVDF_BILINEAR_FILTER: - case EVDF_MIP_MAP: - case EVDF_TEXTURE_NSQUARE: - case EVDF_STENCIL_BUFFER: - case EVDF_ALPHA_TO_COVERAGE: - case EVDF_COLOR_MASK: - case EVDF_POLYGON_OFFSET: - case EVDF_TEXTURE_MATRIX: - return true; - case EVDF_TEXTURE_NPOT: - return FeatureAvailable[IRR_GL_APPLE_texture_2D_limited_npot] || FeatureAvailable[IRR_GL_OES_texture_npot]; - case EVDF_MIP_MAP_AUTO_UPDATE: - return Version > 100; - case EVDF_BLEND_OPERATIONS: - return FeatureAvailable[IRR_GL_OES_blend_subtract]; - case EVDF_BLEND_SEPARATE: - return FeatureAvailable[IRR_GL_OES_blend_func_separate]; - case EVDF_FRAMEBUFFER_OBJECT: - return FeatureAvailable[IRR_GL_OES_framebuffer_object]; - case EVDF_VERTEX_BUFFER_OBJECT: - return Version > 100; - default: - return true; - }; - } - - inline void irrGlActiveTexture(GLenum texture) - { - glActiveTexture(texture); - } - - inline void irrGlCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, - GLsizei imageSize, const void *data) - { - glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); - } - - inline void irrGlCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, - GLenum format, GLsizei imageSize, const void *data) - { - glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); - } - - inline void irrGlUseProgram(GLuint prog) - { - } - - inline void irrGlBindFramebuffer(GLenum target, GLuint framebuffer) - { - if (pGlBindFramebufferOES) - pGlBindFramebufferOES(target, framebuffer); - } - - inline void irrGlDeleteFramebuffers(GLsizei n, const GLuint *framebuffers) - { - if (pGlDeleteFramebuffersOES) - pGlDeleteFramebuffersOES(n, framebuffers); - } - - inline void irrGlGenFramebuffers(GLsizei n, GLuint *framebuffers) - { - if (pGlGenFramebuffersOES) - pGlGenFramebuffersOES(n, framebuffers); - } - - inline GLenum irrGlCheckFramebufferStatus(GLenum target) - { - if (pGlCheckFramebufferStatusOES) - return pGlCheckFramebufferStatusOES(target); - else - return 0; - } - - inline void irrGlFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) - { - if (pGlFramebufferTexture2DOES) - pGlFramebufferTexture2DOES(target, attachment, textarget, texture, level); - } - - inline void irrGlGenerateMipmap(GLenum target) - { - if (pGlGenerateMipmapOES) - pGlGenerateMipmapOES(target); - } - - inline void irrGlActiveStencilFace(GLenum face) - { - } - - inline void irrGlDrawBuffer(GLenum mode) - { - } - - inline void irrGlDrawBuffers(GLsizei n, const GLenum *bufs) - { - } - - inline void irrGlBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) - { - if (pGlBlendFuncSeparateOES) - pGlBlendFuncSeparateOES(srcRGB, dstRGB, srcAlpha, dstAlpha); - } - - inline void irrGlBlendEquation(GLenum mode) - { - if (pGlBlendEquationOES) - pGlBlendEquationOES(mode); - } - - inline void irrGlEnableIndexed(GLenum target, GLuint index) - { - } - - inline void irrGlDisableIndexed(GLenum target, GLuint index) - { - } - - inline void irrGlColorMaskIndexed(GLuint buf, GLboolean r, GLboolean g, GLboolean b, GLboolean a) - { - } - - inline void irrGlBlendFuncIndexed(GLuint buf, GLenum src, GLenum dst) - { - } - - inline void irrGlBlendFuncSeparateIndexed(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) - { - } - - inline void irrGlBlendEquationIndexed(GLuint buf, GLenum mode) - { - } - - inline void irrGlBlendEquationSeparateIndexed(GLuint buf, GLenum modeRGB, GLenum modeAlpha) - { - } - -protected: - u8 MaxLights; - - PFNGLBLENDEQUATIONOESPROC pGlBlendEquationOES; - PFNGLBLENDFUNCSEPARATEOESPROC pGlBlendFuncSeparateOES; - PFNGLBINDFRAMEBUFFEROESPROC pGlBindFramebufferOES; - PFNGLDELETEFRAMEBUFFERSOESPROC pGlDeleteFramebuffersOES; - PFNGLGENFRAMEBUFFERSOESPROC pGlGenFramebuffersOES; - PFNGLCHECKFRAMEBUFFERSTATUSOESPROC pGlCheckFramebufferStatusOES; - PFNGLFRAMEBUFFERTEXTURE2DOESPROC pGlFramebufferTexture2DOES; - PFNGLGENERATEMIPMAPOESPROC pGlGenerateMipmapOES; -}; - -} -} - -#endif diff --git a/irr/src/COGLESMaterialRenderer.h b/irr/src/COGLESMaterialRenderer.h deleted file mode 100644 index 69d1b81ff..000000000 --- a/irr/src/COGLESMaterialRenderer.h +++ /dev/null @@ -1,286 +0,0 @@ -// Copyright (C) 2002-2008 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#pragma once - -#ifdef _IRR_COMPILE_WITH_OGLES1_ - -#include "COGLESDriver.h" -#include "IMaterialRenderer.h" - -namespace irr -{ -namespace video -{ - -//! Base class for all internal OGLES1 material renderers -class COGLES1MaterialRenderer : public IMaterialRenderer -{ -public: - //! Constructor - COGLES1MaterialRenderer(video::COGLES1Driver *driver) : - Driver(driver) - { - } - -protected: - video::COGLES1Driver *Driver; -}; - -//! Solid material renderer -class COGLES1MaterialRenderer_SOLID : public COGLES1MaterialRenderer -{ -public: - COGLES1MaterialRenderer_SOLID(video::COGLES1Driver *d) : - COGLES1MaterialRenderer(d) {} - - virtual void OnSetMaterial(const SMaterial &material, const SMaterial &lastMaterial, - bool resetAllRenderstates, IMaterialRendererServices *services) - { - Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); - - if (resetAllRenderstates || (material.MaterialType != lastMaterial.MaterialType)) { - // thanks to Murphy, the following line removed some - // bugs with several OGLES1 implementations. - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } - } -}; - -//! Generic Texture Blend -class COGLES1MaterialRenderer_ONETEXTURE_BLEND : public COGLES1MaterialRenderer -{ -public: - COGLES1MaterialRenderer_ONETEXTURE_BLEND(video::COGLES1Driver *d) : - COGLES1MaterialRenderer(d) {} - - virtual void OnSetMaterial(const SMaterial &material, const SMaterial &lastMaterial, - bool resetAllRenderstates, IMaterialRendererServices *services) - { - Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); - - // if (material.MaterialType != lastMaterial.MaterialType || - // material.MaterialTypeParam != lastMaterial.MaterialTypeParam || - // resetAllRenderstates) - { - E_BLEND_FACTOR srcRGBFact, dstRGBFact, srcAlphaFact, dstAlphaFact; - E_MODULATE_FUNC modulate; - u32 alphaSource; - unpack_textureBlendFuncSeparate(srcRGBFact, dstRGBFact, srcAlphaFact, dstAlphaFact, modulate, alphaSource, material.MaterialTypeParam); - - Driver->getCacheHandler()->setBlend(true); - - if (Driver->queryFeature(EVDF_BLEND_SEPARATE)) { - Driver->getCacheHandler()->setBlendFuncSeparate(Driver->getGLBlend(srcRGBFact), Driver->getGLBlend(dstRGBFact), - Driver->getGLBlend(srcAlphaFact), Driver->getGLBlend(dstAlphaFact)); - } else { - Driver->getCacheHandler()->setBlendFunc(Driver->getGLBlend(srcRGBFact), Driver->getGLBlend(dstRGBFact)); - } - - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS); - - glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, (f32)modulate); - - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.f); - - if (textureBlendFunc_hasAlpha(srcRGBFact) || textureBlendFunc_hasAlpha(dstRGBFact) || - textureBlendFunc_hasAlpha(srcAlphaFact) || textureBlendFunc_hasAlpha(dstAlphaFact)) { - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE); - - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR); - } - } - } - - virtual void OnUnsetMaterial() - { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE, 1.f); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS); - - Driver->getCacheHandler()->setBlend(false); - glDisable(GL_ALPHA_TEST); - } - - //! Returns if the material is transparent. - /** Is not always transparent, but mostly. */ - virtual bool isTransparent() const - { - return true; - } - -private: - u32 getGLBlend(E_BLEND_FACTOR factor) const - { - u32 r = 0; - switch (factor) { - case EBF_ZERO: - r = GL_ZERO; - break; - case EBF_ONE: - r = GL_ONE; - break; - case EBF_DST_COLOR: - r = GL_DST_COLOR; - break; - case EBF_ONE_MINUS_DST_COLOR: - r = GL_ONE_MINUS_DST_COLOR; - break; - case EBF_SRC_COLOR: - r = GL_SRC_COLOR; - break; - case EBF_ONE_MINUS_SRC_COLOR: - r = GL_ONE_MINUS_SRC_COLOR; - break; - case EBF_SRC_ALPHA: - r = GL_SRC_ALPHA; - break; - case EBF_ONE_MINUS_SRC_ALPHA: - r = GL_ONE_MINUS_SRC_ALPHA; - break; - case EBF_DST_ALPHA: - r = GL_DST_ALPHA; - break; - case EBF_ONE_MINUS_DST_ALPHA: - r = GL_ONE_MINUS_DST_ALPHA; - break; - case EBF_SRC_ALPHA_SATURATE: - r = GL_SRC_ALPHA_SATURATE; - break; - } - return r; - } -}; - -//! Transparent vertex alpha material renderer -class COGLES1MaterialRenderer_TRANSPARENT_VERTEX_ALPHA : public COGLES1MaterialRenderer -{ -public: - COGLES1MaterialRenderer_TRANSPARENT_VERTEX_ALPHA(video::COGLES1Driver *d) : - COGLES1MaterialRenderer(d) {} - - virtual void OnSetMaterial(const SMaterial &material, const SMaterial &lastMaterial, - bool resetAllRenderstates, IMaterialRendererServices *services) - { - Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); - - Driver->getCacheHandler()->setBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - Driver->getCacheHandler()->setBlend(true); - - if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PRIMARY_COLOR); - - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PRIMARY_COLOR); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_TEXTURE); - } - } - - virtual void OnUnsetMaterial() - { - // default values - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_PREVIOUS); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS); - - Driver->getCacheHandler()->setBlend(false); - } - - //! Returns if the material is transparent. - virtual bool isTransparent() const - { - return true; - } -}; - -//! Transparent alpha channel material renderer -class COGLES1MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL : public COGLES1MaterialRenderer -{ -public: - COGLES1MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL(video::COGLES1Driver *d) : - COGLES1MaterialRenderer(d) {} - - virtual void OnSetMaterial(const SMaterial &material, const SMaterial &lastMaterial, - bool resetAllRenderstates, IMaterialRendererServices *services) - { - Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); - - Driver->getCacheHandler()->setBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - Driver->getCacheHandler()->setBlend(true); - - if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates || material.MaterialTypeParam != lastMaterial.MaterialTypeParam) { - glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS); - - glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); - glTexEnvf(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE); - - glEnable(GL_ALPHA_TEST); - - glAlphaFunc(GL_GREATER, material.MaterialTypeParam); - } - } - - virtual void OnUnsetMaterial() - { - glDisable(GL_ALPHA_TEST); - Driver->getCacheHandler()->setBlend(false); - } - - //! Returns if the material is transparent. - virtual bool isTransparent() const - { - return true; - } -}; - -//! Transparent alpha channel material renderer -class COGLES1MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF : public COGLES1MaterialRenderer -{ -public: - COGLES1MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF(video::COGLES1Driver *d) : - COGLES1MaterialRenderer(d) {} - - virtual void OnSetMaterial(const SMaterial &material, const SMaterial &lastMaterial, - bool resetAllRenderstates, IMaterialRendererServices *services) - { - Driver->setBasicRenderStates(material, lastMaterial, resetAllRenderstates); - - if (material.MaterialType != lastMaterial.MaterialType || resetAllRenderstates) { - glEnable(GL_ALPHA_TEST); - glAlphaFunc(GL_GREATER, 0.5f); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - } - } - - virtual void OnUnsetMaterial() - { - glDisable(GL_ALPHA_TEST); - } - - //! Returns if the material is transparent. - virtual bool isTransparent() const - { - return false; // this material is not really transparent because it does no blending. - } -}; - -} // end namespace video -} // end namespace irr - -#endif diff --git a/irr/src/COpenGLCoreCacheHandler.h b/irr/src/COpenGLCoreCacheHandler.h index ae3661313..a1277bfed 100644 --- a/irr/src/COpenGLCoreCacheHandler.h +++ b/irr/src/COpenGLCoreCacheHandler.h @@ -84,12 +84,12 @@ class COpenGLCoreCacheHandler if (curTextureType != prevTextureType) { GL.BindTexture(prevTextureType, 0); -#if (defined(IRR_COMPILE_GL_COMMON) || defined(IRR_COMPILE_GLES_COMMON)) +#if defined(IRR_COMPILE_GL_COMMON) GL.Disable(prevTextureType); GL.Enable(curTextureType); #endif } -#if (defined(IRR_COMPILE_GL_COMMON) || defined(IRR_COMPILE_GLES_COMMON)) +#if defined(IRR_COMPILE_GL_COMMON) else if (!prevTexture) GL.Enable(curTextureType); #endif @@ -109,7 +109,7 @@ class COpenGLCoreCacheHandler GL.BindTexture(prevTextureType, 0); -#if (defined(IRR_COMPILE_GL_COMMON) || defined(IRR_COMPILE_GLES_COMMON)) +#if defined(IRR_COMPILE_GL_COMMON) GL.Disable(prevTextureType); #endif } @@ -222,7 +222,7 @@ public: Driver->irrGlActiveTexture(ActiveTexture); -#if (defined(IRR_COMPILE_GL_COMMON) || defined(IRR_COMPILE_GLES_COMMON)) +#if defined(IRR_COMPILE_GL_COMMON) GL.Disable(GL_TEXTURE_2D); #endif diff --git a/irr/src/COpenGLCoreTexture.h b/irr/src/COpenGLCoreTexture.h index 218eabece..95cac4c22 100644 --- a/irr/src/COpenGLCoreTexture.h +++ b/irr/src/COpenGLCoreTexture.h @@ -296,8 +296,7 @@ public: delete[] tmpBuffer; } -#elif (defined(IRR_COMPILE_GLES2_COMMON) || defined(IRR_COMPILE_GLES_COMMON)) - // TODO: on ES2 we can likely also work with glCopyTexImage2D instead of rendering which should be faster. +#elif defined(IRR_COMPILE_GLES2_COMMON) COpenGLCoreTexture *tmpTexture = new COpenGLCoreTexture("OGL_CORE_LOCK_TEXTURE", Size, ETT_2D, ColorFormat, Driver); GLuint tmpFBO = 0; diff --git a/irr/src/Irrlicht.cpp b/irr/src/Irrlicht.cpp index d119584e3..eb94fa4eb 100644 --- a/irr/src/Irrlicht.cpp +++ b/irr/src/Irrlicht.cpp @@ -100,10 +100,6 @@ extern "C" IRRLICHT_API bool IRRCALLCONV isDriverSupported(E_DRIVER_TYPE driver) case EDT_OPENGL: return true; #endif -#ifdef _IRR_COMPILE_WITH_OGLES1_ - case EDT_OGLES1: - return true; -#endif #ifdef _IRR_COMPILE_WITH_OGLES2_ case EDT_OGLES2: return true; diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 18b9ff158..4400dd90e 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -418,7 +418,6 @@ std::vector RenderingEngine::getSupportedVideoDrivers() video::EDT_OPENGL, video::EDT_OPENGL3, video::EDT_OGLES2, - video::EDT_OGLES1, video::EDT_NULL, }; std::vector drivers; @@ -454,7 +453,6 @@ const VideoDriverInfo &RenderingEngine::getVideoDriverInfo(irr::video::E_DRIVER_ {(int)video::EDT_NULL, {"null", "NULL Driver"}}, {(int)video::EDT_OPENGL, {"opengl", "OpenGL"}}, {(int)video::EDT_OPENGL3, {"opengl3", "OpenGL 3+"}}, - {(int)video::EDT_OGLES1, {"ogles1", "OpenGL ES1"}}, {(int)video::EDT_OGLES2, {"ogles2", "OpenGL ES2"}}, }; return driver_info_map.at((int)type); From 1977517d7ab9aaad1e7991c030c412480eb3a103 Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Wed, 15 May 2024 21:11:16 +0200 Subject: [PATCH 43/75] Rename TouchScreenGUI -> TouchControls to avoid confusion between touchscreen-related settings that affect GUIs (formspecs) and touchscreen-related settings that affect the touch controls (TouchControls / formerly TouchScreenGUI) --- src/client/clientlauncher.cpp | 8 +-- src/client/game.cpp | 54 +++++++++---------- src/client/hud.cpp | 10 ++-- src/client/inputhandler.cpp | 20 +++---- src/clientdynamicinfo.cpp | 4 +- src/gui/CMakeLists.txt | 2 +- src/gui/guiKeyChangeMenu.cpp | 2 +- src/gui/modalMenu.cpp | 6 +-- .../{touchscreengui.cpp => touchcontrols.cpp} | 52 +++++++++--------- src/gui/{touchscreengui.h => touchcontrols.h} | 8 +-- 10 files changed, 83 insertions(+), 83 deletions(-) rename src/gui/{touchscreengui.cpp => touchcontrols.cpp} (94%) rename src/gui/{touchscreengui.h => touchcontrols.h} (97%) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 5365d70f9..5826508f6 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gui/mainmenumanager.h" #include "clouds.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" #include "server.h" #include "filesys.h" #include "gui/guiMainMenu.h" @@ -230,9 +230,9 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) m_rendering_engine->get_scene_manager()->clear(); - if (g_touchscreengui) { - delete g_touchscreengui; - g_touchscreengui = NULL; + if (g_touchcontrols) { + delete g_touchcontrols; + g_touchcontrols = NULL; } /* Save the settings when leaving the game. diff --git a/src/client/game.cpp b/src/client/game.cpp index 1267b32a9..648155d83 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content/subgames.h" #include "client/event_manager.h" #include "fontengine.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" #include "itemdef.h" #include "log.h" #include "filesys.h" @@ -1245,8 +1245,8 @@ void Game::shutdown() // Clear text when exiting. m_game_ui->clearText(); - if (g_touchscreengui) - g_touchscreengui->hide(); + if (g_touchcontrols) + g_touchcontrols->hide(); // only if the shutdown progress bar isn't shown yet if (m_shutdown_progress == 0.0f) @@ -1510,8 +1510,8 @@ bool Game::createClient(const GameStartData &start_data) client->getScript()->on_camera_ready(camera); client->setCamera(camera); - if (g_touchscreengui) { - g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled()); + if (g_touchcontrols) { + g_touchcontrols->setUseCrosshair(!isTouchCrosshairDisabled()); } /* Clouds @@ -1579,7 +1579,7 @@ bool Game::initGui() -1, chat_backend, client, &g_menumgr); if (g_settings->getBool("enable_touch")) - g_touchscreengui = new TouchScreenGUI(device, texture_src); + g_touchcontrols = new TouchControls(device, texture_src); return true; } @@ -2027,15 +2027,15 @@ void Game::processUserInput(f32 dtime) input->clear(); } - if (g_touchscreengui) - g_touchscreengui->hide(); + if (g_touchcontrols) + g_touchcontrols->hide(); } else { - if (g_touchscreengui) { - /* on touchscreengui step may generate own input events which ain't + if (g_touchcontrols) { + /* on touchcontrols step may generate own input events which ain't * what we want in case we just did clear them */ - g_touchscreengui->show(); - g_touchscreengui->step(dtime); + g_touchcontrols->show(); + g_touchcontrols->step(dtime); } m_game_focused = true; @@ -2230,8 +2230,8 @@ void Game::processItemSelection(u16 *new_playeritem) } } - if (g_touchscreengui) { - std::optional selection = g_touchscreengui->getHotbarSelection(); + if (g_touchcontrols) { + std::optional selection = g_touchcontrols->getHotbarSelection(); if (selection) *new_playeritem = *selection; } @@ -2636,7 +2636,7 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime) this results in duplicated input. To avoid that, we don't enable relative mouse mode if we're in touchscreen mode. */ if (cur_control) - cur_control->setRelativeMode(!g_touchscreengui && !isMenuActive()); + cur_control->setRelativeMode(!g_touchcontrols && !isMenuActive()); if ((device->isWindowActive() && device->isWindowFocused() && !isMenuActive()) || input->isRandom()) { @@ -2679,9 +2679,9 @@ f32 Game::getSensitivityScaleFactor() const void Game::updateCameraOrientation(CameraOrientation *cam, float dtime) { - if (g_touchscreengui) { - cam->camera_yaw += g_touchscreengui->getYawChange(); - cam->camera_pitch += g_touchscreengui->getPitchChange(); + if (g_touchcontrols) { + cam->camera_yaw += g_touchcontrols->getYawChange(); + cam->camera_pitch += g_touchcontrols->getPitchChange(); } else { v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2); v2s32 dist = input->getMousePos() - center; @@ -2746,7 +2746,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) * touch then its meaning is inverted (i.e. holding aux1 means walk and * not fast) */ - if (g_touchscreengui && m_touch_simulate_aux1) { + if (g_touchcontrols && m_touch_simulate_aux1) { control.aux1 = control.aux1 ^ true; } @@ -3235,8 +3235,8 @@ void Game::updateCamera(f32 dtime) camera->toggleCameraMode(); - if (g_touchscreengui) - g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled()); + if (g_touchcontrols) + g_touchcontrols->setUseCrosshair(!isTouchCrosshairDisabled()); // Make the player visible depending on camera mode. playercao->updateMeshCulling(); @@ -3337,8 +3337,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) } shootline.end = shootline.start + camera_direction * BS * d; - if (g_touchscreengui && isTouchCrosshairDisabled()) { - shootline = g_touchscreengui->getShootline(); + if (g_touchcontrols && isTouchCrosshairDisabled()) { + shootline = g_touchcontrols->getShootline(); // Scale shootline to the acual distance the player can reach shootline.end = shootline.start + shootline.getVector().normalize() * BS * d; @@ -3355,9 +3355,9 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) if (pointed != runData.pointed_old) infostream << "Pointing at " << pointed.dump() << std::endl; - if (g_touchscreengui) { + if (g_touchcontrols) { auto mode = selected_def.touch_interaction.getMode(pointed.type); - g_touchscreengui->applyContextControls(mode); + g_touchcontrols->applyContextControls(mode); } // Note that updating the selection mesh every frame is not particularly efficient, @@ -4348,7 +4348,7 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats) (player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) && (this->camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT)); - if (g_touchscreengui && isTouchCrosshairDisabled()) + if (g_touchcontrols && isTouchCrosshairDisabled()) draw_crosshair = false; this->m_rendering_engine->draw_scene(sky_color, this->m_game_ui->m_flags.show_hud, @@ -4459,7 +4459,7 @@ void Game::showPauseMenu() { std::string control_text; - if (g_touchscreengui) { + if (g_touchcontrols) { control_text = strgettext("Controls:\n" "No menu open:\n" "- slide finger: look around\n" diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 3f9672f3d..e2d1ee455 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -39,7 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "wieldmesh.h" #include "client/renderingengine.h" #include "client/minimap.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" #include "util/enriched_string.h" #include "irrlicht_changes/CGUITTFont.h" @@ -322,8 +322,8 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, drawItem(mainlist->getItem(i), item_rect, (i + 1) == selectitem); - if (is_hotbar && g_touchscreengui) - g_touchscreengui->registerHotbarRect(i, item_rect); + if (is_hotbar && g_touchcontrols) + g_touchcontrols->registerHotbarRect(i, item_rect); } } @@ -787,8 +787,8 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, void Hud::drawHotbar(u16 playeritem) { - if (g_touchscreengui) - g_touchscreengui->resetHotbarRects(); + if (g_touchcontrols) + g_touchcontrols->resetHotbarRects(); InventoryList *mainlist = inventory->getList("main"); if (mainlist == NULL) { diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 4233916c2..39c212d2f 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "inputhandler.h" #include "gui/mainmenumanager.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" #include "hud.h" void KeyCache::populate_nonchanging() @@ -143,8 +143,8 @@ bool MyEventReceiver::OnEvent(const SEvent &event) // Let the menu handle events, if one is active. if (isMenuActive()) { - if (g_touchscreengui) - g_touchscreengui->setVisible(false); + if (g_touchcontrols) + g_touchcontrols->setVisible(false); return g_menumgr.preprocessEvent(event); } @@ -168,9 +168,9 @@ bool MyEventReceiver::OnEvent(const SEvent &event) return true; } - } else if (g_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { - // In case of touchscreengui, we have to handle different events - g_touchscreengui->translateEvent(event); + } else if (g_touchcontrols && event.EventType == irr::EET_TOUCH_INPUT_EVENT) { + // In case of touchcontrols, we have to handle different events + g_touchcontrols->translateEvent(event); return true; } else if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { // joystick may be nullptr if game is launched with '--random-input' parameter @@ -237,8 +237,8 @@ float RealInputHandler::getMovementSpeed() return 0.0f; return 1.0f; // If there is a keyboard event, assume maximum speed } - if (g_touchscreengui && g_touchscreengui->getMovementSpeed()) - return g_touchscreengui->getMovementSpeed(); + if (g_touchcontrols && g_touchcontrols->getMovementSpeed()) + return g_touchcontrols->getMovementSpeed(); return joystick.getMovementSpeed(); } @@ -260,8 +260,8 @@ float RealInputHandler::getMovementDirection() return std::atan2(x, z); // `getMovementDirection() == 0` means forward, so we cannot use // `getMovementDirection()` as a condition. - else if (g_touchscreengui && g_touchscreengui->getMovementSpeed()) - return g_touchscreengui->getMovementDirection(); + else if (g_touchcontrols && g_touchcontrols->getMovementSpeed()) + return g_touchcontrols->getMovementDirection(); return joystick.getMovementDirection(); } diff --git a/src/clientdynamicinfo.cpp b/src/clientdynamicinfo.cpp index a9b2a6ef3..2e75a6adc 100644 --- a/src/clientdynamicinfo.cpp +++ b/src/clientdynamicinfo.cpp @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "client/renderingengine.h" -#include "gui/touchscreengui.h" +#include "gui/touchcontrols.h" ClientDynamicInfo ClientDynamicInfo::getCurrent() { @@ -33,7 +33,7 @@ ClientDynamicInfo ClientDynamicInfo::getCurrent() f32 hud_scaling = g_settings->getFloat("hud_scaling", 0.5f, 20.0f); f32 real_gui_scaling = gui_scaling * density; f32 real_hud_scaling = hud_scaling * density; - bool touch_controls = g_touchscreengui; + bool touch_controls = g_touchcontrols; return { screen_size, real_gui_scaling, real_hud_scaling, diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 66d6a9ee8..73bbecb02 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -25,6 +25,6 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/modalMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/touchscreengui.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/touchcontrols.cpp PARENT_SCOPE ) diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 7e6c486e0..97e1ae8e0 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -377,7 +377,7 @@ void GUIKeyChangeMenu::add_key(int id, std::wstring button_name, const std::stri key_settings.push_back(k); } -// compare with button_titles in touchscreengui.cpp +// compare with button_titles in touchcontrols.cpp void GUIKeyChangeMenu::init_keys() { this->add_key(GUI_ID_KEY_FORWARD_BUTTON, wstrgettext("Forward"), "keymap_forward"); diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index 83d5036f9..fd60d08c2 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gui/guiInventoryList.h" #include "porting.h" #include "settings.h" -#include "touchscreengui.h" +#include "touchcontrols.h" PointerAction PointerAction::fromEvent(const SEvent &event) { switch (event.EventType) { @@ -111,8 +111,8 @@ void GUIModalMenu::quitMenu() Environment->removeFocus(this); m_menumgr->deletingMenu(this); this->remove(); - if (g_touchscreengui) - g_touchscreengui->show(); + if (g_touchcontrols) + g_touchcontrols->show(); } static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchcontrols.cpp similarity index 94% rename from src/gui/touchscreengui.cpp rename to src/gui/touchcontrols.cpp index c428ef52e..b96ced1bf 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchcontrols.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "touchscreengui.h" +#include "touchcontrols.h" #include "gettime.h" #include "irr_v2d.h" @@ -39,7 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -TouchScreenGUI *g_touchscreengui; +TouchControls *g_touchcontrols; static const char *button_image_names[] = { "jump_btn.png", @@ -266,14 +266,14 @@ static EKEY_CODE id_to_keycode(touch_gui_button_id id) code = keyname_to_keycode(resolved.c_str()); } catch (UnknownKeycode &e) { code = KEY_UNKNOWN; - warningstream << "TouchScreenGUI: Unknown key '" << resolved + warningstream << "TouchControls: Unknown key '" << resolved << "' for '" << key << "', hiding button." << std::endl; } return code; } -TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, ISimpleTextureSource *tsrc): +TouchControls::TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc): m_device(device), m_guienv(device->getGUIEnvironment()), m_receiver(device->getEventReceiver()), @@ -413,7 +413,7 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, ISimpleTextureSource *tsr } } -void TouchScreenGUI::addButton(std::vector &buttons, touch_gui_button_id id, +void TouchControls::addButton(std::vector &buttons, touch_gui_button_id id, const std::string &image, const recti &rect, bool visible) { IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id); @@ -426,7 +426,7 @@ void TouchScreenGUI::addButton(std::vector &buttons, touch_gui_butt btn.gui_button = grab_gui_element(btn_gui_button); } -void TouchScreenGUI::addToggleButton(std::vector &buttons, touch_gui_button_id id, +void TouchControls::addToggleButton(std::vector &buttons, touch_gui_button_id id, const std::string &image_1, const std::string &image_2, const recti &rect, bool visible) { addButton(buttons, id, image_1, rect, visible); @@ -436,7 +436,7 @@ void TouchScreenGUI::addToggleButton(std::vector &buttons, touch_gu btn.toggle_textures[1] = image_2; } -IGUIImage *TouchScreenGUI::makeButtonDirect(touch_gui_button_id id, +IGUIImage *TouchControls::makeButtonDirect(touch_gui_button_id id, const recti &rect, bool visible) { IGUIImage *btn_gui_button = m_guienv->addImage(rect, nullptr, id); @@ -447,7 +447,7 @@ IGUIImage *TouchScreenGUI::makeButtonDirect(touch_gui_button_id id, return btn_gui_button; } -bool TouchScreenGUI::isHotbarButton(const SEvent &event) +bool TouchControls::isHotbarButton(const SEvent &event) { const v2s32 touch_pos = v2s32(event.TouchInput.X, event.TouchInput.Y); // check if hotbar item is pressed @@ -462,14 +462,14 @@ bool TouchScreenGUI::isHotbarButton(const SEvent &event) return false; } -std::optional TouchScreenGUI::getHotbarSelection() +std::optional TouchControls::getHotbarSelection() { auto selection = m_hotbar_selection; m_hotbar_selection = std::nullopt; return selection; } -void TouchScreenGUI::handleReleaseEvent(size_t pointer_id) +void TouchControls::handleReleaseEvent(size_t pointer_id) { // By the way: Android reuses pointer IDs, so m_pointer_pos[pointer_id] // will be overwritten soon anyway. @@ -516,15 +516,15 @@ void TouchScreenGUI::handleReleaseEvent(size_t pointer_id) m_joystick_btn_bg->setVisible(false); m_joystick_btn_center->setVisible(false); } else { - infostream << "TouchScreenGUI::translateEvent released unknown button: " + infostream << "TouchControls::translateEvent released unknown button: " << pointer_id << std::endl; } } -void TouchScreenGUI::translateEvent(const SEvent &event) +void TouchControls::translateEvent(const SEvent &event) { if (!m_visible) { - infostream << "TouchScreenGUI::translateEvent got event but is not visible!" + infostream << "TouchControls::translateEvent got event but is not visible!" << std::endl; return; } @@ -702,7 +702,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } } -void TouchScreenGUI::applyJoystickStatus() +void TouchControls::applyJoystickStatus() { if (m_joystick_triggers_aux1) { SEvent translated{}; @@ -718,7 +718,7 @@ void TouchScreenGUI::applyJoystickStatus() } } -void TouchScreenGUI::step(float dtime) +void TouchControls::step(float dtime) { if (m_overflow_open) { buttons_step(m_overflow_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); @@ -757,17 +757,17 @@ void TouchScreenGUI::step(float dtime) m_had_move_id = false; } -void TouchScreenGUI::resetHotbarRects() +void TouchControls::resetHotbarRects() { m_hotbar_rects.clear(); } -void TouchScreenGUI::registerHotbarRect(u16 index, const recti &rect) +void TouchControls::registerHotbarRect(u16 index, const recti &rect) { m_hotbar_rects[index] = rect; } -void TouchScreenGUI::setVisible(bool visible) +void TouchControls::setVisible(bool visible) { if (m_visible == visible) return; @@ -781,14 +781,14 @@ void TouchScreenGUI::setVisible(bool visible) updateVisibility(); } -void TouchScreenGUI::toggleOverflowMenu() +void TouchControls::toggleOverflowMenu() { releaseAll(); // must be done first m_overflow_open = !m_overflow_open; updateVisibility(); } -void TouchScreenGUI::updateVisibility() +void TouchControls::updateVisibility() { bool regular_visible = m_visible && !m_overflow_open; for (auto &button : m_buttons) @@ -804,7 +804,7 @@ void TouchScreenGUI::updateVisibility() text->setVisible(overflow_visible); } -void TouchScreenGUI::releaseAll() +void TouchControls::releaseAll() { while (!m_pointer_pos.empty()) handleReleaseEvent(m_pointer_pos.begin()->first); @@ -821,17 +821,17 @@ void TouchScreenGUI::releaseAll() } } -void TouchScreenGUI::hide() +void TouchControls::hide() { setVisible(false); } -void TouchScreenGUI::show() +void TouchControls::show() { setVisible(true); } -v2s32 TouchScreenGUI::getPointerPos() +v2s32 TouchControls::getPointerPos() { if (m_draw_crosshair) return v2s32(m_screensize.X / 2, m_screensize.Y / 2); @@ -840,7 +840,7 @@ v2s32 TouchScreenGUI::getPointerPos() return m_move_pos; } -void TouchScreenGUI::emitMouseEvent(EMOUSE_INPUT_EVENT type) +void TouchControls::emitMouseEvent(EMOUSE_INPUT_EVENT type) { v2s32 pointer_pos = getPointerPos(); @@ -855,7 +855,7 @@ void TouchScreenGUI::emitMouseEvent(EMOUSE_INPUT_EVENT type) m_receiver->OnEvent(event); } -void TouchScreenGUI::applyContextControls(const TouchInteractionMode &mode) +void TouchControls::applyContextControls(const TouchInteractionMode &mode) { // Since the pointed thing has already been determined when this function // is called, we cannot use this function to update the shootline. diff --git a/src/gui/touchscreengui.h b/src/gui/touchcontrols.h similarity index 97% rename from src/gui/touchscreengui.h rename to src/gui/touchcontrols.h index 2da9d8151..ebc251bb6 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchcontrols.h @@ -128,10 +128,10 @@ struct button_info }; -class TouchScreenGUI +class TouchControls { public: - TouchScreenGUI(IrrlichtDevice *device, ISimpleTextureSource *tsrc); + TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc); void translateEvent(const SEvent &event); void applyContextControls(const TouchInteractionMode &mode); @@ -182,7 +182,7 @@ private: s32 m_button_size; double m_touchscreen_threshold; u16 m_long_tap_delay; - bool m_visible = true; // is the whole touch screen gui visible + bool m_visible = true; std::unordered_map m_hotbar_rects; std::optional m_hotbar_selection = std::nullopt; @@ -273,4 +273,4 @@ private: u64 m_place_pressed_until = 0; }; -extern TouchScreenGUI *g_touchscreengui; +extern TouchControls *g_touchcontrols; From 3a59fabefe421ffdab9c61b9abfe151bc95e5435 Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Sun, 2 Jun 2024 12:58:41 -0700 Subject: [PATCH 44/75] split enable_touch to touch_controls (for touchscreen controls) and touch_gui touch_gui provide adjustment to the interface, so it's more touch friendly Signed-off-by: David Heidelberg --- builtin/fstk/buttonbar.lua | 2 +- builtin/mainmenu/content/dlg_contentdb.lua | 6 ++-- builtin/mainmenu/content/dlg_install.lua | 14 ++++---- builtin/mainmenu/settings/dlg_settings.lua | 24 +++++++------- builtin/mainmenu/tab_local.lua | 2 +- builtin/settingtypes.txt | 37 ++++++++++++---------- src/client/game.cpp | 2 +- src/clientdynamicinfo.cpp | 2 +- src/defaultsettings.cpp | 3 +- src/gui/guiFormSpecMenu.cpp | 4 +-- src/migratesettings.h | 8 +++++ 11 files changed, 57 insertions(+), 47 deletions(-) diff --git a/builtin/fstk/buttonbar.lua b/builtin/fstk/buttonbar.lua index 59b7a586f..64ac37f03 100644 --- a/builtin/fstk/buttonbar.lua +++ b/builtin/fstk/buttonbar.lua @@ -19,7 +19,7 @@ local BASE_SPACING = 0.1 local function get_scroll_btn_width() - return core.settings:get_bool("enable_touch") and 0.8 or 0.5 + return core.settings:get_bool("touch_gui") and 0.8 or 0.5 end local function buttonbar_formspec(self) diff --git a/builtin/mainmenu/content/dlg_contentdb.lua b/builtin/mainmenu/content/dlg_contentdb.lua index 84ef96800..bcc89f7cd 100644 --- a/builtin/mainmenu/content/dlg_contentdb.lua +++ b/builtin/mainmenu/content/dlg_contentdb.lua @@ -181,7 +181,7 @@ local function get_info_formspec(text) return table.concat({ "formspec_version[6]", "size[15.75,9.5]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]", + core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "position[0.5,0.55]", "label[4,4.35;", text, "]", "container[0,", H - 0.8 - 0.375, "]", @@ -212,7 +212,7 @@ local function get_formspec(dlgdata) local formspec = { "formspec_version[6]", "size[15.75,9.5]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]", + core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "position[0.5,0.55]", "style[status,downloading,queued;border=false]", @@ -463,7 +463,7 @@ end local function handle_events(event) if event == "DialogShow" then -- On touchscreen, don't show the "MINETEST" header behind the dialog. - mm_game_theme.set_engine(core.settings:get_bool("enable_touch")) + mm_game_theme.set_engine(core.settings:get_bool("touch_gui")) -- If ContentDB is already loaded, auto-install packages here. do_auto_install() diff --git a/builtin/mainmenu/content/dlg_install.lua b/builtin/mainmenu/content/dlg_install.lua index 0549e23be..89819be2a 100644 --- a/builtin/mainmenu/content/dlg_install.lua +++ b/builtin/mainmenu/content/dlg_install.lua @@ -22,13 +22,13 @@ end local function get_loading_formspec() - local ENABLE_TOUCH = core.settings:get_bool("enable_touch") - local w = ENABLE_TOUCH and 14 or 7 + local TOUCH_GUI = core.settings:get_bool("touch_gui") + local w = TOUCH_GUI and 14 or 7 local formspec = { "formspec_version[3]", "size[", w, ",9.05]", - ENABLE_TOUCH and "padding[0.01,0.01]" or "position[0.5,0.55]", + TOUCH_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]", "label[3,4.525;", fgettext("Loading..."), "]", } return table.concat(formspec) @@ -110,18 +110,18 @@ local function get_formspec(data) message_bg = mt_color_orange end - local ENABLE_TOUCH = core.settings:get_bool("enable_touch") + local TOUCH_GUI = core.settings:get_bool("touch_gui") - local w = ENABLE_TOUCH and 14 or 7 + local w = TOUCH_GUI and 14 or 7 local padded_w = w - 2*0.375 - local dropdown_w = ENABLE_TOUCH and 10.2 or 4.25 + local dropdown_w = TOUCH_GUI and 10.2 or 4.25 local button_w = (padded_w - 0.25) / 3 local button_pad = button_w / 2 local formspec = { "formspec_version[3]", "size[", w, ",9.05]", - ENABLE_TOUCH and "padding[0.01,0.01]" or "position[0.5,0.55]", + TOUCH_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]", "style[title;border=false]", "box[0,0;", w, ",0.8;#3333]", "button[0,0;", w, ",0.8;title;", fgettext("Install $1", package.title) , "]", diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index 73a72769b..75f99376d 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -110,7 +110,7 @@ local function load() local change_keys = { query_text = "Controls", requires = { - keyboard_mouse = true, + touch_controls = false, }, get_formspec = function(self, avail_w) local btn_w = math.min(avail_w, 3) @@ -324,8 +324,6 @@ local function check_requirements(name, requires) local special = { android = PLATFORM == "Android", desktop = PLATFORM ~= "Android", - touchscreen_gui = core.settings:get_bool("enable_touch"), - keyboard_mouse = not core.settings:get_bool("enable_touch"), shaders_support = shaders_support, shaders = core.settings:get_bool("enable_shaders") and shaders_support, opengl = video_driver == "opengl", @@ -457,13 +455,13 @@ local function get_formspec(dialogdata) local extra_h = 1 -- not included in tabsize.height local tabsize = { - width = core.settings:get_bool("enable_touch") and 16.5 or 15.5, - height = core.settings:get_bool("enable_touch") and (10 - extra_h) or 12, + width = core.settings:get_bool("touch_gui") and 16.5 or 15.5, + height = core.settings:get_bool("touch_gui") and (10 - extra_h) or 12, } - local scrollbar_w = core.settings:get_bool("enable_touch") and 0.6 or 0.4 + local scrollbar_w = core.settings:get_bool("touch_gui") and 0.6 or 0.4 - local left_pane_width = core.settings:get_bool("enable_touch") and 4.5 or 4.25 + local left_pane_width = core.settings:get_bool("touch_gui") and 4.5 or 4.25 local left_pane_padding = 0.25 local search_width = left_pane_width + scrollbar_w - (0.75 * 2) @@ -477,7 +475,7 @@ local function get_formspec(dialogdata) local fs = { "formspec_version[6]", "size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]", - core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "", + core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "", "bgcolor[#0000]", -- HACK: this is needed to allow resubmitting the same formspec @@ -652,15 +650,15 @@ local function buttonhandler(this, fields) write_settings_early() end - -- enable_touch is a checkbox in a setting component. We handle this + -- touch_controls is a checkbox in a setting component. We handle this -- setting differently so we can hide/show pages using the next if-statement - if fields.enable_touch ~= nil then - local value = core.is_yes(fields.enable_touch) - core.settings:set_bool("enable_touch", value) + if fields.touch_controls ~= nil then + local value = core.is_yes(fields.touch_controls) + core.settings:set_bool("touch_controls", value) write_settings_early() end - if fields.show_advanced ~= nil or fields.enable_touch ~= nil then + if fields.show_advanced ~= nil or fields.touch_controls ~= nil then local suggested_page_id = update_filtered_pages(dialogdata.query) dialogdata.components = nil diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 1ed08d825..7f46be213 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -94,7 +94,7 @@ function singleplayer_refresh_gamebar() local btnbar = buttonbar_create( "game_button_bar", - core.settings:get_bool("enable_touch") and {x = 0, y = 7.25} or {x = 0, y = 7.475}, + core.settings:get_bool("touch_gui") and {x = 0, y = 7.25} or {x = 0, y = 7.475}, {x = 15.5, y = 1.25}, "#000000", game_buttonbar_button_handler) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 001eb7288..b5aa142b8 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -61,7 +61,7 @@ # # # This is a comment # # -# # Requires: shaders, enable_dynamic_shadows, !touchscreen_gui +# # Requires: shaders, enable_dynamic_shadows, !touch_controls # name (Readable name) type type_args # # A requirement can be the name of a boolean setting or an engine-defined value. @@ -72,7 +72,6 @@ # * shaders_support (a video driver that supports shaders, may not be enabled) # * shaders (both enable_shaders and shaders_support) # * desktop / android -# * touchscreen_gui / keyboard_mouse # * opengl / gles # * You can negate any requirement by prepending with ! # @@ -92,7 +91,7 @@ camera_smoothing (Camera smoothing) float 0.0 0.0 0.99 # Smooths rotation of camera when in cinematic mode, 0 to disable. Enter cinematic mode by using the key set in Controls. # -# Requires: keyboard_mouse +# Requires: !touch_controls cinematic_camera_smoothing (Camera smoothing in cinematic mode) float 0.7 0.0 0.99 # If enabled, you can place nodes at the position (feet + eye level) where you stand. @@ -113,7 +112,7 @@ always_fly_fast (Always fly fast) bool true # The time in seconds it takes between repeated node placements when holding # the place button. # -# Requires: keyboard_mouse +# Requires: !touch_controls repeat_place_time (Place repetition interval) float 0.25 0.16 2.0 # The minimum time in seconds it takes between digging nodes when holding @@ -132,62 +131,62 @@ safe_dig_and_place (Safe digging and placing) bool false # Invert vertical mouse movement. # -# Requires: keyboard_mouse +# Requires: !touch_controls invert_mouse (Invert mouse) bool false # Mouse sensitivity multiplier. # -# Requires: keyboard_mouse +# Requires: !touch_controls mouse_sensitivity (Mouse sensitivity) float 0.2 0.001 10.0 # Enable mouse wheel (scroll) for item selection in hotbar. # -# Requires: keyboard_mouse +# Requires: !touch_controls enable_hotbar_mouse_wheel (Hotbar: Enable mouse wheel for selection) bool true # Invert mouse wheel (scroll) direction for item selection in hotbar. # -# Requires: keyboard_mouse +# Requires: !touch_controls invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false [*Touchscreen] -# Enables touchscreen mode, allowing you to play the game with a touchscreen. +# Enables the touchscreen controls, allowing you to play the game with a touchscreen. # # Requires: !android -enable_touch (Enable touchscreen) bool true +touch_controls (Enable touchscreen controls) bool true # Touchscreen sensitivity multiplier. # -# Requires: touchscreen_gui +# Requires: touch_controls touchscreen_sensitivity (Touchscreen sensitivity) float 0.2 0.001 10.0 # The length in pixels after which a touch interaction is considered movement. # -# Requires: touchscreen_gui +# Requires: touch_controls touchscreen_threshold (Movement threshold) int 20 0 100 # The delay in milliseconds after which a touch interaction is considered a long tap. # -# Requires: touchscreen_gui +# Requires: touch_controls touch_long_tap_delay (Threshold for long taps) int 400 100 1000 # Use crosshair to select object instead of whole screen. # If enabled, a crosshair will be shown and will be used for selecting object. # -# Requires: touchscreen_gui +# Requires: touch_controls touch_use_crosshair (Use crosshair for touch screen) bool false # Fixes the position of virtual joystick. # If disabled, virtual joystick will center to first-touch's position. # -# Requires: touchscreen_gui +# Requires: touch_controls fixed_virtual_joystick (Fixed virtual joystick) bool false # Use virtual joystick to trigger "Aux1" button. # If enabled, virtual joystick will also tap "Aux1" button when out of main circle. # -# Requires: touchscreen_gui +# Requires: touch_controls virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false # The gesture for punching players/entities. @@ -200,7 +199,7 @@ virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool fals # Known from the classic Minetest mobile controls. # Combat is more or less impossible. # -# Requires: touchscreen_gui +# Requires: touch_controls touch_punch_gesture (Punch gesture) enum short_tap short_tap,long_tap @@ -687,6 +686,10 @@ language (Language) enum ,be,bg,ca,cs,da,de,el,en,eo,es,et,eu,fi,fr,gd,gl,hu,i [**GUI] +# When enabled, the GUI is optimized to be more usable on touchscreens. +# Whether this is enabled by default depends on your hardware form-factor. +touch_gui (Optimize GUI for touchscreens) bool false + # Scale GUI by a user specified value. # Use a nearest-neighbor-anti-alias filter to scale the GUI. # This will smooth over some of the rough edges, and blend diff --git a/src/client/game.cpp b/src/client/game.cpp index 648155d83..53871d5ac 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1578,7 +1578,7 @@ bool Game::initGui() gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), -1, chat_backend, client, &g_menumgr); - if (g_settings->getBool("enable_touch")) + if (g_settings->getBool("touch_controls")) g_touchcontrols = new TouchControls(device, texture_src); return true; diff --git a/src/clientdynamicinfo.cpp b/src/clientdynamicinfo.cpp index 2e75a6adc..c206018f3 100644 --- a/src/clientdynamicinfo.cpp +++ b/src/clientdynamicinfo.cpp @@ -44,7 +44,7 @@ ClientDynamicInfo ClientDynamicInfo::getCurrent() v2f32 ClientDynamicInfo::calculateMaxFSSize(v2u32 render_target_size, f32 gui_scaling) { - f32 factor = (g_settings->getBool("enable_touch") ? 10 : 15) / gui_scaling; + f32 factor = (g_settings->getBool("touch_gui") ? 10 : 15) / gui_scaling; f32 ratio = (f32)render_target_size.X / (f32)render_target_size.Y; if (ratio < 1) return { factor, factor / ratio }; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index ccfb12971..8e2411df5 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -97,7 +97,8 @@ void set_default_settings() // Client settings->setDefault("address", ""); settings->setDefault("enable_sound", "true"); - settings->setDefault("enable_touch", bool_to_cstr(has_touch)); + settings->setDefault("touch_controls", bool_to_cstr(has_touch)); + settings->setDefault("touch_gui", bool_to_cstr(has_touch)); settings->setDefault("sound_volume", "0.8"); settings->setDefault("sound_volume_unfocused", "0.3"); settings->setDefault("mute_sound", "false"); diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index a2982fdd2..3f22fb3c4 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -315,7 +315,7 @@ void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element) data->invsize.Y = MYMAX(0, stof(parts[1])); lockSize(false); - if (!g_settings->getBool("enable_touch") && parts.size() == 3) { + if (!g_settings->getBool("touch_gui") && parts.size() == 3) { if (parts[2] == "true") { lockSize(true,v2u32(800,600)); } @@ -3166,7 +3166,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) s32 min_screen_dim = std::min(padded_screensize.X, padded_screensize.Y); double prefer_imgsize; - if (g_settings->getBool("enable_touch")) { + if (g_settings->getBool("touch_gui")) { // The preferred imgsize should be larger to accommodate the // smaller screensize. prefer_imgsize = min_screen_dim / 10 * gui_scaling; diff --git a/src/migratesettings.h b/src/migratesettings.h index 60435f18a..d4488702f 100644 --- a/src/migratesettings.h +++ b/src/migratesettings.h @@ -11,4 +11,12 @@ void migrate_settings() g_settings->getBool("opaque_water") ? "false" : "true"); g_settings->remove("opaque_water"); } + + // Converts enable_touch to touch_controls/touch_gui + if (g_settings->existsLocal("enable_touch")) { + bool value = g_settings->getBool("enable_touch"); + g_settings->setBool("touch_controls", value); + g_settings->setBool("touch_gui", value); + g_settings->remove("enable_touch"); + } } From 7f5a19792c4d27da5f1a072c5d95cc995cc1bbff Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Thu, 8 Aug 2024 09:24:53 +0900 Subject: [PATCH 45/75] enable option to toggle touch controls on Android Signed-off-by: David Heidelberg --- builtin/settingtypes.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index b5aa142b8..ccafde036 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -152,8 +152,6 @@ invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false [*Touchscreen] # Enables the touchscreen controls, allowing you to play the game with a touchscreen. -# -# Requires: !android touch_controls (Enable touchscreen controls) bool true # Touchscreen sensitivity multiplier. From 3971b6afcc3dffc0d77c19dc1bb138a8ae0abfc9 Mon Sep 17 00:00:00 2001 From: cx384 Date: Wed, 28 Aug 2024 21:32:31 +0200 Subject: [PATCH 46/75] Main menu: formspec escape world name (#15064) --- builtin/mainmenu/dlg_config_world.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/mainmenu/dlg_config_world.lua b/builtin/mainmenu/dlg_config_world.lua index e8f49b230..a8c5221de 100644 --- a/builtin/mainmenu/dlg_config_world.lua +++ b/builtin/mainmenu/dlg_config_world.lua @@ -126,7 +126,7 @@ local function get_formspec(data) local retval = "size[11.5,7.5,true]" .. "label[0.5,0;" .. fgettext("World:") .. "]" .. - "label[1.75,0;" .. data.worldspec.name .. "]" + "label[1.75,0;" .. core.formspec_escape(data.worldspec.name) .. "]" if mod.is_modpack or mod.type == "game" then local info = core.formspec_escape( From a6ba5304c41bb66d310b2a2e749ec72b37b608bf Mon Sep 17 00:00:00 2001 From: kromka-chleba <65868699+kromka-chleba@users.noreply.github.com> Date: Sat, 31 Aug 2024 09:43:52 +0000 Subject: [PATCH 47/75] Add new vector utils (ceil, sign, abs, random_in_area) (#14807) --- .luacheckrc | 8 ++++- builtin/common/math.lua | 41 +++++++++++++++++++++ builtin/common/misc_helpers.lua | 42 +--------------------- builtin/common/tests/after_spec.lua | 1 + builtin/common/tests/math_spec.lua | 16 +++++++++ builtin/common/tests/misc_helpers_spec.lua | 1 + builtin/common/tests/vector_spec.lua | 36 +++++++++++++++++++ builtin/common/vector.lua | 35 +++++++++++++----- builtin/init.lua | 1 + doc/lua_api.md | 14 +++++++- 10 files changed, 143 insertions(+), 52 deletions(-) create mode 100644 builtin/common/math.lua create mode 100644 builtin/common/tests/math_spec.lua diff --git a/.luacheckrc b/.luacheckrc index f184e6d59..afc136c7c 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -37,6 +37,12 @@ files["builtin/client/register.lua"] = { } } +files["builtin/common/math.lua"] = { + globals = { + "math", + }, +} + files["builtin/common/misc_helpers.lua"] = { globals = { "dump", "dump2", "table", "math", "string", @@ -46,7 +52,7 @@ files["builtin/common/misc_helpers.lua"] = { } files["builtin/common/vector.lua"] = { - globals = { "vector" }, + globals = { "vector", "math" }, } files["builtin/game/voxelarea.lua"] = { diff --git a/builtin/common/math.lua b/builtin/common/math.lua new file mode 100644 index 000000000..84c6c619b --- /dev/null +++ b/builtin/common/math.lua @@ -0,0 +1,41 @@ +--[[ + Math utils. +--]] + +function math.hypot(x, y) + return math.sqrt(x * x + y * y) +end + +function math.sign(x, tolerance) + tolerance = tolerance or 0 + if x > tolerance then + return 1 + elseif x < -tolerance then + return -1 + end + return 0 +end + +function math.factorial(x) + assert(x % 1 == 0 and x >= 0, "factorial expects a non-negative integer") + if x >= 171 then + -- 171! is greater than the biggest double, no need to calculate + return math.huge + end + local v = 1 + for k = 2, x do + v = v * k + end + return v +end + +function math.round(x) + if x < 0 then + local int = math.ceil(x) + local frac = x - int + return int - ((frac <= -0.5) and 1 or 0) + end + local int = math.floor(x) + local frac = x - int + return int + ((frac >= 0.5) and 1 or 0) +end diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index 21752ce7f..9c25b826f 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -3,6 +3,7 @@ -------------------------------------------------------------------------------- -- Localize functions to avoid table lookups (better performance). local string_sub, string_find = string.sub, string.find +local math = math -------------------------------------------------------------------------------- local function basic_dump(o) @@ -220,47 +221,6 @@ function string:trim() return self:match("^%s*(.-)%s*$") end --------------------------------------------------------------------------------- -function math.hypot(x, y) - return math.sqrt(x * x + y * y) -end - --------------------------------------------------------------------------------- -function math.sign(x, tolerance) - tolerance = tolerance or 0 - if x > tolerance then - return 1 - elseif x < -tolerance then - return -1 - end - return 0 -end - --------------------------------------------------------------------------------- -function math.factorial(x) - assert(x % 1 == 0 and x >= 0, "factorial expects a non-negative integer") - if x >= 171 then - -- 171! is greater than the biggest double, no need to calculate - return math.huge - end - local v = 1 - for k = 2, x do - v = v * k - end - return v -end - -function math.round(x) - if x < 0 then - local int = math.ceil(x) - local frac = x - int - return int - ((frac <= -0.5) and 1 or 0) - end - local int = math.floor(x) - local frac = x - int - return int + ((frac >= 0.5) and 1 or 0) -end - local formspec_escapes = { ["\\"] = "\\\\", ["["] = "\\[", diff --git a/builtin/common/tests/after_spec.lua b/builtin/common/tests/after_spec.lua index cca1871a5..f23bbd70d 100644 --- a/builtin/common/tests/after_spec.lua +++ b/builtin/common/tests/after_spec.lua @@ -1,6 +1,7 @@ _G.core = {} _G.vector = {metatable = {}} +dofile("builtin/common/math.lua") dofile("builtin/common/vector.lua") dofile("builtin/common/misc_helpers.lua") diff --git a/builtin/common/tests/math_spec.lua b/builtin/common/tests/math_spec.lua new file mode 100644 index 000000000..108ab2b6a --- /dev/null +++ b/builtin/common/tests/math_spec.lua @@ -0,0 +1,16 @@ +_G.core = {} +dofile("builtin/common/math.lua") + +describe("math", function() + it("round()", function() + assert.equal(0, math.round(0)) + assert.equal(10, math.round(10.3)) + assert.equal(11, math.round(10.5)) + assert.equal(11, math.round(10.7)) + assert.equal(-10, math.round(-10.3)) + assert.equal(-11, math.round(-10.5)) + assert.equal(-11, math.round(-10.7)) + assert.equal(0, math.round(0.49999999999999994)) + assert.equal(0, math.round(-0.49999999999999994)) + end) +end) diff --git a/builtin/common/tests/misc_helpers_spec.lua b/builtin/common/tests/misc_helpers_spec.lua index 611c4b20f..10e2bf277 100644 --- a/builtin/common/tests/misc_helpers_spec.lua +++ b/builtin/common/tests/misc_helpers_spec.lua @@ -1,4 +1,5 @@ _G.core = {} +dofile("builtin/common/math.lua") dofile("builtin/common/vector.lua") dofile("builtin/common/misc_helpers.lua") diff --git a/builtin/common/tests/vector_spec.lua b/builtin/common/tests/vector_spec.lua index 67dbd2d6b..9a0458be4 100644 --- a/builtin/common/tests/vector_spec.lua +++ b/builtin/common/tests/vector_spec.lua @@ -1,4 +1,5 @@ _G.vector = {} +dofile("builtin/common/math.lua") dofile("builtin/common/vector.lua") describe("vector", function() @@ -113,12 +114,35 @@ describe("vector", function() assert.equal(vector.new(0, 1, -1), a:round()) end) + it("ceil()", function() + local a = vector.new(0.1, 0.9, -0.5) + assert.equal(vector.new(1, 1, 0), vector.ceil(a)) + assert.equal(vector.new(1, 1, 0), a:ceil()) + end) + + it("sign()", function() + local a = vector.new(-120.3, 0, 231.5) + assert.equal(vector.new(-1, 0, 1), vector.sign(a)) + assert.equal(vector.new(-1, 0, 1), a:sign()) + assert.equal(vector.new(0, 0, 1), vector.sign(a, 200)) + assert.equal(vector.new(0, 0, 1), a:sign(200)) + end) + + it("abs()", function() + local a = vector.new(-123.456, 0, 13) + assert.equal(vector.new(123.456, 0, 13), vector.abs(a)) + assert.equal(vector.new(123.456, 0, 13), a:abs()) + end) + it("apply()", function() local i = 0 local f = function(x) i = i + 1 return x + i end + local f2 = function(x, opt1, opt2, opt3) + return x + opt1 + opt2 + opt3 + end local a = vector.new(0.1, 0.9, -0.5) assert.equal(vector.new(1, 1, 0), vector.apply(a, math.ceil)) assert.equal(vector.new(1, 1, 0), a:apply(math.ceil)) @@ -126,6 +150,9 @@ describe("vector", function() assert.equal(vector.new(0.1, 0.9, 0.5), a:apply(math.abs)) assert.equal(vector.new(1.1, 2.9, 2.5), vector.apply(a, f)) assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f)) + local b = vector.new(1, 2, 3) + assert.equal(vector.new(4, 5, 6), vector.apply(b, f2, 1, 1, 1)) + assert.equal(vector.new(4, 5, 6), b:apply(f2, 1, 1, 1)) end) it("combine()", function() @@ -469,4 +496,13 @@ describe("vector", function() assert.True(vector.in_area(vector.new(-10, -10, -10), vector.new(-10, -10, -10), vector.new(10, 10, 10))) assert.False(vector.in_area(vector.new(-10, -10, -10), vector.new(10, 10, 10), vector.new(-11, -10, -10))) end) + + it("random_in_area()", function() + local min = vector.new(-100, -100, -100) + local max = vector.new(100, 100, 100) + for i = 1, 1000 do + local random = vector.random_in_area(min, max) + assert.True(vector.in_area(random, min, max)) + end + end) end) diff --git a/builtin/common/vector.lua b/builtin/common/vector.lua index be82401c3..fb1d2a7d9 100644 --- a/builtin/common/vector.lua +++ b/builtin/common/vector.lua @@ -5,6 +5,7 @@ Note: The vector.*-functions must be able to accept old vectors that had no meta -- localize functions local setmetatable = setmetatable +local math = math vector = {} @@ -97,18 +98,26 @@ function vector.floor(v) end function vector.round(v) - return fast_new( - math.round(v.x), - math.round(v.y), - math.round(v.z) - ) + return vector.apply(v, math.round) end -function vector.apply(v, func) +function vector.ceil(v) + return vector.apply(v, math.ceil) +end + +function vector.sign(v, tolerance) + return vector.apply(v, math.sign, tolerance) +end + +function vector.abs(v) + return vector.apply(v, math.abs) +end + +function vector.apply(v, func, ...) return fast_new( - func(v.x), - func(v.y), - func(v.z) + func(v.x, ...), + func(v.y, ...), + func(v.z, ...) ) end @@ -387,6 +396,14 @@ function vector.random_direction() return fast_new(x/l, y/l, z/l) end +function vector.random_in_area(min, max) + return fast_new( + math.random(min.x, max.x), + math.random(min.y, max.y), + math.random(min.z, max.z) + ) +end + if rawget(_G, "core") and core.set_read_vector and core.set_push_vector then local function read_vector(v) return v.x, v.y, v.z diff --git a/builtin/init.lua b/builtin/init.lua index 49df70971..415087a54 100644 --- a/builtin/init.lua +++ b/builtin/init.lua @@ -42,6 +42,7 @@ local scriptdir = core.get_builtin_path() local commonpath = scriptdir .. "common" .. DIR_DELIM local asyncpath = scriptdir .. "async" .. DIR_DELIM +dofile(commonpath .. "math.lua") dofile(commonpath .. "vector.lua") dofile(commonpath .. "strict.lua") dofile(commonpath .. "serialize.lua") diff --git a/doc/lua_api.md b/doc/lua_api.md index ae046dd3a..bf714a515 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -3850,12 +3850,20 @@ vectors are written like this: `(x, y, z)`: * If `v` has zero length, returns `(0, 0, 0)`. * `vector.floor(v)`: * Returns a vector, each dimension rounded down. +* `vector.ceil(v)`: + * Returns a vector, each dimension rounded up. * `vector.round(v)`: * Returns a vector, each dimension rounded to nearest integer. * At a multiple of 0.5, rounds away from zero. -* `vector.apply(v, func)`: +* `vector.sign(v, tolerance)`: + * Returns a vector where `math.sign` was called for each component. + * See [Helper functions] for details. +* `vector.abs(v)`: + * Returns a vector with absolute values for each component. +* `vector.apply(v, func, ...)`: * Returns a vector where the function `func` has been applied to each component. + * `...` are optional arguments passed to `func`. * `vector.combine(v, w, func)`: * Returns a vector where the function `func` has combined both components of `v` and `w` for each component @@ -3880,6 +3888,10 @@ vectors are written like this: `(x, y, z)`: * `min` and `max` are inclusive. * If `min` is bigger than `max` on some axis, function always returns false. * You can use `vector.sort` if you have two vectors and don't know which are the minimum and the maximum. +* `vector.random_in_area(min, max)`: + * Returns a random integer position in area formed by `min` and `max` + * `min` and `max` are inclusive. + * You can use `vector.sort` if you have two vectors and don't know which are the minimum and the maximum. For the following functions `x` can be either a vector or a number: From efd7792add70ccfafb14e4b2e3ddd6d52457dd8e Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sat, 31 Aug 2024 11:44:30 +0200 Subject: [PATCH 48/75] Debloat IVideoDriver and IrrlichtDevice includes (#15080) As the project grows, compile time will not go down unless the header mess is cleaned up one by one to only include exactly what's needed. --- irr/include/EVideoTypes.h | 75 +++++++++++++++++++++ irr/include/IVideoDriver.h | 71 ++----------------- irr/include/IrrlichtDevice.h | 7 +- irr/include/SViewFrustum.h | 2 +- irr/src/CIrrDeviceLinux.cpp | 1 + irr/src/CIrrDeviceSDL.cpp | 1 + irr/src/CIrrDeviceStub.cpp | 1 + irr/src/CIrrDeviceWin32.cpp | 1 + irr/src/CSceneCollisionManager.cpp | 1 - src/client/camera.cpp | 1 + src/client/clientmap.cpp | 8 +++ src/client/clientmap.h | 7 +- src/client/imagefilters.cpp | 1 + src/client/render/anaglyph.cpp | 1 + src/client/render/core.h | 5 ++ src/client/render/pipeline.h | 7 ++ src/client/renderingengine.h | 1 + src/client/shadows/dynamicshadows.cpp | 1 + src/client/shadows/dynamicshadowsrender.cpp | 1 + src/client/shadows/dynamicshadowsrender.h | 1 + src/client/shadows/shadowsScreenQuad.cpp | 1 + src/gui/guiAnimatedImage.cpp | 1 + src/gui/guiBox.cpp | 1 + src/gui/guiFormSpecMenu.h | 1 + src/gui/guiInventoryList.cpp | 1 + src/gui/guiKeyChangeMenu.cpp | 1 + src/gui/guiOpenURL.cpp | 1 + src/gui/guiPasswordChange.cpp | 1 + src/gui/guiScene.cpp | 1 + src/gui/guiVolumeChange.cpp | 1 + src/gui/profilergraph.cpp | 1 + src/gui/profilergraph.h | 5 +- src/gui/touchcontrols.cpp | 1 + src/gui/touchcontrols.h | 6 +- src/irrlichttypes_extrabloated.h | 1 - 35 files changed, 139 insertions(+), 79 deletions(-) create mode 100644 irr/include/EVideoTypes.h diff --git a/irr/include/EVideoTypes.h b/irr/include/EVideoTypes.h new file mode 100644 index 000000000..fe90f0652 --- /dev/null +++ b/irr/include/EVideoTypes.h @@ -0,0 +1,75 @@ +// 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 "SMaterial.h" // MATERIAL_MAX_TEXTURES + +namespace irr::video +{ + +//! enumeration for geometry transformation states +enum E_TRANSFORMATION_STATE +{ + //! View transformation + ETS_VIEW = 0, + //! World transformation + ETS_WORLD, + //! Projection transformation + ETS_PROJECTION, + //! Texture 0 transformation + //! Use E_TRANSFORMATION_STATE(ETS_TEXTURE_0 + texture_number) to access other texture transformations + ETS_TEXTURE_0, + //! Only used internally + ETS_COUNT = ETS_TEXTURE_0 + MATERIAL_MAX_TEXTURES +}; + +//! Special render targets, which usually map to dedicated hardware +/** These render targets (besides 0 and 1) need not be supported by gfx cards */ +enum E_RENDER_TARGET +{ + //! Render target is the main color frame buffer + ERT_FRAME_BUFFER = 0, + //! Render target is a render texture + ERT_RENDER_TEXTURE, + //! Multi-Render target textures + ERT_MULTI_RENDER_TEXTURES, + //! Render target is the main color frame buffer + ERT_STEREO_LEFT_BUFFER, + //! Render target is the right color buffer (left is the main buffer) + ERT_STEREO_RIGHT_BUFFER, + //! Render to both stereo buffers at once + ERT_STEREO_BOTH_BUFFERS, + //! Auxiliary buffer 0 + ERT_AUX_BUFFER0, + //! Auxiliary buffer 1 + ERT_AUX_BUFFER1, + //! Auxiliary buffer 2 + ERT_AUX_BUFFER2, + //! Auxiliary buffer 3 + ERT_AUX_BUFFER3, + //! Auxiliary buffer 4 + ERT_AUX_BUFFER4 +}; + +//! Enum for the flags of clear buffer +enum E_CLEAR_BUFFER_FLAG +{ + ECBF_NONE = 0, + ECBF_COLOR = 1, + ECBF_DEPTH = 2, + ECBF_STENCIL = 4, + ECBF_ALL = ECBF_COLOR | ECBF_DEPTH | ECBF_STENCIL +}; + +//! Enum for the types of fog distributions to choose from +enum E_FOG_TYPE +{ + EFT_FOG_EXP = 0, + EFT_FOG_LINEAR, + EFT_FOG_EXP2 +}; + +} // irr::video + diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index 7b15fdd1b..6d2182e8a 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -9,14 +9,16 @@ #include "ITexture.h" #include "irrArray.h" #include "matrix4.h" -#include "plane3d.h" #include "dimension2d.h" #include "position2d.h" -#include "IMeshBuffer.h" #include "EDriverTypes.h" #include "EDriverFeatures.h" +#include "EPrimitiveTypes.h" +#include "EVideoTypes.h" #include "SExposedVideoData.h" #include "SOverrideMaterial.h" +#include "S3DVertex.h" // E_VERTEX_TYPE +#include "SVertexIndex.h" // E_INDEX_TYPE namespace irr { @@ -36,77 +38,12 @@ class ISceneNode; namespace video { -struct S3DVertex; -struct S3DVertex2TCoords; -struct S3DVertexTangents; class IImageLoader; class IImageWriter; class IMaterialRenderer; class IGPUProgrammingServices; class IRenderTarget; -//! enumeration for geometry transformation states -enum E_TRANSFORMATION_STATE -{ - //! View transformation - ETS_VIEW = 0, - //! World transformation - ETS_WORLD, - //! Projection transformation - ETS_PROJECTION, - //! Texture 0 transformation - //! Use E_TRANSFORMATION_STATE(ETS_TEXTURE_0 + texture_number) to access other texture transformations - ETS_TEXTURE_0, - //! Only used internally - ETS_COUNT = ETS_TEXTURE_0 + MATERIAL_MAX_TEXTURES -}; - -//! Special render targets, which usually map to dedicated hardware -/** These render targets (besides 0 and 1) need not be supported by gfx cards */ -enum E_RENDER_TARGET -{ - //! Render target is the main color frame buffer - ERT_FRAME_BUFFER = 0, - //! Render target is a render texture - ERT_RENDER_TEXTURE, - //! Multi-Render target textures - ERT_MULTI_RENDER_TEXTURES, - //! Render target is the main color frame buffer - ERT_STEREO_LEFT_BUFFER, - //! Render target is the right color buffer (left is the main buffer) - ERT_STEREO_RIGHT_BUFFER, - //! Render to both stereo buffers at once - ERT_STEREO_BOTH_BUFFERS, - //! Auxiliary buffer 0 - ERT_AUX_BUFFER0, - //! Auxiliary buffer 1 - ERT_AUX_BUFFER1, - //! Auxiliary buffer 2 - ERT_AUX_BUFFER2, - //! Auxiliary buffer 3 - ERT_AUX_BUFFER3, - //! Auxiliary buffer 4 - ERT_AUX_BUFFER4 -}; - -//! Enum for the flags of clear buffer -enum E_CLEAR_BUFFER_FLAG -{ - ECBF_NONE = 0, - ECBF_COLOR = 1, - ECBF_DEPTH = 2, - ECBF_STENCIL = 4, - ECBF_ALL = ECBF_COLOR | ECBF_DEPTH | ECBF_STENCIL -}; - -//! Enum for the types of fog distributions to choose from -enum E_FOG_TYPE -{ - EFT_FOG_EXP = 0, - EFT_FOG_LINEAR, - EFT_FOG_EXP2 -}; - const c8 *const FogTypeNames[] = { "FogExp", "FogLinear", diff --git a/irr/include/IrrlichtDevice.h b/irr/include/IrrlichtDevice.h index 11619010c..777a23420 100644 --- a/irr/include/IrrlichtDevice.h +++ b/irr/include/IrrlichtDevice.h @@ -6,14 +6,16 @@ #include "IReferenceCounted.h" #include "dimension2d.h" -#include "IVideoDriver.h" #include "EDriverTypes.h" #include "EDeviceTypes.h" #include "IEventReceiver.h" #include "ICursorControl.h" #include "ITimer.h" #include "IOSOperator.h" +#include "irrArray.h" #include "IrrCompileConfig.h" +#include "position2d.h" +#include "SColor.h" // video::ECOLOR_FORMAT namespace irr { @@ -38,6 +40,9 @@ class ISceneManager; namespace video { class IContextManager; +class IImage; +class ITexture; +class IVideoDriver; extern "C" IRRLICHT_API bool IRRCALLCONV isDriverSupported(E_DRIVER_TYPE driver); } // end namespace video diff --git a/irr/include/SViewFrustum.h b/irr/include/SViewFrustum.h index 06983cc5e..cd898e032 100644 --- a/irr/include/SViewFrustum.h +++ b/irr/include/SViewFrustum.h @@ -9,7 +9,7 @@ #include "line3d.h" #include "aabbox3d.h" #include "matrix4.h" -#include "IVideoDriver.h" +#include "EVideoTypes.h" namespace irr { diff --git a/irr/src/CIrrDeviceLinux.cpp b/irr/src/CIrrDeviceLinux.cpp index 5e3afae36..6ecb499b2 100644 --- a/irr/src/CIrrDeviceLinux.cpp +++ b/irr/src/CIrrDeviceLinux.cpp @@ -26,6 +26,7 @@ #include "IGUISpriteBank.h" #include "IImageLoader.h" #include "IFileSystem.h" +#include "IVideoDriver.h" #include #include diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index 58fba4f25..14d996e47 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -10,6 +10,7 @@ #include "IGUIEnvironment.h" #include "IImageLoader.h" #include "IFileSystem.h" +#include "IVideoDriver.h" #include "os.h" #include "CTimer.h" #include "irrString.h" diff --git a/irr/src/CIrrDeviceStub.cpp b/irr/src/CIrrDeviceStub.cpp index 1ff120d10..fd8e458c8 100644 --- a/irr/src/CIrrDeviceStub.cpp +++ b/irr/src/CIrrDeviceStub.cpp @@ -8,6 +8,7 @@ #include "IFileSystem.h" #include "IGUIElement.h" #include "IGUIEnvironment.h" +#include "IVideoDriver.h" #include "os.h" #include "CTimer.h" #include "CLogger.h" diff --git a/irr/src/CIrrDeviceWin32.cpp b/irr/src/CIrrDeviceWin32.cpp index fe5293988..366be8013 100644 --- a/irr/src/CIrrDeviceWin32.cpp +++ b/irr/src/CIrrDeviceWin32.cpp @@ -17,6 +17,7 @@ #include "COSOperator.h" #include "dimension2d.h" #include "IGUISpriteBank.h" +#include "IVideoDriver.h" #include #include "SExposedVideoData.h" diff --git a/irr/src/CSceneCollisionManager.cpp b/irr/src/CSceneCollisionManager.cpp index 692f3c44f..77549a7dc 100644 --- a/irr/src/CSceneCollisionManager.cpp +++ b/irr/src/CSceneCollisionManager.cpp @@ -6,7 +6,6 @@ #include "ICameraSceneNode.h" #include "SViewFrustum.h" -#include "os.h" #include "irrMath.h" namespace irr diff --git a/src/client/camera.cpp b/src/client/camera.cpp index 5e724d05e..f13046848 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "script/scripting_client.h" #include "gettext.h" #include +#include #define CAMERA_OFFSET_STEP 200 #define WIELDMESH_OFFSET_X 55.0f diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index cdad3146c..7d40dec92 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/mesh.h" #include "mapblock_mesh.h" #include +#include #include #include "mapsector.h" #include "mapblock.h" @@ -192,6 +193,13 @@ void ClientMap::OnRegisterSceneNode() // we have other way to find it } +void ClientMap::render() +{ + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); + renderMap(driver, SceneManager->getSceneNodeRenderPass()); +} + void ClientMap::getBlocksInViewRange(v3s16 cam_pos_nodes, v3s16 *p_blocks_min, v3s16 *p_blocks_max, float range) { diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 7456902c8..8fac5a471 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -75,12 +75,7 @@ public: virtual void OnRegisterSceneNode() override; - virtual void render() override - { - video::IVideoDriver* driver = SceneManager->getVideoDriver(); - driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); - renderMap(driver, SceneManager->getSceneNodeRenderPass()); - } + virtual void render() override; virtual const aabb3f &getBoundingBox() const override { diff --git a/src/client/imagefilters.cpp b/src/client/imagefilters.cpp index db6523ad3..57e444151 100644 --- a/src/client/imagefilters.cpp +++ b/src/client/imagefilters.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include // Simple 2D bitmap class with just the functionality needed here class Bitmap { diff --git a/src/client/render/anaglyph.cpp b/src/client/render/anaglyph.cpp index 4cb42db50..b1117998f 100644 --- a/src/client/render/anaglyph.cpp +++ b/src/client/render/anaglyph.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "anaglyph.h" #include "client/camera.h" +#include /// SetColorMaskStep step diff --git a/src/client/render/core.h b/src/client/render/core.h index c5617bcb2..fb60b274e 100644 --- a/src/client/render/core.h +++ b/src/client/render/core.h @@ -21,6 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "irrlichttypes_extrabloated.h" +namespace irr +{ + class IrrlichtDevice; +} + class ShadowRenderer; class Camera; class Client; diff --git a/src/client/render/pipeline.h b/src/client/render/pipeline.h index abb108652..d0e3b2c38 100644 --- a/src/client/render/pipeline.h +++ b/src/client/render/pipeline.h @@ -19,6 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "irrlichttypes_extrabloated.h" +#include // used in all render/*.cpp +#include // used in all render/*.cpp #include #include @@ -31,6 +33,11 @@ class Client; class Hud; class ShadowRenderer; +namespace irr::video +{ + class IRenderTarget; +} + struct PipelineContext { PipelineContext(IrrlichtDevice *_device, Client *_client, Hud *_hud, ShadowRenderer *_shadow_renderer, video::SColor _color, v2u32 _target_size) diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 1a2a63513..7f7518f61 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/render/core.h" // include the shadow mapper classes too #include "client/shadows/dynamicshadowsrender.h" +#include #ifdef SERVER #error Do not include in server builds diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp index 11db9bea7..ffe7d4de5 100644 --- a/src/client/shadows/dynamicshadows.cpp +++ b/src/client/shadows/dynamicshadows.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/clientenvironment.h" #include "client/clientmap.h" #include "client/camera.h" +#include using m4f = core::matrix4; diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp index 91992bc08..cfa54dfb7 100644 --- a/src/client/shadows/dynamicshadowsrender.cpp +++ b/src/client/shadows/dynamicshadowsrender.cpp @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "EShaderTypes.h" #include "IGPUProgrammingServices.h" #include "IMaterialRenderer.h" +#include ShadowRenderer::ShadowRenderer(IrrlichtDevice *device, Client *client) : m_smgr(device->getSceneManager()), m_driver(device->getVideoDriver()), diff --git a/src/client/shadows/dynamicshadowsrender.h b/src/client/shadows/dynamicshadowsrender.h index fc139e28b..c4ffb39e2 100644 --- a/src/client/shadows/dynamicshadowsrender.h +++ b/src/client/shadows/dynamicshadowsrender.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include #include "irrlichttypes_extrabloated.h" #include "client/shadows/dynamicshadows.h" diff --git a/src/client/shadows/shadowsScreenQuad.cpp b/src/client/shadows/shadowsScreenQuad.cpp index 5f6d38157..f0eb9ab4a 100644 --- a/src/client/shadows/shadowsScreenQuad.cpp +++ b/src/client/shadows/shadowsScreenQuad.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "shadowsScreenQuad.h" +#include shadowScreenQuad::shadowScreenQuad() { diff --git a/src/gui/guiAnimatedImage.cpp b/src/gui/guiAnimatedImage.cpp index 5e14d2ef2..1c13531dc 100644 --- a/src/gui/guiAnimatedImage.cpp +++ b/src/gui/guiAnimatedImage.cpp @@ -6,6 +6,7 @@ #include "util/string.h" #include #include +#include GUIAnimatedImage::GUIAnimatedImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, const core::rect &rectangle) : diff --git a/src/gui/guiBox.cpp b/src/gui/guiBox.cpp index 443f1064f..972eb4538 100644 --- a/src/gui/guiBox.cpp +++ b/src/gui/guiBox.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "guiBox.h" +#include GUIBox::GUIBox(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, const core::rect &rectangle, diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 8c4fda32e..30045a168 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -37,6 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/string.h" #include "util/enriched_string.h" #include "StyleSpec.h" +#include // gui::ECURSOR_ICON #include class InventoryManager; diff --git a/src/gui/guiInventoryList.cpp b/src/gui/guiInventoryList.cpp index 02505d436..e5ed6e6ef 100644 --- a/src/gui/guiInventoryList.cpp +++ b/src/gui/guiInventoryList.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiFormSpecMenu.h" #include "client/hud.h" #include "client/client.h" +#include GUIInventoryList::GUIInventoryList(gui::IGUIEnvironment *env, gui::IGUIElement *parent, diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 97e1ae8e0..08ae5987e 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "settings.h" #include diff --git a/src/gui/guiOpenURL.cpp b/src/gui/guiOpenURL.cpp index f91d31391..7ce79b0e5 100644 --- a/src/gui/guiOpenURL.cpp +++ b/src/gui/guiOpenURL.cpp @@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "guiEditBoxWithScrollbar.h" #include #include +#include #include "client/renderingengine.h" #include "porting.h" #include "gettext.h" diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp index 414ff36a8..a9b73dfe8 100644 --- a/src/gui/guiPasswordChange.cpp +++ b/src/gui/guiPasswordChange.cpp @@ -24,6 +24,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include "porting.h" #include "gettext.h" diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp index 239fbe015..a26cfa93f 100644 --- a/src/gui/guiScene.cpp +++ b/src/gui/guiScene.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include #include "porting.h" GUIScene::GUIScene(gui::IGUIEnvironment *env, scene::ISceneManager *smgr, diff --git a/src/gui/guiVolumeChange.cpp b/src/gui/guiVolumeChange.cpp index a6608dd18..4d1139dce 100644 --- a/src/gui/guiVolumeChange.cpp +++ b/src/gui/guiVolumeChange.cpp @@ -27,6 +27,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include #include +#include #include "settings.h" #include "gettext.h" diff --git a/src/gui/profilergraph.cpp b/src/gui/profilergraph.cpp index e6fdf9ae8..ab4796cb9 100644 --- a/src/gui/profilergraph.cpp +++ b/src/gui/profilergraph.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "profilergraph.h" +#include "IVideoDriver.h" #include "util/string.h" void ProfilerGraph::put(const Profiler::GraphValues &values) diff --git a/src/gui/profilergraph.h b/src/gui/profilergraph.h index 6354ac9ef..c92cbf837 100644 --- a/src/gui/profilergraph.h +++ b/src/gui/profilergraph.h @@ -23,9 +23,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include #include "profiler.h" +namespace irr::video { + class IVideoDriver; +} + /* Profiler display */ class ProfilerGraph { diff --git a/src/gui/touchcontrols.cpp b/src/gui/touchcontrols.cpp index b96ced1bf..1e4f8a99b 100644 --- a/src/gui/touchcontrols.cpp +++ b/src/gui/touchcontrols.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gettext.h" #include "IGUIStaticText.h" #include "IGUIFont.h" +#include #include #include diff --git a/src/gui/touchcontrols.h b/src/gui/touchcontrols.h index ebc251bb6..0a86fe34e 100644 --- a/src/gui/touchcontrols.h +++ b/src/gui/touchcontrols.h @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include #include #include @@ -35,6 +34,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "itemdef.h" #include "client/game.h" +namespace irr +{ + class IrrlichtDevice; +} + using namespace irr; using namespace irr::core; using namespace irr::gui; diff --git a/src/irrlichttypes_extrabloated.h b/src/irrlichttypes_extrabloated.h index b03ba7955..a3de2c3c8 100644 --- a/src/irrlichttypes_extrabloated.h +++ b/src/irrlichttypes_extrabloated.h @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef SERVER #include #include -#include #include #include #include From 52376fd87a0cebb75bcd99113deed0fd569e6786 Mon Sep 17 00:00:00 2001 From: cx384 Date: Mon, 29 Jan 2024 16:43:37 +0100 Subject: [PATCH 49/75] Add hotbar Lua HUD element and replace hardcoded hotbar --- builtin/game/features.lua | 1 + builtin/game/hud.lua | 15 +++++ doc/lua_api.md | 16 +++++- games/devtest/mods/testhud/init.lua | 89 +++++++++++++++++++++++++++++ src/client/hud.cpp | 54 ++++++++++------- src/client/hud.h | 4 +- src/client/render/plain.cpp | 1 - src/hud.cpp | 1 + src/hud.h | 3 +- src/network/networkprotocol.h | 7 ++- src/script/common/c_content.cpp | 5 +- 11 files changed, 165 insertions(+), 31 deletions(-) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 22bf1859d..358ee8ff0 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -42,6 +42,7 @@ core.features = { node_interaction_actor = true, moveresult_new_pos = true, override_item_remove_fields = true, + hotbar_hud_element = true, } function core.has_feature(arg) diff --git a/builtin/game/hud.lua b/builtin/game/hud.lua index 1bc4b48d7..a2a046848 100644 --- a/builtin/game/hud.lua +++ b/builtin/game/hud.lua @@ -259,3 +259,18 @@ register_builtin_hud_element("minimap", { core.get_player_information(player:get_player_name()).protocol_version >= 44 end, }) + +--- Hotbar + +register_builtin_hud_element("hotbar", { + elem_def = { + type = "hotbar", + position = {x = 0.5, y = 1}, + direction = 0, + alignment = {x = 0, y = -1}, + offset = {x = 0, y = -4}, -- Extra padding below. + }, + show_elem = function(player, flags) + return flags.hotbar + end, +}) diff --git a/doc/lua_api.md b/doc/lua_api.md index bf714a515..2e9c57c9e 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -1744,6 +1744,13 @@ Displays a horizontal bar made up of half-images with an optional background. * `item`: Position of item that is selected. * `direction`: Direction the list will be displayed in * `offset`: offset in pixels from position. +* `alignment`: The alignment of the inventory. Aligned at the top left corner if not specified. + +### `hotbar` + +* `direction`: Direction the list will be displayed in +* `offset`: offset in pixels from position. +* `alignment`: The alignment of the inventory. ### `waypoint` @@ -5471,6 +5478,8 @@ Utilities moveresult_new_pos = true, -- Allow removing definition fields in `minetest.override_item` (5.9.0) override_item_remove_fields = true, + -- The predefined hotbar is a Lua HUD element of type `hotbar` (5.10.0) + hotbar_hud_element = true, } ``` @@ -7117,7 +7126,7 @@ Misc. could be used as a player name (regardless of whether said player exists). * `minetest.hud_replace_builtin(name, hud_definition)` * Replaces definition of a builtin hud element - * `name`: `"breath"`, `"health"` or `"minimap"` + * `name`: `"breath"`, `"health"`, `"minimap"` or `"hotbar"` * `hud_definition`: definition to replace builtin definition * `minetest.parse_relative_number(arg, relative_to)`: returns number or nil * Helper function for chat commands. @@ -10664,8 +10673,9 @@ Used by `ObjectRef:hud_add`. Returned by `ObjectRef:hud_get`. ```lua { type = "image", - -- Type of element, can be "image", "text", "statbar", "inventory", - -- "waypoint", "image_waypoint", "compass" or "minimap" + -- Type of element, can be "compass", "hotbar" (46 ¹), "image", "image_waypoint", + -- "inventory", "minimap" (44 ¹), "statbar", "text" or "waypoint" + -- ¹: minimal protocol version for client-side support -- If undefined "text" will be used. hud_elem_type = "image", diff --git a/games/devtest/mods/testhud/init.lua b/games/devtest/mods/testhud/init.lua index 9afed8fc7..1788ad77e 100644 --- a/games/devtest/mods/testhud/init.lua +++ b/games/devtest/mods/testhud/init.lua @@ -208,9 +208,75 @@ minetest.register_chatcommand("zoomfov", { end, }) +-- Hotbars + +local hud_hotbar_defs = { + { + type = "hotbar", + position = {x=0.2, y=0.5}, + direction = 0, + alignment = {x=1, y=-1}, + }, + { + type = "hotbar", + position = {x=0.2, y=0.5}, + direction = 2, + alignment = {x=1, y=1}, + }, + { + type = "hotbar", + position = {x=0.7, y=0.5}, + direction = 0, + offset = {x=140, y=20}, + alignment = {x=-1, y=-1}, + }, + { + type = "hotbar", + position = {x=0.7, y=0.5}, + direction = 2, + offset = {x=140, y=20}, + alignment = {x=-1, y=1}, + }, +} + + +local player_hud_hotbars= {} +minetest.register_chatcommand("hudhotbars", { + description = "Shows some test Lua HUD elements of type hotbar. " .. + "(add: Adds elements (default). remove: Removes elements)", + params = "[ add | remove ]", + func = function(name, params) + local player = minetest.get_player_by_name(name) + if not player then + return false, "No player." + end + + local id_table = player_hud_hotbars[name] + if not id_table then + id_table = {} + player_hud_hotbars[name] = id_table + end + + if params == "remove" then + for _, id in ipairs(id_table) do + player:hud_remove(id) + end + return true, "Hotbars removed." + end + + -- params == "add" or default + for _, def in ipairs(hud_hotbar_defs) do + table.insert(id_table, player:hud_add(def)) + end + return true, #hud_hotbar_defs .." Hotbars added." + end +}) + + minetest.register_on_leaveplayer(function(player) player_font_huds[player:get_player_name()] = nil player_waypoints[player:get_player_name()] = nil + player_hud_hotbars[player:get_player_name()] = nil end) minetest.register_chatcommand("hudprint", { @@ -232,3 +298,26 @@ minetest.register_chatcommand("hudprint", { return true, s end }) + +local hud_flags = {"hotbar", "healthbar", "crosshair", "wielditem", "breathbar", + "minimap", "minimap_radar", "basic_debug", "chat"} + +minetest.register_chatcommand("hudtoggleflag", { + description = "Toggles a hud flag.", + params = "[ ".. table.concat(hud_flags, " | ") .." ]", + func = function(name, params) + local player = minetest.get_player_by_name(name) + if not player then + return false, "No player." + end + + local flags = player:hud_get_flags() + if flags[params] == nil then + return false, "Unknown hud flag." + end + + flags[params] = not flags[params] + player:hud_set_flags(flags) + return true, "Flag \"".. params .."\" set to ".. tostring(flags[params]) .. "." + end +}) diff --git a/src/client/hud.cpp b/src/client/hud.cpp index e2d1ee455..261d01c8e 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -255,7 +255,7 @@ void Hud::drawItem(const ItemStack &item, const core::rect& rect, // NOTE: selectitem = 0 -> no selected; selectitem is 1-based // mainlist can be NULL, but draw the frame anyway. -void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, +void Hud::drawItems(v2s32 screen_pos, v2s32 screen_offset, s32 itemcount, v2f alignment, s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction, bool is_hotbar) { @@ -268,9 +268,11 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, width = tmp; } - // Position of upper left corner of bar - v2s32 pos = screen_offset * m_scale_factor; - pos += upperleftpos; + // Position: screen_pos + screen_offset + alignment + v2s32 pos(screen_offset.X * m_scale_factor, screen_offset.Y * m_scale_factor); + pos += screen_pos; + pos.X += (alignment.X - 1.0f) * (width * 0.5f); + pos.Y += (alignment.Y - 1.0f) * (height * 0.5f); // Store hotbar_image in member variable, used by drawItem() if (hotbar_image != player->hotbar_image) { @@ -368,13 +370,20 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) std::vector elems; elems.reserve(player->maxHudId()); - // Add builtin minimap if the server doesn't send it. + // Add builtin elements if the server doesn't send them. + // Declared here such that they have the same lifetime as the elems vector HudElement minimap; + HudElement hotbar; if (client->getProtoVersion() < 44 && (player->hud_flags & HUD_FLAG_MINIMAP_VISIBLE)) { minimap = {HUD_ELEM_MINIMAP, v2f(1, 0), "", v2f(), "", 0 , 0, 0, v2f(-1, 1), v2f(-10, 10), v3f(), v2s32(256, 256), 0, "", 0}; elems.push_back(&minimap); } + if (client->getProtoVersion() < 46 && player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) { + hotbar = {HUD_ELEM_HOTBAR, v2f(0.5, 1), "", v2f(), "", 0 , 0, 0, v2f(-0.5, -1), + v2f(0, -4), v3f(), v2s32(), 0, "", 0}; + elems.push_back(&hotbar); + } for (size_t i = 0; i != player->maxHudId(); i++) { HudElement *e = player->getHud(i); @@ -449,7 +458,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) InventoryList *inv = inventory->getList(e->text); if (!inv) warningstream << "HUD: Unknown inventory list. name=" << e->text << std::endl; - drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, 0, + drawItems(pos, v2s32(e->offset.X, e->offset.Y), e->number, e->align, 0, inv, e->item, e->dir, false); break; } case HUD_ELEM_WAYPOINT: { @@ -574,6 +583,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) e->offset.Y * m_scale_factor); client->getMinimap()->drawMinimap(rect); break; } + case HUD_ELEM_HOTBAR: { + drawHotbar(pos, e->offset, e->dir, e->align); + break; } default: infostream << "Hud::drawLuaElements: ignoring drawform " << e->type << " due to unrecognized type" << std::endl; @@ -783,9 +795,7 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir, } } } - - -void Hud::drawHotbar(u16 playeritem) +void Hud::drawHotbar(const v2s32 &pos, const v2f &offset, u16 dir, const v2f &align) { if (g_touchcontrols) g_touchcontrols->resetHotbarRects(); @@ -796,30 +806,30 @@ void Hud::drawHotbar(u16 playeritem) return; } + u16 playeritem = player->getWieldIndex(); + v2s32 screen_offset(offset.X, offset.Y); + v2s32 centerlowerpos(m_displaycenter.X, m_screensize.Y); s32 hotbar_itemcount = player->getMaxHotbarItemcount(); s32 width = hotbar_itemcount * (m_hotbar_imagesize + m_padding * 2); - v2s32 pos = centerlowerpos - v2s32(width / 2, m_hotbar_imagesize + m_padding * 3); const v2u32 &window_size = RenderingEngine::getWindowSize(); if ((float) width / (float) window_size.X <= g_settings->getFloat("hud_hotbar_max_width")) { - if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) { - drawItems(pos, v2s32(0, 0), hotbar_itemcount, 0, mainlist, playeritem + 1, 0, true); - } + drawItems(pos, screen_offset, hotbar_itemcount, align, 0, + mainlist, playeritem + 1, dir, true); } else { - pos.X += width/4; + v2s32 firstpos = pos; + firstpos.X += width/4; - v2s32 secondpos = pos; - pos = pos - v2s32(0, m_hotbar_imagesize + m_padding); + v2s32 secondpos = firstpos; + firstpos = firstpos - v2s32(0, m_hotbar_imagesize + m_padding); - if (player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) { - drawItems(pos, v2s32(0, 0), hotbar_itemcount / 2, 0, - mainlist, playeritem + 1, 0, true); - drawItems(secondpos, v2s32(0, 0), hotbar_itemcount, - hotbar_itemcount / 2, mainlist, playeritem + 1, 0, true); - } + drawItems(firstpos, screen_offset, hotbar_itemcount / 2, align, 0, + mainlist, playeritem + 1, dir, true); + drawItems(secondpos, screen_offset, hotbar_itemcount, align, + hotbar_itemcount / 2, mainlist, playeritem + 1, dir, true); } } diff --git a/src/client/hud.h b/src/client/hud.h index 3a9d27022..07df1c66b 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -63,7 +63,7 @@ public: void disableBlockBounds(); void drawBlockBounds(); - void drawHotbar(u16 playeritem); + void drawHotbar(const v2s32 &pos, const v2f &offset, u16 direction, const v2f &align); void resizeHotbar(); void drawCrosshair(); void drawSelectionMesh(); @@ -99,7 +99,7 @@ private: const std::string &texture, const std::string& bgtexture, s32 count, s32 maxcount, v2s32 offset, v2s32 size = v2s32()); - void drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount, + void drawItems(v2s32 screen_pos, v2s32 screen_offset, s32 itemcount, v2f alignment, s32 inv_offset, InventoryList *mainlist, u16 selectitem, u16 direction, bool is_hotbar); diff --git a/src/client/render/plain.cpp b/src/client/render/plain.cpp index 60a732415..0ca2476ee 100644 --- a/src/client/render/plain.cpp +++ b/src/client/render/plain.cpp @@ -61,7 +61,6 @@ void DrawHUD::run(PipelineContext &context) if (context.draw_crosshair) context.hud->drawCrosshair(); - context.hud->drawHotbar(context.client->getEnv().getLocalPlayer()->getWieldIndex()); context.hud->drawLuaElements(context.client->getCamera()->getOffset()); context.client->getCamera()->drawNametags(); } diff --git a/src/hud.cpp b/src/hud.cpp index 9b336cdef..e66eff1e5 100644 --- a/src/hud.cpp +++ b/src/hud.cpp @@ -30,6 +30,7 @@ const struct EnumString es_HudElementType[] = {HUD_ELEM_IMAGE_WAYPOINT, "image_waypoint"}, {HUD_ELEM_COMPASS, "compass"}, {HUD_ELEM_MINIMAP, "minimap"}, + {HUD_ELEM_HOTBAR, "hotbar"}, {0, NULL}, }; diff --git a/src/hud.h b/src/hud.h index 5540828d4..d32ab784e 100644 --- a/src/hud.h +++ b/src/hud.h @@ -67,7 +67,8 @@ enum HudElementType { HUD_ELEM_WAYPOINT = 4, HUD_ELEM_IMAGE_WAYPOINT = 5, HUD_ELEM_COMPASS = 6, - HUD_ELEM_MINIMAP = 7 + HUD_ELEM_MINIMAP = 7, + HUD_ELEM_HOTBAR = 8, }; enum HudElementStat : u8 { diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 743aac831..0ad82835e 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -224,9 +224,14 @@ with this program; if not, write to the Free Software Foundation, Inc., Add TOCLIENT_MOVE_PLAYER_REL Move default minimap from client-side C++ to server-side builtin Lua [scheduled bump for 5.9.0] + PROTOCOL VERSION 45: + Reserved for minimap size change + PROTOCOL VERSION 46: + Move default hotbar from client-side C++ to server-side builtin Lua + [scheduled bump for 5.10.0] */ -#define LATEST_PROTOCOL_VERSION 44 +#define LATEST_PROTOCOL_VERSION 46 #define LATEST_PROTOCOL_VERSION_STRING TOSTRING(LATEST_PROTOCOL_VERSION) // Server's supported network protocol range diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index bd76a3ad3..2e5873bbf 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -2306,7 +2306,10 @@ void read_hud_element(lua_State *L, HudElement *elem) elem->dir = getintfield_default(L, 2, "dir", 0); lua_getfield(L, 2, "alignment"); - elem->align = lua_istable(L, -1) ? read_v2f(L, -1) : v2f(); + if (lua_istable(L, -1)) + elem->align = read_v2f(L, -1); + else + elem->align = elem->type == HUD_ELEM_INVENTORY ? v2f(1.0f, 1.0f) : v2f(); lua_pop(L, 1); lua_getfield(L, 2, "offset"); From 322a9c2f74a83603b709497c34ca03edf43c7bfd Mon Sep 17 00:00:00 2001 From: grorp Date: Sat, 31 Aug 2024 18:11:56 +0200 Subject: [PATCH 50/75] Restore proportional minimap scaling (#15022) --- builtin/game/hud.lua | 11 ++++++++--- doc/lua_api.md | 5 +++++ src/client/hud.cpp | 13 ++++++++++--- src/network/networkprotocol.h | 3 ++- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/builtin/game/hud.lua b/builtin/game/hud.lua index a2a046848..eaf037e68 100644 --- a/builtin/game/hud.lua +++ b/builtin/game/hud.lua @@ -251,12 +251,17 @@ register_builtin_hud_element("minimap", { position = {x = 1, y = 0}, alignment = {x = -1, y = 1}, offset = {x = -10, y = 10}, - size = {x = 256, y = 256}, + size = {x = 0, y = -25}, }, show_elem = function(player, flags) + local proto_ver = core.get_player_information(player:get_player_name()).protocol_version -- Don't add a minimap for clients which already have it hardcoded in C++. - return flags.minimap and - core.get_player_information(player:get_player_name()).protocol_version >= 44 + return flags.minimap and proto_ver >= 44 + end, + update_def = function(player, elem_def) + local proto_ver = core.get_player_information(player:get_player_name()).protocol_version + -- Only use percentage when the client supports it. + elem_def.size = proto_ver >= 45 and {x = 0, y = -25} or {x = 256, y = 256} end, }) diff --git a/doc/lua_api.md b/doc/lua_api.md index 2e9c57c9e..e6a351918 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -1809,6 +1809,11 @@ Displays a minimap on the HUD. * `size`: Size of the minimap to display. Minimap should be a square to avoid distortion. + * Negative values represent percentages of the screen. If either `x` or `y` + is specified as a percentage, the resulting pixel size will be used for + both `x` and `y`. Example: On a 1920x1080 screen, `{x = 0, y = -25}` will + result in a 270x270 minimap. + * Negative values are supported starting with protocol version 45. * `alignment`: The alignment of the minimap. * `offset`: offset in pixels from position. diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 261d01c8e..007421f7a 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -568,14 +568,21 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) } break; } case HUD_ELEM_MINIMAP: { - if (e->size.X <= 0 || e->size.Y <= 0) - break; if (!client->getMinimap()) break; // Draw a minimap of size "size" v2s32 dstsize(e->size.X * m_scale_factor, e->size.Y * m_scale_factor); - // (no percent size as minimap would likely be anamorphosed) + + // Only one percentage is supported to avoid distortion. + if (e->size.X < 0) + dstsize.X = dstsize.Y = m_screensize.X * (e->size.X * -0.01); + else if (e->size.Y < 0) + dstsize.X = dstsize.Y = m_screensize.Y * (e->size.Y * -0.01); + + if (dstsize.X <= 0 || dstsize.Y <= 0) + return; + v2s32 offset((e->align.X - 1.0) * dstsize.X / 2, (e->align.Y - 1.0) * dstsize.Y / 2); core::rect rect(0, 0, dstsize.X, dstsize.Y); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 0ad82835e..467124f60 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -225,7 +225,8 @@ with this program; if not, write to the Free Software Foundation, Inc., Move default minimap from client-side C++ to server-side builtin Lua [scheduled bump for 5.9.0] PROTOCOL VERSION 45: - Reserved for minimap size change + Minimap HUD element supports negative size values as percentages + [bump for 5.9.1] PROTOCOL VERSION 46: Move default hotbar from client-side C++ to server-side builtin Lua [scheduled bump for 5.10.0] From eae9a70385d0cafde1df178c7be12f33dd09689d Mon Sep 17 00:00:00 2001 From: grorp Date: Sat, 31 Aug 2024 20:45:32 +0200 Subject: [PATCH 51/75] TouchControls: Fix outdated player controls in TOSERVER_INTERACT --- src/client/game.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/client/game.cpp b/src/client/game.cpp index 53871d5ac..0a9b4ec58 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -3358,6 +3358,10 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) if (g_touchcontrols) { auto mode = selected_def.touch_interaction.getMode(pointed.type); g_touchcontrols->applyContextControls(mode); + // applyContextControls may change dig/place input. + // Update again so that TOSERVER_INTERACT packets have the correct controls set. + player->control.dig = isKeyDown(KeyType::DIG); + player->control.place = isKeyDown(KeyType::PLACE); } // Note that updating the selection mesh every frame is not particularly efficient, From 5c171f6d61b6ddab0d0d28482bfabb6e3564197e Mon Sep 17 00:00:00 2001 From: grorp Date: Sat, 31 Aug 2024 20:45:53 +0200 Subject: [PATCH 52/75] Basic unittest for HP change calculation --- games/devtest/mods/unittests/player.lua | 44 ++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/games/devtest/mods/unittests/player.lua b/games/devtest/mods/unittests/player.lua index 0dbe450b0..7650d5f57 100644 --- a/games/devtest/mods/unittests/player.lua +++ b/games/devtest/mods/unittests/player.lua @@ -2,7 +2,7 @@ -- HP Change Reasons -- local expect = nil -minetest.register_on_player_hpchange(function(player, hp, reason) +core.register_on_player_hpchange(function(player, hp_change, reason) if expect == nil then return end @@ -37,6 +37,48 @@ local function run_hpchangereason_tests(player) end unittests.register("test_hpchangereason", run_hpchangereason_tests, {player=true}) +-- +-- HP differences +-- + +local expected_diff = nil +core.register_on_player_hpchange(function(player, hp_change, reason) + if expected_diff then + assert(hp_change == expected_diff) + end +end) + +local function run_hp_difference_tests(player) + local old_hp = player:get_hp() + local old_hp_max = player:get_properties().hp_max + + expected_diff = nil + player:set_properties({hp_max = 30}) + player:set_hp(22) + + -- final HP value is clamped to >= 0 before difference calculation + expected_diff = -22 + player:set_hp(-3) + -- and actual final HP value is clamped to >= 0 too + assert(player:get_hp() == 0) + + expected_diff = 22 + player:set_hp(22) + assert(player:get_hp() == 22) + + -- final HP value is clamped to <= U16_MAX before difference calculation + expected_diff = 65535 - 22 + player:set_hp(1000000) + -- and actual final HP value is clamped to <= hp_max + assert(player:get_hp() == 30) + + expected_diff = nil + player:set_properties({hp_max = old_hp_max}) + player:set_hp(old_hp) + core.close_formspec(player:get_player_name(), "") -- hide death screen +end +unittests.register("test_hp_difference", run_hp_difference_tests, {player=true}) + -- -- Player meta -- From 6608057971f7ce247b2a7e10f0b88fd0e9bc9794 Mon Sep 17 00:00:00 2001 From: grorp Date: Sat, 31 Aug 2024 20:46:14 +0200 Subject: [PATCH 53/75] Fix uninitialized SkyboxParams::fog_color --- src/skyparams.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skyparams.h b/src/skyparams.h index a4d0fadac..2ff918f36 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -46,7 +46,7 @@ struct SkyboxParams float body_orbit_tilt { INVALID_SKYBOX_TILT }; s16 fog_distance { -1 }; float fog_start { -1.0f }; - video::SColor fog_color; // override, only used if alpha > 0 + video::SColor fog_color { 0 }; // override, only used if alpha > 0 }; struct SunParams From 1b8b84bee828db44a9c3e5d48e741ee618fff082 Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 31 Aug 2024 16:44:12 +0100 Subject: [PATCH 54/75] connection: Remove unused timeout feature Was only used for a unit test and incorrectly at that. --- src/client/client.cpp | 2 -- src/network/connection.h | 2 -- src/network/mtp/impl.cpp | 7 ----- src/network/mtp/impl.h | 3 -- src/network/networkexceptions.h | 6 ---- src/server.cpp | 1 - src/unittest/test_connection.cpp | 53 +++++++++++++++----------------- 7 files changed, 24 insertions(+), 50 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index d15c9608a..0c222b111 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -398,8 +398,6 @@ void Client::connect(const Address &address, const std::string &address_name, address.print(infostream); infostream << std::endl; - // Since we use TryReceive() a timeout here would be ineffective anyway - m_con->SetTimeoutMs(0); m_con->Connect(address); initLocalMapSaving(address, m_address_name, is_local_server); diff --git a/src/network/connection.h b/src/network/connection.h index a3665566e..bec6f98f0 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -48,7 +48,6 @@ class IConnection public: virtual ~IConnection() = default; - virtual void SetTimeoutMs(u32 timeout) = 0; virtual void Serve(Address bind_addr) = 0; virtual void Connect(Address address) = 0; virtual bool Connected() = 0; @@ -56,7 +55,6 @@ public: virtual void DisconnectPeer(session_t peer_id) = 0; virtual bool ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms) = 0; - virtual void Receive(NetworkPacket *pkt) = 0; bool TryReceive(NetworkPacket *pkt) { return ReceiveTimeoutMs(pkt, 0); } diff --git a/src/network/mtp/impl.cpp b/src/network/mtp/impl.cpp index 2d4a99119..00535945e 100644 --- a/src/network/mtp/impl.cpp +++ b/src/network/mtp/impl.cpp @@ -1486,13 +1486,6 @@ bool Connection::ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms) return false; } -void Connection::Receive(NetworkPacket *pkt) -{ - bool any = ReceiveTimeoutMs(pkt, m_bc_receive_timeout); - if (!any) - throw NoIncomingDataException("No incoming data"); -} - void Connection::Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable) { diff --git a/src/network/mtp/impl.h b/src/network/mtp/impl.h index 0232ebc0d..cc1d4c2ed 100644 --- a/src/network/mtp/impl.h +++ b/src/network/mtp/impl.h @@ -249,13 +249,11 @@ public: void putCommand(ConnectionCommandPtr c); - void SetTimeoutMs(u32 timeout) { m_bc_receive_timeout = timeout; } void Serve(Address bind_addr); void Connect(Address address); bool Connected(); void Disconnect(); bool ReceiveTimeoutMs(NetworkPacket *pkt, u32 timeout_ms); - void Receive(NetworkPacket *pkt); void Send(session_t peer_id, u8 channelnum, NetworkPacket *pkt, bool reliable); session_t GetPeerID() const { return m_peer_id; } Address GetPeerAddress(session_t peer_id); @@ -317,7 +315,6 @@ private: // Backwards compatibility PeerHandler *m_bc_peerhandler; - u32 m_bc_receive_timeout = 0; bool m_shutting_down = false; }; diff --git a/src/network/networkexceptions.h b/src/network/networkexceptions.h index 2e9c2a6e8..1810106e5 100644 --- a/src/network/networkexceptions.h +++ b/src/network/networkexceptions.h @@ -51,12 +51,6 @@ public: InvalidIncomingDataException(const char *s) : BaseException(s) {} }; -class NoIncomingDataException : public BaseException -{ -public: - NoIncomingDataException(const char *s) : BaseException(s) {} -}; - } class SocketException : public BaseException diff --git a/src/server.cpp b/src/server.cpp index a78244e99..bad0e70d2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -551,7 +551,6 @@ void Server::start() m_thread->stop(); // Initialize connection - m_con->SetTimeoutMs(30); m_con->Serve(m_bind_addr); // Start thread diff --git a/src/unittest/test_connection.cpp b/src/unittest/test_connection.cpp index 3c0043389..b7d751dc3 100644 --- a/src/unittest/test_connection.cpp +++ b/src/unittest/test_connection.cpp @@ -160,6 +160,9 @@ void TestConnection::testHelpers() void TestConnection::testConnectSendReceive() { + + constexpr u32 timeout_ms = 100; + /* Test some real connections @@ -210,13 +213,11 @@ void TestConnection::testConnectSendReceive() // Client should not have added client yet UASSERT(hand_client.count == 0); - try { - NetworkPacket pkt; - infostream << "** running client.Receive()" << std::endl; - client.Receive(&pkt); + NetworkPacket pkt; + infostream << "** running client.Receive()" << std::endl; + if (client.ReceiveTimeoutMs(&pkt, timeout_ms)) { infostream << "** Client received: peer_id=" << pkt.getPeerId() << ", size=" << pkt.getSize() << std::endl; - } catch (con::NoIncomingDataException &e) { } // Client should have added server now @@ -227,14 +228,14 @@ void TestConnection::testConnectSendReceive() sleep_ms(100); - try { - NetworkPacket pkt; - infostream << "** running server.Receive()" << std::endl; - server.Receive(&pkt); + NetworkPacket pkt1; + infostream << "** running server.Receive()" << std::endl; + if (server.ReceiveTimeoutMs(&pkt, timeout_ms)) { infostream << "** Server received: peer_id=" << pkt.getPeerId() - << ", size=" << pkt.getSize() - << std::endl; - } catch (con::NoIncomingDataException &e) { + << ", size=" << pkt.getSize() + << std::endl; + } + else { // No actual data received, but the client has // probably been connected } @@ -249,27 +250,23 @@ void TestConnection::testConnectSendReceive() //sleep_ms(50); while (client.Connected() == false) { - try { - NetworkPacket pkt; - infostream << "** running client.Receive()" << std::endl; - client.Receive(&pkt); + NetworkPacket pkt; + infostream << "** running client.Receive()" << std::endl; + if (client.TryReceive(&pkt)) { infostream << "** Client received: peer_id=" << pkt.getPeerId() << ", size=" << pkt.getSize() << std::endl; - } catch (con::NoIncomingDataException &e) { } sleep_ms(50); } sleep_ms(50); - try { - NetworkPacket pkt; - infostream << "** running server.Receive()" << std::endl; - server.Receive(&pkt); + NetworkPacket pkt2; + infostream << "** running server.Receive()" << std::endl; + if (server.ReceiveTimeoutMs(&pkt, timeout_ms)) { infostream << "** Server received: peer_id=" << pkt.getPeerId() - << ", size=" << pkt.getSize() - << std::endl; - } catch (con::NoIncomingDataException &e) { + << ", size=" << pkt.getSize() + << std::endl; } /* @@ -288,7 +285,7 @@ void TestConnection::testConnectSendReceive() NetworkPacket recvpacket; infostream << "** running server.Receive()" << std::endl; - server.Receive(&recvpacket); + UASSERT(server.ReceiveTimeoutMs(&recvpacket, timeout_ms)); infostream << "** Server received: peer_id=" << pkt.getPeerId() << ", size=" << pkt.getSize() << ", data=" << (const char*)pkt.getU8Ptr(0) @@ -338,14 +335,12 @@ void TestConnection::testConnectSendReceive() for (;;) { if (porting::getTimeMs() - timems0 > 5000 || received) break; - try { - NetworkPacket pkt; - client.Receive(&pkt); + NetworkPacket pkt; + if (client.ReceiveTimeoutMs(&pkt, timeout_ms)) { size = pkt.getSize(); peer_id = pkt.getPeerId(); recvdata = pkt.oldForgePacket(); received = true; - } catch (con::NoIncomingDataException &e) { } sleep_ms(10); } From 48e65ac846479c2712496851e635ef65b8e532b1 Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 31 Aug 2024 16:42:37 +0100 Subject: [PATCH 55/75] Don't attempt to process packets when there are none Under certain unlikely circumstances the main server loop could attempt to process packets even when the connection didn't return one. This would result in the default empty packet being processed resulting in spurious warnings about a missing client. --- src/server.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server.cpp b/src/server.cpp index bad0e70d2..fe831d0f7 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1076,6 +1076,8 @@ void Server::Receive(float timeout) // and a faster server-step is better than busy waiting. if (remaining_time_us() < 1000.0f) break; + else + continue; } peer_id = pkt.getPeerId(); From 43363ee0665840218307c37b931d643d54dfbf85 Mon Sep 17 00:00:00 2001 From: red-001 Date: Sat, 31 Aug 2024 19:47:29 +0100 Subject: [PATCH 56/75] Disable CRT security warnings in MSVC (#15077) MSVC by default warns if Annex-K style secure functions with additional parameter validation are not used. For better or worse, afaik other major compilers don't implement it, so it's not a very useful warning for a cross-platform project. --- irr/src/CMakeLists.txt | 4 ++++ lib/lua/CMakeLists.txt | 4 ++++ src/CMakeLists.txt | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index 2bc935b9f..161a0c060 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -83,6 +83,10 @@ if(LINUX_PLATFORM) add_definitions(-D_IRR_LINUX_PLATFORM_) endif() +if(MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + if(USE_SDL2) set(DEVICE "SDL") elseif(DEVICE STREQUAL "SDL") diff --git a/lib/lua/CMakeLists.txt b/lib/lua/CMakeLists.txt index 2de4840cb..869a3c320 100644 --- a/lib/lua/CMakeLists.txt +++ b/lib/lua/CMakeLists.txt @@ -22,6 +22,10 @@ else() set(DEFAULT_ANSI TRUE) endif() +if(MSVC) + set(COMMON_CFLAGS "${COMMON_CFLAGS} -D_CRT_SECURE_NO_WARNINGS") +endif() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") set(COMMON_LDFLAGS "${COMMON_LDFLAGS} -Wl,-E -lm") set(DEFAULT_DLOPEN ON) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 955bf05f2..0ea8a40a9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -781,7 +781,7 @@ endif() if(MSVC) # Visual Studio - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _WIN32_WINNT=0x0601 /D WIN32_LEAN_AND_MEAN") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D _WIN32_WINNT=0x0601 /D WIN32_LEAN_AND_MEAN /D _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) From 7afa78ec82243a118d8954a75587486d54a6b378 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 19 Aug 2024 22:05:59 +0200 Subject: [PATCH 57/75] Remove obsolete client connection init workaround m_connection_reinit_timer has a head-start of 0.1s and this code only took effect for the very first game session so it was broken anyway. --- src/client/client.cpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 0c222b111..d07a8eb4a 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -436,20 +436,14 @@ void Client::step(float dtime) } } - // UGLY hack to fix 2 second startup delay caused by non existent - // server client startup synchronization in local server or singleplayer mode - static bool initial_step = true; - if (initial_step) { - initial_step = false; - } - else if(m_state == LC_Created) { + if (m_state == LC_Created) { float &counter = m_connection_reinit_timer; counter -= dtime; - if(counter <= 0.0) { - counter = 2.0; + if (counter <= 0) { + counter = 1.5f; LocalPlayer *myplayer = m_env.getLocalPlayer(); - FATAL_ERROR_IF(myplayer == NULL, "Local player not found in environment."); + FATAL_ERROR_IF(!myplayer, "Local player not found in environment"); sendInit(myplayer->getName()); } From 1380bf9b88521ccd57d510abe51c7eef68919b92 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 21 Aug 2024 22:13:03 +0200 Subject: [PATCH 58/75] Fix ordering issue with new server peers --- src/client/client.cpp | 2 ++ src/network/peerhandler.h | 21 ++------------------ src/server.cpp | 41 +++++---------------------------------- src/server.h | 7 ------- 4 files changed, 9 insertions(+), 62 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index d07a8eb4a..9c12be8f2 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -436,6 +436,8 @@ void Client::step(float dtime) } } + // The issue that made this workaround necessary was fixed in August 2024, but + // it's not like we can remove this code - ever. if (m_state == LC_Created) { float &counter = m_connection_reinit_timer; counter -= dtime; diff --git a/src/network/peerhandler.h b/src/network/peerhandler.h index 1e00249ca..64e2c85fb 100644 --- a/src/network/peerhandler.h +++ b/src/network/peerhandler.h @@ -30,9 +30,10 @@ class PeerHandler { public: PeerHandler() = default; - virtual ~PeerHandler() = default; + // Note: all functions are called from within a Receive() call on the same thread. + /* This is called after the Peer has been inserted into the Connection's peer container. @@ -46,22 +47,4 @@ public: virtual void deletingPeer(IPeer *peer, bool timeout) = 0; }; -enum PeerChangeType : u8 -{ - PEER_ADDED, - PEER_REMOVED -}; - -struct PeerChange -{ - PeerChange(PeerChangeType t, session_t _peer_id, bool _timeout) : - type(t), peer_id(_peer_id), timeout(_timeout) - { - } - PeerChange() = delete; - - PeerChangeType type; - session_t peer_id; - bool timeout; -}; } diff --git a/src/server.cpp b/src/server.cpp index fe831d0f7..609b7188b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -623,8 +623,6 @@ void Server::AsyncRunStep(float dtime, bool initial_step) */ m_uptime_counter->increment(dtime); - handlePeerChanges(); - /* Update time of day and overall game time */ @@ -1257,19 +1255,18 @@ void Server::onMapEditEvent(const MapEditEvent &event) void Server::peerAdded(con::IPeer *peer) { - verbosestream<<"Server::peerAdded(): peer->id=" - <id<id=" - <id<<", timeout="< m_peer_change_queue; - std::unordered_map m_formspec_state_data; /* From 8972c80d7d5c5abdd31a0ab7caec7076769e1adb Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 18 Aug 2024 22:06:48 +0200 Subject: [PATCH 59/75] Warn if max_packets_per_iteration reduced --- builtin/settingtypes.txt | 5 ++--- src/network/mtp/threads.cpp | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index ccafde036..dd193a0c9 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2024,9 +2024,8 @@ max_simultaneous_block_sends_per_client (Maximum simultaneous block sends per cl # This determines how long they are slowed down after placing or removing a node. full_block_send_enable_min_time_from_building (Delay in sending blocks after building) float 2.0 0.0 -# Maximum number of packets sent per send step, if you have a slow connection -# try reducing it, but don't reduce it to a number below double of targeted -# client number. +# Maximum number of packets sent per send step in the low-level networking code. +# You generally don't need to change this, however busy servers may benefit from a higher number. max_packets_per_iteration (Max. packets per iteration) int 1024 1 65535 # Compression level to use when sending mapblocks to the client. diff --git a/src/network/mtp/threads.cpp b/src/network/mtp/threads.cpp index c7c728c9d..d1a1e2a34 100644 --- a/src/network/mtp/threads.cpp +++ b/src/network/mtp/threads.cpp @@ -59,14 +59,24 @@ static inline u8 readChannel(const u8 *packetdata) /* Connection Threads */ /******************************************************************************/ +#define MPPI_SETTING "max_packets_per_iteration" + ConnectionSendThread::ConnectionSendThread(unsigned int max_packet_size, float timeout) : Thread("ConnectionSend"), m_max_packet_size(max_packet_size), m_timeout(timeout), - m_max_data_packets_per_iteration(g_settings->getU16("max_packets_per_iteration")) + m_max_data_packets_per_iteration(g_settings->getU16(MPPI_SETTING)) { - SANITY_CHECK(m_max_data_packets_per_iteration > 1); + auto &mppi = m_max_data_packets_per_iteration; + mppi = MYMAX(mppi, 1); + + const auto mppi_default = Settings::getLayer(SL_DEFAULTS)->getU16(MPPI_SETTING); + if (mppi < mppi_default) { + warningstream << "You are running the network code with a non-default " + "configuration (" MPPI_SETTING "=" << mppi << "). " + "This is not recommended in production." << std::endl; + } } void *ConnectionSendThread::run() @@ -769,7 +779,7 @@ void ConnectionSendThread::sendPackets(float dtime, u32 peer_packet_quota) } } - if (peer_packet_quota > 0) { + if (peer_packet_quota > 0 && !stopRequested()) { for (session_t peerId : peerIds) { PeerHelper peer = m_connection->getPeerNoEx(peerId); if (!peer) From ac11a14509067604df23834d993a6251962db043 Mon Sep 17 00:00:00 2001 From: JosiahWI <41302989+JosiahWI@users.noreply.github.com> Date: Mon, 2 Sep 2024 07:50:30 -0500 Subject: [PATCH 60/75] Add static glTF support (#14557) Co-authored-by: Lars Mueller Co-authored-by: jordan4ibanez Co-authored-by: sfan5 Co-authored-by: SmallJoker --- CMakeLists.txt | 2 + doc/lua_api.md | 39 +- games/devtest/mods/gltf/LICENSE.md | 14 + games/devtest/mods/gltf/init.lua | 51 + games/devtest/mods/gltf/invalid/empty.gltf | 0 .../invalid/invalid_bufferview_bounds.gltf | 1 + .../mods/gltf/invalid/json_missing_brace.gltf | 1 + games/devtest/mods/gltf/mod.conf | 2 + .../mods/gltf/models/gltf_blender_cube.gltf | 1 + .../gltf_blender_cube_matrix_transform.gltf | 1 + .../gltf/models/gltf_blender_cube_scaled.gltf | 1 + games/devtest/mods/gltf/models/gltf_frog.gltf | 1 + .../gltf/models/gltf_minimal_triangle.gltf | 1 + .../models/gltf_simple_sparse_accessor.gltf | 1 + .../mods/gltf/models/gltf_snow_man.gltf | 1 + .../devtest/mods/gltf/models/gltf_spider.gltf | 1 + .../gltf_triangle_with_vertex_stride.gltf | 1 + .../models/gltf_triangle_without_indices.gltf | 1 + .../devtest/mods/gltf/textures/gltf_cube.png | Bin 0 -> 203 bytes .../devtest/mods/gltf/textures/gltf_frog.png | Bin 0 -> 272 bytes .../mods/gltf/textures/gltf_snow_man.png | Bin 0 -> 205 bytes .../mods/gltf/textures/gltf_spider.png | Bin 0 -> 10957 bytes irr/include/IMesh.h | 11 + irr/include/ISkinnedMesh.h | 3 + irr/include/SMesh.h | 14 + irr/include/SSkinMeshBuffer.h | 11 +- irr/include/irrArray.h | 4 + irr/include/irrString.h | 13 +- irr/src/CGLTFMeshFileLoader.cpp | 695 +++++++++ irr/src/CGLTFMeshFileLoader.h | 147 ++ irr/src/CMakeLists.txt | 10 +- irr/src/CSceneManager.cpp | 2 + irr/src/CSkinnedMesh.cpp | 17 + irr/src/CSkinnedMesh.h | 9 + lib/tiniergltf/.gitignore | 6 + lib/tiniergltf/CMakeLists.txt | 22 + lib/tiniergltf/Readme.md | 39 + lib/tiniergltf/tiniergltf.hpp | 1357 +++++++++++++++++ src/client/client.cpp | 2 +- src/client/content_cao.cpp | 21 +- src/client/content_mapblock.cpp | 5 +- src/client/mesh.cpp | 2 +- src/gui/guiFormSpecMenu.cpp | 9 +- src/server.cpp | 2 +- src/unittest/CMakeLists.txt | 2 +- src/unittest/test_irr_gltf_mesh_loader.cpp | 366 +++++ src/unittest/test_servermodmanager.cpp | 2 +- 47 files changed, 2863 insertions(+), 28 deletions(-) create mode 100644 games/devtest/mods/gltf/LICENSE.md create mode 100644 games/devtest/mods/gltf/init.lua create mode 100644 games/devtest/mods/gltf/invalid/empty.gltf create mode 100644 games/devtest/mods/gltf/invalid/invalid_bufferview_bounds.gltf create mode 100644 games/devtest/mods/gltf/invalid/json_missing_brace.gltf create mode 100644 games/devtest/mods/gltf/mod.conf create mode 100644 games/devtest/mods/gltf/models/gltf_blender_cube.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_blender_cube_matrix_transform.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_blender_cube_scaled.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_frog.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_minimal_triangle.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_simple_sparse_accessor.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_snow_man.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_spider.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_triangle_with_vertex_stride.gltf create mode 100644 games/devtest/mods/gltf/models/gltf_triangle_without_indices.gltf create mode 100644 games/devtest/mods/gltf/textures/gltf_cube.png create mode 100644 games/devtest/mods/gltf/textures/gltf_frog.png create mode 100644 games/devtest/mods/gltf/textures/gltf_snow_man.png create mode 100644 games/devtest/mods/gltf/textures/gltf_spider.png create mode 100644 irr/src/CGLTFMeshFileLoader.cpp create mode 100644 irr/src/CGLTFMeshFileLoader.h create mode 100644 lib/tiniergltf/.gitignore create mode 100644 lib/tiniergltf/CMakeLists.txt create mode 100644 lib/tiniergltf/Readme.md create mode 100644 lib/tiniergltf/tiniergltf.hpp create mode 100644 src/unittest/test_irr_gltf_mesh_loader.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6623fa828..54a830089 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -283,6 +283,8 @@ if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) add_subdirectory(lib/catch2) endif() +add_subdirectory(lib/tiniergltf) + # Subdirectories # Be sure to add all relevant definitions above this add_subdirectory(src) diff --git a/doc/lua_api.md b/doc/lua_api.md index e6a351918..6989f3483 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -274,7 +274,7 @@ Accepted formats are: images: .png, .jpg, .tga, (deprecated:) .bmp sounds: .ogg vorbis - models: .x, .b3d, .obj + models: .x, .b3d, .obj, .gltf (Minetest 5.10 or newer) Other formats won't be sent to the client (e.g. you can store .blend files in a folder for convenience, without the risk that such files are transferred) @@ -291,6 +291,43 @@ in one of its parents, the parent's file is used. Although it is discouraged, a mod can overwrite a media file of any mod that it depends on by supplying a file with an equal name. +Only a subset of model file format features is supported: + +Simple textured meshes (with multiple textures), optionally with normals. +The .x and .b3d formats additionally support skeletal animation. + +#### glTF + +The glTF model file format for now only serves as a +more modern alternative to the other static model file formats; +it unlocks no special rendering features. + +This means that many glTF features are not supported *yet*, including: + +* Animation +* 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 + * References to files via URIs + +Textures are supplied solely via the same means as for the other model file formats: +The `textures` object property, the `tiles` node definition field and +the list of textures used in the `model[]` formspec element. + +The order in which textures are to be supplied +is that in which they appear in the `textures` array in the glTF file. + +Do not rely on glTF features not being supported; they may be supported in the future. +The backwards compatibility guarantee does not extend to ignoring unsupported features. + +For example, if your model used an emissive material, +you should expect that a future version of Minetest may respect this, +and thus cause your model to render differently there. + Naming conventions ------------------ diff --git a/games/devtest/mods/gltf/LICENSE.md b/games/devtest/mods/gltf/LICENSE.md new file mode 100644 index 000000000..b0ae5fef5 --- /dev/null +++ b/games/devtest/mods/gltf/LICENSE.md @@ -0,0 +1,14 @@ +glTF test model (and corresponding texture) licenses: + +* Spider (`gltf_spider.gltf`, `gltf_spider.png`): + * By [archfan7411](https://github.com/archfan7411) + * Licensed under CC0, public domain "wherever public domain carries fewer rights or legal protections" +* Frog (`gltf_frog.gltf`, `gltf_frog.png`): + * By [Susybaka1234](https://sketchfab.com/3d-models/african-clawed-frog-v2-c81152c93948480c931c280d18957358) + * Licensed under CC-BY 4.0 +* Snow Man (`gltf_snow_man.gltf`, `gltf_snow_man.png`): + * By [jordan4ibanez](https://github.com/jordan4ibanez) + * Licensed under CC0 +* Minimal triangle, triangle without indices (`gltf_minimal_triangle.gltf`, `gltf_triangle_without_indices.gltf`) + * From [the glTF sample model collection](https://github.com/KhronosGroup/glTF-Sample-Models) + * Licensed under CC0 / public domain diff --git a/games/devtest/mods/gltf/init.lua b/games/devtest/mods/gltf/init.lua new file mode 100644 index 000000000..b5c2032bc --- /dev/null +++ b/games/devtest/mods/gltf/init.lua @@ -0,0 +1,51 @@ +local function register_entity(name, textures, backface_culling) + minetest.register_entity("gltf:" .. name, { + initial_properties = { + visual = "mesh", + mesh = "gltf_" .. name .. ".gltf", + textures = textures, + backface_culling = backface_culling, + }, + }) +end + +-- These do not have texture coordinates; they simple render as black surfaces. +register_entity("minimal_triangle", {}, false) +register_entity("triangle_with_vertex_stride", {}, false) +register_entity("triangle_without_indices", {}, false) +do + local cube_textures = {"gltf_cube.png"} + register_entity("blender_cube", cube_textures) + register_entity("blender_cube_scaled", cube_textures) + register_entity("blender_cube_matrix_transform", cube_textures) +end +register_entity("snow_man", {"gltf_snow_man.png"}) +register_entity("spider", {"gltf_spider.png"}) +-- Note: Model has an animation, but we can use it as a static test nevertheless +-- The claws rendering incorrectly from one side is expected behavior: +-- They use an unsupported double-sided material. +register_entity("frog", {"gltf_frog.png"}, false) + +minetest.register_node("gltf:frog", { + description = "glTF frog, but it's a node", + tiles = {{name = "gltf_frog.png", backface_culling = false}}, + drawtype = "mesh", + mesh = "gltf_frog.gltf", +}) + +minetest.register_chatcommand("show_model", { + params = " [textures]", + description = "Show a model (defaults to gltf models, for example '/show_model frog').", + func = function(name, param) + local model, textures = param:match"^(.-)%s+(.+)$" + if not model then + model = "gltf_" .. param .. ".gltf" + textures = "gltf_" .. param .. ".png" + end + minetest.show_formspec(name, "gltf:model", table.concat{ + "formspec_version[7]", + "size[10,10]", + "model[0,0;10,10;model;", model, ";", textures, ";0,0;true;true;0,0;0]", + }) + end, +}) diff --git a/games/devtest/mods/gltf/invalid/empty.gltf b/games/devtest/mods/gltf/invalid/empty.gltf new file mode 100644 index 000000000..e69de29bb diff --git a/games/devtest/mods/gltf/invalid/invalid_bufferview_bounds.gltf b/games/devtest/mods/gltf/invalid/invalid_bufferview_bounds.gltf new file mode 100644 index 000000000..2182861c6 --- /dev/null +++ b/games/devtest/mods/gltf/invalid/invalid_bufferview_bounds.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":0}}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA","byteLength":36}],"bufferViews":[{"buffer":0,"byteOffset":1,"byteLength":36,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/games/devtest/mods/gltf/invalid/json_missing_brace.gltf b/games/devtest/mods/gltf/invalid/json_missing_brace.gltf new file mode 100644 index 000000000..98232c64f --- /dev/null +++ b/games/devtest/mods/gltf/invalid/json_missing_brace.gltf @@ -0,0 +1 @@ +{ diff --git a/games/devtest/mods/gltf/mod.conf b/games/devtest/mods/gltf/mod.conf new file mode 100644 index 000000000..3ec50d2ef --- /dev/null +++ b/games/devtest/mods/gltf/mod.conf @@ -0,0 +1,2 @@ +name = gltf +description = Hosts gltf test models, both for the C++ unit tests and for in-game viewing diff --git a/games/devtest/mods/gltf/models/gltf_blender_cube.gltf b/games/devtest/mods/gltf/models/gltf_blender_cube.gltf new file mode 100644 index 000000000..041b4a1fc --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_blender_cube.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[0]}],"nodes":[{"mesh":0,"name":"Cube","scale":[10,10,10]}],"meshes":[{"name":"Cube.004","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3}]}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"type":"SCALAR"}],"bufferViews":[{"buffer":0,"byteLength":288,"byteOffset":0},{"buffer":0,"byteLength":288,"byteOffset":288},{"buffer":0,"byteLength":192,"byteOffset":576},{"buffer":0,"byteLength":72,"byteOffset":768}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAADAAkAAAAJAAYACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAQADQAEAAIABwASAAwABwAMAAEAFgALAAUAFgAFABAA"}]} diff --git a/games/devtest/mods/gltf/models/gltf_blender_cube_matrix_transform.gltf b/games/devtest/mods/gltf/models/gltf_blender_cube_matrix_transform.gltf new file mode 100644 index 000000000..50235ceae --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_blender_cube_matrix_transform.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[0]}],"nodes":[{"mesh":0,"name":"Cube","matrix":[1,0,0,0,0,2,0,0,0,0,3,0,4,5,6,1]}],"meshes":[{"name":"Cube.004","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3}]}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"type":"SCALAR"}],"bufferViews":[{"buffer":0,"byteLength":288,"byteOffset":0},{"buffer":0,"byteLength":288,"byteOffset":288},{"buffer":0,"byteLength":192,"byteOffset":576},{"buffer":0,"byteLength":72,"byteOffset":768}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAADAAkAAAAJAAYACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAQADQAEAAIABwASAAwABwAMAAEAFgALAAUAFgAFABAA"}]} diff --git a/games/devtest/mods/gltf/models/gltf_blender_cube_scaled.gltf b/games/devtest/mods/gltf/models/gltf_blender_cube_scaled.gltf new file mode 100644 index 000000000..3b626b37e --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_blender_cube_scaled.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[0]}],"nodes":[{"mesh":0,"name":"Cube","scale":[150,1,21.5]}],"meshes":[{"name":"Cube.004","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3}]}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"type":"SCALAR"}],"bufferViews":[{"buffer":0,"byteLength":288,"byteOffset":0},{"buffer":0,"byteLength":288,"byteOffset":288},{"buffer":0,"byteLength":192,"byteOffset":576},{"buffer":0,"byteLength":72,"byteOffset":768}],"buffers":[{"byteLength":840,"uri":"data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIC/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgD8AAIA/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIC/AACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAADAPgAAgD8AAAA+AACAPgAAwD4AAAAAAAAgPwAAgD8AACA/AAAAAAAAYD8AAIA+AADAPgAAQD8AAAA+AAAAPwAAwD4AAEA/AAAgPwAAQD8AACA/AABAPwAAYD8AAAA/AADAPgAAgD4AAMA+AACAPgAAwD4AAIA+AAAgPwAAgD4AACA/AACAPgAAID8AAIA+AADAPgAAAD8AAMA+AAAAPwAAwD4AAAA/AAAgPwAAAD8AACA/AAAAPwAAID8AAAA/AAADAAkAAAAJAAYACAAKABUACAAVABMAFAAXABEAFAARAA4ADQAPAAQADQAEAAIABwASAAwABwAMAAEAFgALAAUAFgAFABAA"}]} diff --git a/games/devtest/mods/gltf/models/gltf_frog.gltf b/games/devtest/mods/gltf/models/gltf_frog.gltf new file mode 100644 index 000000000..201604fd3 --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_frog.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.9.4 glTF exporter"},"scenes":[{"nodes":[20],"name":"blockbench_export"}],"scene":0,"nodes":[{"name":"cube","mesh":0},{"name":"cube","mesh":1},{"name":"cube","mesh":2},{"name":"body","children":[0,1,2]},{"translation":[0,0,-0.0625],"name":"cube","mesh":3},{"translation":[0.03125,0,-0.3125],"name":"cube","mesh":4},{"rotation":[0,-0.19509032201612825,0,0.9807852804032304],"translation":[0.01812248876854733,-0.0625,-0.25194388507103505],"name":"cube","mesh":5},{"translation":[0.0625,0,0.3125],"name":"leftleg","children":[4,5,6]},{"translation":[0.0625,0,-0.3125],"name":"cube","mesh":6},{"translation":[-0.03125,0,-0.3125],"name":"cube","mesh":7},{"rotation":[0,0.19509032201612825,0,0.9807852804032304],"translation":[-0.01812248876854733,-0.0625,-0.25194388507103505],"name":"cube","mesh":8},{"translation":[-0.0625,0,0.3125],"name":"rightleg","children":[8,9,10]},{"translation":[-0.125,-0.0625,0.125],"name":"cube","mesh":9},{"rotation":[0,0.5372996083468239,0,0.8433914458128857],"translation":[0.10431178959951112,-0.0625,0.2349474087973531],"name":"cube","mesh":10},{"rotation":[0,0.5372996083468239,0,0.8433914458128857],"translation":[0.10431178959951112,-0.0625,0.2349474087973531],"name":"cube","mesh":11},{"translation":[0.125,0.0625,-0.125],"name":"leftarm","children":[12,13,14]},{"translation":[0.125,-0.0625,0.125],"name":"cube","mesh":12},{"rotation":[0,-0.5372996083468239,0,0.8433914458128857],"translation":[-0.10431178959951112,-0.0625,0.2349474087973531],"name":"cube","mesh":13},{"rotation":[0,-0.5372996083468239,0,0.8433914458128857],"translation":[-0.10431178959951112,-0.0625,0.2349474087973531],"name":"cube","mesh":14},{"translation":[-0.125,0.0625,-0.125],"name":"rightarm","children":[16,17,18]},{"children":[3,7,11,15,19]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":2520,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2808,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3096,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":3288,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":3360,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3648,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":3936,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":4128,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":4200,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4488,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":4776,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":4968,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":5040,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":5328,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":5616,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":5808,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":5880,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6168,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":6456,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":6648,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":6720,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7008,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7296,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":7488,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":7560,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":7848,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8136,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":8328,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":8400,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8688,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":8976,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":9168,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":9240,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":9528,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":9816,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":10008,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":10080,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":10368,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":10656,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":10848,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":10920,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":11208,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":11496,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":11688,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":11760,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":12048,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":12336,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":12528,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":12600,"byteLength":12},{"buffer":0,"byteOffset":12612,"byteLength":48},{"buffer":0,"byteOffset":12660,"byteLength":12},{"buffer":0,"byteOffset":12672,"byteLength":48},{"buffer":0,"byteOffset":12720,"byteLength":12},{"buffer":0,"byteOffset":12732,"byteLength":48},{"buffer":0,"byteOffset":12780,"byteLength":12},{"buffer":0,"byteOffset":12792,"byteLength":48},{"buffer":0,"byteOffset":12840,"byteLength":12},{"buffer":0,"byteOffset":12852,"byteLength":48},{"buffer":0,"byteOffset":12900,"byteLength":12},{"buffer":0,"byteOffset":12912,"byteLength":48},{"buffer":0,"byteOffset":12960,"byteLength":12},{"buffer":0,"byteOffset":12972,"byteLength":48},{"buffer":0,"byteOffset":13020,"byteLength":12},{"buffer":0,"byteOffset":13032,"byteLength":48},{"buffer":0,"byteOffset":13080,"byteLength":12},{"buffer":0,"byteOffset":13092,"byteLength":48},{"buffer":0,"byteOffset":13140,"byteLength":4},{"buffer":0,"byteOffset":13144,"byteLength":16},{"buffer":0,"byteOffset":13160,"byteLength":4},{"buffer":0,"byteOffset":13164,"byteLength":16}],"buffers":[{"byteLength":13180,"uri":"data:application/octet-stream;base64,AAAgPgAAAD4AAIA+AAAgPgAAAD4AAIC9AAAgPgAAAAAAAIA+AAAgPgAAAAAAAIC9AAAgvgAAAD4AAIC9AAAgvgAAAD4AAIA+AAAgvgAAAAAAAIC9AAAgvgAAAAAAAIA+AAAgvgAAAD4AAIC9AAAgPgAAAD4AAIC9AAAgvgAAAD4AAIA+AAAgPgAAAD4AAIA+AAAgvgAAAAAAAIA+AAAgPgAAAAAAAIA+AAAgvgAAAAAAAIC9AAAgPgAAAAAAAIC9AAAgvgAAAD4AAIA+AAAgPgAAAD4AAIA+AAAgvgAAAAAAAIA+AAAgPgAAAAAAAIA+AAAgPgAAAD4AAIC9AAAgvgAAAD4AAIC9AAAgPgAAAAAAAIC9AAAgvgAAAAAAAIC9AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgCAID4AgB8+AIAgPgAAADoAgF8+AIAfPgCAXz4AQKA+AIAgPgDA7z4AgCA+AECgPgCAXz4AwO8+AIBfPgDAnz4AgB8+AIAgPgCAHz4AwJ8+AAAAOgCAID4AAAA6AMDvPgAAADoAQKA+AAAAOgDA7z4AgB8+AECgPgCAHz4AQPA+AIAgPgDgHz8AgCA+AEDwPgCAXz4A4B8/AIBfPgCAID4AgCA+AMCfPgCAID4AgCA+AIBfPgDAnz4AgF8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAPgAAAD4AAIC9AAAAPgAAAD4AAKC+AAAAPgAAAAAAAIC9AAAAPgAAAAAAAKC+AAAAvgAAAD4AAKC+AAAAvgAAAD4AAIC9AAAAvgAAAAAAAKC+AAAAvgAAAAAAAIC9AAAAvgAAAD4AAKC+AAAAPgAAAD4AAKC+AAAAvgAAAD4AAIC9AAAAPgAAAD4AAIC9AAAAvgAAAAAAAIC9AAAAPgAAAAAAAIC9AAAAvgAAAAAAAKC+AAAAPgAAAAAAAKC+AAAAvgAAAD4AAIC9AAAAPgAAAD4AAIC9AAAAvgAAAAAAAIC9AAAAPgAAAAAAAIC9AAAAPgAAAD4AAKC+AAAAvgAAAD4AAKC+AAAAPgAAAAAAAKC+AAAAvgAAAAAAAKC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgBAsD4AAP89AECwPgAAADoAwM8+AAD/PQDAzz4AQIA+AECwPgDAvz4AQLA+AECAPgDAzz4AwL8+AMDPPgCAfz4AwK8+AIAAPgDArz4AgH8+AIBgPgCAAD4AgGA+AMC/PgCAYD4AQIA+AIBgPgDAvz4AwK8+AECAPgDArz4AQMA+AECwPgDA/z4AQLA+AEDAPgDAzz4AwP8+AMDPPgCAAD4AQLA+AIB/PgBAsD4AgAA+AMDPPgCAfz4AwM8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAPQAAAD4AAKA+AAAAPQAAAD4AAIA+AAAAPQAAAAAAAKA+AAAAPQAAAAAAAIA+AAAAvQAAAD4AAIA+AAAAvQAAAD4AAKA+AAAAvQAAAAAAAIA+AAAAvQAAAAAAAKA+AAAAvQAAAD4AAIA+AAAAPQAAAD4AAIA+AAAAvQAAAD4AAKA+AAAAPQAAAD4AAKA+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAvQAAAAAAAIA+AAAAPQAAAAAAAIA+AAAAvQAAAD4AAKA+AAAAPQAAAD4AAKA+AAAAvQAAAAAAAKA+AAAAPQAAAAAAAKA+AAAAPQAAAD4AAIA+AAAAvQAAAD4AAIA+AAAAPQAAAAAAAIA+AAAAvQAAAAAAAIA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AEDwPgAAAj0AwP8+AAACPQBA8D4AAL89AMD/PgAAvz0AIAg/AAACPQDgDz8AAAI9ACAIPwAAvz0A4A8/AAC/PQDgBz8AAPw8ACAAPwAA/DwA4Ac/AAAAOgAgAD8AAAA6AOAPPwAAADoAIAg/AAAAOgDgDz8AAPw8ACAIPwAA/DwAIBA/AAACPQDgFz8AAAI9ACAQPwAAvz0A4Bc/AAC/PQAgAD8AAAI9AOAHPwAAAj0AIAA/AAC/PQDgBz8AAL89AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAABgPgAAAD4AAEA+AABgPgAAAD4AAAAAAABgPgAAAAAAAEA+AABgPgAAAAAAAAAAAAAAvQAAAD4AAAAAAAAAvQAAAD4AAEA+AAAAvQAAAAAAAAAAAAAAvQAAAAAAAEA+AAAAvQAAAD4AAAAAAABgPgAAAD4AAAAAAAAAvQAAAD4AAEA+AABgPgAAAD4AAEA+AAAAvQAAAAAAAEA+AABgPgAAAAAAAEA+AAAAvQAAAAAAAAAAAABgPgAAAAAAAAAAAAAAvQAAAD4AAEA+AABgPgAAAD4AAEA+AAAAvQAAAAAAAEA+AABgPgAAAAAAAEA+AABgPgAAAD4AAAAAAAAAvQAAAD4AAAAAAABgPgAAAAAAAAAAAAAAvQAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AEDQPgBA0D4AwP8+AEDQPgBA0D4AwO8+AMD/PgDA7z4AICA/AEDQPgDgNz8AQNA+ACAgPwDA7z4A4Dc/AMDvPgDgHz8AwM8+ACAAPwDAzz4A4B8/AECgPgAgAD8AQKA+AOA/PwBAoD4AICA/AECgPgDgPz8AwM8+ACAgPwDAzz4AIDg/AEDQPgDgVz8AQNA+ACA4PwDA7z4A4Fc/AMDvPgAgAD8AQNA+AOAfPwBA0D4AIAA/AMDvPgDgHz8AwO8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAABgPgAAwD0AABA/AABgPgAAwD0AAKA+AABgPgAAAD0AABA/AABgPgAAAD0AAKA+AADAPQAAwD0AAKA+AADAPQAAwD0AABA/AADAPQAAAD0AAKA+AADAPQAAAD0AABA/AADAPQAAwD0AAKA+AABgPgAAwD0AAKA+AADAPQAAwD0AABA/AABgPgAAwD0AABA/AADAPQAAAD0AABA/AABgPgAAAD0AABA/AADAPQAAAD0AAKA+AABgPgAAAD0AAKA+AADAPQAAwD0AABA/AABgPgAAwD0AABA/AADAPQAAAD0AABA/AABgPgAAAD0AABA/AABgPgAAwD0AAKA+AADAPQAAwD0AAKA+AABgPgAAAD0AAKA+AADAPQAAAD0AAKA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AECgPgAgGD8AwN8+ACAYPwBAoD4A4B8/AMDfPgDgHz8AIAA/ACAYPwDgHz8AIBg/ACAAPwDgHz8A4B8/AOAfPwDA/z4A4Bc/AEDgPgDgFz8AwP8+AEDwPgBA4D4AQPA+AOAPPwBA8D4AIAA/AEDwPgDgDz8A4Bc/ACAAPwDgFz8AICA/ACAYPwDgLz8AIBg/ACAgPwDgHz8A4C8/AOAfPwBA4D4AIBg/AMD/PgAgGD8AQOA+AOAfPwDA/z4A4B8/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAMQjQPgAAQD4AAMA+MQjQPgAAQD4AAIA+MQjQPgAAgD0AAMA+MQjQPgAAgD0AAIA+AADQPgAAQD4AAIA+AADQPgAAQD4AAMA+AADQPgAAgD0AAIA+AADQPgAAgD0AAMA+AADQPgAAQD4AAIA+MQjQPgAAQD4AAIA+AADQPgAAQD4AAMA+MQjQPgAAQD4AAMA+AADQPgAAgD0AAMA+MQjQPgAAgD0AAMA+AADQPgAAgD0AAIA+MQjQPgAAgD0AAIA+AADQPgAAQD4AAMA+MQjQPgAAQD4AAMA+AADQPgAAgD0AAMA+MQjQPgAAgD0AAMA+MQjQPgAAQD4AAIA+AADQPgAAQD4AAIA+MQjQPgAAgD0AAIA+AADQPgAAgD0AAIA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgCAYD4AAH49AIBgPgAAADoAwI8+AAB+PQDAjz4AAIE9AIBgPgAA/z0AgGA+AACBPQDAjz4AAP89AMCPPgAAgT0AgF8+AAB+PQCAXz4AAIE9AIAgPgAAfj0AgCA+AACBPQCAID4AAH49AIAgPgAAgT0AgF8+AAB+PQCAXz4AgAA+AIBgPgAA/z0AgGA+AIAAPgDAjz4AAP89AMCPPgAAgT0AgGA+AAB+PQCAYD4AAIE9AMCPPgAAfj0AwI8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAvQAAAD4AAOA+AAAAvQAAAD4AAIA+AAAAvQAAAAAAAOA+AAAAvQAAAAAAAIA+AACQvgAAAD4AAIA+AACQvgAAAD4AAOA+AACQvgAAAAAAAIA+AACQvgAAAAAAAOA+AACQvgAAAD4AAIA+AAAAvQAAAD4AAIA+AACQvgAAAD4AAOA+AAAAvQAAAD4AAOA+AACQvgAAAAAAAOA+AAAAvQAAAAAAAOA+AACQvgAAAAAAAIA+AAAAvQAAAAAAAIA+AACQvgAAAD4AAOA+AAAAvQAAAD4AAOA+AACQvgAAAAAAAOA+AAAAvQAAAAAAAOA+AAAAvQAAAD4AAIA+AACQvgAAAD4AAIA+AAAAvQAAAAAAAIA+AACQvgAAAAAAAIA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgAgAD8AAL89ACAAPwAAADoA4A8/AAC/PQDgDz8AgGA+ACAAPwDAnz4AIAA/AIBgPgDgDz8AwJ8+AOAPPwCAXz4AwP8+AADBPQDA/z4AgF8+AEDQPgAAwT0AQNA+AMCvPgBA0D4AgGA+AEDQPgDArz4AwP8+AIBgPgDA/z4AQKA+ACAAPwDA3z4AIAA/AECgPgDgDz8AwN8+AOAPPwAAwT0AIAA/AIBfPgAgAD8AAME9AOAPPwCAXz4A4A8/AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAADAvQAAwD0AABA/AADAvQAAwD0AAKA+AADAvQAAAD0AABA/AADAvQAAAD0AAKA+AABgvgAAwD0AAKA+AABgvgAAwD0AABA/AABgvgAAAD0AAKA+AABgvgAAAD0AABA/AABgvgAAwD0AAKA+AADAvQAAwD0AAKA+AABgvgAAwD0AABA/AADAvQAAwD0AABA/AABgvgAAAD0AABA/AADAvQAAAD0AABA/AABgvgAAAD0AAKA+AADAvQAAAD0AAKA+AABgvgAAwD0AABA/AADAvQAAwD0AABA/AABgvgAAAD0AABA/AADAvQAAAD0AABA/AADAvQAAwD0AAKA+AABgvgAAwD0AAKA+AADAvQAAAD0AAKA+AABgvgAAAD0AAKA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AEDwPgCAAD4A4Bc/AIAAPgBA8D4AgB8+AOAXPwCAHz4AICg/AIAAPgDgRz8AgAA+ACAoPwCAHz4A4Ec/AIAfPgDgJz8AAP89ACAYPwAA/z0A4Cc/AAAAOgAgGD8AAAA6AOA3PwAAADoAICg/AAAAOgDgNz8AAP89ACAoPwAA/z0AIEg/AIAAPgDgVz8AgAA+ACBIPwCAHz4A4Fc/AIAfPgAgGD8AgAA+AOAnPwCAAD4AIBg/AIAfPgDgJz8AgB8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAz/fPvgAAQD4AAMA+z/fPvgAAQD4AAIA+z/fPvgAAgD0AAMA+z/fPvgAAgD0AAIA+AADQvgAAQD4AAIA+AADQvgAAQD4AAMA+AADQvgAAgD0AAIA+AADQvgAAgD0AAMA+AADQvgAAQD4AAIA+z/fPvgAAQD4AAIA+AADQvgAAQD4AAMA+z/fPvgAAQD4AAMA+AADQvgAAgD0AAMA+z/fPvgAAgD0AAMA+AADQvgAAgD0AAIA+z/fPvgAAgD0AAIA+AADQvgAAQD4AAMA+z/fPvgAAQD4AAMA+AADQvgAAgD0AAMA+z/fPvgAAgD0AAMA+z/fPvgAAQD4AAIA+AADQvgAAQD4AAIA+z/fPvgAAgD0AAIA+AADQvgAAgD0AAIA+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgAAgT0AAH49AACBPQAAADoAAP89AAB+PQAA/z0AAIE9AACBPQAA/z0AAIE9AACBPQAA/z0AAP89AAD/PQAAgT0AAH49AAB+PQAAfj0AAIE9AAAAOgAAfj0AAAA6AACBPQAAADoAAH49AAAAOgAAgT0AAH49AAB+PQAAfj0AgAA+AACBPQAA/z0AAIE9AIAAPgAA/z0AAP89AAD/PQAAgT0AAIE9AAB+PQAAgT0AAIE9AAD/PQAAfj0AAP89AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACAPgAAwD0AAMC9AACAPgAAwD0AACC+AACAPgAAAD0AAMC9AACAPgAAAD0AACC+AAAAPgAAwD0AACC+AAAAPgAAwD0AAMC9AAAAPgAAAD0AACC+AAAAPgAAAD0AAMC9AAAAPgAAwD0AACC+AACAPgAAwD0AACC+AAAAPgAAwD0AAMC9AACAPgAAwD0AAMC9AAAAPgAAAD0AAMC9AACAPgAAAD0AAMC9AAAAPgAAAD0AACC+AACAPgAAAD0AACC+AAAAPgAAwD0AAMC9AACAPgAAwD0AAMC9AAAAPgAAAD0AAMC9AACAPgAAAD0AAMC9AACAPgAAwD0AACC+AAAAPgAAwD0AACC+AACAPgAAAD0AACC+AAAAPgAAAD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/ACAIPwBAkD4A4A8/AECQPgAgCD8AwJ8+AOAPPwDAnz4AICA/AECQPgDgJz8AQJA+ACAgPwDAnz4A4Cc/AMCfPgDgHz8AwI8+ACAQPwDAjz4A4B8/AECAPgAgED8AQIA+AOAvPwBAgD4AICA/AECAPgDgLz8AwI8+ACAgPwDAjz4AICg/AECQPgDgNz8AQJA+ACAoPwDAnz4A4Dc/AMCfPgAgED8AQJA+AOAfPwBAkD4AIBA/AMCfPgDgHz8AwJ8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACQPgAAwD0AAMC9AACQPgAAwD0AACC+AACQPgAAAD0AAMC9AACQPgAAAD0AACC+AABgPgAAwD0AACC+AABgPgAAwD0AAMC9AABgPgAAAD0AACC+AABgPgAAAD0AAMC9AABgPgAAwD0AACC+AACQPgAAwD0AACC+AABgPgAAwD0AAMC9AACQPgAAwD0AAMC9AABgPgAAAD0AAMC9AACQPgAAAD0AAMC9AABgPgAAAD0AACC+AACQPgAAAD0AACC+AABgPgAAwD0AAMC9AACQPgAAwD0AAMC9AABgPgAAAD0AAMC9AACQPgAAAD0AAMC9AACQPgAAwD0AACC+AABgPgAAwD0AACC+AACQPgAAAD0AACC+AABgPgAAAD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgBAoD4AAPw8AECgPgAAADoAwK8+AAD8PADArz4AAIE9AECgPgAAvz0AQKA+AACBPQDArz4AAL89AMCvPgAAfj0AwJ8+AAACPQDAnz4AAH49AECQPgAAAj0AQJA+AAC/PQBAkD4AAIE9AECQPgAAvz0AwJ8+AACBPQDAnz4AAME9AECgPgAA/z0AQKA+AADBPQDArz4AAP89AMCvPgAAAj0AQKA+AAB+PQBAoD4AAAI9AMCvPgAAfj0AwK8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACwPsUggD0AAMC9AACwPsUggD0AACC+AACwPgAAgD0AAMC9AACwPgAAgD0AACC+AACQPsUggD0AACC+AACQPsUggD0AAMC9AACQPgAAgD0AACC+AACQPgAAgD0AAMC9AACQPsUggD0AACC+AACwPsUggD0AACC+AACQPsUggD0AAMC9AACwPsUggD0AAMC9AACQPgAAgD0AAMC9AACwPgAAgD0AAMC9AACQPgAAgD0AACC+AACwPgAAgD0AACC+AACQPsUggD0AAMC9AACwPsUggD0AAMC9AACQPgAAgD0AAMC9AACwPgAAgD0AAMC9AACwPsUggD0AACC+AACQPsUggD0AACC+AACwPgAAgD0AACC+AACQPgAAgD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgCAID4AAPw8AIAgPgAAADoAgB8+AAD8PACAHz4AAIE9AIAgPgAAvz0AgCA+AACBPQCAHz4AAL89AIAfPgAAfj0AgB8+AAACPQCAHz4AAH49AIAAPgAAAj0AgAA+AAC/PQCAAD4AAIE9AIAAPgAAvz0AgB8+AACBPQCAHz4AAME9AIAgPgAA/z0AgCA+AADBPQCAHz4AAP89AIAfPgAAAj0AgCA+AAB+PQCAID4AAAI9AIAfPgAAfj0AgB8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAvgAAwD0AAMC9AAAAvgAAwD0AACC+AAAAvgAAAD0AAMC9AAAAvgAAAD0AACC+AACAvgAAwD0AACC+AACAvgAAwD0AAMC9AACAvgAAAD0AACC+AACAvgAAAD0AAMC9AACAvgAAwD0AACC+AAAAvgAAwD0AACC+AACAvgAAwD0AAMC9AAAAvgAAwD0AAMC9AACAvgAAAD0AAMC9AAAAvgAAAD0AAMC9AACAvgAAAD0AACC+AAAAvgAAAD0AACC+AACAvgAAwD0AAMC9AAAAvgAAwD0AAMC9AACAvgAAAD0AAMC9AAAAvgAAAD0AAMC9AAAAvgAAwD0AACC+AACAvgAAwD0AACC+AAAAvgAAAD0AACC+AACAvgAAAD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AEDAPgBAgD4AwM8+AECAPgBAwD4AwI8+AMDPPgDAjz4AQPA+AECAPgDA/z4AQIA+AEDwPgDAjz4AwP8+AMCPPgDA7z4AgH8+AEDQPgCAfz4AwO8+AIBgPgBA0D4AgGA+AOAHPwCAYD4AQPA+AIBgPgDgBz8AgH8+AEDwPgCAfz4AIAA/AECAPgDgDz8AQIA+ACAAPwDAjz4A4A8/AMCPPgBA0D4AQIA+AMDvPgBAgD4AQNA+AMCPPgDA7z4AwI8+AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAABgvgAAwD0AAMC9AABgvgAAwD0AACC+AABgvgAAAD0AAMC9AABgvgAAAD0AACC+AACQvgAAwD0AACC+AACQvgAAwD0AAMC9AACQvgAAAD0AACC+AACQvgAAAD0AAMC9AACQvgAAwD0AACC+AABgvgAAwD0AACC+AACQvgAAwD0AAMC9AABgvgAAwD0AAMC9AACQvgAAAD0AAMC9AABgvgAAAD0AAMC9AACQvgAAAD0AACC+AABgvgAAAD0AACC+AACQvgAAwD0AAMC9AABgvgAAwD0AAMC9AACQvgAAAD0AAMC9AABgvgAAAD0AAMC9AABgvgAAwD0AACC+AACQvgAAwD0AACC+AABgvgAAAD0AACC+AACQvgAAAD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAOgAAAj0AAPw8AAACPQAAADoAAH49AAD8PAAAfj0AAIE9AAACPQAAvz0AAAI9AACBPQAAfj0AAL89AAB+PQAAfj0AAPw8AAACPQAA/DwAAH49AAAAOgAAAj0AAAA6AAC/PQAAADoAAIE9AAAAOgAAvz0AAPw8AACBPQAA/DwAAME9AAACPQAA/z0AAAI9AADBPQAAfj0AAP89AAB+PQAAAj0AAAI9AAB+PQAAAj0AAAI9AAB+PQAAfj0AAH49AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAACQvsUggD0AAMC9AACQvsUggD0AACC+AACQvgAAgD0AAMC9AACQvgAAgD0AACC+AACwvsUggD0AACC+AACwvsUggD0AAMC9AACwvgAAgD0AACC+AACwvgAAgD0AAMC9AACwvsUggD0AACC+AACQvsUggD0AACC+AACwvsUggD0AAMC9AACQvsUggD0AAMC9AACwvgAAgD0AAMC9AACQvgAAgD0AAMC9AACwvgAAgD0AACC+AACQvgAAgD0AACC+AACwvsUggD0AAMC9AACQvsUggD0AAMC9AACwvgAAgD0AAMC9AACQvgAAgD0AAMC9AACQvsUggD0AACC+AACwvsUggD0AACC+AACQvgAAgD0AACC+AACwvgAAgD0AACC+AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACBPQAAAj0AAL89AAACPQAAgT0AAPw8AAC/PQAA/DwAgAA+AAACPQCAHz4AAAI9AIAAPgAA/DwAgB8+AAD8PAAA/z0AAPw8AADBPQAA/DwAAP89AAAAOgAAwT0AAAA6AIAfPgAAADoAgAA+AAAAOgCAHz4AAPw8AIAAPgAA/DwAgCA+AAACPQCAPz4AAAI9AIAgPgAA/DwAgD8+AAD8PAAAwT0AAAI9AAD/PQAAAj0AAME9AAD8PAAA/z0AAPw8AAACAAEAAgADAAEABAAGAAUABgAHAAUACAAKAAkACgALAAkADAAOAA0ADgAPAA0AEAASABEAEgATABEAFAAWABUAFgAXABUAAAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPxPyhT0AAAAAAAAAAK9zfz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPwAAAADug4Q+AAAAAOpGdz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPwAAAADug4S+AAAAAOpGdz8AAAAAIbWyvAAAAABn8H8/AAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPwAAAACoqAU+AAAAAFXPfT8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAwD4AAEA/AAAAAAAAAAAAAAAAAACAPwAAAACoqAW+AAAAAFXPfT8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAAD+rqko/AAAAAAAAAAAAAAAAAACAPwAAAAAhtbI8AAAAAGfwfz8AAAAAx71QPAAAAACu+n8/AAAAAAAAAD+rqko/AAAAAAAAAAAAAAAAAACAPwAAAAAhtbK8AAAAAGfwfz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAQD8AAMA/AAAAAAAAAAAAAAAAAACAPwAAAAC2frK9AAAAAJ4Gfz8AAAAAAAAAAAAAAAAAAIA/AAAAAAAAQD8AAMA/AAAAAAAAAAAAAAAAAACAPwAAAAC2frI9AAAAAJ4Gfz8AAAAAAAAAAAAAAAAAAIA/AAAAAKioBb4AAAAAAAAAAFXPfT8AAIA+qKgFvgAAAAAAAAAAVc99Pw=="}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[0.15625,0.125,0.25],"min":[-0.15625,0,-0.0625],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.62451171875,0.21826171875],"min":[0.00048828125,0.00048828125],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[0.125,0.125,-0.0625],"min":[-0.125,0,-0.3125],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.49951171875,0.40576171875],"min":[0.00048828125,0.21923828125],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":24,"max":[0.03125,0.125,0.3125],"min":[-0.03125,0,0.25],"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":10,"componentType":5126,"count":24,"max":[0.59326171875,0.09326171875],"min":[0.46923828125,0.00048828125],"type":"VEC2"},{"bufferView":11,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":12,"componentType":5126,"count":24,"max":[0.21875,0.125,0.1875],"min":[-0.03125,0,0],"type":"VEC3"},{"bufferView":13,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":14,"componentType":5126,"count":24,"max":[0.84326171875,0.46826171875],"min":[0.40673828125,0.31298828125],"type":"VEC2"},{"bufferView":15,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":16,"componentType":5126,"count":24,"max":[0.21875,0.09375,0.5625],"min":[0.09375,0.03125,0.3125],"type":"VEC3"},{"bufferView":17,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":18,"componentType":5126,"count":24,"max":[0.68701171875,0.62451171875],"min":[0.31298828125,0.46923828125],"type":"VEC2"},{"bufferView":19,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":20,"componentType":5126,"count":24,"max":[0.406312495470047,0.1875,0.375],"min":[0.40625,0.0625,0.25],"type":"VEC3"},{"bufferView":21,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":22,"componentType":5126,"count":24,"max":[0.12548828125,0.28076171875],"min":[0.00048828125,0.15673828125],"type":"VEC2"},{"bufferView":23,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":24,"componentType":5126,"count":24,"max":[-0.03125,0.125,0.4375],"min":[-0.28125,0,0.25],"type":"VEC3"},{"bufferView":25,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":26,"componentType":5126,"count":24,"max":[0.43701171875,0.56201171875],"min":[0.00048828125,0.40673828125],"type":"VEC2"},{"bufferView":27,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":28,"componentType":5126,"count":24,"max":[-0.09375,0.09375,0.5625],"min":[-0.21875,0.03125,0.3125],"type":"VEC3"},{"bufferView":29,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":30,"componentType":5126,"count":24,"max":[0.84326171875,0.15576171875],"min":[0.46923828125,0.00048828125],"type":"VEC2"},{"bufferView":31,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":32,"componentType":5126,"count":24,"max":[-0.406187504529953,0.1875,0.375],"min":[-0.40625,0.0625,0.25],"type":"VEC3"},{"bufferView":33,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":34,"componentType":5126,"count":24,"max":[0.12548828125,0.12451171875],"min":[0.00048828125,0.00048828125],"type":"VEC2"},{"bufferView":35,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":36,"componentType":5126,"count":24,"max":[0.25,0.09375,-0.09375],"min":[0.125,0.03125,-0.15625],"type":"VEC3"},{"bufferView":37,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":38,"componentType":5126,"count":24,"max":[0.71826171875,0.31201171875],"min":[0.53173828125,0.25048828125],"type":"VEC2"},{"bufferView":39,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":40,"componentType":5126,"count":24,"max":[0.28125,0.09375,-0.09375],"min":[0.21875,0.03125,-0.15625],"type":"VEC3"},{"bufferView":41,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":42,"componentType":5126,"count":24,"max":[0.12451171875,0.34326171875],"min":[0.00048828125,0.28173828125],"type":"VEC2"},{"bufferView":43,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":44,"componentType":5126,"count":24,"max":[0.34375,0.0625625029206276,-0.09375],"min":[0.28125,0.0625,-0.15625],"type":"VEC3"},{"bufferView":45,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":46,"componentType":5126,"count":24,"max":[0.12451171875,0.15673828125],"min":[0.00048828125,0.12548828125],"type":"VEC2"},{"bufferView":47,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":48,"componentType":5126,"count":24,"max":[-0.125,0.09375,-0.09375],"min":[-0.25,0.03125,-0.15625],"type":"VEC3"},{"bufferView":49,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":50,"componentType":5126,"count":24,"max":[0.56201171875,0.28076171875],"min":[0.37548828125,0.21923828125],"type":"VEC2"},{"bufferView":51,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":52,"componentType":5126,"count":24,"max":[-0.21875,0.09375,-0.09375],"min":[-0.28125,0.03125,-0.15625],"type":"VEC3"},{"bufferView":53,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":54,"componentType":5126,"count":24,"max":[0.12451171875,0.06201171875],"min":[0.00048828125,0.00048828125],"type":"VEC2"},{"bufferView":55,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":56,"componentType":5126,"count":24,"max":[-0.28125,0.0625625029206276,-0.09375],"min":[-0.34375,0.0625,-0.15625],"type":"VEC3"},{"bufferView":57,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":58,"componentType":5126,"count":24,"max":[0.18701171875,0.03173828125],"min":[0.06298828125,0.00048828125],"type":"VEC2"},{"bufferView":59,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":60,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":61,"componentType":5126,"count":3,"max":[0.06540312618017197,0,0,1],"min":[0,0,0,0.9978589415550232],"type":"VEC4"},{"bufferView":62,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":63,"componentType":5126,"count":3,"max":[0,0.258819043636322,0,1],"min":[0,0,0,0.9659258127212524],"type":"VEC4"},{"bufferView":64,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":65,"componentType":5126,"count":3,"max":[0,0,0,1],"min":[0,-0.258819043636322,0,0.9659258127212524],"type":"VEC4"},{"bufferView":66,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":67,"componentType":5126,"count":3,"max":[0,0.13052618503570557,0,1],"min":[0,0,0,0.9914448857307434],"type":"VEC4"},{"bufferView":68,"componentType":5126,"count":3,"max":[0.75],"min":[0],"type":"SCALAR"},{"bufferView":69,"componentType":5126,"count":3,"max":[0,0,0,1],"min":[0,-0.13052618503570557,0,0.9914448857307434],"type":"VEC4"},{"bufferView":70,"componentType":5126,"count":3,"max":[0.7916666865348816],"min":[0],"type":"SCALAR"},{"bufferView":71,"componentType":5126,"count":3,"max":[0,0.02181488461792469,0,1],"min":[0,0,0,0.9997619986534119],"type":"VEC4"},{"bufferView":72,"componentType":5126,"count":3,"max":[0.7916666865348816],"min":[0],"type":"SCALAR"},{"bufferView":73,"componentType":5126,"count":3,"max":[0,0,0,1],"min":[0,-0.02181488461792469,0,0.9997619986534119],"type":"VEC4"},{"bufferView":74,"componentType":5126,"count":3,"max":[1.5],"min":[0],"type":"SCALAR"},{"bufferView":75,"componentType":5126,"count":3,"max":[0,0,0,1],"min":[0,-0.08715574443340302,0,0.9961947202682495],"type":"VEC4"},{"bufferView":76,"componentType":5126,"count":3,"max":[1.5],"min":[0],"type":"SCALAR"},{"bufferView":77,"componentType":5126,"count":3,"max":[0,0.08715574443340302,0,1],"min":[0,0,0,0.9961947202682495],"type":"VEC4"},{"bufferView":78,"componentType":5126,"count":1,"max":[0],"min":[0],"type":"SCALAR"},{"bufferView":79,"componentType":5126,"count":1,"max":[-0.13052618503570557,0,0,0.9914448857307434],"min":[-0.13052618503570557,0,0,0.9914448857307434],"type":"VEC4"},{"bufferView":80,"componentType":5126,"count":1,"max":[0.25],"min":[0.25],"type":"SCALAR"},{"bufferView":81,"componentType":5126,"count":1,"max":[-0.13052618503570557,0,0,0.9914448857307434],"min":[-0.13052618503570557,0,0,0.9914448857307434],"type":"VEC4"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{"sampler":0}],"samplers":[{"magFilter":9728,"minFilter":9728,"wrapS":33071,"wrapT":33071}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":8,"NORMAL":9,"TEXCOORD_0":10},"indices":11,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":12,"NORMAL":13,"TEXCOORD_0":14},"indices":15,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":16,"NORMAL":17,"TEXCOORD_0":18},"indices":19,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":20,"NORMAL":21,"TEXCOORD_0":22},"indices":23,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":24,"NORMAL":25,"TEXCOORD_0":26},"indices":27,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":28,"NORMAL":29,"TEXCOORD_0":30},"indices":31,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":32,"NORMAL":33,"TEXCOORD_0":34},"indices":35,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":36,"NORMAL":37,"TEXCOORD_0":38},"indices":39,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":40,"NORMAL":41,"TEXCOORD_0":42},"indices":43,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":44,"NORMAL":45,"TEXCOORD_0":46},"indices":47,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":48,"NORMAL":49,"TEXCOORD_0":50},"indices":51,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":52,"NORMAL":53,"TEXCOORD_0":54},"indices":55,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":56,"NORMAL":57,"TEXCOORD_0":58},"indices":59,"material":0}]}],"animations":[{"name":"animation.model.walk","samplers":[{"input":60,"output":61,"interpolation":"LINEAR"},{"input":62,"output":63,"interpolation":"LINEAR"},{"input":64,"output":65,"interpolation":"LINEAR"},{"input":66,"output":67,"interpolation":"LINEAR"},{"input":68,"output":69,"interpolation":"LINEAR"}],"channels":[{"sampler":0,"target":{"node":3,"path":"rotation"}},{"sampler":1,"target":{"node":7,"path":"rotation"}},{"sampler":2,"target":{"node":11,"path":"rotation"}},{"sampler":3,"target":{"node":15,"path":"rotation"}},{"sampler":4,"target":{"node":19,"path":"rotation"}}]},{"name":"animation.model.idle","samplers":[{"input":70,"output":71,"interpolation":"LINEAR"},{"input":72,"output":73,"interpolation":"LINEAR"},{"input":74,"output":75,"interpolation":"LINEAR"},{"input":76,"output":77,"interpolation":"LINEAR"}],"channels":[{"sampler":0,"target":{"node":7,"path":"rotation"}},{"sampler":1,"target":{"node":11,"path":"rotation"}},{"sampler":2,"target":{"node":15,"path":"rotation"}},{"sampler":3,"target":{"node":19,"path":"rotation"}}]},{"name":"animation.model.back","samplers":[{"input":78,"output":79,"interpolation":"LINEAR"},{"input":80,"output":81,"interpolation":"LINEAR"}],"channels":[{"sampler":0,"target":{"node":15,"path":"rotation"}},{"sampler":1,"target":{"node":19,"path":"rotation"}}]}]} diff --git a/games/devtest/mods/gltf/models/gltf_minimal_triangle.gltf b/games/devtest/mods/gltf/models/gltf_minimal_triangle.gltf new file mode 100644 index 000000000..9a624f085 --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_minimal_triangle.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":1},"indices":0}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAACAPwAAAAA=","byteLength":44}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":6,"target":34963},{"buffer":0,"byteOffset":8,"byteLength":36,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":3,"type":"SCALAR","max":[2],"min":[0]},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/games/devtest/mods/gltf/models/gltf_simple_sparse_accessor.gltf b/games/devtest/mods/gltf/models/gltf_simple_sparse_accessor.gltf new file mode 100644 index 000000000..979896825 --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_simple_sparse_accessor.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":1},"indices":0}]}],"buffers":[{"uri":"data:application/gltf-buffer;base64,AAAIAAcAAAABAAgAAQAJAAgAAQACAAkAAgAKAAkAAgADAAoAAwALAAoAAwAEAAsABAAMAAsABAAFAAwABQANAAwABQAGAA0AAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAQAAAAAAAAAAAAABAQAAAAAAAAAAAAACAQAAAAAAAAAAAAACgQAAAAAAAAAAAAADAQAAAAAAAAAAAAAAAAAAAgD8AAAAAAACAPwAAgD8AAAAAAAAAQAAAgD8AAAAAAABAQAAAgD8AAAAAAACAQAAAgD8AAAAAAACgQAAAgD8AAAAAAADAQAAAgD8AAAAACAAKAAwAAAAAAIA/AAAAQAAAAAAAAEBAAABAQAAAAAAAAKBAAACAQAAAAAA=","byteLength":284}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":72,"byteLength":168},{"buffer":0,"byteOffset":240,"byteLength":6},{"buffer":0,"byteOffset":248,"byteLength":36}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":36,"type":"SCALAR","max":[13],"min":[0]},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":14,"type":"VEC3","max":[6,4,0],"min":[0,0,0],"sparse":{"count":3,"indices":{"bufferView":2,"byteOffset":0,"componentType":5123},"values":{"bufferView":3,"byteOffset":0}}}],"asset":{"version":"2.0"}} diff --git a/games/devtest/mods/gltf/models/gltf_snow_man.gltf b/games/devtest/mods/gltf/models/gltf_snow_man.gltf new file mode 100644 index 000000000..cd8c347d2 --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_snow_man.gltf @@ -0,0 +1 @@ +{"asset":{"version":"2.0","generator":"Blockbench 4.6.0 glTF exporter"},"scenes":[{"nodes":[3],"name":"blockbench_export"}],"scene":0,"nodes":[{"name":"cube","mesh":0},{"name":"cube","mesh":1},{"name":"cube","mesh":2},{"children":[0,1,2]}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":288,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":576,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":768,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":840,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1128,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1416,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":1608,"byteLength":72,"target":34963},{"buffer":0,"byteOffset":1680,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":1968,"byteLength":288,"target":34962,"byteStride":12},{"buffer":0,"byteOffset":2256,"byteLength":192,"target":34962,"byteStride":8},{"buffer":0,"byteOffset":2448,"byteLength":72,"target":34963}],"buffers":[{"byteLength":2520,"uri":"data:application/octet-stream;base64,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"}],"accessors":[{"bufferView":0,"componentType":5126,"count":24,"max":[3,24,3],"min":[-3,18,-3],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":24,"max":[0.8333333134651184,1],"min":[0,0.25],"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":4,"componentType":5126,"count":24,"max":[5,10,5],"min":[-5,0,-5],"type":"VEC3"},{"bufferView":5,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":6,"componentType":5126,"count":24,"max":[0.625,1],"min":[0,0.375],"type":"VEC2"},{"bufferView":7,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"},{"bufferView":8,"componentType":5126,"count":24,"max":[4,18,4],"min":[-4,10,-4],"type":"VEC3"},{"bufferView":9,"componentType":5126,"count":24,"max":[1,1,1],"min":[-1,-1,-1],"type":"VEC3"},{"bufferView":10,"componentType":5126,"count":24,"max":[0.7083333134651184,0.7916666865348816],"min":[0.2083333283662796,0.25],"type":"VEC2"},{"bufferView":11,"componentType":5123,"count":36,"max":[23],"min":[0],"type":"SCALAR"}],"materials":[{"pbrMetallicRoughness":{"metallicFactor":0,"roughnessFactor":1,"baseColorTexture":{"index":0,"texCoord":0}},"alphaMode":"MASK","alphaCutoff":0.05,"doubleSided":true}],"textures":[{}],"meshes":[{"primitives":[{"mode":4,"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":4,"NORMAL":5,"TEXCOORD_0":6},"indices":7,"material":0}]},{"primitives":[{"mode":4,"attributes":{"POSITION":8,"NORMAL":9,"TEXCOORD_0":10},"indices":11,"material":0}]}]} diff --git a/games/devtest/mods/gltf/models/gltf_spider.gltf b/games/devtest/mods/gltf/models/gltf_spider.gltf new file mode 100644 index 000000000..6698b6bb4 --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_spider.gltf @@ -0,0 +1 @@ +{"asset":{"generator":"Khronos glTF Blender I/O v1.7.33","version":"2.0"},"scene":0,"scenes":[{"name":"Scene","nodes":[0]}],"nodes":[{"mesh":0,"name":"Spider"}],"materials":[{"doubleSided":true,"name":"Material.001","pbrMetallicRoughness":{}}],"meshes":[{"name":"Cube","primitives":[{"attributes":{"POSITION":0,"NORMAL":1,"TEXCOORD_0":2},"indices":3,"material":0}]}],"accessors":[{"bufferView":0,"componentType":5126,"count":1000,"max":[2.742279291152954,1.4045029878616333,2.0192716121673584],"min":[-2.742279291152954,-0.6434623599052429,-3.534085512161255],"type":"VEC3"},{"bufferView":1,"componentType":5126,"count":1000,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":1000,"type":"VEC2"},{"bufferView":3,"componentType":5123,"count":1500,"type":"SCALAR"}],"bufferViews":[{"buffer":0,"byteLength":12000,"byteOffset":0},{"buffer":0,"byteLength":12000,"byteOffset":12000},{"buffer":0,"byteLength":8000,"byteOffset":24000},{"buffer":0,"byteLength":3000,"byteOffset":32000}],"buffers":[{"byteLength":35000,"uri":"data:application/octet-stream;base64,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dQCGAHUAgACIAIsAkQCIAJEAjgCQAJIAnQCQAJ0AmwCcAJ8AmQCcAJkAlgCVAJcAjACVAIwAigCPAJoAlACPAJQAiQCeAJMAjQCeAI0AmACiAKgAqwCiAKsApQCnALQAtwCnALcAqgCyAKwArwCyAK8AtQCtAKAAowCtAKMAsACmAKEArgCmAK4AswC2ALEApAC2AKQAqQC6AMAAwwC6AMMAvQC/AMwAzgC/AM4AwQDKAMQAxwDKAMcAzQDGALkAuwDGALsAyAC+ALgAxQC+AMUAywDPAMkAvADPALwAwgDSANgA2wDSANsA1QDXAOQA5gDXAOYA2QDiANwA3wDiAN8A5QDeANEA0wDeANMA4ADWANAA3QDWAN0A4wDnAOEA1ADnANQA2gDpAOsA8QDpAPEA7wDwAPIA/QDwAP0A+wD8AP4A+AD8APgA9gD1APcA7AD1AOwA6gDuAPoA9ADuAPQA6AD/APMA7QD/AO0A+QAAAQMBCQEAAQkBBgEHAQoBFQEHARUBEgEUARcBEQEUAREBDgENARABBQENAQUBAgEIARMBDAEIAQwBAQEWAQsBBAEWAQQBDwEpAR0BIwEpASMBLwEoASYBGQEoARkBGwEtASsBJQEtASUBJwEhAR8BLAEhASwBLgEYASQBKgEYASoBHgEcARoBIAEcASABIgExATMBOgExAToBOAE3ATkBRQE3AUUBQwFEAUYBPwFEAT8BPQE+AUABNAE+ATQBMgE2AUIBPAE2ATwBMAFHATsBNQFHATUBQQFJAUsBUQFJAVEBTwFQAVIBXQFQAV0BWwFcAV4BWAFcAVgBVgFVAVcBTAFVAUwBSgFOAVoBVAFOAVQBSAFfAVMBTQFfAU0BWQFgAWMBaQFgAWkBZgFoAWoBdQFoAXUBcwF0AXcBcQF0AXEBbgFtAW8BZAFtAWQBYgFnAXIBbAFnAWwBYQF2AWsBZQF2AWUBcAGJAX0BgwGJAYMBjwGIAYYBeQGIAXkBewGNAYsBhQGNAYUBhwGBAX8BjAGBAYwBjgF4AYQBigF4AYoBfgF8AXoBgAF8AYABggGRAZMBmQGRAZkBlwGYAZoBpQGYAaUBowGkAaYBoAGkAaABngGdAZ8BlAGdAZQBkgGWAaIBnAGWAZwBkAGnAZsBlQGnAZUBoQGpAasBsQGpAbEBrwGwAbIBvQGwAb0BuwG8Ab4BuAG8AbgBtgG1AbcBrAG1AawBqgGuAboBtAGuAbQBqAG/AbMBrQG/Aa0BuQHAAcMByQHAAckBxgHIAcoB1QHIAdUB0wHUAdcB0QHUAdEBzgHNAc8BxAHNAcQBwgHHAdIBzAHHAcwBwQHWAcsBxQHWAcUB0AHpAd0B4wHpAeMB7wHoAeYB2QHoAdkB2wHtAesB5QHtAeUB5wHhAd8B7AHhAewB7gHYAeQB6gHYAeoB3gHcAdoB4AHcAeAB4gHxAfMB+QHxAfkB9wH4AfoBBQL4AQUCAwIEAgYCAAIEAgAC/gH9Af8B9AH9AfQB8gH2AQIC/AH2AfwB8AEHAvsB9QEHAvUBAQIJAgsCEQIJAhECDwIQAhICHQIQAh0CGwIcAh4CGAIcAhgCFgIVAhcCDAIVAgwCCgIOAhoCFAIOAhQCCAIfAhMCDQIfAg0CGQIgAiMCKQIgAikCJgIoAisCNgIoAjYCMwI0AjcCMQI0AjECLgIsAi8CJAIsAiQCIQInAjICLQInAi0CIgI1AioCJQI1AiUCMAJJAj0CQwJJAkMCTwJIAkYCOQJIAjkCOwJNAksCRQJNAkUCRwJBAj8CTAJBAkwCTgI4AkQCSgI4AkoCPgI8AjoCQAI8AkACQgJSAlQCWQJSAlkCVwJYAloCZgJYAmYCZAJjAmUCYAJjAmACXgJdAl8CUwJdAlMCUQJWAmICXAJWAlwCUAJnAlsCVQJnAlUCYQJpAm8CcwJpAnMCbQJuAnsCfwJuAn8CcgJ6AnQCeAJ6AngCfgJ1AmgCbAJ1AmwCeQJwAmoCdgJwAnYCfAJ9AncCawJ9AmsCcQKCAogCiwKCAosChQKHApQClwKHApcCigKSAowCjwKSAo8ClQKNAoACgwKNAoMCkAKGAoECjgKGAo4CkwKWApEChAKWAoQCiQKnAq0CoQKnAqECmwKoAp0CmQKoApkCpAKvAqkCpQKvAqUCqwKjAq4CqgKjAqoCnwKaAqACrAKaAqwCpgKcAqICngKcAp4CmAKxArYCugKxAroCtQK3AsMCxwK3AscCuwLCAr0CwQLCAsECxgK8ArACtAK8ArQCwAK4ArICvgK4Ar4CxALFAr8CswLFArMCuQLJAs8C0wLJAtMCzQLOAtsC3wLOAt8C0gLaAtQC2ALaAtgC3gLVAsgCzALVAswC2QLQAsoC1gLQAtYC3ALdAtcCywLdAssC0QLiAugC6wLiAusC5QLmAvMC9wLmAvcC6gLyAuwC7wLyAu8C9QLtAuAC5ALtAuQC8QLnAuEC7gLnAu4C9AL2AvAC4wL2AuMC6QIHAw0DAQMHAwED+wIIA/0C+QIIA/kCBAMPAwkDBQMPAwUDCwMDAw4DCgMDAwoD/wL6AgADDAP6AgwDBgP8AgID/gL8Av4C+AIRAxcDGwMRAxsDFQMWAyMDJwMWAycDGgMiAxwDIAMiAyADJgMdAxADFAMdAxQDIQMYAxIDHgMYAx4DJAMlAx8DEwMlAxMDGQMpAy8DMwMpAzMDLQMuAzsDPwMuAz8DMgM6AzQDOAM6AzgDPgM1AygDLAM1AywDOQMwAyoDNgMwAzYDPAM9AzcDKwM9AysDMQNCA0gDSwNCA0sDRQNGA1MDVwNGA1cDSgNSA0wDTwNSA08DVQNNA0ADRANNA0QDUQNHA0EDTgNHA04DVANWA1ADQwNWA0MDSQNnA20DYQNnA2EDWwNoA10DWQNoA1kDZANvA2kDZQNvA2UDawNjA24DagNjA2oDXwNaA2ADbANaA2wDZgNcA2IDXgNcA14DWANxA3cDewNxA3sDdQN2A4MDhwN2A4cDegOCA3wDgAOCA4ADhgN9A3ADdAN9A3QDgQN4A3IDfgN4A34DhAOFA38DcwOFA3MDeQOJA48DkwOJA5MDjQOOA5sDnwOOA58DkgOaA5QDmAOaA5gDngOVA4gDjAOVA4wDmQOQA4oDlgOQA5YDnAOdA5cDiwOdA4sDkQOiA6gDqwOiA6sDpQOmA7MDtgOmA7YDqQOyA6wDrwOyA68DtQOuA6EDpAOuA6QDsQOnA6ADrQOnA60DtAO3A7ADowO3A6MDqgPHA80DwQPHA8EDuwPIA70DuQPIA7kDxAPPA8kDxQPPA8UDywPDA84DygPDA8oDvwO6A8ADzAO6A8wDxgO8A8IDvgO8A74DuAPQA9cD2wPQA9sD1APWA+ID5gPWA+YD2gPjA9wD4APjA+AD5wPdA9ED1QPdA9UD4QPYA9ID3gPYA94D5APlA98D0wPlA9MD2QM="}]} diff --git a/games/devtest/mods/gltf/models/gltf_triangle_with_vertex_stride.gltf b/games/devtest/mods/gltf/models/gltf_triangle_with_vertex_stride.gltf new file mode 100644 index 000000000..feddfbb02 --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_triangle_with_vertex_stride.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":1},"indices":0}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAABAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAA=","byteLength":80}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":6,"target":34963},{"buffer":0,"byteOffset":8,"byteLength":72,"byteStride":24,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5123,"count":3,"type":"SCALAR","max":[2],"min":[0]},{"bufferView":1,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/games/devtest/mods/gltf/models/gltf_triangle_without_indices.gltf b/games/devtest/mods/gltf/models/gltf_triangle_without_indices.gltf new file mode 100644 index 000000000..e91cc0e5a --- /dev/null +++ b/games/devtest/mods/gltf/models/gltf_triangle_without_indices.gltf @@ -0,0 +1 @@ +{"scene":0,"scenes":[{"nodes":[0]}],"nodes":[{"mesh":0}],"meshes":[{"primitives":[{"attributes":{"POSITION":0}}]}],"buffers":[{"uri":"data:application/octet-stream;base64,AAAAAAAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAgD8AAAAA","byteLength":36}],"bufferViews":[{"buffer":0,"byteOffset":0,"byteLength":36,"target":34962}],"accessors":[{"bufferView":0,"byteOffset":0,"componentType":5126,"count":3,"type":"VEC3","max":[1,1,0],"min":[0,0,0]}],"asset":{"version":"2.0"}} diff --git a/games/devtest/mods/gltf/textures/gltf_cube.png b/games/devtest/mods/gltf/textures/gltf_cube.png new file mode 100644 index 0000000000000000000000000000000000000000..1d019108550471be78c1efa02d4367a5aaacdc92 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxc>kD|E9eB^b_}10Yw@- zT^vI+&hMRgm-B!EPs?MDz6TnTOJ_@LQFZ=u)N}1I`3qqhB2w{o=bEjB7#buN_JkT= zklJ;8HkW(z-bQ}jZ8vMquQFZKQ}e`}$+SSlVV20tH7!q0W{TZ3P_wxBxO0)kBPKcB zF#pE(4z9h&cK@~bm2E%&{ZU)l=bvI9miBHu&l=+9n`|96og3&D22WQ%mvv4FO#sL{ BPoDq) literal 0 HcmV?d00001 diff --git a/games/devtest/mods/gltf/textures/gltf_frog.png b/games/devtest/mods/gltf/textures/gltf_frog.png new file mode 100644 index 0000000000000000000000000000000000000000..552ae36493c59c6107de9eb9c08b85cf494d3647 GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvi2$DvS0D`p(auuIo^pl3@)r** zx_@QWpC9+F^;c*BB^XPB{DK)Ap4~_Ta+Z0zIEF|_-aUA+uqi>H^`iAA)g?_HjC?@{ zj{W`jy*_R^t3pE7>UrIx=4=#B{D``d7B}@YEIM2ItZx z*QMOrdNy~*B)&;M7Pqfs_j>8Tuikej{qR=KR}%X?qh{`}?U|hNz{{a{{?cuXs%EB7 z-B&WO-uQeqJJq*WdS$6@&$mZzi4UVfYQC+ry|tl(VN2s7um6qnCOWNuCjLFyc#?Pi RV-=9cJYD@<);T3K0RXN~Zvg-R literal 0 HcmV?d00001 diff --git a/games/devtest/mods/gltf/textures/gltf_snow_man.png b/games/devtest/mods/gltf/textures/gltf_snow_man.png new file mode 100644 index 0000000000000000000000000000000000000000..7f2784358b94af2238337bc6a1555c32c06f9c80 GIT binary patch literal 205 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDB3?!H8JlO)I_yc@GT!AzY1Sgi{RrLJ-|3BuI z$}}LKu_VYZn8D%MjWi%9$J50z#G?1@g@e2e3Oub3*Et+w+0f|Y@aet%Wc9!^s~5)Y zx$%1XzC*^p(<>Q_8F+1fGx*F_*5a_@s<{yO`N)DPK`K|SZOcD!CZ=Hi>5HpcmN2|X z;k9~o)lFniy^Eslft!zXSM}YSuF%?e`lIZ5wHa=yVzLW>_A+?7`njxgN@xNA7I;nX literal 0 HcmV?d00001 diff --git a/games/devtest/mods/gltf/textures/gltf_spider.png b/games/devtest/mods/gltf/textures/gltf_spider.png new file mode 100644 index 0000000000000000000000000000000000000000..1e3d3ae8cbb31c80219081ed7ebdc1bb1ad78373 GIT binary patch literal 10957 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 literal 0 HcmV?d00001 diff --git a/irr/include/IMesh.h b/irr/include/IMesh.h index 6d06eb762..8ee180d5d 100644 --- a/irr/include/IMesh.h +++ b/irr/include/IMesh.h @@ -86,6 +86,17 @@ public: mesh buffer. */ virtual IMeshBuffer *getMeshBuffer(const video::SMaterial &material) const = 0; + //! Minetest binds textures (node tiles, object textures) to models. + // glTF allows multiple primitives (mesh buffers) to reference the same texture. + // This is reflected here: This function gets the texture slot for a mesh buffer. + /** \param meshbufNr: Zero based index of the mesh buffer. The maximum value is + getMeshBufferCount() - 1; + \return number of texture slot to bind to the given mesh buffer */ + virtual u32 getTextureSlot(u32 meshbufNr) const + { + return meshbufNr; + } + //! Get an axis aligned bounding box of the mesh. /** \return Bounding box of this mesh. */ virtual const core::aabbox3d &getBoundingBox() const = 0; diff --git a/irr/include/ISkinnedMesh.h b/irr/include/ISkinnedMesh.h index 9cc7469cb..bb611bba2 100644 --- a/irr/include/ISkinnedMesh.h +++ b/irr/include/ISkinnedMesh.h @@ -199,6 +199,9 @@ public: //! 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; diff --git a/irr/include/SMesh.h b/irr/include/SMesh.h index a391255a1..66e6ecc08 100644 --- a/irr/include/SMesh.h +++ b/irr/include/SMesh.h @@ -64,6 +64,17 @@ struct SMesh : public IMesh return nullptr; } + u32 getTextureSlot(u32 meshbufNr) const override + { + return TextureSlots.at(meshbufNr); + } + + void setTextureSlot(u32 meshbufNr, u32 textureSlot) + { + TextureSlots.at(meshbufNr) = textureSlot; + } + + //! returns an axis aligned bounding box const core::aabbox3d &getBoundingBox() const override { @@ -103,6 +114,7 @@ struct SMesh : public IMesh if (buf) { buf->grab(); MeshBuffers.push_back(buf); + TextureSlots.push_back(getMeshBufferCount() - 1); } } @@ -122,6 +134,8 @@ struct SMesh : public IMesh //! The meshbuffers of this mesh std::vector MeshBuffers; + //! Mapping from meshbuffer number to bindable texture slot + std::vector TextureSlots; //! The bounding box of this mesh core::aabbox3d BoundingBox; diff --git a/irr/include/SSkinMeshBuffer.h b/irr/include/SSkinMeshBuffer.h index fe9d78321..2b71cf6c5 100644 --- a/irr/include/SSkinMeshBuffer.h +++ b/irr/include/SSkinMeshBuffer.h @@ -6,6 +6,7 @@ #include "IMeshBuffer.h" #include "S3DVertex.h" +#include "irrArray.h" namespace irr { @@ -21,10 +22,14 @@ struct SSkinMeshBuffer : public IMeshBuffer PrimitiveType(EPT_TRIANGLES), HWBuffer(nullptr), MappingHint_Vertex(EHM_NEVER), MappingHint_Index(EHM_NEVER), BoundingBoxNeedsRecalculated(true) + {} + + //! Constructor for standard vertices + SSkinMeshBuffer(std::vector &&vertices, std::vector &&indices) : + SSkinMeshBuffer() { -#ifdef _DEBUG - setDebugName("SSkinMeshBuffer"); -#endif + Vertices_Standard = std::move(vertices); + Indices = std::move(indices); } //! Get Material of this buffer. diff --git a/irr/include/irrArray.h b/irr/include/irrArray.h index 66978048f..9f390e79b 100644 --- a/irr/include/irrArray.h +++ b/irr/include/irrArray.h @@ -45,6 +45,10 @@ public: { } + //! Move constructor + array(std::vector &&data) : + m_data(std::move(data)), is_sorted(false) {} + //! Reallocates the array, make it bigger or smaller. /** \param new_size New size of array. \param canShrink Specifies whether the array is reallocated even if diff --git a/irr/include/irrString.h b/irr/include/irrString.h index a583c9e4b..9d9b288d8 100644 --- a/irr/include/irrString.h +++ b/irr/include/irrString.h @@ -11,7 +11,6 @@ #include #include #include -#include /* HACK: import these string methods from MT's util/string.h */ extern std::wstring utf8_to_wide(std::string_view input); @@ -65,6 +64,7 @@ static inline u32 locale_upper(u32 x) template class string { + using stl_type = std::basic_string; public: typedef T char_type; @@ -79,6 +79,10 @@ public: *this = other; } + string(const stl_type &str) : str(str) {} + + string(stl_type &&str) : str(std::move(str)) {} + //! Constructor from other string types template string(const string &other) @@ -814,13 +818,6 @@ public: friend size_t wStringToUTF8(stringc &destination, const wchar_t *source); private: - typedef std::basic_string stl_type; - - //! Private constructor - string(stl_type &&str) : - str(str) - { - } //! strlen wrapper template diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp new file mode 100644 index 000000000..64bbc10f1 --- /dev/null +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -0,0 +1,695 @@ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "CGLTFMeshFileLoader.h" + +#include "coreutil.h" +#include "CSkinnedMesh.h" +#include "ISkinnedMesh.h" +#include "irrTypes.h" +#include "IReadFile.h" +#include "matrix4.h" +#include "path.h" +#include "quaternion.h" +#include "vector3d.h" +#include "os.h" + +#include "tiniergltf.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace irr { + +/* Notes on the coordinate system. + * + * glTF uses a right-handed coordinate system where +Z is the + * front-facing axis, and Irrlicht uses a left-handed coordinate + * system where -Z is the front-facing axis. + * We convert between them by mirroring the mesh across the X axis. + * Doing this correctly requires negating the Z coordinate on + * vertex positions and normals, and reversing the winding order + * of the vertex indices. + */ + +// Right-to-left handedness conversions + +template +static inline T convertHandedness(const T &t); + +template <> +core::vector3df convertHandedness(const core::vector3df &p) +{ + return core::vector3df(p.X, p.Y, -p.Z); +} + +namespace scene { + +using SelfType = CGLTFMeshFileLoader; + +template +SelfType::Accessor +SelfType::Accessor::sparseIndices(const tiniergltf::GlTF &model, + const tiniergltf::AccessorSparseIndices &indices, + const std::size_t count) +{ + const auto &view = model.bufferViews->at(indices.bufferView); + const auto byteStride = view.byteStride.value_or(indices.elementSize()); + + const auto &buffer = model.buffers->at(view.buffer); + const auto source = buffer.data.data() + view.byteOffset + indices.byteOffset; + + return SelfType::Accessor(source, byteStride, count); +} + +template +SelfType::Accessor +SelfType::Accessor::sparseValues(const tiniergltf::GlTF &model, + const tiniergltf::AccessorSparseValues &values, + const std::size_t count, + const std::size_t defaultByteStride) +{ + const auto &view = model.bufferViews->at(values.bufferView); + const auto byteStride = view.byteStride.value_or(defaultByteStride); + + const auto &buffer = model.buffers->at(view.buffer); + const auto source = buffer.data.data() + view.byteOffset + values.byteOffset; + + return SelfType::Accessor(source, byteStride, count); +} + +template +SelfType::Accessor +SelfType::Accessor::base(const tiniergltf::GlTF &model, std::size_t accessorIdx) +{ + const auto &accessor = model.accessors->at(accessorIdx); + + if (!accessor.bufferView.has_value()) { + return Accessor(accessor.count); + } + + const auto &view = model.bufferViews->at(accessor.bufferView.value()); + const auto byteStride = view.byteStride.value_or(accessor.elementSize()); + + const auto &buffer = model.buffers->at(view.buffer); + const auto source = buffer.data.data() + view.byteOffset + accessor.byteOffset; + + return Accessor(source, byteStride, accessor.count); +} + +template +SelfType::Accessor +SelfType::Accessor::make(const tiniergltf::GlTF &model, std::size_t accessorIdx) +{ + const auto &accessor = model.accessors->at(accessorIdx); + if (accessor.componentType != getComponentType() || accessor.type != getType()) + throw std::runtime_error("invalid accessor"); + + const auto base = Accessor::base(model, accessorIdx); + + if (accessor.sparse.has_value()) { + std::vector vec(accessor.count); + for (std::size_t i = 0; i < accessor.count; ++i) { + vec[i] = base.get(i); + } + const auto overriddenCount = accessor.sparse->count; + const auto indicesAccessor = ([&]() -> AccessorVariant { + switch (accessor.sparse->indices.componentType) { + case tiniergltf::AccessorSparseIndices::ComponentType::UNSIGNED_BYTE: + return Accessor::sparseIndices(model, accessor.sparse->indices, overriddenCount); + case tiniergltf::AccessorSparseIndices::ComponentType::UNSIGNED_SHORT: + return Accessor::sparseIndices(model, accessor.sparse->indices, overriddenCount); + case tiniergltf::AccessorSparseIndices::ComponentType::UNSIGNED_INT: + return Accessor::sparseIndices(model, accessor.sparse->indices, overriddenCount); + } + throw std::logic_error("invalid enum value"); + })(); + + const auto valuesAccessor = Accessor::sparseValues(model, + accessor.sparse->values, overriddenCount, + accessor.bufferView.has_value() + ? model.bufferViews->at(*accessor.bufferView).byteStride.value_or(accessor.elementSize()) + : accessor.elementSize()); + + for (std::size_t i = 0; i < overriddenCount; ++i) { + u32 index; + std::visit([&](auto &&acc) { index = acc.get(i); }, indicesAccessor); + if (index >= accessor.count) + throw std::runtime_error("index out of bounds"); + vec[index] = valuesAccessor.get(i); + } + return Accessor(vec, accessor.count); + } + + return base; +} + +#define ACCESSOR_TYPES(T, U, V) \ + template <> \ + constexpr tiniergltf::Accessor::Type SelfType::Accessor::getType() \ + { \ + return tiniergltf::Accessor::Type::U; \ + } \ + template <> \ + constexpr tiniergltf::Accessor::ComponentType SelfType::Accessor::getComponentType() \ + { \ + return tiniergltf::Accessor::ComponentType::V; \ + } + +#define VEC_ACCESSOR_TYPES(T, U, N) \ + template <> \ + constexpr tiniergltf::Accessor::Type SelfType::Accessor>::getType() \ + { \ + return tiniergltf::Accessor::Type::VEC##N; \ + } \ + template <> \ + constexpr tiniergltf::Accessor::ComponentType SelfType::Accessor>::getComponentType() \ + { \ + return tiniergltf::Accessor::ComponentType::U; \ + } \ + template <> \ + std::array SelfType::rawget(const char *ptr) \ + { \ + std::array res; \ + for (int i = 0; i < N; ++i) \ + res[i] = rawget(ptr + sizeof(T) * i); \ + return res; \ + } + +#define ACCESSOR_PRIMITIVE(T, U) \ + ACCESSOR_TYPES(T, SCALAR, U) \ + VEC_ACCESSOR_TYPES(T, U, 2) \ + VEC_ACCESSOR_TYPES(T, U, 3) \ + VEC_ACCESSOR_TYPES(T, U, 4) + +ACCESSOR_PRIMITIVE(f32, FLOAT) +ACCESSOR_PRIMITIVE(u8, UNSIGNED_BYTE) +ACCESSOR_PRIMITIVE(u16, UNSIGNED_SHORT) +ACCESSOR_PRIMITIVE(u32, UNSIGNED_INT) + +ACCESSOR_TYPES(core::vector3df, VEC3, FLOAT) + +template +T SelfType::Accessor::get(std::size_t i) const +{ + // Buffer-based accessor: Read directly from the buffer. + if (std::holds_alternative(source)) { + const auto bufsrc = std::get(source); + return rawget(bufsrc.ptr + i * bufsrc.byteStride); + } + // Array-based accessor (used for sparse accessors): Read from array. + if (std::holds_alternative>(source)) { + return std::get>(source)[i]; + } + // Default-initialized accessor. + // We differ slightly from glTF here in that + // we default-initialize quaternions and matrices properly, + // but this does not cause any discrepancies for valid glTF models. + std::get>(source); + return T(); +} + +template +T SelfType::rawget(const char *ptr) +{ + T dest; + std::memcpy(&dest, ptr, sizeof(dest)); +#ifdef __BIG_ENDIAN__ + return os::Byteswap::byteswap(dest); +#else + return dest; +#endif +} + +// Note that these "more specialized templates" should win. + +template <> +core::matrix4 SelfType::rawget(const char *ptr) +{ + core::matrix4 mat; + for (u8 i = 0; i < 16; ++i) { + mat[i] = rawget(ptr + i * sizeof(f32)); + } + return mat; +} + +template <> +core::vector3df SelfType::rawget(const char *ptr) +{ + return core::vector3df( + rawget(ptr), + rawget(ptr + sizeof(f32)), + rawget(ptr + 2 * sizeof(f32))); +} + +template <> +core::quaternion SelfType::rawget(const char *ptr) +{ + return core::quaternion( + rawget(ptr), + rawget(ptr + sizeof(f32)), + rawget(ptr + 2 * sizeof(f32)), + rawget(ptr + 3 * sizeof(f32))); +} + +template +SelfType::NormalizedValuesAccessor +SelfType::createNormalizedValuesAccessor( + const tiniergltf::GlTF &model, + const std::size_t accessorIdx) +{ + const auto &acc = model.accessors->at(accessorIdx); + switch (acc.componentType) { + case tiniergltf::Accessor::ComponentType::UNSIGNED_BYTE: + return Accessor>::make(model, accessorIdx); + case tiniergltf::Accessor::ComponentType::UNSIGNED_SHORT: + return Accessor>::make(model, accessorIdx); + case tiniergltf::Accessor::ComponentType::FLOAT: + return Accessor>::make(model, accessorIdx); + default: + throw std::runtime_error("invalid component type"); + } +} + +template +std::array SelfType::getNormalizedValues( + const NormalizedValuesAccessor &accessor, + const std::size_t i) +{ + 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(); + } 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(); + } 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"); + } + } + return values; +} + +/** + * The most basic portion of the code base. This tells irllicht if this file has a .gltf extension. +*/ +bool SelfType::isALoadableFileExtension( + const io::path& filename) const +{ + return core::hasFileExtension(filename, "gltf"); +} + +/** + * Entry point into loading a GLTF model. +*/ +IAnimatedMesh* SelfType::createMesh(io::IReadFile* file) +{ + if (file->getSize() <= 0) { + return nullptr; + } + std::optional model = tryParseGLTF(file); + if (!model.has_value()) { + return nullptr; + } + + if (!(model->buffers.has_value() + && model->bufferViews.has_value() + && model->accessors.has_value() + && model->meshes.has_value() + && model->nodes.has_value())) { + os::Printer::log("glTF loader", "missing required fields", ELL_ERROR); + return nullptr; + } + + auto *mesh = new CSkinnedMesh(); + MeshExtractor parser(std::move(model.value()), mesh); + try { + parser.loadNodes(); + } catch (std::runtime_error &e) { + os::Printer::log("glTF loader", e.what(), ELL_ERROR); + mesh->drop(); + return nullptr; + } + if (model->images.has_value()) + os::Printer::log("glTF loader", "embedded images are not supported", ELL_WARNING); + return mesh; +} + +static void transformVertices(std::vector &vertices, const core::matrix4 &transform) +{ + for (auto &vertex : vertices) { + // Apply scaling, rotation and rotation (in that order) to the position. + transform.transformVect(vertex.Pos); + // For the normal, we do not want to apply the translation. + // TODO note that this also applies scaling; the Irrlicht method is misnamed. + transform.rotateVect(vertex.Normal); + // Renormalize (length might have been affected by scaling). + vertex.Normal.normalize(); + } +} + +static void checkIndices(const std::vector &indices, const std::size_t nVerts) +{ + for (u16 index : indices) { + if (index >= nVerts) + throw std::runtime_error("index out of bounds"); + } +} + +static std::vector generateIndices(const std::size_t nVerts) +{ + std::vector indices(nVerts); + for (std::size_t i = 0; i < nVerts; i += 3) { + // Reverse winding order per triangle + indices[i] = i + 2; + indices[i + 1] = i + 1; + indices[i + 2] = i; + } + return indices; +} + +/** + * Load up the rawest form of the model. The vertex positions and indices. + * Documentation: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#meshes + * If material is undefined, then a default material MUST be used. +*/ +void SelfType::MeshExtractor::loadMesh( + const std::size_t meshIdx, + ISkinnedMesh::SJoint *parent) const +{ + for (std::size_t pi = 0; pi < getPrimitiveCount(meshIdx); ++pi) { + const auto &primitive = m_gltf_model.meshes->at(meshIdx).primitives.at(pi); + auto vertices = getVertices(primitive); + if (!vertices.has_value()) + continue; // "When positions are not specified, client implementations SHOULD skip primitive’s rendering" + + // Excludes the max value for consistency. + if (vertices->size() >= std::numeric_limits::max()) + throw std::runtime_error("too many vertices"); + + // Apply the global transform along the parent chain. + transformVertices(*vertices, parent->GlobalMatrix); + + auto maybeIndices = getIndices(primitive); + std::vector indices; + if (maybeIndices.has_value()) { + indices = std::move(*maybeIndices); + checkIndices(indices, vertices->size()); + } else { + // Non-indexed geometry + indices = generateIndices(vertices->size()); + } + + m_irr_model->addMeshBuffer( + new SSkinMeshBuffer(std::move(*vertices), std::move(indices))); + + if (primitive.material.has_value()) { + const auto &material = m_gltf_model.materials->at(*primitive.material); + if (material.pbrMetallicRoughness.has_value()) { + const auto &texture = material.pbrMetallicRoughness->baseColorTexture; + if (texture.has_value()) { + const auto meshbufNr = m_irr_model->getMeshBufferCount() - 1; + m_irr_model->setTextureSlot(meshbufNr, static_cast(texture->index)); + } + } + } + } +} + +// Base transformation between left & right handed coordinate systems. +// This just inverts the Z axis. +static const core::matrix4 leftToRight = core::matrix4( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, -1, 0, + 0, 0, 0, 1 +); +static const core::matrix4 rightToLeft = leftToRight; + +static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m) +{ + // Note: Under the hood, this casts these doubles to floats. + return core::matrix4( + m[0], m[1], m[2], m[3], + m[4], m[5], m[6], m[7], + m[8], m[9], m[10], m[11], + m[12], m[13], m[14], m[15]); +} + +static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs) +{ + const auto &trans = trs.translation; + const auto &rot = trs.rotation; + const auto &scale = trs.scale; + core::matrix4 transMat; + transMat.setTranslation(core::vector3df(trans[0], trans[1], trans[2])); + core::matrix4 rotMat = core::quaternion(rot[0], rot[1], rot[2], rot[3]).getMatrix(); + core::matrix4 scaleMat; + scaleMat.setScale(core::vector3df(scale[0], scale[1], scale[2])); + return transMat * rotMat * scaleMat; +} + +static core::matrix4 loadTransform(std::optional> transform) { + if (!transform.has_value()) { + return core::matrix4(); + } + core::matrix4 mat = std::visit([](const auto &t) { return loadTransform(t); }, *transform); + return rightToLeft * mat * leftToRight; +} + +void SelfType::MeshExtractor::loadNode( + const std::size_t nodeIdx, + ISkinnedMesh::SJoint *parent) const +{ + const auto &node = m_gltf_model.nodes->at(nodeIdx); + auto *joint = m_irr_model->addJoint(parent); + const core::matrix4 transform = loadTransform(node.transform); + joint->LocalMatrix = transform; + joint->GlobalMatrix = parent ? parent->GlobalMatrix * joint->LocalMatrix : joint->LocalMatrix; + if (node.name.has_value()) { + joint->Name = node.name->c_str(); + } + if (node.mesh.has_value()) { + loadMesh(*node.mesh, joint); + } + if (node.children.has_value()) { + for (const auto &child : *node.children) { + loadNode(child, joint); + } + } +} + +void SelfType::MeshExtractor::loadNodes() const +{ + std::vector isChild(m_gltf_model.nodes->size()); + for (const auto &node : *m_gltf_model.nodes) { + if (!node.children.has_value()) + continue; + for (const auto &child : *node.children) { + isChild[child] = true; + } + } + // Load all nodes that aren't children. + // Children will be loaded by their parent nodes. + for (std::size_t i = 0; i < m_gltf_model.nodes->size(); ++i) { + if (!isChild[i]) { + loadNode(i, nullptr); + } + } +} + +/** + * Extracts GLTF mesh indices. + */ +std::optional> SelfType::MeshExtractor::getIndices( + const tiniergltf::MeshPrimitive &primitive) const +{ + const auto accessorIdx = primitive.indices; + if (!accessorIdx.has_value()) + return std::nullopt; // non-indexed geometry + + const auto accessor = ([&]() -> AccessorVariant { + const auto &acc = m_gltf_model.accessors->at(*accessorIdx); + switch (acc.componentType) { + case tiniergltf::Accessor::ComponentType::UNSIGNED_BYTE: + return Accessor::make(m_gltf_model, *accessorIdx); + case tiniergltf::Accessor::ComponentType::UNSIGNED_SHORT: + return Accessor::make(m_gltf_model, *accessorIdx); + case tiniergltf::Accessor::ComponentType::UNSIGNED_INT: + return Accessor::make(m_gltf_model, *accessorIdx); + default: + throw std::runtime_error("invalid component type"); + } + })(); + const auto count = std::visit([](auto &&a) { return a.getCount(); }, accessor); + + std::vector indices; + for (std::size_t i = 0; i < count; ++i) { + // TODO (low-priority, maybe never) also reverse winding order based on determinant of global transform + // FIXME this hack also reverses triangle draw order + std::size_t elemIdx = count - i - 1; // reverse index order + u16 index; + // Note: glTF forbids the max value for each component type. + if (std::holds_alternative>(accessor)) { + index = std::get>(accessor).get(elemIdx); + if (index == std::numeric_limits::max()) + throw std::runtime_error("invalid index"); + } else if (std::holds_alternative>(accessor)) { + index = std::get>(accessor).get(elemIdx); + if (index == std::numeric_limits::max()) + throw std::runtime_error("invalid index"); + } else if (std::holds_alternative>(accessor)) { + u32 indexWide = std::get>(accessor).get(elemIdx); + // Use >= here for consistency. + if (indexWide >= std::numeric_limits::max()) + throw std::runtime_error("index too large (>= 65536)"); + index = static_cast(indexWide); + } + indices.push_back(index); + } + + return indices; +} + +/** + * Create a vector of video::S3DVertex (model data) from a mesh & primitive index. + */ +std::optional> SelfType::MeshExtractor::getVertices( + const tiniergltf::MeshPrimitive &primitive) const +{ + const auto &attributes = primitive.attributes; + const auto positionAccessorIdx = attributes.position; + if (!positionAccessorIdx.has_value()) { + // "When positions are not specified, client implementations SHOULD skip primitive's rendering" + return std::nullopt; + } + + std::vector vertices; + const auto vertexCount = m_gltf_model.accessors->at(*positionAccessorIdx).count; + vertices.resize(vertexCount); + copyPositions(*positionAccessorIdx, vertices); + + const auto normalAccessorIdx = attributes.normal; + if (normalAccessorIdx.has_value()) { + copyNormals(normalAccessorIdx.value(), vertices); + } + // TODO verify that the automatic normal recalculation done in Minetest indeed works correctly + + const auto &texcoords = attributes.texcoord; + if (texcoords.has_value()) { + const auto tCoordAccessorIdx = texcoords->at(0); + copyTCoords(tCoordAccessorIdx, vertices); + } + + return vertices; +} + +/** + * Get the amount of meshes that a model contains. +*/ +std::size_t SelfType::MeshExtractor::getMeshCount() const +{ + return m_gltf_model.meshes->size(); +} + +/** + * Get the amount of primitives that a mesh in a model contains. +*/ +std::size_t SelfType::MeshExtractor::getPrimitiveCount( + const std::size_t meshIdx) const +{ + return m_gltf_model.meshes->at(meshIdx).primitives.size(); +} + +/** + * Streams vertex positions raw data into usable buffer via reference. + * Buffer: ref Vector +*/ +void SelfType::MeshExtractor::copyPositions( + const std::size_t accessorIdx, + std::vector& vertices) const +{ + const auto accessor = Accessor::make(m_gltf_model, accessorIdx); + for (std::size_t i = 0; i < accessor.getCount(); i++) { + vertices[i].Pos = convertHandedness(accessor.get(i)); + } +} + +/** + * Streams normals raw data into usable buffer via reference. + * Buffer: ref Vector +*/ +void SelfType::MeshExtractor::copyNormals( + const std::size_t accessorIdx, + std::vector& vertices) const +{ + const auto accessor = Accessor::make(m_gltf_model, accessorIdx); + for (std::size_t i = 0; i < accessor.getCount(); ++i) { + vertices[i].Normal = convertHandedness(accessor.get(i)); + } +} + +/** + * Streams texture coordinate raw data into usable buffer via reference. + * Buffer: ref Vector +*/ +void SelfType::MeshExtractor::copyTCoords( + const std::size_t accessorIdx, + std::vector& vertices) const +{ + const auto accessor = createNormalizedValuesAccessor<2>(m_gltf_model, accessorIdx); + const auto count = std::visit([](auto &&a) { return a.getCount(); }, accessor); + for (std::size_t i = 0; i < count; ++i) { + const auto vals = getNormalizedValues(accessor, i); + vertices[i].TCoords = core::vector2df(vals[0], vals[1]); + } +} + +/** + * This is where the actual model's GLTF file is loaded and parsed by tiniergltf. +*/ +std::optional SelfType::tryParseGLTF(io::IReadFile* file) +{ + auto size = file->getSize(); + if (size < 0) // this can happen if `ftell` fails + return std::nullopt; + std::unique_ptr buf(new char[size + 1]); + if (file->read(buf.get(), size) != static_cast(size)) + return std::nullopt; + // We probably don't need this, but add it just to be sure. + buf[size] = '\0'; + Json::CharReaderBuilder builder; + const std::unique_ptr reader(builder.newCharReader()); + Json::Value json; + JSONCPP_STRING err; + if (!reader->parse(buf.get(), buf.get() + size, &json, &err)) { + return std::nullopt; + } + try { + return tiniergltf::GlTF(json); + } catch (const std::runtime_error &e) { + os::Printer::log("glTF loader", e.what(), ELL_ERROR); + return std::nullopt; + } catch (const std::out_of_range &e) { + os::Printer::log("glTF loader", e.what(), ELL_ERROR); + return std::nullopt; + } +} + +} // namespace scene + +} // namespace irr + diff --git a/irr/src/CGLTFMeshFileLoader.h b/irr/src/CGLTFMeshFileLoader.h new file mode 100644 index 000000000..39c3ea6dd --- /dev/null +++ b/irr/src/CGLTFMeshFileLoader.h @@ -0,0 +1,147 @@ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "CSkinnedMesh.h" +#include "IMeshLoader.h" +#include "IReadFile.h" +#include "irrTypes.h" +#include "path.h" +#include "S3DVertex.h" + +#include + +#include +#include + +namespace irr +{ + +namespace scene +{ + +class CGLTFMeshFileLoader : public IMeshLoader +{ +public: + CGLTFMeshFileLoader() noexcept {}; + + bool isALoadableFileExtension(const io::path& filename) const override; + + IAnimatedMesh* createMesh(io::IReadFile* file) override; + +private: + template + static T rawget(const char *ptr); + + template + class Accessor + { + struct BufferSource + { + const char *ptr; + std::size_t byteStride; + }; + using Source = std::variant, std::tuple<>>; + + public: + static Accessor sparseIndices( + const tiniergltf::GlTF &model, + const tiniergltf::AccessorSparseIndices &indices, + const std::size_t count); + static Accessor sparseValues( + const tiniergltf::GlTF &model, + const tiniergltf::AccessorSparseValues &values, + const std::size_t count, + const std::size_t defaultByteStride); + static Accessor base( + const tiniergltf::GlTF &model, + std::size_t accessorIdx); + static Accessor make(const tiniergltf::GlTF &model, std::size_t accessorIdx); + static constexpr tiniergltf::Accessor::Type getType(); + static constexpr tiniergltf::Accessor::ComponentType getComponentType(); + std::size_t getCount() const { return count; } + T get(std::size_t i) const; + + private: + Accessor(const char *ptr, std::size_t byteStride, std::size_t count) : + source(BufferSource{ptr, byteStride}), count(count) {} + Accessor(std::vector vec, std::size_t count) : + source(vec), count(count) {} + Accessor(std::size_t count) : + source(std::make_tuple()), count(count) {} + // Directly from buffer, sparse, or default-initialized + const Source source; + const std::size_t count; + }; + + template + using AccessorVariant = std::variant...>; + + template + using ArrayAccessorVariant = std::variant>...>; + + template + using NormalizedValuesAccessor = ArrayAccessorVariant; + + template + static NormalizedValuesAccessor createNormalizedValuesAccessor( + const tiniergltf::GlTF &model, + const std::size_t accessorIdx); + + template + static std::array getNormalizedValues( + const NormalizedValuesAccessor &accessor, + const std::size_t i); + + class MeshExtractor { + public: + MeshExtractor(tiniergltf::GlTF &&model, + CSkinnedMesh *mesh) noexcept + : m_gltf_model(model), m_irr_model(mesh) {}; + + /* Gets indices for the given mesh/primitive. + * + * Values are return in Irrlicht winding order. + */ + std::optional> getIndices( + const tiniergltf::MeshPrimitive &primitive) const; + + std::optional> getVertices( + const tiniergltf::MeshPrimitive &primitive) const; + + std::size_t getMeshCount() const; + + std::size_t getPrimitiveCount(const std::size_t meshIdx) const; + + void loadNodes() const; + + private: + const tiniergltf::GlTF m_gltf_model; + CSkinnedMesh *m_irr_model; + + void copyPositions(const std::size_t accessorIdx, + std::vector& vertices) const; + + void copyNormals(const std::size_t accessorIdx, + std::vector& vertices) const; + + void copyTCoords(const std::size_t accessorIdx, + std::vector& vertices) const; + + void loadMesh( + std::size_t meshIdx, + ISkinnedMesh::SJoint *parentJoint) const; + + void loadNode( + const std::size_t nodeIdx, + ISkinnedMesh::SJoint *parentJoint) const; + }; + + std::optional tryParseGLTF(io::IReadFile* file); +}; + +} // namespace scene + +} // namespace irr + diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index 161a0c060..f5bc675e4 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -17,7 +17,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") set(CMAKE_CXX_FLAGS_RELEASE "-O3") set(CMAKE_CXX_FLAGS_DEBUG "-g") - add_compile_options(-Wall -pipe -fno-exceptions) + add_compile_options(-Wall -pipe) # Enable SSE for floating point math on 32-bit x86 by default # reasoning see minetest issue #11810 and https://gcc.gnu.org/wiki/FloatingPointMath @@ -298,6 +298,7 @@ set(link_includes set(IRRMESHLOADER CB3DMeshFileLoader.cpp + CGLTFMeshFileLoader.cpp COBJMeshFileLoader.cpp CXMeshFileLoader.cpp ) @@ -310,6 +311,8 @@ add_library(IRRMESHOBJ OBJECT ${IRRMESHLOADER} ) +target_link_libraries(IRRMESHOBJ PUBLIC tiniergltf::tiniergltf) + add_library(IRROBJ OBJECT CBillboardSceneNode.cpp CCameraSceneNode.cpp @@ -321,6 +324,10 @@ add_library(IRROBJ OBJECT CMeshCache.cpp ) +# Make sure IRROBJ gets the transitive include directories for +# tiniergltf from IRRMESHOBJ. +target_link_libraries(IRROBJ PRIVATE IRRMESHOBJ) + set(IRRDRVROBJ CNullDriver.cpp CGLXManager.cpp @@ -477,6 +484,7 @@ target_include_directories(IrrlichtMt # this needs to be here and not in a variable (like link_includes) due to issues # with the generator expressions on at least CMake 3.22, but not 3.28 or later target_link_libraries(IrrlichtMt PRIVATE + tiniergltf::tiniergltf ${ZLIB_LIBRARY} ${JPEG_LIBRARY} ${PNG_LIBRARY} diff --git a/irr/src/CSceneManager.cpp b/irr/src/CSceneManager.cpp index cd88e1a6e..c75a28a48 100644 --- a/irr/src/CSceneManager.cpp +++ b/irr/src/CSceneManager.cpp @@ -20,6 +20,7 @@ #include "CXMeshFileLoader.h" #include "COBJMeshFileLoader.h" #include "CB3DMeshFileLoader.h" +#include "CGLTFMeshFileLoader.h" #include "CBillboardSceneNode.h" #include "CAnimatedMeshSceneNode.h" #include "CCameraSceneNode.h" @@ -78,6 +79,7 @@ CSceneManager::CSceneManager(video::IVideoDriver *driver, MeshLoaderList.push_back(new CXMeshFileLoader(this)); MeshLoaderList.push_back(new COBJMeshFileLoader(this)); MeshLoaderList.push_back(new CB3DMeshFileLoader(this)); + MeshLoaderList.push_back(new CGLTFMeshFileLoader()); } //! destructor diff --git a/irr/src/CSkinnedMesh.cpp b/irr/src/CSkinnedMesh.cpp index eb7317309..5db027abc 100644 --- a/irr/src/CSkinnedMesh.cpp +++ b/irr/src/CSkinnedMesh.cpp @@ -6,6 +6,7 @@ #include #include "CBoneSceneNode.h" #include "IAnimatedMeshSceneNode.h" +#include "SSkinMeshBuffer.h" #include "os.h" namespace @@ -596,6 +597,15 @@ IMeshBuffer *CSkinnedMesh::getMeshBuffer(const video::SMaterial &material) const return 0; } +u32 CSkinnedMesh::getTextureSlot(u32 meshbufNr) const +{ + return TextureSlots.at(meshbufNr); +} + +void CSkinnedMesh::setTextureSlot(u32 meshbufNr, u32 textureSlot) { + TextureSlots.at(meshbufNr) = textureSlot; +} + //! returns an axis aligned bounding box const core::aabbox3d &CSkinnedMesh::getBoundingBox() const { @@ -1057,10 +1067,17 @@ void CSkinnedMesh::updateBoundingBox(void) scene::SSkinMeshBuffer *CSkinnedMesh::addMeshBuffer() { scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer(); + TextureSlots.push_back(LocalBuffers.size()); LocalBuffers.push_back(buffer); return buffer; } +void CSkinnedMesh::addMeshBuffer(SSkinMeshBuffer *meshbuf) +{ + TextureSlots.push_back(LocalBuffers.size()); + LocalBuffers.push_back(meshbuf); +} + CSkinnedMesh::SJoint *CSkinnedMesh::addJoint(SJoint *parent) { SJoint *joint = new SJoint; diff --git a/irr/src/CSkinnedMesh.h b/irr/src/CSkinnedMesh.h index b0228c93b..4b4c5e3b7 100644 --- a/irr/src/CSkinnedMesh.h +++ b/irr/src/CSkinnedMesh.h @@ -61,6 +61,10 @@ public: NULL if there is no such mesh buffer. */ IMeshBuffer *getMeshBuffer(const video::SMaterial &material) const override; + u32 getTextureSlot(u32 meshbufNr) const override; + + void setTextureSlot(u32 meshbufNr, u32 textureSlot); + //! returns an axis aligned bounding box const core::aabbox3d &getBoundingBox() const override; @@ -129,6 +133,9 @@ public: //! 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; @@ -184,6 +191,8 @@ private: core::array *SkinningBuffers; // Meshbuffer to skin, default is to skin localBuffers core::array LocalBuffers; + //! Mapping from meshbuffer number to bindable texture slot + std::vector TextureSlots; core::array AllJoints; core::array RootJoints; diff --git a/lib/tiniergltf/.gitignore b/lib/tiniergltf/.gitignore new file mode 100644 index 000000000..6d105f52b --- /dev/null +++ b/lib/tiniergltf/.gitignore @@ -0,0 +1,6 @@ +cmake +CMakeCache.txt +CMakeFiles +.cache +compile_commands.json +build \ No newline at end of file diff --git a/lib/tiniergltf/CMakeLists.txt b/lib/tiniergltf/CMakeLists.txt new file mode 100644 index 000000000..889203e49 --- /dev/null +++ b/lib/tiniergltf/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.12) + +project(tiniergltf + VERSION 1.0.0 + LANGUAGES CXX +) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_library(tiniergltf OBJECT tiniergltf.hpp) +add_library(tiniergltf::tiniergltf ALIAS tiniergltf) +set_target_properties(tiniergltf PROPERTIES LINKER_LANGUAGE CXX) + +target_include_directories(tiniergltf + INTERFACE + "$" + "${JSON_INCLUDE_DIR}" # Set in FindJson.cmake + "${CMAKE_SOURCE_DIR}/src" # util/base64.h +) + +target_link_libraries(tiniergltf) diff --git a/lib/tiniergltf/Readme.md b/lib/tiniergltf/Readme.md new file mode 100644 index 000000000..a8cc65c93 --- /dev/null +++ b/lib/tiniergltf/Readme.md @@ -0,0 +1,39 @@ +# TinierGLTF + +A safe, modern, tiny glTF loader for C++ 17. + +What this is: + +* A tiny glTF deserializer which maps JSON objects to appropriate C++ structures. +* Intended to be safe for loading untrusted input. +* Slightly tailored to the needs of [Minetest](https://github.com/minetest/minetest). + +What this doesn't and shouldn't do: + +* Serialization +* Loading images +* Resolving resources +* Support glTF extensions + +## TODOs + +- [ ] Add GLB support. +- [ ] Add further checks according to the specification. + - Everything in the JSON schema (+ indices and misc. stuff) is already validated. + Much of the code was generated by a Lua script from the JSON schemata. + +## License + +`tiniergltf.hpp` was written by Lars Müller and is licensed under the MIT license: + +> Copyright 2024 Lars Müller +> +> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## Bug Bounty + +I offer a reward of one (1) virtual headpat per valid bug report. diff --git a/lib/tiniergltf/tiniergltf.hpp b/lib/tiniergltf/tiniergltf.hpp new file mode 100644 index 000000000..6a861556e --- /dev/null +++ b/lib/tiniergltf/tiniergltf.hpp @@ -0,0 +1,1357 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util/base64.h" + +namespace tiniergltf { + +static inline void check(bool cond) { + if (!cond) + throw std::runtime_error("invalid glTF"); +} + +template +static inline void checkIndex(const std::optional> &vec, + const std::optional &i) { + if (!i.has_value()) return; + check(vec.has_value()); + check(i < vec->size()); +} + +template +static inline void checkIndex(const std::vector &vec, + const std::optional &i) { + if (!i.has_value()) return; + check(i < vec.size()); +} + +template +static inline void checkForall(const std::optional> &vec, const F &cond) { + if (!vec.has_value()) + return; + for (const T &v : vec.value()) + cond(v); +} + +template +static inline void checkDuplicateFree(const std::vector &vec) { + check(std::unordered_set(vec.begin(), vec.end()).size() == vec.size()); +} + +template +static inline T as(const Json::Value &o); + +template<> +bool as(const Json::Value &o) { + check(o.isBool()); + return o.asBool(); +} + +template<> +double as (const Json::Value &o) { + check(o.isDouble()); + return o.asDouble(); +} + +template<> +std::size_t as(const Json::Value &o) { + check(o.isUInt64()); + auto u = o.asUInt64(); + check(u <= std::numeric_limits::max()); + return u; +} + +template<> +std::string as(const Json::Value &o) { + check(o.isString()); + return o.asString(); +} + +template +std::vector asVec(const Json::Value &o) { + check(o.isArray()); + std::vector res; + res.reserve(o.size()); + for (Json::ArrayIndex i = 0; i < o.size(); ++i) { + res.push_back(as(o[i])); + } + return res; +} + +template +std::array asArr(const Json::Value &o) { + check(o.isArray()); + check(o.size() == n); + std::array res; + for (Json::ArrayIndex i = 0; i < n; ++i) { + res[i] = as(o[i]); + } + return res; +} + +struct AccessorSparseIndices { + std::size_t bufferView; + std::size_t byteOffset; + // as defined in the glTF specification + enum class ComponentType { + UNSIGNED_BYTE, + UNSIGNED_SHORT, + UNSIGNED_INT, + }; + ComponentType componentType; + std::size_t componentSize() const { + switch (componentType) { + case ComponentType::UNSIGNED_BYTE: + return 1; + case ComponentType::UNSIGNED_SHORT: + return 2; + case ComponentType::UNSIGNED_INT: + return 4; + } + throw std::logic_error("invalid component type"); + } + std::size_t elementSize() const { + return componentSize(); + } + AccessorSparseIndices(const Json::Value &o) + : bufferView(as(o["bufferView"])) + , byteOffset(0) + { + check(o.isObject()); + if (o.isMember("byteOffset")) { + byteOffset = as(o["byteOffset"]); + check(byteOffset >= 0); + } + { + static std::unordered_map map = { + {5121, ComponentType::UNSIGNED_BYTE}, + {5123, ComponentType::UNSIGNED_SHORT}, + {5125, ComponentType::UNSIGNED_INT}, + }; + const auto &v = o["componentType"]; check(v.isUInt64()); + componentType = map.at(v.asUInt64()); + } + } +}; +template<> AccessorSparseIndices as(const Json::Value &o) { return o; } + +struct AccessorSparseValues { + std::size_t bufferView; + std::size_t byteOffset; + AccessorSparseValues(const Json::Value &o) + : bufferView(as(o["bufferView"])) + , byteOffset(0) + { + check(o.isObject()); + if (o.isMember("byteOffset")) { + byteOffset = as(o["byteOffset"]); + check(byteOffset >= 0); + } + } +}; +template<> AccessorSparseValues as(const Json::Value &o) { return o; } + +struct AccessorSparse { + std::size_t count; + AccessorSparseIndices indices; + AccessorSparseValues values; + AccessorSparse(const Json::Value &o) + : count(as(o["count"])) + , indices(as(o["indices"])) + , values(as(o["values"])) + { + check(o.isObject()); + check(count >= 1); + } +}; +template<> AccessorSparse as(const Json::Value &o) { return o; } + +struct Accessor { + std::optional bufferView; + std::size_t byteOffset; + // as defined in the glTF specification + enum class ComponentType { + BYTE, + UNSIGNED_BYTE, + SHORT, + UNSIGNED_SHORT, + UNSIGNED_INT, + FLOAT, + }; + ComponentType componentType; + std::size_t componentSize() const { + switch (componentType) { + case ComponentType::BYTE: + case ComponentType::UNSIGNED_BYTE: + return 1; + case ComponentType::SHORT: + case ComponentType::UNSIGNED_SHORT: + return 2; + case ComponentType::UNSIGNED_INT: + case ComponentType::FLOAT: + return 4; + } + throw std::logic_error("invalid component type"); + } + std::size_t count; + std::optional> max; + std::optional> min; + std::optional name; + bool normalized; + std::optional sparse; + enum class Type { + MAT2, + MAT3, + MAT4, + SCALAR, + VEC2, + VEC3, + VEC4, + }; + std::size_t typeCount() const { + switch (type) { + case Type::SCALAR: + return 1; + case Type::VEC2: + return 2; + case Type::VEC3: + return 3; + case Type::MAT2: + case Type::VEC4: + return 4; + case Type::MAT3: + return 9; + case Type::MAT4: + return 16; + } + throw std::logic_error("invalid type"); + } + Type type; + std::size_t elementSize() const { + return componentSize() * typeCount(); + } + Accessor(const Json::Value &o) + : byteOffset(0) + , count(as(o["count"])) + , normalized(false) + { + check(o.isObject()); + if (o.isMember("bufferView")) { + bufferView = as(o["bufferView"]); + } + { + static std::unordered_map map = { + {5120, ComponentType::BYTE}, + {5121, ComponentType::UNSIGNED_BYTE}, + {5122, ComponentType::SHORT}, + {5123, ComponentType::UNSIGNED_SHORT}, + {5125, ComponentType::UNSIGNED_INT}, + {5126, ComponentType::FLOAT}, + }; + const auto &v = o["componentType"]; check(v.isUInt64()); + componentType = map.at(v.asUInt64()); + } + if (o.isMember("byteOffset")) { + byteOffset = as(o["byteOffset"]); + check(byteOffset >= 0); + check(byteOffset % componentSize() == 0); + } + check(count >= 1); + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("normalized")) { + normalized = as(o["normalized"]); + } + if (o.isMember("sparse")) { + sparse = as(o["sparse"]); + } + { + static std::unordered_map map = { + {"MAT2", Type::MAT2}, + {"MAT3", Type::MAT3}, + {"MAT4", Type::MAT4}, + {"SCALAR", Type::SCALAR}, + {"VEC2", Type::VEC2}, + {"VEC3", Type::VEC3}, + {"VEC4", Type::VEC4}, + }; + const auto &v = o["type"]; check(v.isString()); + type = map.at(v.asString()); + } + if (o.isMember("max")) { + max = asVec(o["max"]); + check(max->size() == typeCount()); + } + if (o.isMember("min")) { + min = asVec(o["min"]); + check(min->size() == typeCount()); + } + } +}; +template<> Accessor as(const Json::Value &o) { return o; } + +struct AnimationChannelTarget { + std::optional node; + enum class Path { + ROTATION, + SCALE, + TRANSLATION, + WEIGHTS, + }; + Path path; + AnimationChannelTarget(const Json::Value &o) + { + check(o.isObject()); + if (o.isMember("node")) { + node = as(o["node"]); + } + { + static std::unordered_map map = { + {"rotation", Path::ROTATION}, + {"scale", Path::SCALE}, + {"translation", Path::TRANSLATION}, + {"weights", Path::WEIGHTS}, + }; + const auto &v = o["path"]; check(v.isString()); + path = map.at(v.asString()); + } + } +}; +template<> AnimationChannelTarget as(const Json::Value &o) { return o; } + +struct AnimationChannel { + std::size_t sampler; + AnimationChannelTarget target; + AnimationChannel(const Json::Value &o) + : sampler(as(o["sampler"])) + , target(as(o["target"])) + { + check(o.isObject()); + } +}; +template<> AnimationChannel as(const Json::Value &o) { return o; } + +struct AnimationSampler { + std::size_t input; + enum class Interpolation { + CUBICSPLINE, + LINEAR, + STEP, + }; + Interpolation interpolation; + std::size_t output; + AnimationSampler(const Json::Value &o) + : input(as(o["input"])) + , interpolation(Interpolation::LINEAR) + , output(as(o["output"])) + { + check(o.isObject()); + if (o.isMember("interpolation")) { + static std::unordered_map map = { + {"CUBICSPLINE", Interpolation::CUBICSPLINE}, + {"LINEAR", Interpolation::LINEAR}, + {"STEP", Interpolation::STEP}, + }; + const auto &v = o["interpolation"]; check(v.isString()); + interpolation = map.at(v.asString()); + } + } +}; +template<> AnimationSampler as(const Json::Value &o) { return o; } + +struct Animation { + std::vector channels; + std::optional name; + std::vector samplers; + Animation(const Json::Value &o) + : channels(asVec(o["channels"])) + , samplers(asVec(o["samplers"])) + { + check(o.isObject()); + check(channels.size() >= 1); + if (o.isMember("name")) { + name = as(o["name"]); + } + check(samplers.size() >= 1); + } +}; +template<> Animation as(const Json::Value &o) { return o; } + +struct Asset { + std::optional copyright; + std::optional generator; + std::optional minVersion; + std::string version; + Asset(const Json::Value &o) + : version(as(o["version"])) + { + check(o.isObject()); + if (o.isMember("copyright")) { + copyright = as(o["copyright"]); + } + if (o.isMember("generator")) { + generator = as(o["generator"]); + } + if (o.isMember("minVersion")) { + minVersion = as(o["minVersion"]); + } + } +}; +template<> Asset as(const Json::Value &o) { return o; } + +struct BufferView { + std::size_t buffer; + std::size_t byteLength; + std::size_t byteOffset; + std::optional byteStride; + std::optional name; + enum class Target { + ARRAY_BUFFER, + ELEMENT_ARRAY_BUFFER, + }; + std::optional target; + BufferView(const Json::Value &o) + : buffer(as(o["buffer"])) + , byteLength(as(o["byteLength"])) + , byteOffset(0) + { + check(o.isObject()); + check(byteLength >= 1); + if (o.isMember("byteOffset")) { + byteOffset = as(o["byteOffset"]); + check(byteOffset >= 0); + } + if (o.isMember("byteStride")) { + byteStride = as(o["byteStride"]); + check(byteStride.value() >= 4); + check(byteStride.value() <= 252); + check(byteStride.value() % 4 == 0); + } + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("target")) { + static std::unordered_map map = { + {34962, Target::ARRAY_BUFFER}, + {34963, Target::ELEMENT_ARRAY_BUFFER}, + }; + const auto &v = o["target"]; check(v.isUInt64()); + target = map.at(v.asUInt64()); + } + } +}; +template<> BufferView as(const Json::Value &o) { return o; } + +struct Buffer { + std::size_t byteLength; + std::optional name; + std::string data; + Buffer(const Json::Value &o, + const std::function &resolveURI) + : byteLength(as(o["byteLength"])) + { + check(o.isObject()); + check(byteLength >= 1); + if (o.isMember("name")) { + name = as(o["name"]); + } + check(o.isMember("uri")); + bool dataURI = false; + const std::string uri = as(o["uri"]); + for (auto &prefix : std::array { + "data:application/octet-stream;base64,", + "data:application/gltf-buffer;base64," + }) { + if (std::string_view(uri).substr(0, prefix.length()) == prefix) { + auto view = std::string_view(uri).substr(prefix.length()); + check(base64_is_valid(view)); + data = base64_decode(view); + dataURI = true; + break; + } + } + if (!dataURI) + data = resolveURI(uri); + check(data.size() >= byteLength); + data.resize(byteLength); + } +}; + +struct CameraOrthographic { + double xmag; + double ymag; + double zfar; + double znear; + CameraOrthographic(const Json::Value &o) + : xmag(as(o["xmag"])) + , ymag(as(o["ymag"])) + , zfar(as(o["zfar"])) + , znear(as(o["znear"])) + { + check(o.isObject()); + check(zfar > 0); + check(znear >= 0); + } +}; +template<> CameraOrthographic as(const Json::Value &o) { return o; } + +struct CameraPerspective { + std::optional aspectRatio; + double yfov; + std::optional zfar; + double znear; + CameraPerspective(const Json::Value &o) + : yfov(as(o["yfov"])) + , znear(as(o["znear"])) + { + check(o.isObject()); + if (o.isMember("aspectRatio")) { + aspectRatio = as(o["aspectRatio"]); + check(aspectRatio.value() > 0); + } + check(yfov > 0); + if (o.isMember("zfar")) { + zfar = as(o["zfar"]); + check(zfar.value() > 0); + } + check(znear > 0); + } +}; +template<> CameraPerspective as(const Json::Value &o) { return o; } + +struct Camera { + std::optional name; + std::optional orthographic; + std::optional perspective; + enum class Type { + ORTHOGRAPHIC, + PERSPECTIVE, + }; + Type type; + Camera(const Json::Value &o) + { + check(o.isObject()); + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("orthographic")) { + orthographic = as(o["orthographic"]); + } + if (o.isMember("perspective")) { + perspective = as(o["perspective"]); + } + { + static std::unordered_map map = { + {"orthographic", Type::ORTHOGRAPHIC}, + {"perspective", Type::PERSPECTIVE}, + }; + const auto &v = o["type"]; check(v.isString()); + type = map.at(v.asString()); + } + } +}; +template<> Camera as(const Json::Value &o) { return o; } + +struct Image { + std::optional bufferView; + enum class MimeType { + IMAGE_JPEG, + IMAGE_PNG, + }; + std::optional mimeType; + std::optional name; + std::optional uri; + Image(const Json::Value &o) + { + check(o.isObject()); + if (o.isMember("bufferView")) { + bufferView = as(o["bufferView"]); + } + if (o.isMember("mimeType")) { + static std::unordered_map map = { + {"image/jpeg", MimeType::IMAGE_JPEG}, + {"image/png", MimeType::IMAGE_PNG}, + }; + const auto &v = o["mimeType"]; check(v.isString()); + mimeType = map.at(v.asString()); + } + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("uri")) { + uri = as(o["uri"]); + } + } +}; +template<> Image as(const Json::Value &o) { return o; } + +struct TextureInfo { + std::size_t index; + std::size_t texCoord; + TextureInfo(const Json::Value &o) + : index(as(o["index"])) + , texCoord(0) + { + check(o.isObject()); + if (o.isMember("texCoord")) { + texCoord = as(o["texCoord"]); + check(texCoord >= 0); + } + } +}; +template<> TextureInfo as(const Json::Value &o) { return o; } + +struct MaterialNormalTextureInfo { + std::size_t index; + double scale; + std::size_t texCoord; + MaterialNormalTextureInfo(const Json::Value &o) + : index(as(o["index"])) + , scale(1) + , texCoord(0) + { + check(o.isObject()); + if (o.isMember("scale")) { + scale = as(o["scale"]); + } + if (o.isMember("texCoord")) { + texCoord = as(o["texCoord"]); + } + } +}; +template<> MaterialNormalTextureInfo as(const Json::Value &o) { return o; } + +struct MaterialOcclusionTextureInfo { + std::size_t index; + double strength; + std::size_t texCoord; + MaterialOcclusionTextureInfo(const Json::Value &o) + : index(as(o["index"])) + , strength(1) + , texCoord(0) + { + check(o.isObject()); + if (o.isMember("strength")) { + strength = as(o["strength"]); + check(strength >= 0); + check(strength <= 1); + } + if (o.isMember("texCoord")) { + texCoord = as(o["texCoord"]); + } + } +}; +template<> MaterialOcclusionTextureInfo as(const Json::Value &o) { return o; } + +struct MaterialPbrMetallicRoughness { + std::array baseColorFactor; + std::optional baseColorTexture; + double metallicFactor; + std::optional metallicRoughnessTexture; + double roughnessFactor; + MaterialPbrMetallicRoughness(const Json::Value &o) + : baseColorFactor{1, 1, 1, 1} + , metallicFactor(1) + , roughnessFactor(1) + { + check(o.isObject()); + if (o.isMember("baseColorFactor")) { + baseColorFactor = asArr(o["baseColorFactor"]); + for (auto v: baseColorFactor) { + check(v >= 0); + check(v <= 1); + } + } + if (o.isMember("baseColorTexture")) { + baseColorTexture = as(o["baseColorTexture"]); + } + if (o.isMember("metallicFactor")) { + metallicFactor = as(o["metallicFactor"]); + check(metallicFactor >= 0); + check(metallicFactor <= 1); + } + if (o.isMember("metallicRoughnessTexture")) { + metallicRoughnessTexture = as(o["metallicRoughnessTexture"]); + } + if (o.isMember("roughnessFactor")) { + roughnessFactor = as(o["roughnessFactor"]); + check(roughnessFactor >= 0); + check(roughnessFactor <= 1); + } + } +}; +template<> MaterialPbrMetallicRoughness as(const Json::Value &o) { return o; } + +struct Material { + double alphaCutoff; + enum class AlphaMode { + BLEND, + MASK, + OPAQUE, + }; + AlphaMode alphaMode; + bool doubleSided; + std::array emissiveFactor; + std::optional emissiveTexture; + std::optional name; + std::optional normalTexture; + std::optional occlusionTexture; + std::optional pbrMetallicRoughness; + Material(const Json::Value &o) + : alphaCutoff(0.5) + , alphaMode(AlphaMode::OPAQUE) + , doubleSided(false) + , emissiveFactor{0, 0, 0} + { + check(o.isObject()); + if (o.isMember("alphaCutoff")) { + alphaCutoff = as(o["alphaCutoff"]); + check(alphaCutoff >= 0); + } + if (o.isMember("alphaMode")){ + static std::unordered_map map = { + {"BLEND", AlphaMode::BLEND}, + {"MASK", AlphaMode::MASK}, + {"OPAQUE", AlphaMode::OPAQUE}, + }; + const auto &v = o["alphaMode"]; check(v.isString()); + alphaMode = map.at(v.asString()); + } + if (o.isMember("doubleSided")) { + doubleSided = as(o["doubleSided"]); + } + if (o.isMember("emissiveFactor")) { + emissiveFactor = asArr(o["emissiveFactor"]); + for (const auto &v: emissiveFactor) { + check(v >= 0); + check(v <= 1); + } + } + if (o.isMember("emissiveTexture")) { + emissiveTexture = as(o["emissiveTexture"]); + } + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("normalTexture")) { + normalTexture = as(o["normalTexture"]); + } + if (o.isMember("occlusionTexture")) { + occlusionTexture = as(o["occlusionTexture"]); + } + if (o.isMember("pbrMetallicRoughness")) { + pbrMetallicRoughness = as(o["pbrMetallicRoughness"]); + } + } +}; +template<> Material as(const Json::Value &o) { return o; } + +struct MeshPrimitive { + static void enumeratedProps(const Json::Value &o, const std::string &name, std::optional> &attr) { + for (std::size_t i = 0;; ++i) { + const std::string s = name + "_" + std::to_string(i); + if (!o.isMember(s)) break; + if (i == 0) { + attr = std::vector(); + } + attr->push_back(as(o[s])); + } + } + struct Attributes { + std::optional position, normal, tangent; + std::optional> texcoord, color, joints, weights; + Attributes(const Json::Value &o) { + if (o.isMember("POSITION")) + position = as(o["POSITION"]); + if (o.isMember("NORMAL")) + normal = as(o["NORMAL"]); + if (o.isMember("TANGENT")) + tangent = as(o["TANGENT"]); + enumeratedProps(o, "TEXCOORD", texcoord); + enumeratedProps(o, "COLOR", color); + enumeratedProps(o, "JOINTS", joints); + enumeratedProps(o, "WEIGHTS", weights); + check(joints.has_value() == weights.has_value()); + if (joints.has_value()) { + check(joints->size() == weights->size()); + } + check(position.has_value() + || normal.has_value() + || tangent.has_value() + || texcoord.has_value() + || color.has_value() + || joints.has_value() + || weights.has_value()); + } + }; + Attributes attributes; + std::optional indices; + std::optional material; + enum class Mode { + POINTS, + LINES, + LINE_LOOP, + LINE_STRIP, + TRIANGLES, + TRIANGLE_STRIP, + TRIANGLE_FAN, + }; + Mode mode; + struct MorphTargets { + std::optional position, normal, tangent; + std::optional> texcoord, color; + MorphTargets(const Json::Value &o) { + if (o.isMember("POSITION")) + position = as(o["POSITION"]); + if (o.isMember("NORMAL")) + normal = as(o["NORMAL"]); + if (o.isMember("TANGENT")) + tangent = as(o["TANGENT"]); + enumeratedProps(o, "TEXCOORD", texcoord); + enumeratedProps(o, "COLOR", color); + check(position.has_value() + || normal.has_value() + || tangent.has_value() + || texcoord.has_value() + || color.has_value()); + } + }; + std::optional> targets; + MeshPrimitive(const Json::Value &o) + : attributes(Attributes(o["attributes"])) + , mode(Mode::TRIANGLES) + { + check(o.isObject()); + if (o.isMember("indices")) { + indices = as(o["indices"]); + } + if (o.isMember("material")) { + material = as(o["material"]); + } + if (o.isMember("mode")) { + static std::unordered_map map = { + {0, Mode::POINTS}, + {1, Mode::LINES}, + {2, Mode::LINE_LOOP}, + {3, Mode::LINE_STRIP}, + {4, Mode::TRIANGLES}, + {5, Mode::TRIANGLE_STRIP}, + {6, Mode::TRIANGLE_FAN}, + }; + const auto &v = o["mode"]; check(v.isUInt64()); + mode = map.at(v.asUInt64()); + } + if (o.isMember("targets")) { + targets = asVec(o["targets"]); + check(targets->size() >= 1); + } + } +}; +template<> MeshPrimitive::MorphTargets as(const Json::Value &o) { return o; } +template<> MeshPrimitive as(const Json::Value &o) { return o; } + +struct Mesh { + std::optional name; + std::vector primitives; + std::optional> weights; + Mesh(const Json::Value &o) + : primitives(asVec(o["primitives"])) + { + check(o.isObject()); + if (o.isMember("name")) { + name = as(o["name"]); + } + check(primitives.size() >= 1); + if (o.isMember("weights")) { + weights = asVec(o["weights"]); + check(weights->size() >= 1); + } + } +}; +template<> Mesh as(const Json::Value &o) { return o; } + +struct Node { + std::optional camera; + std::optional> children; + typedef std::array Matrix; + struct TRS { + std::array translation = {0, 0, 0}; + std::array rotation = {0, 0, 0, 1}; + std::array scale = {1, 1, 1}; + }; + std::variant transform; + std::optional mesh; + std::optional name; + std::optional skin; + std::optional> weights; + Node(const Json::Value &o) + : transform(Matrix { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }) + { + check(o.isObject()); + if (o.isMember("camera")) { + camera = as(o["camera"]); + } + if (o.isMember("children")) { + children = asVec(o["children"]); + check(children->size() >= 1); + checkDuplicateFree(*children); + } + bool hasTRS = o.isMember("translation") || o.isMember("rotation") || o.isMember("scale"); + if (o.isMember("matrix")) { + check(!hasTRS); + transform = asArr(o["matrix"]); + } else if (hasTRS) { + TRS trs; + if (o.isMember("translation")) { + trs.translation = asArr(o["translation"]); + } + if (o.isMember("rotation")) { + trs.rotation = asArr(o["rotation"]); + for (auto v: trs.rotation) { + check(v >= -1); + check(v <= 1); + } + } + if (o.isMember("scale")) { + trs.scale = asArr(o["scale"]); + } + transform = trs; + } + if (o.isMember("mesh")) { + mesh = as(o["mesh"]); + } + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("skin")) { + check(mesh.has_value()); + skin = as(o["skin"]); + } + if (o.isMember("weights")) { + weights = asVec(o["weights"]); + check(weights->size() >= 1); + } + } +}; +template<> Node as(const Json::Value &o) { return o; } + +struct Sampler { + enum class MagFilter { + NEAREST, + LINEAR, + }; + std::optional magFilter; + enum class MinFilter { + NEAREST, + LINEAR, + NEAREST_MIPMAP_NEAREST, + LINEAR_MIPMAP_NEAREST, + NEAREST_MIPMAP_LINEAR, + LINEAR_MIPMAP_LINEAR, + }; + std::optional minFilter; + std::optional name; + enum class WrapS { + REPEAT, + CLAMP_TO_EDGE, + MIRRORED_REPEAT, + }; + WrapS wrapS; + enum class WrapT { + REPEAT, + CLAMP_TO_EDGE, + MIRRORED_REPEAT, + }; + WrapT wrapT; + Sampler(const Json::Value &o) + : wrapS(WrapS::REPEAT) + , wrapT(WrapT::REPEAT) + { + check(o.isObject()); + if (o.isMember("magFilter")) { + static std::unordered_map map = { + {9728, MagFilter::NEAREST}, + {9729, MagFilter::LINEAR}, + }; + const auto &v = o["magFilter"]; check(v.isUInt64()); + magFilter = map.at(v.asUInt64()); + } + if (o.isMember("minFilter")) { + static std::unordered_map map = { + {9728, MinFilter::NEAREST}, + {9729, MinFilter::LINEAR}, + {9984, MinFilter::NEAREST_MIPMAP_NEAREST}, + {9985, MinFilter::LINEAR_MIPMAP_NEAREST}, + {9986, MinFilter::NEAREST_MIPMAP_LINEAR}, + {9987, MinFilter::LINEAR_MIPMAP_LINEAR}, + }; + const auto &v = o["minFilter"]; check(v.isUInt64()); + minFilter = map.at(v.asUInt64()); + } + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("wrapS")) { + static std::unordered_map map = { + {10497, WrapS::REPEAT}, + {33071, WrapS::CLAMP_TO_EDGE}, + {33648, WrapS::MIRRORED_REPEAT}, + }; + const auto &v = o["wrapS"]; check(v.isUInt64()); + wrapS = map.at(v.asUInt64()); + } + if (o.isMember("wrapT")) { + static std::unordered_map map = { + {10497, WrapT::REPEAT}, + {33071, WrapT::CLAMP_TO_EDGE}, + {33648, WrapT::MIRRORED_REPEAT}, + }; + const auto &v = o["wrapT"]; check(v.isUInt64()); + wrapT = map.at(v.asUInt64()); + } + } +}; +template<> Sampler as(const Json::Value &o) { return o; } + +struct Scene { + std::optional name; + std::optional> nodes; + Scene(const Json::Value &o) + { + check(o.isObject()); + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("nodes")) { + nodes = asVec(o["nodes"]); + check(nodes->size() >= 1); + checkDuplicateFree(*nodes); + } + } +}; +template<> Scene as(const Json::Value &o) { return o; } + +struct Skin { + std::optional inverseBindMatrices; + std::vector joints; + std::optional name; + std::optional skeleton; + Skin(const Json::Value &o) + : joints(asVec(o["joints"])) + { + check(o.isObject()); + if (o.isMember("inverseBindMatrices")) { + inverseBindMatrices = as(o["inverseBindMatrices"]); + } + check(joints.size() >= 1); + checkDuplicateFree(joints); + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("skeleton")) { + skeleton = as(o["skeleton"]); + } + } +}; +template<> Skin as(const Json::Value &o) { return o; } + +struct Texture { + std::optional name; + std::optional sampler; + std::optional source; + Texture(const Json::Value &o) + { + check(o.isObject()); + if (o.isMember("name")) { + name = as(o["name"]); + } + if (o.isMember("sampler")) { + sampler = as(o["sampler"]); + } + if (o.isMember("source")) { + source = as(o["source"]); + } + } +}; +template<> Texture as(const Json::Value &o) { return o; } + +struct GlTF { + std::optional> accessors; + std::optional> animations; + Asset asset; + std::optional> bufferViews; + std::optional> buffers; + std::optional> cameras; + std::optional> extensionsRequired; + std::optional> extensionsUsed; + std::optional> images; + std::optional> materials; + std::optional> meshes; + std::optional> nodes; + std::optional> samplers; + std::optional scene; + std::optional> scenes; + std::optional> skins; + std::optional> textures; + static std::string uriError(const std::string &uri) { + // only base64 data URI support by default + throw std::runtime_error("unsupported URI: " + uri); + } + GlTF(const Json::Value &o, + const std::function &resolveURI = uriError) + : asset(as(o["asset"])) + { + check(o.isObject()); + if (o.isMember("accessors")) { + accessors = asVec(o["accessors"]); + check(accessors->size() >= 1); + } + if (o.isMember("animations")) { + animations = asVec(o["animations"]); + check(animations->size() >= 1); + } + if (o.isMember("bufferViews")) { + bufferViews = asVec(o["bufferViews"]); + check(bufferViews->size() >= 1); + } + if (o.isMember("buffers")) { + auto b = o["buffers"]; + check(b.isArray()); + std::vector bufs; + bufs.reserve(b.size()); + for (Json::ArrayIndex i = 0; i < b.size(); ++i) { + bufs.emplace_back(b[i], resolveURI); + } + check(bufs.size() >= 1); + buffers = std::move(bufs); + } + if (o.isMember("cameras")) { + cameras = asVec(o["cameras"]); + check(cameras->size() >= 1); + } + if (o.isMember("extensionsRequired")) { + extensionsRequired = asVec(o["extensionsRequired"]); + check(extensionsRequired->size() >= 1); + checkDuplicateFree(*extensionsRequired); + } + if (o.isMember("extensionsUsed")) { + extensionsUsed = asVec(o["extensionsUsed"]); + check(extensionsUsed->size() >= 1); + checkDuplicateFree(*extensionsUsed); + } + if (o.isMember("images")) { + images = asVec(o["images"]); + check(images->size() >= 1); + } + if (o.isMember("materials")) { + materials = asVec(o["materials"]); + check(materials->size() >= 1); + } + if (o.isMember("meshes")) { + meshes = asVec(o["meshes"]); + check(meshes->size() >= 1); + } + if (o.isMember("nodes")) { + nodes = asVec(o["nodes"]); + check(nodes->size() >= 1); + // Nodes must be a forest: + // 1. Each node should have indegree 0 or 1: + std::vector indeg(nodes->size()); + for (std::size_t i = 0; i < nodes->size(); ++i) { + auto children = nodes->at(i).children; + if (!children.has_value()) continue; + for (auto child : children.value()) { + ++indeg.at(child); + } + } + for (const auto deg : indeg) { + check(deg <= 1); + } + // 2. There should be no cycles: + std::vector visited(nodes->size()); + std::stack> toVisit; + for (std::size_t i = 0; i < nodes->size(); ++i) { + // Only start DFS in roots. + if (indeg[i] > 0) + continue; + + toVisit.push(i); + do { + std::size_t j = toVisit.top(); + check(!visited.at(j)); + visited[j] = true; + toVisit.pop(); + auto children = nodes->at(j).children; + if (!children.has_value()) + continue; + for (auto child : *children) { + toVisit.push(child); + } + } while (!toVisit.empty()); + } + } + if (o.isMember("samplers")) { + samplers = asVec(o["samplers"]); + check(samplers->size() >= 1); + } + if (o.isMember("scene")) { + scene = as(o["scene"]); + } + if (o.isMember("scenes")) { + scenes = asVec(o["scenes"]); + check(scenes->size() >= 1); + } + if (o.isMember("skins")) { + skins = asVec(o["skins"]); + check(skins->size() >= 1); + } + if (o.isMember("textures")) { + textures = asVec(o["textures"]); + check(textures->size() >= 1); + } + + // Validation + + checkForall(bufferViews, [&](const BufferView &view) { + check(buffers.has_value()); + const Buffer &buf = buffers->at(view.buffer); + // Be careful because of possible integer overflows. + check(view.byteOffset < buf.byteLength); + check(view.byteLength <= buf.byteLength); + check(view.byteOffset <= buf.byteLength - view.byteLength); + }); + + const auto checkAccessor = [&](const auto &accessor, + std::size_t bufferView, std::size_t byteOffset, std::size_t count) { + const BufferView &view = bufferViews->at(bufferView); + if (view.byteStride.has_value()) + check(*view.byteStride % accessor.componentSize() == 0); + check(byteOffset < view.byteLength); + // Use division to avoid overflows. + const auto effective_byte_stride = view.byteStride.value_or(accessor.elementSize()); + check(count <= (view.byteLength - byteOffset) / effective_byte_stride); + }; + checkForall(accessors, [&](const Accessor &accessor) { + if (accessor.bufferView.has_value()) + checkAccessor(accessor, *accessor.bufferView, accessor.byteOffset, accessor.count); + if (accessor.sparse.has_value()) { + const auto &indices = accessor.sparse->indices; + checkAccessor(indices, indices.bufferView, indices.byteOffset, accessor.sparse->count); + const auto &values = accessor.sparse->values; + checkAccessor(accessor, values.bufferView, values.byteOffset, accessor.sparse->count); + } + }); + + checkForall(images, [&](const Image &image) { + checkIndex(bufferViews, image.bufferView); + }); + + checkForall(meshes, [&](const Mesh &mesh) { + for (const auto &primitive : mesh.primitives) { + checkIndex(accessors, primitive.indices); + checkIndex(materials, primitive.material); + checkIndex(accessors, primitive.attributes.normal); + checkIndex(accessors, primitive.attributes.position); + checkIndex(accessors, primitive.attributes.tangent); + checkForall(primitive.attributes.texcoord, [&](const std::size_t &i) { + checkIndex(accessors, i); + }); + checkForall(primitive.attributes.color, [&](const std::size_t &i) { + checkIndex(accessors, i); + }); + checkForall(primitive.attributes.joints, [&](const std::size_t &i) { + checkIndex(accessors, i); + }); + checkForall(primitive.attributes.weights, [&](const std::size_t &i) { + checkIndex(accessors, i); + }); + if (primitive.material.has_value()) { + const Material &material = materials->at(primitive.material.value()); + if (material.emissiveTexture.has_value()) { + check(primitive.attributes.texcoord.has_value()); + check(material.emissiveTexture->texCoord < primitive.attributes.texcoord->size()); + } + if (material.normalTexture.has_value()) { + check(primitive.attributes.texcoord.has_value()); + check(material.normalTexture->texCoord < primitive.attributes.texcoord->size()); + } + if (material.occlusionTexture.has_value()) { + check(primitive.attributes.texcoord.has_value()); + check(material.occlusionTexture->texCoord < primitive.attributes.texcoord->size()); + } + } + checkForall(primitive.targets, [&](const MeshPrimitive::MorphTargets &target) { + checkIndex(accessors, target.normal); + checkIndex(accessors, target.position); + checkIndex(accessors, target.tangent); + checkForall(target.texcoord, [&](const std::size_t &i) { + checkIndex(accessors, i); + }); + checkForall(target.color, [&](const std::size_t &i) { + checkIndex(accessors, i); + }); + }); + } + }); + + checkForall(nodes, [&](const Node &node) { + checkIndex(cameras, node.camera); + checkIndex(meshes, node.mesh); + checkIndex(skins, node.skin); + }); + + checkForall(scenes, [&](const Scene &scene) { + checkForall(scene.nodes, [&](const size_t &i) { + checkIndex(nodes, i); + }); + }); + + checkForall(skins, [&](const Skin &skin) { + checkIndex(accessors, skin.inverseBindMatrices); + for (const std::size_t &i : skin.joints) + checkIndex(nodes, i); + checkIndex(nodes, skin.skeleton); + }); + + checkForall(textures, [&](const Texture &texture) { + checkIndex(samplers, texture.sampler); + checkIndex(images, texture.source); + }); + + checkForall(animations, [&](const Animation &animation) { + for (const auto &sampler : animation.samplers) { + checkIndex(accessors, sampler.input); + const auto &accessor = accessors->at(sampler.input); + check(accessor.type == Accessor::Type::SCALAR); + check(accessor.componentType == Accessor::ComponentType::FLOAT); + checkIndex(accessors, sampler.output); + } + for (const auto &channel : animation.channels) { + checkIndex(nodes, channel.target.node); + checkIndex(animation.samplers, channel.sampler); + } + }); + + checkIndex(scenes, scene); + } +}; + +} diff --git a/src/client/client.cpp b/src/client/client.cpp index 9c12be8f2..3720ec54f 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -826,7 +826,7 @@ bool Client::loadMedia(const std::string &data, const std::string &filename, } const char *model_ext[] = { - ".x", ".b3d", ".obj", + ".x", ".b3d", ".obj", ".gltf", NULL }; name = removeStringEnd(filename, model_ext); diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 19bee6f7f..563fe0abd 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -844,14 +844,19 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) if (m_animated_meshnode) { u32 mat_count = m_animated_meshnode->getMaterialCount(); + assert(mat_count == m_animated_meshnode->getMesh()->getMeshBufferCount()); + u32 max_tex_idx = 0; + for (u32 i = 0; i < mat_count; ++i) { + max_tex_idx = std::max(max_tex_idx, + m_animated_meshnode->getMesh()->getTextureSlot(i)); + } if (mat_count == 0 || m_prop.textures.empty()) { // nothing - } else if (mat_count > m_prop.textures.size()) { + } else if (max_tex_idx >= m_prop.textures.size()) { std::ostringstream oss; oss << "GenericCAO::addToScene(): Model " - << m_prop.mesh << " loaded with " << mat_count - << " mesh buffers but only " << m_prop.textures.size() - << " texture(s) specified, this is deprecated."; + << m_prop.mesh << " is missing " << (max_tex_idx + 1 - m_prop.textures.size()) + << " more texture(s), this is deprecated."; logOnce(oss, warningstream); video::ITexture *last = m_animated_meshnode->getMaterial(0).TextureLayers[0].Texture; @@ -1370,9 +1375,11 @@ void GenericCAO::updateTextures(std::string mod) else if (m_animated_meshnode) { if (m_prop.visual == "mesh") { - for (u32 i = 0; i < m_prop.textures.size() && - i < m_animated_meshnode->getMaterialCount(); ++i) { - std::string texturestring = m_prop.textures[i]; + for (u32 i = 0; i < m_animated_meshnode->getMaterialCount(); ++i) { + const auto texture_idx = m_animated_meshnode->getMesh()->getTextureSlot(i); + if (texture_idx >= m_prop.textures.size()) + continue; + std::string texturestring = m_prop.textures[texture_idx]; if (texturestring.empty()) continue; // Empty texture string means don't modify that material texturestring += mod; diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 1de36c9c5..4f4056668 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "content_mapblock.h" +#include "util/basic_macros.h" #include "util/numeric.h" #include "util/directiontables.h" #include "mapblock_mesh.h" @@ -1676,7 +1677,9 @@ void MapblockMeshGenerator::drawMeshNode() int mesh_buffer_count = mesh->getMeshBufferCount(); for (int j = 0; j < mesh_buffer_count; j++) { - useTile(j); + // Only up to 6 tiles are supported + const auto 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(); diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 711f7e1c6..711712c33 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -397,8 +397,8 @@ scene::SMesh* cloneMesh(scene::IMesh *src_mesh) scene::IMeshBuffer *temp_buf = cloneMeshBuffer( src_mesh->getMeshBuffer(j)); dst_mesh->addMeshBuffer(temp_buf); + dst_mesh->setTextureSlot(j, src_mesh->getTextureSlot(j)); temp_buf->drop(); - } return dst_mesh; } diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 3f22fb3c4..1fd373b9c 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -2807,8 +2807,13 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) auto meshnode = e->setMesh(mesh); - for (u32 i = 0; i < textures.size() && i < meshnode->getMaterialCount(); ++i) - e->setTexture(i, m_tsrc->getTexture(unescape_string(textures[i]))); + for (u32 i = 0; i < meshnode->getMaterialCount(); ++i) { + const auto texture_idx = mesh->getTextureSlot(i); + if (texture_idx >= textures.size()) + warningstream << "Invalid model element: Not enough textures" << std::endl; + else + e->setTexture(i, m_tsrc->getTexture(unescape_string(textures[texture_idx]))); + } if (vec_rot.size() >= 2) e->setRotation(v2f(stof(vec_rot[0]), stof(vec_rot[1]))); diff --git a/src/server.cpp b/src/server.cpp index 609b7188b..c76155015 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2465,7 +2465,7 @@ bool Server::addMediaFile(const std::string &filename, const char *supported_ext[] = { ".png", ".jpg", ".bmp", ".tga", ".ogg", - ".x", ".b3d", ".obj", + ".x", ".b3d", ".obj", ".gltf", // Custom translation file format ".tr", NULL diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt index ec52ee6bf..93803c912 100644 --- a/src/unittest/CMakeLists.txt +++ b/src/unittest/CMakeLists.txt @@ -49,7 +49,7 @@ set (UNITTEST_CLIENT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_content_mapblock.cpp ${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_mesh_compare.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_keycode.cpp PARENT_SCOPE) - diff --git a/src/unittest/test_irr_gltf_mesh_loader.cpp b/src/unittest/test_irr_gltf_mesh_loader.cpp new file mode 100644 index 000000000..8ab57e590 --- /dev/null +++ b/src/unittest/test_irr_gltf_mesh_loader.cpp @@ -0,0 +1,366 @@ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "CSceneManager.h" +#include "content/subgames.h" +#include "filesys.h" + +#include "CReadFile.h" +#include "irr_v3d.h" +#include "irr_v2d.h" + +#include + +#include "catch.h" + +TEST_CASE("gltf") { + +const auto gamespec = findSubgame("devtest"); + +if (!gamespec.isValid()) + SKIP(); + +irr::scene::CSceneManager smgr(nullptr, nullptr, nullptr); +const auto loadMesh = [&smgr](const irr::io::path& filepath) { + irr::io::CReadFile file(filepath); + return smgr.getMesh(&file); +}; + +const static auto model_stem = gamespec.gamemods_path + + DIR_DELIM + "gltf" + DIR_DELIM + "models" + DIR_DELIM + "gltf_"; + +SECTION("error cases") { + const static auto invalid_model_path = gamespec.gamemods_path + DIR_DELIM + "gltf" + DIR_DELIM + "invalid" + DIR_DELIM; + + SECTION("empty gltf file") { + CHECK(loadMesh(invalid_model_path + "empty.gltf") == nullptr); + } + + SECTION("null file pointer") { + CHECK(smgr.getMesh(nullptr) == nullptr); + } + + SECTION("invalid JSON") { + CHECK(loadMesh(invalid_model_path + "json_missing_brace.gltf") == nullptr); + } + + // This is an example of something that should be validated by tiniergltf. + SECTION("invalid bufferview bounds") + { + CHECK(loadMesh(invalid_model_path + "invalid_bufferview_bounds.gltf") == nullptr); + } +} + +SECTION("minimal triangle") { + const auto path = GENERATE( + model_stem + "minimal_triangle.gltf", + model_stem + "triangle_with_vertex_stride.gltf", + // Test non-indexed geometry. + model_stem + "triangle_without_indices.gltf"); + INFO(path); + const auto mesh = loadMesh(path); + REQUIRE(mesh != nullptr); + REQUIRE(mesh->getMeshBufferCount() == 1); + + SECTION("vertex coordinates are correct") { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 3); + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + CHECK(vertices[0].Pos == v3f {0.0f, 0.0f, 0.0f}); + CHECK(vertices[1].Pos == v3f {1.0f, 0.0f, 0.0f}); + CHECK(vertices[2].Pos == v3f {0.0f, 1.0f, 0.0f}); + } + + SECTION("vertex indices are correct") { + REQUIRE(mesh->getMeshBuffer(0)->getIndexCount() == 3); + auto indices = static_cast( + mesh->getMeshBuffer(0)->getIndices()); + CHECK(indices[0] == 2); + CHECK(indices[1] == 1); + CHECK(indices[2] == 0); + } +} + +SECTION("blender cube") { + const auto mesh = loadMesh(model_stem + "blender_cube.gltf"); + REQUIRE(mesh != nullptr); + REQUIRE(mesh->getMeshBufferCount() == 1); + SECTION("vertex coordinates are correct") { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + CHECK(vertices[0].Pos == v3f{-10.0f, -10.0f, -10.0f}); + CHECK(vertices[3].Pos == v3f{-10.0f, 10.0f, -10.0f}); + CHECK(vertices[6].Pos == v3f{-10.0f, -10.0f, 10.0f}); + CHECK(vertices[9].Pos == v3f{-10.0f, 10.0f, 10.0f}); + CHECK(vertices[12].Pos == v3f{10.0f, -10.0f, -10.0f}); + CHECK(vertices[15].Pos == v3f{10.0f, 10.0f, -10.0f}); + CHECK(vertices[18].Pos == v3f{10.0f, -10.0f, 10.0f}); + CHECK(vertices[21].Pos == v3f{10.0f, 10.0f, 10.0f}); + } + + SECTION("vertex indices are correct") { + REQUIRE(mesh->getMeshBuffer(0)->getIndexCount() == 36); + auto indices = static_cast( + mesh->getMeshBuffer(0)->getIndices()); + CHECK(indices[0] == 16); + CHECK(indices[1] == 5); + CHECK(indices[2] == 22); + CHECK(indices[35] == 0); + } + + SECTION("vertex normals are correct") { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + CHECK(vertices[0].Normal == v3f{-1.0f, 0.0f, 0.0f}); + CHECK(vertices[1].Normal == v3f{0.0f, -1.0f, 0.0f}); + CHECK(vertices[2].Normal == v3f{0.0f, 0.0f, -1.0f}); + CHECK(vertices[3].Normal == v3f{-1.0f, 0.0f, 0.0f}); + CHECK(vertices[6].Normal == v3f{-1.0f, 0.0f, 0.0f}); + CHECK(vertices[23].Normal == v3f{1.0f, 0.0f, 0.0f}); + + } + + SECTION("texture coords are correct") { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + CHECK(vertices[0].TCoords == v2f{0.375f, 1.0f}); + CHECK(vertices[1].TCoords == v2f{0.125f, 0.25f}); + CHECK(vertices[2].TCoords == v2f{0.375f, 0.0f}); + CHECK(vertices[3].TCoords == v2f{0.6250f, 1.0f}); + CHECK(vertices[6].TCoords == v2f{0.375f, 0.75f}); + } +} + +SECTION("blender cube scaled") { + const auto mesh = loadMesh(model_stem + "blender_cube_scaled.gltf"); + REQUIRE(mesh != nullptr); + REQUIRE(mesh->getMeshBufferCount() == 1); + + SECTION("Scaling is correct") { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + + CHECK(vertices[0].Pos == v3f{-150.0f, -1.0f, -21.5f}); + CHECK(vertices[3].Pos == v3f{-150.0f, 1.0f, -21.5f}); + CHECK(vertices[6].Pos == v3f{-150.0f, -1.0f, 21.5f}); + CHECK(vertices[9].Pos == v3f{-150.0f, 1.0f, 21.5f}); + CHECK(vertices[12].Pos == v3f{150.0f, -1.0f, -21.5f}); + CHECK(vertices[15].Pos == v3f{150.0f, 1.0f, -21.5f}); + CHECK(vertices[18].Pos == v3f{150.0f, -1.0f, 21.5f}); + CHECK(vertices[21].Pos == v3f{150.0f, 1.0f, 21.5f}); + } +} + +SECTION("blender cube matrix transform") { + const auto mesh = loadMesh(model_stem + "blender_cube_matrix_transform.gltf"); + REQUIRE(mesh != nullptr); + REQUIRE(mesh->getMeshBufferCount() == 1); + + SECTION("Transformation is correct") { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + const auto checkVertex = [&](const std::size_t i, v3f vec) { + // The transform scales by (1, 2, 3) and translates by (4, 5, 6). + CHECK(vertices[i].Pos == vec * v3f{1, 2, 3} + // The -6 is due to the coordinate system conversion. + + v3f{4, 5, -6}); + }; + checkVertex(0, v3f{-1, -1, -1}); + checkVertex(3, v3f{-1, 1, -1}); + checkVertex(6, v3f{-1, -1, 1}); + checkVertex(9, v3f{-1, 1, 1}); + checkVertex(12, v3f{1, -1, -1}); + checkVertex(15, v3f{1, 1, -1}); + checkVertex(18, v3f{1, -1, 1}); + checkVertex(21, v3f{1, 1, 1}); + } +} + +SECTION("snow man") { + const auto mesh = loadMesh(model_stem + "snow_man.gltf"); + REQUIRE(mesh != nullptr); + REQUIRE(mesh->getMeshBufferCount() == 3); + + SECTION("vertex coordinates are correct for all buffers") { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 24); + { + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + CHECK(vertices[0].Pos == v3f{3.0f, 24.0f, -3.0f}); + CHECK(vertices[3].Pos == v3f{3.0f, 18.0f, 3.0f}); + CHECK(vertices[6].Pos == v3f{-3.0f, 18.0f, -3.0f}); + CHECK(vertices[9].Pos == v3f{3.0f, 24.0f, 3.0f}); + CHECK(vertices[12].Pos == v3f{3.0f, 18.0f, -3.0f}); + CHECK(vertices[15].Pos == v3f{-3.0f, 18.0f, 3.0f}); + CHECK(vertices[18].Pos == v3f{3.0f, 18.0f, -3.0f}); + CHECK(vertices[21].Pos == v3f{3.0f, 18.0f, 3.0f}); + } + { + REQUIRE(mesh->getMeshBuffer(1)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(1)->getVertices()); + CHECK(vertices[2].Pos == v3f{5.0f, 10.0f, 5.0f}); + CHECK(vertices[3].Pos == v3f{5.0f, 0.0f, 5.0f}); + CHECK(vertices[7].Pos == v3f{-5.0f, 0.0f, 5.0f}); + CHECK(vertices[8].Pos == v3f{5.0f, 10.0f, -5.0f}); + CHECK(vertices[14].Pos == v3f{5.0f, 0.0f, 5.0f}); + CHECK(vertices[16].Pos == v3f{5.0f, 10.0f, -5.0f}); + CHECK(vertices[22].Pos == v3f{-5.0f, 10.0f, 5.0f}); + CHECK(vertices[23].Pos == v3f{-5.0f, 0.0f, 5.0f}); + } + { + REQUIRE(mesh->getMeshBuffer(2)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(2)->getVertices()); + CHECK(vertices[1].Pos == v3f{4.0f, 10.0f, -4.0f}); + CHECK(vertices[2].Pos == v3f{4.0f, 18.0f, 4.0f}); + CHECK(vertices[3].Pos == v3f{4.0f, 10.0f, 4.0f}); + CHECK(vertices[10].Pos == v3f{-4.0f, 18.0f, -4.0f}); + CHECK(vertices[11].Pos == v3f{-4.0f, 18.0f, 4.0f}); + CHECK(vertices[12].Pos == v3f{4.0f, 10.0f, -4.0f}); + CHECK(vertices[17].Pos == v3f{-4.0f, 18.0f, -4.0f}); + CHECK(vertices[18].Pos == v3f{4.0f, 10.0f, -4.0f}); + } + } + + SECTION("vertex indices are correct for all buffers") { + { + REQUIRE(mesh->getMeshBuffer(0)->getIndexCount() == 36); + auto indices = static_cast( + mesh->getMeshBuffer(0)->getIndices()); + CHECK(indices[0] == 23); + CHECK(indices[1] == 21); + CHECK(indices[2] == 22); + CHECK(indices[35] == 2); + } + { + REQUIRE(mesh->getMeshBuffer(1)->getIndexCount() == 36); + auto indices = static_cast( + mesh->getMeshBuffer(1)->getIndices()); + CHECK(indices[10] == 16); + CHECK(indices[11] == 18); + CHECK(indices[15] == 13); + CHECK(indices[27] == 5); + } + { + REQUIRE(mesh->getMeshBuffer(2)->getIndexCount() == 36); + auto indices = static_cast( + mesh->getMeshBuffer(2)->getIndices()); + CHECK(indices[26] == 6); + CHECK(indices[27] == 5); + CHECK(indices[29] == 6); + CHECK(indices[32] == 2); + } + } + + + SECTION("vertex normals are correct for all buffers") { + { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + CHECK(vertices[0].Normal == v3f{1.0f, 0.0f, -0.0f}); + CHECK(vertices[1].Normal == v3f{1.0f, 0.0f, -0.0f}); + CHECK(vertices[2].Normal == v3f{1.0f, 0.0f, -0.0f}); + CHECK(vertices[3].Normal == v3f{1.0f, 0.0f, -0.0f}); + CHECK(vertices[6].Normal == v3f{-1.0f, 0.0f, -0.0f}); + CHECK(vertices[23].Normal == v3f{0.0f, 0.0f, 1.0f}); + } + { + REQUIRE(mesh->getMeshBuffer(1)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(1)->getVertices()); + CHECK(vertices[0].Normal == v3f{1.0f, 0.0f, -0.0f}); + CHECK(vertices[1].Normal == v3f{1.0f, 0.0f, -0.0f}); + CHECK(vertices[3].Normal == v3f{1.0f, 0.0f, -0.0f}); + CHECK(vertices[6].Normal == v3f{-1.0f, 0.0f, -0.0f}); + CHECK(vertices[7].Normal == v3f{-1.0f, 0.0f, -0.0f}); + CHECK(vertices[22].Normal == v3f{0.0f, 0.0f, 1.0f}); + } + { + REQUIRE(mesh->getMeshBuffer(2)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(2)->getVertices()); + CHECK(vertices[3].Normal == v3f{1.0f, 0.0f, -0.0f}); + CHECK(vertices[4].Normal == v3f{-1.0f, 0.0f, -0.0f}); + CHECK(vertices[5].Normal == v3f{-1.0f, 0.0f, -0.0f}); + CHECK(vertices[10].Normal == v3f{0.0f, 1.0f, -0.0f}); + CHECK(vertices[11].Normal == v3f{0.0f, 1.0f, -0.0f}); + CHECK(vertices[19].Normal == v3f{0.0f, 0.0f, -1.0f}); + } + } + + + SECTION("texture coords are correct for all buffers") { + { + REQUIRE(mesh->getMeshBuffer(0)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(0)->getVertices()); + CHECK(vertices[0].TCoords == v2f{0.583333313f, 0.791666686f}); + CHECK(vertices[1].TCoords == v2f{0.583333313f, 0.666666686f}); + CHECK(vertices[2].TCoords == v2f{0.708333313f, 0.791666686f}); + CHECK(vertices[5].TCoords == v2f{0.375f, 0.416666657f}); + CHECK(vertices[6].TCoords == v2f{0.5f, 0.291666657f}); + CHECK(vertices[19].TCoords == v2f{0.708333313f, 0.75f}); + } + { + REQUIRE(mesh->getMeshBuffer(1)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(1)->getVertices()); + + CHECK(vertices[1].TCoords == v2f{0.0f, 0.791666686f}); + CHECK(vertices[4].TCoords == v2f{0.208333328f, 0.791666686f}); + CHECK(vertices[5].TCoords == v2f{0.0f, 0.791666686f}); + CHECK(vertices[6].TCoords == v2f{0.208333328f, 0.583333313f}); + CHECK(vertices[12].TCoords == v2f{0.416666657f, 0.791666686f}); + CHECK(vertices[15].TCoords == v2f{0.208333328f, 0.583333313f}); + } + { + REQUIRE(mesh->getMeshBuffer(2)->getVertexCount() == 24); + auto vertices = static_cast( + mesh->getMeshBuffer(2)->getVertices()); + CHECK(vertices[10].TCoords == v2f{0.375f, 0.416666657f}); + CHECK(vertices[11].TCoords == v2f{0.375f, 0.583333313f}); + CHECK(vertices[12].TCoords == v2f{0.708333313f, 0.625f}); + CHECK(vertices[17].TCoords == v2f{0.541666687f, 0.458333343f}); + CHECK(vertices[20].TCoords == v2f{0.208333328f, 0.416666657f}); + CHECK(vertices[22].TCoords == v2f{0.375f, 0.416666657f}); + } + } +} + +// https://github.com/KhronosGroup/glTF-Sample-Models/tree/main/2.0/SimpleSparseAccessor +SECTION("simple sparse accessor") +{ + const auto mesh = loadMesh(model_stem + "simple_sparse_accessor.gltf"); + REQUIRE(mesh != nullptr); + const auto *vertices = reinterpret_cast( + mesh->getMeshBuffer(0)->getVertices()); + const std::array expectedPositions = { + // Lower + v3f(0, 0, 0), + v3f(1, 0, 0), + v3f(2, 0, 0), + v3f(3, 0, 0), + v3f(4, 0, 0), + v3f(5, 0, 0), + v3f(6, 0, 0), + // Upper + v3f(0, 1, 0), + v3f(1, 2, 0), // overridden + v3f(2, 1, 0), + v3f(3, 3, 0), // overridden + v3f(4, 1, 0), + v3f(5, 4, 0), // overridden + v3f(6, 1, 0), + }; + for (std::size_t i = 0; i < expectedPositions.size(); ++i) + CHECK(vertices[i].Pos == expectedPositions[i]); +} + +} diff --git a/src/unittest/test_servermodmanager.cpp b/src/unittest/test_servermodmanager.cpp index 90c29e125..f26734ab3 100644 --- a/src/unittest/test_servermodmanager.cpp +++ b/src/unittest/test_servermodmanager.cpp @@ -122,7 +122,7 @@ void TestServerModManager::testGetMods() ServerModManager sm(m_worlddir); const auto &mods = sm.getMods(); // `ls ./games/devtest/mods | wc -l` + 1 (test mod) - UASSERTEQ(std::size_t, mods.size(), 31 + 1); + UASSERTEQ(std::size_t, mods.size(), 32 + 1); // Ensure we found basenodes mod (part of devtest) // and test_mod (for testing MINETEST_MOD_PATH). From b8b99d5cf1c430f373d9b384e5d57d08f823b809 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 31 Aug 2024 21:23:16 +0200 Subject: [PATCH 61/75] Use std::string_view in logging code --- src/log.cpp | 25 +++++++++++++------------ src/log.h | 32 ++++++++++++++++---------------- src/script/common/c_internal.cpp | 23 +++++++++++++---------- src/script/common/c_internal.h | 7 +++++-- src/script/lua_api/l_util.cpp | 12 ++++++------ src/terminal_chat_console.h | 6 +++--- src/util/stream.h | 10 +++++----- 7 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/log.cpp b/src/log.cpp index 98939c9bf..f7eb691ac 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -55,7 +55,7 @@ public: return m_logger.hasOutput(m_level); } - virtual void log(const std::string &buf) override { + virtual void log(std::string_view buf) override { if (!m_raw) { m_logger.log(m_level, buf); } else { @@ -106,7 +106,7 @@ thread_local LogStream dout_con(trace_target); // Android #ifdef __ANDROID__ -static unsigned int g_level_to_android[] = { +constexpr static unsigned int g_level_to_android[] = { ANDROID_LOG_INFO, // LL_NONE ANDROID_LOG_ERROR, // LL_ERROR ANDROID_LOG_WARN, // LL_WARNING @@ -116,11 +116,12 @@ static unsigned int g_level_to_android[] = { ANDROID_LOG_VERBOSE, // LL_TRACE }; -void AndroidLogOutput::logRaw(LogLevel lev, const std::string &line) +void AndroidLogOutput::logRaw(LogLevel lev, std::string_view line) { static_assert(ARRLEN(g_level_to_android) == LL_MAX, "mismatch between android and internal loglevels"); - __android_log_write(g_level_to_android[lev], PROJECT_NAME_C, line.c_str()); + __android_log_print(g_level_to_android[lev], PROJECT_NAME_C, "%.*s", + line.size(), line.data()); } #endif @@ -131,7 +132,7 @@ void AndroidLogOutput::logRaw(LogLevel lev, const std::string &line) //// Logger //// -LogLevel Logger::stringToLevel(const std::string &name) +LogLevel Logger::stringToLevel(std::string_view name) { if (name == "none") return LL_NONE; @@ -202,7 +203,7 @@ void Logger::setLevelSilenced(LogLevel lev, bool silenced) m_silenced_levels[lev] = silenced; } -void Logger::registerThread(const std::string &name) +void Logger::registerThread(std::string_view name) { std::thread::id id = std::this_thread::get_id(); MutexAutoLock lock(m_mutex); @@ -252,7 +253,7 @@ const std::string &Logger::getThreadName() return fallback_name; } -void Logger::log(LogLevel lev, const std::string &text) +void Logger::log(LogLevel lev, std::string_view text) { if (isLevelSilenced(lev)) return; @@ -268,7 +269,7 @@ void Logger::log(LogLevel lev, const std::string &text) logToOutputs(lev, line, timestamp, thread_name, text); } -void Logger::logRaw(LogLevel lev, const std::string &text) +void Logger::logRaw(LogLevel lev, std::string_view text) { if (isLevelSilenced(lev)) return; @@ -276,7 +277,7 @@ void Logger::logRaw(LogLevel lev, const std::string &text) logToOutputsRaw(lev, text); } -void Logger::logToOutputsRaw(LogLevel lev, const std::string &line) +void Logger::logToOutputsRaw(LogLevel lev, std::string_view line) { MutexAutoLock lock(m_mutex); for (size_t i = 0; i != m_outputs[lev].size(); i++) @@ -285,7 +286,7 @@ void Logger::logToOutputsRaw(LogLevel lev, const std::string &line) void Logger::logToOutputs(LogLevel lev, const std::string &combined, const std::string &time, const std::string &thread_name, - const std::string &payload_text) + std::string_view payload_text) { MutexAutoLock lock(m_mutex); for (size_t i = 0; i != m_outputs[lev].size(); i++) @@ -334,7 +335,7 @@ StreamLogOutput::StreamLogOutput(std::ostream &stream) : #endif } -void StreamLogOutput::logRaw(LogLevel lev, const std::string &line) +void StreamLogOutput::logRaw(LogLevel lev, std::string_view line) { bool colored_message = (Logger::color_mode == LOG_COLOR_ALWAYS) || (Logger::color_mode == LOG_COLOR_AUTO && is_tty); @@ -385,7 +386,7 @@ void LogOutputBuffer::updateLogLevel() m_logger.addOutputMaxLevel(this, log_level); } -void LogOutputBuffer::logRaw(LogLevel lev, const std::string &line) +void LogOutputBuffer::logRaw(LogLevel lev, std::string_view line) { std::string color; diff --git a/src/log.h b/src/log.h index 9ac4e5767..721ce58ed 100644 --- a/src/log.h +++ b/src/log.h @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include +#include #include #include #include @@ -62,14 +62,14 @@ public: LogLevelMask removeOutput(ILogOutput *out); void setLevelSilenced(LogLevel lev, bool silenced); - void registerThread(const std::string &name); + void registerThread(std::string_view name); void deregisterThread(); - void log(LogLevel lev, const std::string &text); + void log(LogLevel lev, std::string_view text); // Logs without a prefix - void logRaw(LogLevel lev, const std::string &text); + void logRaw(LogLevel lev, std::string_view text); - static LogLevel stringToLevel(const std::string &name); + static LogLevel stringToLevel(std::string_view name); static const char *getLevelLabel(LogLevel lev); bool hasOutput(LogLevel level) { @@ -83,10 +83,10 @@ public: static LogColor color_mode; private: - void logToOutputsRaw(LogLevel, const std::string &line); + void logToOutputsRaw(LogLevel, std::string_view line); void logToOutputs(LogLevel, const std::string &combined, const std::string &time, const std::string &thread_name, - const std::string &payload_text); + std::string_view payload_text); const std::string &getThreadName(); @@ -99,17 +99,17 @@ private: class ILogOutput { public: - virtual void logRaw(LogLevel, const std::string &line) = 0; + virtual void logRaw(LogLevel, std::string_view line) = 0; virtual void log(LogLevel, const std::string &combined, const std::string &time, const std::string &thread_name, - const std::string &payload_text) = 0; + std::string_view payload_text) = 0; }; class ICombinedLogOutput : public ILogOutput { public: void log(LogLevel lev, const std::string &combined, const std::string &time, const std::string &thread_name, - const std::string &payload_text) + std::string_view payload_text) { logRaw(lev, combined); } @@ -119,7 +119,7 @@ class StreamLogOutput : public ICombinedLogOutput { public: StreamLogOutput(std::ostream &stream); - void logRaw(LogLevel lev, const std::string &line); + void logRaw(LogLevel lev, std::string_view line); private: std::ostream &m_stream; @@ -130,7 +130,7 @@ class FileLogOutput : public ICombinedLogOutput { public: void setFile(const std::string &filename, s64 file_size_max); - void logRaw(LogLevel lev, const std::string &line) + void logRaw(LogLevel lev, std::string_view line) { m_stream << line << std::endl; } @@ -154,7 +154,7 @@ public: void updateLogLevel(); - void logRaw(LogLevel lev, const std::string &line); + void logRaw(LogLevel lev, std::string_view line); void clear() { @@ -190,7 +190,7 @@ private: #ifdef __ANDROID__ class AndroidLogOutput : public ICombinedLogOutput { public: - void logRaw(LogLevel lev, const std::string &line); + void logRaw(LogLevel lev, std::string_view line); }; #endif @@ -206,7 +206,7 @@ class LogTarget { public: // Must be thread-safe. These can be called from any thread. virtual bool hasOutput() = 0; - virtual void log(const std::string &buf) = 0; + virtual void log(std::string_view buf) = 0; }; @@ -304,7 +304,7 @@ public: return m_target.hasOutput(); } - void internalFlush(const std::string &buf) { + void internalFlush(std::string_view buf) { m_target.log(buf); } diff --git a/src/script/common/c_internal.cpp b/src/script/common/c_internal.cpp index ae83b8df0..e6bfafdcd 100644 --- a/src/script/common/c_internal.cpp +++ b/src/script/common/c_internal.cpp @@ -122,27 +122,30 @@ void script_error(lua_State *L, int pcall_result, const char *mod, const char *f throw LuaError(err_msg); } -static void script_log_add_source(lua_State *L, std::string &message, int stack_depth) +[[nodiscard]] static std::string script_log_add_source(lua_State *L, + std::string_view message, int stack_depth) { + std::string ret(message); if (stack_depth <= 0) - return; + return ret; lua_Debug ar; if (lua_getstack(L, stack_depth, &ar)) { FATAL_ERROR_IF(!lua_getinfo(L, "Sl", &ar), "lua_getinfo() failed"); - message.append(" (at " + std::string(ar.short_src) + ":" + ret.append(" (at ").append(ar.short_src).append(":" + std::to_string(ar.currentline) + ")"); } else { - message.append(" (at ?:?)"); + ret.append(" (at ?:?)"); } + return ret; } -bool script_log_unique(lua_State *L, std::string message, std::ostream &log_to, +bool script_log_unique(lua_State *L, std::string_view message_in, std::ostream &log_to, int stack_depth) { thread_local std::vector logged_messages; - script_log_add_source(L, message, stack_depth); + auto message = script_log_add_source(L, message_in, stack_depth); u64 hash = murmur_hash_64_ua(message.data(), message.length(), 0xBADBABE); if (std::find(logged_messages.begin(), logged_messages.end(), hash) @@ -174,7 +177,7 @@ DeprecatedHandlingMode get_deprecated_handling_mode() return ret; } -void log_deprecated(lua_State *L, std::string message, int stack_depth, bool once) +void log_deprecated(lua_State *L, std::string_view message, int stack_depth, bool once) { DeprecatedHandlingMode mode = get_deprecated_handling_mode(); if (mode == DeprecatedHandlingMode::Ignore) @@ -184,12 +187,12 @@ void log_deprecated(lua_State *L, std::string message, int stack_depth, bool onc if (once) { log = script_log_unique(L, message, warningstream, stack_depth); } else { - script_log_add_source(L, message, stack_depth); - warningstream << message << std::endl; + auto message2 = script_log_add_source(L, message, stack_depth); + warningstream << message2 << std::endl; } if (mode == DeprecatedHandlingMode::Error) - throw LuaError(message); + throw LuaError(std::string(message)); else if (log) infostream << script_get_backtrace(L) << std::endl; } diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h index a67b0dad9..a9f9fe226 100644 --- a/src/script/common/c_internal.h +++ b/src/script/common/c_internal.h @@ -26,6 +26,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include + extern "C" { #include #include @@ -127,7 +129,7 @@ int script_error_handler(lua_State *L); // Takes an error from lua_pcall and throws it as a LuaError void script_error(lua_State *L, int pcall_result, const char *mod, const char *fxn); -bool script_log_unique(lua_State *L, std::string message, std::ostream &log_to, +bool script_log_unique(lua_State *L, std::string_view message, std::ostream &log_to, int stack_depth = 1); enum DeprecatedHandlingMode { @@ -152,7 +154,8 @@ DeprecatedHandlingMode get_deprecated_handling_mode(); * (ie: not builtin or core). -1 to disabled. * @param once Log the deprecation warning only once per callsite. */ -void log_deprecated(lua_State *L, std::string message, int stack_depth = 1, bool once = false); +void log_deprecated(lua_State *L, std::string_view message, + int stack_depth = 1, bool once = false); // Safely call string.dump on a function value // (does not pop, leaves one value on stack) diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index fac3e54d1..79eb38629 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -61,13 +61,13 @@ with this program; if not, write to the Free Software Foundation, Inc., int ModApiUtil::l_log(lua_State *L) { NO_MAP_LOCK_REQUIRED; - std::string text; + std::string_view text; LogLevel level = LL_NONE; - if (lua_isnone(L, 2)) { - text = luaL_checkstring(L, 1); + if (lua_isnoneornil(L, 2)) { + text = readParam(L, 1); } else { - std::string name = luaL_checkstring(L, 1); - text = luaL_checkstring(L, 2); + auto name = readParam(L, 1); + text = readParam(L, 2); if (name == "deprecated") { log_deprecated(L, text, 2); return 0; @@ -75,7 +75,7 @@ int ModApiUtil::l_log(lua_State *L) level = Logger::stringToLevel(name); if (level == LL_MAX) { warningstream << "Tried to log at unknown level '" << name - << "'. Defaulting to \"none\"." << std::endl; + << "'. Defaulting to \"none\"." << std::endl; level = LL_NONE; } } diff --git a/src/terminal_chat_console.h b/src/terminal_chat_console.h index 825c76ef4..1bd226609 100644 --- a/src/terminal_chat_console.h +++ b/src/terminal_chat_console.h @@ -32,14 +32,14 @@ struct ChatInterface; class TermLogOutput : public ILogOutput { public: - void logRaw(LogLevel lev, const std::string &line) + void logRaw(LogLevel lev, std::string_view line) { - queue.push_back(std::make_pair(lev, line)); + queue.push_back(std::make_pair(lev, std::string(line))); } virtual void log(LogLevel lev, const std::string &combined, const std::string &time, const std::string &thread_name, - const std::string &payload_text) + std::string_view payload_text) { std::ostringstream os(std::ios_base::binary); os << time << ": [" << thread_name << "] " << payload_text; diff --git a/src/util/stream.h b/src/util/stream.h index 2e61b46d2..620ad74ba 100644 --- a/src/util/stream.h +++ b/src/util/stream.h @@ -20,10 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include -#include +#include #include -template > +template > class StringStreamBuffer : public std::streambuf { public: StringStreamBuffer(Emitter emitter) : m_emitter(emitter) { @@ -38,19 +38,19 @@ public: void push_back(char c) { if (c == '\n' || c == '\r') { if (buffer_index) - m_emitter(std::string(buffer, buffer_index)); + m_emitter(std::string_view(buffer, buffer_index)); buffer_index = 0; } else { buffer[buffer_index++] = c; if (buffer_index >= BufferLength) { - m_emitter(std::string(buffer, buffer_index)); + m_emitter(std::string_view(buffer, buffer_index)); buffer_index = 0; } } } std::streamsize xsputn(const char *s, std::streamsize n) { - for (int i = 0; i < n; ++i) + for (std::streamsize i = 0; i < n; ++i) push_back(s[i]); return n; } From 0c4f03d9a5c082ada54925a062fa224662532eec Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Mon, 2 Sep 2024 16:09:32 +0200 Subject: [PATCH 62/75] Reduce include count in headers --- src/client/camera.h | 2 +- src/client/client.cpp | 1 + src/client/client.h | 53 +++++++++++++++---------------- src/client/clientevent.h | 3 +- src/client/clientlauncher.cpp | 1 + src/client/clientlauncher.h | 9 ++++-- src/client/clientmap.h | 12 ++++++- src/client/clientobject.h | 9 +++++- src/client/clouds.h | 10 +++++- src/client/content_cso.cpp | 1 + src/client/content_cso.h | 7 +++- src/client/filecache.cpp | 1 - src/client/gameui.cpp | 3 ++ src/client/gameui.h | 6 ++-- src/client/guiscalingfilter.cpp | 3 ++ src/client/guiscalingfilter.h | 12 ++++++- src/client/hud.h | 13 ++++++++ src/client/imagefilters.h | 9 +++++- src/client/inputhandler.h | 2 +- src/client/sky.h | 12 +++++-- src/client/texturesource.h | 8 ++++- src/client/wieldmesh.h | 15 ++++++++- src/gui/StyleSpec.h | 5 +-- src/gui/guiBackgroundImage.cpp | 1 + src/gui/guiFormSpecMenu.cpp | 1 + src/gui/guiFormSpecMenu.h | 5 ++- src/gui/guiHyperText.cpp | 1 + src/gui/guiItemImage.cpp | 2 ++ src/gui/guiPathSelectMenu.cpp | 1 + src/gui/guiPathSelectMenu.h | 3 +- src/hud.h | 2 +- src/itemdef.h | 2 +- src/mapnode.cpp | 2 +- src/nameidmapping.h | 2 +- src/network/peerhandler.h | 2 -- src/network/serveropcodes.h | 1 - src/player.h | 1 - src/remoteplayer.h | 1 + src/script/lua_api/l_mainmenu.cpp | 1 + 39 files changed, 163 insertions(+), 62 deletions(-) diff --git a/src/client/camera.h b/src/client/camera.h index 88533181b..8f85da0e1 100644 --- a/src/client/camera.h +++ b/src/client/camera.h @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes.h" #include "inventory.h" #include "util/numeric.h" #include "client/localplayer.h" diff --git a/src/client/client.cpp b/src/client/client.cpp index 3720ec54f..d3ad62512 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -52,6 +52,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "profiler.h" #include "shader.h" #include "gettext.h" +#include "clientdynamicinfo.h" #include "clientmap.h" #include "clientmedia.h" #include "version.h" diff --git a/src/client/client.h b/src/client/client.h index 64ab90eb3..adbe7a70d 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -20,22 +20,20 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "clientenvironment.h" -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes.h" #include #include #include #include #include #include -#include "clientobject.h" #include "gamedef.h" #include "inventorymanager.h" -#include "client/hud.h" -#include "tileanimation.h" #include "network/address.h" +#include "network/networkprotocol.h" // multiple enums #include "network/peerhandler.h" #include "gameparams.h" -#include "clientdynamicinfo.h" +#include "script/common/c_types.h" // LuaError #include "util/numeric.h" #ifdef SERVER @@ -44,32 +42,33 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f -struct ClientEvent; -struct MeshMakeData; -struct ChatMessage; -class MapBlockMesh; -class RenderingEngine; -class IWritableTextureSource; -class IWritableShaderSource; -class IWritableItemDefManager; -class ISoundManager; -class NodeDefManager; -//class IWritableCraftDefManager; +class Camera; class ClientMediaDownloader; -class SingleMediaDownloader; -struct MapDrawControl; +class ISoundManager; +class IWritableItemDefManager; +class IWritableShaderSource; +class IWritableTextureSource; +class MapBlockMesh; +class MapDatabase; +class MeshUpdateManager; +class Minimap; class ModChannelMgr; class MtEventManager; -struct PointedThing; -struct MapNode; -class MapDatabase; -class Minimap; -struct MinimapMapblock; -class MeshUpdateManager; -class ParticleManager; -class Camera; -struct PlayerControl; class NetworkPacket; +class NodeDefManager; +class ParticleManager; +class RenderingEngine; +class SingleMediaDownloader; +struct ChatMessage; +struct ClientDynamicInfo; +struct ClientEvent; +struct MapDrawControl; +struct MapNode; +struct MeshMakeData; +struct MinimapMapblock; +struct PlayerControl; +struct PointedThing; + namespace con { class IConnection; } diff --git a/src/client/clientevent.h b/src/client/clientevent.h index 243a94596..a53c007dd 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -20,7 +20,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include -#include "irrlichttypes_bloated.h" +#include "irrlichttypes.h" +#include "client/hud.h" // HudElementStat struct ParticleParameters; struct ParticleSpawnerParameters; diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 5826508f6..e86fb4425 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "player.h" #include "chat.h" #include "gettext.h" +#include "inputhandler.h" #include "profiler.h" #include "gui/guiEngine.h" #include "fontengine.h" diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index 7b070451a..ad7604c87 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -19,11 +19,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" -#include "client/inputhandler.h" -#include "gameparams.h" +#include class RenderingEngine; +class Settings; +class MyEventReceiver; +class InputHandler; +struct GameStartData; +struct MainMenuData; class ClientLauncher { diff --git a/src/client/clientmap.h b/src/client/clientmap.h index 8fac5a471..2f0a2e986 100644 --- a/src/client/clientmap.h +++ b/src/client/clientmap.h @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include "map.h" #include "camera.h" #include @@ -41,6 +41,16 @@ class Client; class ITextureSource; class PartialMeshBuffer; +namespace irr::scene +{ + class IMeshBuffer; +} + +namespace irr::video +{ + class IVideoDriver; +} + /* ClientMap diff --git a/src/client/clientobject.h b/src/client/clientobject.h index f02815e04..8c2c68d15 100644 --- a/src/client/clientobject.h +++ b/src/client/clientobject.h @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include "activeobject.h" #include #include @@ -34,6 +34,13 @@ class LocalPlayer; struct ItemStack; class WieldMeshSceneNode; +namespace irr::scene +{ + class IAnimatedMeshSceneNode; + class ISceneNode; + class ISceneManager; +} + class ClientActiveObject : public ActiveObject { public: diff --git a/src/client/clouds.h b/src/client/clouds.h index e288c5e8a..332aa81e9 100644 --- a/src/client/clouds.h +++ b/src/client/clouds.h @@ -19,14 +19,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once +#include "irrlichttypes_bloated.h" #include "constants.h" #include "irr_ptr.h" -#include "irrlichttypes_extrabloated.h" #include "skyparams.h" #include +#include +#include +#include class IShaderSource; +namespace irr::scene +{ + class ISceneManager; +} + // Menu clouds class Clouds; extern Clouds *g_menuclouds; diff --git a/src/client/content_cso.cpp b/src/client/content_cso.cpp index c175df72e..821c2507d 100644 --- a/src/client/content_cso.cpp +++ b/src/client/content_cso.cpp @@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_cso.h" #include +#include #include "client/texturesource.h" #include "clientenvironment.h" #include "client.h" diff --git a/src/client/content_cso.h b/src/client/content_cso.h index cc9213175..b94580a61 100644 --- a/src/client/content_cso.h +++ b/src/client/content_cso.h @@ -19,8 +19,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include "clientsimpleobject.h" +namespace irr::scene +{ + class ISceneManager; +} + ClientSimpleObject* createSmokePuff(scene::ISceneManager *smgr, ClientEnvironment *env, v3f pos, v2f size); diff --git a/src/client/filecache.cpp b/src/client/filecache.cpp index 1f7e605d1..4dd7ec72f 100644 --- a/src/client/filecache.cpp +++ b/src/client/filecache.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filecache.h" -#include "network/networkprotocol.h" #include "log.h" #include "filesys.h" #include diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 0577943cb..7631f9c78 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -23,10 +23,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include "gui/mainmenumanager.h" #include "gui/guiChatConsole.h" +#include "gui/guiFormSpecMenu.h" +#include "util/enriched_string.h" #include "util/pointedthing.h" #include "client.h" #include "clientmap.h" #include "fontengine.h" +#include "hud.h" // HUD_FLAG_* #include "nodedef.h" #include "profiler.h" #include "renderingengine.h" diff --git a/src/client/gameui.h b/src/client/gameui.h index 5b87d43e6..59741c96c 100644 --- a/src/client/gameui.h +++ b/src/client/gameui.h @@ -22,15 +22,15 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include -#include "gui/guiFormSpecMenu.h" -#include "util/enriched_string.h" -#include "util/pointedthing.h" #include "game.h" using namespace irr; class Client; +class EnrichedString; class GUIChatConsole; +class GUIFormSpecMenu; struct MapDrawControl; +struct PointedThing; /* * This object intend to contain the core UI elements diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp index b7b9af0ff..eebf44103 100644 --- a/src/client/guiscalingfilter.cpp +++ b/src/client/guiscalingfilter.cpp @@ -23,6 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include #include "client/renderingengine.h" +#include +#include +#include /* Maintain a static cache to store the images that correspond to textures * in a format that's manipulable by code. Some platforms exhibit issues diff --git a/src/client/guiscalingfilter.h b/src/client/guiscalingfilter.h index f2d2fce10..c929bb2d3 100644 --- a/src/client/guiscalingfilter.h +++ b/src/client/guiscalingfilter.h @@ -18,7 +18,17 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes.h" +#include +#include +#include + +namespace irr::video +{ + class IImage; + class ITexture; + class IVideoDriver; +} /* Manually insert an image into the cache, useful to avoid texture-to-image * conversion whenever we can intercept it. diff --git a/src/client/hud.h b/src/client/hud.h index 07df1c66b..55b24abd3 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#include +#include #include "irr_aabb3d.h" #include "../hud.h" @@ -32,6 +34,17 @@ class InventoryList; class LocalPlayer; struct ItemStack; +namespace irr::scene +{ + class IMesh; +} + +namespace irr::video +{ + class ITexture; + class IVideoDriver; +} + class Hud { public: diff --git a/src/client/imagefilters.h b/src/client/imagefilters.h index e6cbf2d29..f7e06ad09 100644 --- a/src/client/imagefilters.h +++ b/src/client/imagefilters.h @@ -18,7 +18,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes.h" +#include + +namespace irr::video +{ + class IVideoDriver; + class IImage; +} /* Fill in RGB values for transparent pixels, to correct for odd colors * appearing at borders when blending. This is because many PNG optimizers diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 400503a3d..ba85b30ad 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes.h" #include "joystick_controller.h" #include #include "keycode.h" diff --git a/src/client/sky.h b/src/client/sky.h index 2eadea561..eba301e18 100644 --- a/src/client/sky.h +++ b/src/client/sky.h @@ -19,16 +19,22 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include +#include #include -#include "camera.h" +#include "camera.h" // CameraMode #include "irr_ptr.h" -#include "shader.h" #include "skyparams.h" #define SKY_MATERIAL_COUNT 12 +namespace irr::video +{ + class IVideoDriver; +} + +class IShaderSource; class ITextureSource; // Skybox, rendered with zbuffer turned off, before all other nodes. diff --git a/src/client/texturesource.h b/src/client/texturesource.h index 5fef20821..d4880ed4c 100644 --- a/src/client/texturesource.h +++ b/src/client/texturesource.h @@ -20,10 +20,16 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "irrlichttypes.h" -#include +#include #include #include +namespace irr::video +{ + class IImage; + class ITexture; +} + typedef std::vector Palette; /* diff --git a/src/client/wieldmesh.h b/src/client/wieldmesh.h index 6358a6665..e2c6cd445 100644 --- a/src/client/wieldmesh.h +++ b/src/client/wieldmesh.h @@ -21,7 +21,20 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -#include "irrlichttypes_extrabloated.h" +#include "irr_aabb3d.h" +#include "irr_v3d.h" +#include +#include +#include + +namespace irr::scene +{ + class ISceneManager; + class IMesh; + struct SMesh; +} + +using namespace irr; struct ItemStack; class Client; diff --git a/src/gui/StyleSpec.h b/src/gui/StyleSpec.h index ef7416bca..373590a50 100644 --- a/src/gui/StyleSpec.h +++ b/src/gui/StyleSpec.h @@ -17,16 +17,17 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#pragma once + #include "client/texturesource.h" #include "client/fontengine.h" #include "debug.h" -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include "util/string.h" #include #include #include -#pragma once class StyleSpec { diff --git a/src/gui/guiBackgroundImage.cpp b/src/gui/guiBackgroundImage.cpp index 96197ca2c..f3b71bb8c 100644 --- a/src/gui/guiBackgroundImage.cpp +++ b/src/gui/guiBackgroundImage.cpp @@ -19,6 +19,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "client/guiscalingfilter.h" #include "log.h" #include "client/texturesource.h" +#include GUIBackgroundImage::GUIBackgroundImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, const core::rect &rectangle, diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 1fd373b9c..8b572276c 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "client/renderingengine.h" +#include "client/joystick_controller.h" #include "log.h" #include "client/hud.h" // drawItemStack #include "filesys.h" diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 30045a168..12add12e6 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include "irr_ptr.h" #include "inventory.h" #include "inventorymanager.h" @@ -32,8 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiInventoryList.h" #include "guiScrollBar.h" #include "guiTable.h" -#include "network/networkprotocol.h" -#include "client/joystick_controller.h" #include "util/string.h" #include "util/enriched_string.h" #include "StyleSpec.h" @@ -45,6 +43,7 @@ class ISimpleTextureSource; class Client; class GUIScrollContainer; class ISoundManager; +class JoystickController; enum FormspecFieldType { f_Button, diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 45e5d6e98..6f30ac8ce 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiHyperText.h" #include "guiScrollBar.h" #include "client/fontengine.h" +#include "client/hud.h" // drawItemStack #include "IVideoDriver.h" #include "client/client.h" #include "client/renderingengine.h" diff --git a/src/gui/guiItemImage.cpp b/src/gui/guiItemImage.cpp index a02285fcb..0c543e391 100644 --- a/src/gui/guiItemImage.cpp +++ b/src/gui/guiItemImage.cpp @@ -19,7 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiItemImage.h" #include "client/client.h" +#include "client/hud.h" // drawItemStack #include "inventory.h" +#include GUIItemImage::GUIItemImage(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id, const core::rect &rectangle, const std::string &item_name, diff --git a/src/gui/guiPathSelectMenu.cpp b/src/gui/guiPathSelectMenu.cpp index 9c63e06b5..b4c3d36c3 100644 --- a/src/gui/guiPathSelectMenu.cpp +++ b/src/gui/guiPathSelectMenu.cpp @@ -18,6 +18,7 @@ */ #include "guiPathSelectMenu.h" +#include "guiFormSpecMenu.h" //required because of TextDest only !!! GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, diff --git a/src/gui/guiPathSelectMenu.h b/src/gui/guiPathSelectMenu.h index 11307d682..7757b2d7b 100644 --- a/src/gui/guiPathSelectMenu.h +++ b/src/gui/guiPathSelectMenu.h @@ -23,7 +23,8 @@ #include "modalMenu.h" #include "IGUIFileOpenDialog.h" -#include "guiFormSpecMenu.h" //required because of TextDest only !!! + +struct TextDest; class GUIFileSelectMenu : public GUIModalMenu { diff --git a/src/hud.h b/src/hud.h index d32ab784e..ac79aa750 100644 --- a/src/hud.h +++ b/src/hud.h @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include #include "common/c_types.h" diff --git a/src/itemdef.h b/src/itemdef.h index 782dad816..4a227ebe1 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include #include #include diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 6fe169e90..4e3c60192 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -17,7 +17,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include "mapnode.h" #include "porting.h" #include "nodedef.h" diff --git a/src/nameidmapping.h b/src/nameidmapping.h index 47f424cbc..b200e3e7c 100644 --- a/src/nameidmapping.h +++ b/src/nameidmapping.h @@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include "irrlichttypes_bloated.h" +#include "irrlichttypes.h" typedef std::unordered_map IdToNameMap; typedef std::unordered_map NameToIdMap; diff --git a/src/network/peerhandler.h b/src/network/peerhandler.h index 64e2c85fb..adda995b3 100644 --- a/src/network/peerhandler.h +++ b/src/network/peerhandler.h @@ -19,8 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once -#include "networkprotocol.h" - namespace con { diff --git a/src/network/serveropcodes.h b/src/network/serveropcodes.h index 275270ab9..509d2b4b2 100644 --- a/src/network/serveropcodes.h +++ b/src/network/serveropcodes.h @@ -21,7 +21,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "server.h" -#include "networkprotocol.h" class NetworkPacket; // Note: don't forward-declare Server here (#14324) diff --git a/src/player.h b/src/player.h index dd2be4986..7d92808cf 100644 --- a/src/player.h +++ b/src/player.h @@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include "inventory.h" #include "constants.h" -#include "network/networkprotocol.h" #include "util/basic_macros.h" #include "util/string.h" #include diff --git a/src/remoteplayer.h b/src/remoteplayer.h index e0c7ab744..4923c307d 100644 --- a/src/remoteplayer.h +++ b/src/remoteplayer.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "player.h" #include "skyparams.h" #include "lighting.h" +#include "network/networkprotocol.h" // session_t class PlayerSAO; diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp index 78808792b..bf20f14ba 100644 --- a/src/script/lua_api/l_mainmenu.cpp +++ b/src/script/lua_api/l_mainmenu.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content/subgames.h" #include "mapgen/mapgen.h" #include "settings.h" +#include "clientdynamicinfo.h" #include "client/client.h" #include "client/renderingengine.h" #include "network/networkprotocol.h" From f23d7459b3bc92a2f28295a5674e20df8e7e449d Mon Sep 17 00:00:00 2001 From: DS Date: Mon, 2 Sep 2024 16:09:42 +0200 Subject: [PATCH 63/75] Allow to disable transparency sorting entirely (#15101) --- builtin/settingtypes.txt | 5 +++-- src/client/clientmap.cpp | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index dd193a0c9..d03634506 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1854,8 +1854,9 @@ shader_path (Shader path) path # OpenGL is the default for desktop, and OGLES2 for Android. video_driver (Video driver) enum ,opengl,opengl3,ogles2 -# Distance in nodes at which transparency depth sorting is enabled -# Use this to limit the performance impact of transparency depth sorting +# Distance in nodes at which transparency depth sorting is enabled. +# Use this to limit the performance impact of transparency depth sorting. +# Set to 0 to disable it entirely. transparency_sorting_distance (Transparency Sorting Distance) int 16 0 128 # Radius of cloud area stated in number of 64 node cloud squares. diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 7d40dec92..5e4023569 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -1311,9 +1311,9 @@ void ClientMap::updateTransparentMeshBuffers() ScopeProfiler sp(g_profiler, "CM::updateTransparentMeshBuffers", SPT_AVG); u32 sorted_blocks = 0; u32 unsorted_blocks = 0; + bool transparency_sorting_enabled = m_cache_transparency_sorting_distance > 0; f32 sorting_distance = m_cache_transparency_sorting_distance * BS; - // Update the order of transparent mesh buffers in each mesh for (auto it = m_drawlist.begin(); it != m_drawlist.end(); it++) { MapBlock *block = it->second; @@ -1323,13 +1323,19 @@ void ClientMap::updateTransparentMeshBuffers() if (m_needs_update_transparent_meshes || blockmesh->getTransparentBuffers().size() == 0) { + bool do_sort_block = transparency_sorting_enabled; - v3f mesh_sphere_center = intToFloat(block->getPosRelative(), BS) - + blockmesh->getBoundingSphereCenter(); - f32 mesh_sphere_radius = blockmesh->getBoundingRadius(); - f32 distance_sq = m_camera_position.getDistanceFromSQ(mesh_sphere_center); + if (do_sort_block) { + v3f mesh_sphere_center = intToFloat(block->getPosRelative(), BS) + + blockmesh->getBoundingSphereCenter(); + f32 mesh_sphere_radius = blockmesh->getBoundingRadius(); + f32 distance_sq = m_camera_position.getDistanceFromSQ(mesh_sphere_center); - if (distance_sq <= std::pow(sorting_distance + mesh_sphere_radius, 2.0f)) { + if (distance_sq > std::pow(sorting_distance + mesh_sphere_radius, 2.0f)) + do_sort_block = false; + } + + if (do_sort_block) { blockmesh->updateTransparentBuffers(m_camera_position, block->getPos()); ++sorted_blocks; } else { From 6105804f0020a77f2e3f6ddcf6d300e76abc01b5 Mon Sep 17 00:00:00 2001 From: 1F616EMO~nya Date: Mon, 2 Sep 2024 22:10:01 +0800 Subject: [PATCH 64/75] Show full texture string in "generateImage(): Failed to generate" errors (#15033) --- src/client/imagesource.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/imagesource.cpp b/src/client/imagesource.cpp index adc39f130..bb6668264 100644 --- a/src/client/imagesource.cpp +++ b/src/client/imagesource.cpp @@ -1908,7 +1908,8 @@ video::IImage* ImageSource::generateImage(std::string_view name, video::IImage *tmp = generateImage(name2, source_image_names); if (!tmp) { errorstream << "generateImage(): " - "Failed to generate \"" << name2 << "\"" + "Failed to generate \"" << name2 << "\"\n" + "part of texture \"" << name << "\"" << std::endl; return NULL; } @@ -1923,7 +1924,8 @@ video::IImage* ImageSource::generateImage(std::string_view name, } else if (!generateImagePart(last_part_of_name, baseimg, source_image_names)) { // Generate image according to part of name errorstream << "generateImage(): " - "Failed to generate \"" << last_part_of_name << "\"" + "Failed to generate \"" << last_part_of_name << "\"\n" + "part of texture \"" << name << "\"" << std::endl; } From 538b8b9b3457bb492b2b10a4acf869ad0ca98c26 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 29 Aug 2024 16:01:29 +0200 Subject: [PATCH 65/75] Avoid unsafety with stack-allocated mesh buffer --- src/client/hud.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 007421f7a..1521e62f8 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -154,7 +154,7 @@ Hud::Hud(Client *client, LocalPlayer *player, b.getMaterial().Lighting = false; b.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - b.setHardwareMappingHint(scene::EHM_STATIC); + //b.setHardwareMappingHint(scene::EHM_STATIC); // FIXME: incorrectly stack allocated, not safe! } void Hud::readScalingSetting() From 5d6e15bc49db8b0cd40de9fb4a1a7534071e40f0 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 27 Aug 2024 16:50:41 +0200 Subject: [PATCH 66/75] Split CVertexBuffer from CMeshBuffer --- irr/include/CMeshBuffer.h | 62 +++++++++------- irr/include/CVertexBuffer.h | 122 ++++++++++++++++++++++++++++++++ irr/include/IVertexBuffer.h | 51 +++++++++---- irr/src/CBillboardSceneNode.cpp | 39 +++++----- irr/src/CMeshManipulator.cpp | 6 +- irr/src/COBJMeshFileLoader.cpp | 4 +- src/client/clouds.cpp | 7 +- src/client/hud.cpp | 11 +-- src/client/minimap.cpp | 11 +-- src/client/sky.cpp | 11 +-- 10 files changed, 246 insertions(+), 78 deletions(-) create mode 100644 irr/include/CVertexBuffer.h diff --git a/irr/include/CMeshBuffer.h b/irr/include/CMeshBuffer.h index 0b0c3bb92..a81d668bd 100644 --- a/irr/include/CMeshBuffer.h +++ b/irr/include/CMeshBuffer.h @@ -6,6 +6,7 @@ #include #include "IMeshBuffer.h" +#include "CVertexBuffer.h" namespace irr { @@ -18,11 +19,17 @@ class CMeshBuffer : public IMeshBuffer public: //! Default constructor for empty meshbuffer CMeshBuffer() : - ChangedID_Vertex(1), ChangedID_Index(1), MappingHint_Vertex(EHM_NEVER), MappingHint_Index(EHM_NEVER), HWBuffer(NULL), PrimitiveType(EPT_TRIANGLES) + ChangedID_Index(1), MappingHint_Index(EHM_NEVER), HWBuffer(NULL), PrimitiveType(EPT_TRIANGLES) { #ifdef _DEBUG setDebugName("CMeshBuffer"); #endif + Vertices = new CVertexBuffer(); + } + + ~CMeshBuffer() + { + Vertices->drop(); } //! Get material of this meshbuffer @@ -43,21 +50,27 @@ public: /** \return Pointer to vertices. */ const void *getVertices() const override { - return Vertices.data(); + return Vertices->getData(); } //! Get pointer to vertices /** \return Pointer to vertices. */ void *getVertices() override { - return Vertices.data(); + return Vertices->getData(); } //! Get number of vertices /** \return Number of vertices. */ u32 getVertexCount() const override { - return static_cast(Vertices.size()); + return Vertices->getCount(); + } + + // TEMPORARY helper for direct buffer acess + inline auto &VertexBuffer() + { + return Vertices->Data; } //! Get type of index data which is stored in this meshbuffer. @@ -107,11 +120,11 @@ public: /** should be called if the mesh changed. */ void recalculateBoundingBox() override { - if (!Vertices.empty()) { - BoundingBox.reset(Vertices[0].Pos); - const irr::u32 vsize = Vertices.size(); + if (Vertices->getCount()) { + BoundingBox.reset(Vertices->getPosition(0)); + const irr::u32 vsize = Vertices->getCount(); for (u32 i = 1; i < vsize; ++i) - BoundingBox.addInternalPoint(Vertices[i].Pos); + BoundingBox.addInternalPoint(Vertices->getPosition(i)); } else BoundingBox.reset(0, 0, 0); } @@ -120,43 +133,43 @@ public: /** \return Type of vertex data. */ video::E_VERTEX_TYPE getVertexType() const override { - return T::getType(); + return Vertices->getType(); } //! returns position of vertex i const core::vector3df &getPosition(u32 i) const override { - return Vertices[i].Pos; + return Vertices->getPosition(i); } //! returns position of vertex i core::vector3df &getPosition(u32 i) override { - return Vertices[i].Pos; + return Vertices->getPosition(i); } //! returns normal of vertex i const core::vector3df &getNormal(u32 i) const override { - return Vertices[i].Normal; + return Vertices->getNormal(i); } //! returns normal of vertex i core::vector3df &getNormal(u32 i) override { - return Vertices[i].Normal; + return Vertices->getNormal(i); } //! returns texture coord of vertex i const core::vector2df &getTCoords(u32 i) const override { - return Vertices[i].TCoords; + return Vertices->getTCoords(i); } //! returns texture coord of vertex i core::vector2df &getTCoords(u32 i) override { - return Vertices[i].TCoords; + return Vertices->getTCoords(i); } //! Append the vertices and indices to the current buffer @@ -169,9 +182,9 @@ public: const u32 indexCount = getIndexCount(); auto *vt = static_cast(vertices); - Vertices.insert(Vertices.end(), vt, vt + numVertices); + Vertices->Data.insert(Vertices->Data.end(), vt, vt + numVertices); for (u32 i = vertexCount; i < getVertexCount(); i++) - BoundingBox.addInternalPoint(Vertices[i].Pos); + BoundingBox.addInternalPoint(Vertices->getPosition(i)); Indices.insert(Indices.end(), indices, indices + numIndices); if (vertexCount != 0) { @@ -183,7 +196,7 @@ public: //! get the current hardware mapping hint E_HARDWARE_MAPPING getHardwareMappingHint_Vertex() const override { - return MappingHint_Vertex; + return Vertices->getHardwareMappingHint(); } //! get the current hardware mapping hint @@ -196,7 +209,7 @@ public: void setHardwareMappingHint(E_HARDWARE_MAPPING NewMappingHint, E_BUFFER_TYPE Buffer = EBT_VERTEX_AND_INDEX) override { if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_VERTEX) - MappingHint_Vertex = NewMappingHint; + Vertices->setHardwareMappingHint(NewMappingHint); if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_INDEX) MappingHint_Index = NewMappingHint; } @@ -217,14 +230,14 @@ public: void setDirty(E_BUFFER_TYPE Buffer = EBT_VERTEX_AND_INDEX) override { if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_VERTEX) - ++ChangedID_Vertex; + Vertices->setDirty(); if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_INDEX) ++ChangedID_Index; } //! Get the currently used ID for identification of changes. /** This shouldn't be used for anything outside the VideoDriver. */ - u32 getChangedID_Vertex() const override { return ChangedID_Vertex; } + u32 getChangedID_Vertex() const override { return Vertices->getChangedID(); } //! Get the currently used ID for identification of changes. /** This shouldn't be used for anything outside the VideoDriver. */ @@ -240,18 +253,15 @@ public: return HWBuffer; } - u32 ChangedID_Vertex; u32 ChangedID_Index; - //! hardware mapping hint - E_HARDWARE_MAPPING MappingHint_Vertex; E_HARDWARE_MAPPING MappingHint_Index; mutable void *HWBuffer; //! Material for this meshbuffer. video::SMaterial Material; - //! Vertices of this buffer - std::vector Vertices; + //! Vertex buffer + CVertexBuffer *Vertices; //! Indices into the vertices of this buffer. std::vector Indices; //! Bounding box of this meshbuffer. diff --git a/irr/include/CVertexBuffer.h b/irr/include/CVertexBuffer.h new file mode 100644 index 000000000..6861913bc --- /dev/null +++ b/irr/include/CVertexBuffer.h @@ -0,0 +1,122 @@ +// 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 +#include "IVertexBuffer.h" + +namespace irr +{ +namespace scene +{ +//! Template implementation of the IVertexBuffer interface +template +class CVertexBuffer : public IVertexBuffer +{ +public: + //! Default constructor for empty buffer + CVertexBuffer() + { +#ifdef _DEBUG + setDebugName("CVertexBuffer"); +#endif + } + + const void *getData() const override + { + return Data.data(); + } + + void *getData() override + { + return Data.data(); + } + + u32 getCount() const override + { + return static_cast(Data.size()); + } + + video::E_VERTEX_TYPE getType() const override + { + return T::getType(); + } + + const core::vector3df &getPosition(u32 i) const override + { + return Data[i].Pos; + } + + core::vector3df &getPosition(u32 i) override + { + return Data[i].Pos; + } + + const core::vector3df &getNormal(u32 i) const override + { + return Data[i].Normal; + } + + core::vector3df &getNormal(u32 i) override + { + return Data[i].Normal; + } + + const core::vector2df &getTCoords(u32 i) const override + { + return Data[i].TCoords; + } + + core::vector2df &getTCoords(u32 i) override + { + return Data[i].TCoords; + } + + E_HARDWARE_MAPPING getHardwareMappingHint() const override + { + return MappingHint; + } + + void setHardwareMappingHint(E_HARDWARE_MAPPING NewMappingHint) override + { + MappingHint = NewMappingHint; + } + + void setDirty() override + { + ++ChangedID; + } + + u32 getChangedID() const override { return ChangedID; } + + void setHWBuffer(void *ptr) const override + { + HWBuffer = ptr; + } + + void *getHWBuffer() const override + { + return HWBuffer; + } + + u32 ChangedID = 1; + + //! hardware mapping hint + E_HARDWARE_MAPPING MappingHint = EHM_NEVER; + mutable void *HWBuffer = nullptr; + + //! Vertices of this buffer + std::vector Data; +}; + +//! Standard buffer +typedef CVertexBuffer SVertexBuffer; +//! Buffer with two texture coords per vertex, e.g. for lightmaps +typedef CVertexBuffer SVertexBufferLightMap; +//! Buffer with vertices having tangents stored, e.g. for normal mapping +typedef CVertexBuffer SVertexBufferTangents; + +} // end namespace scene +} // end namespace irr diff --git a/irr/include/IVertexBuffer.h b/irr/include/IVertexBuffer.h index a4ec5efd9..e5be83904 100644 --- a/irr/include/IVertexBuffer.h +++ b/irr/include/IVertexBuffer.h @@ -17,24 +17,47 @@ namespace scene class IVertexBuffer : public virtual IReferenceCounted { public: - virtual void *getData() = 0; + //! Get type of vertex data which is stored in this meshbuffer. + /** \return Vertex type of this buffer. */ virtual video::E_VERTEX_TYPE getType() const = 0; - virtual void setType(video::E_VERTEX_TYPE vertexType) = 0; - virtual u32 stride() const = 0; - virtual u32 size() const = 0; - virtual void push_back(const video::S3DVertex &element) = 0; - virtual video::S3DVertex &operator[](const u32 index) const = 0; - virtual video::S3DVertex &getLast() = 0; - virtual void set_used(u32 usedNow) = 0; - virtual void reallocate(u32 new_size) = 0; - virtual u32 allocated_size() const = 0; - virtual video::S3DVertex *pointer() = 0; + + //! Get access to vertex data. The data is an array of vertices. + /** Which vertex type is used can be determined by getVertexType(). + \return Pointer to array of vertices. */ + virtual const void *getData() const = 0; + + //! Get access to vertex data. The data is an array of vertices. + /** Which vertex type is used can be determined by getVertexType(). + \return Pointer to array of vertices. */ + virtual void *getData() = 0; + + //! Get amount of vertices in meshbuffer. + /** \return Number of vertices in this buffer. */ + virtual u32 getCount() const = 0; + + //! returns position of vertex i + virtual const core::vector3df &getPosition(u32 i) const = 0; + + //! returns position of vertex i + virtual core::vector3df &getPosition(u32 i) = 0; + + //! returns normal of vertex i + virtual const core::vector3df &getNormal(u32 i) const = 0; + + //! returns normal of vertex i + virtual core::vector3df &getNormal(u32 i) = 0; + + //! returns texture coord of vertex i + virtual const core::vector2df &getTCoords(u32 i) const = 0; + + //! returns texture coord of vertex i + virtual core::vector2df &getTCoords(u32 i) = 0; //! get the current hardware mapping hint virtual E_HARDWARE_MAPPING getHardwareMappingHint() const = 0; //! set the hardware mapping hint, for driver - virtual void setHardwareMappingHint(E_HARDWARE_MAPPING NewMappingHint) = 0; + virtual void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint) = 0; //! flags the meshbuffer as changed, reloads hardware buffers virtual void setDirty() = 0; @@ -42,6 +65,10 @@ public: //! Get the currently used ID for identification of changes. /** This shouldn't be used for anything outside the VideoDriver. */ virtual u32 getChangedID() const = 0; + + //! Used by the VideoDriver to remember the buffer link. + virtual void setHWBuffer(void *ptr) const = 0; + virtual void *getHWBuffer() const = 0; }; } // end namespace scene diff --git a/irr/src/CBillboardSceneNode.cpp b/irr/src/CBillboardSceneNode.cpp index 7be139f96..cf9a58818 100644 --- a/irr/src/CBillboardSceneNode.cpp +++ b/irr/src/CBillboardSceneNode.cpp @@ -26,7 +26,9 @@ CBillboardSceneNode::CBillboardSceneNode(ISceneNode *parent, ISceneManager *mgr, setSize(size); - Buffer->Vertices.resize(4); + auto &Vertices = Buffer->Vertices->Data; + + Vertices.resize(4); Buffer->Indices.resize(6); Buffer->Indices[0] = 0; @@ -36,17 +38,17 @@ CBillboardSceneNode::CBillboardSceneNode(ISceneNode *parent, ISceneManager *mgr, Buffer->Indices[4] = 3; Buffer->Indices[5] = 2; - Buffer->Vertices[0].TCoords.set(1.0f, 1.0f); - Buffer->Vertices[0].Color = colorBottom; + Vertices[0].TCoords.set(1.0f, 1.0f); + Vertices[0].Color = colorBottom; - Buffer->Vertices[1].TCoords.set(1.0f, 0.0f); - Buffer->Vertices[1].Color = colorTop; + Vertices[1].TCoords.set(1.0f, 0.0f); + Vertices[1].Color = colorTop; - Buffer->Vertices[2].TCoords.set(0.0f, 0.0f); - Buffer->Vertices[2].Color = colorTop; + Vertices[2].TCoords.set(0.0f, 0.0f); + Vertices[2].Color = colorTop; - Buffer->Vertices[3].TCoords.set(0.0f, 1.0f); - Buffer->Vertices[3].Color = colorBottom; + Vertices[3].TCoords.set(0.0f, 1.0f); + Vertices[3].Color = colorBottom; } CBillboardSceneNode::~CBillboardSceneNode() @@ -114,7 +116,7 @@ void CBillboardSceneNode::updateMesh(const irr::scene::ICameraSceneNode *camera) view *= -1.0f; - auto *vertices = Buffer->Vertices.data(); + auto &vertices = Buffer->Vertices->Data; for (s32 i = 0; i < 4; ++i) vertices[i].Normal = view; @@ -211,8 +213,9 @@ void CBillboardSceneNode::getSize(f32 &height, f32 &bottomEdgeWidth, //! \param overallColor: the color to set void CBillboardSceneNode::setColor(const video::SColor &overallColor) { + auto &vertices = Buffer->Vertices->Data; for (u32 vertex = 0; vertex < 4; ++vertex) - Buffer->Vertices[vertex].Color = overallColor; + vertices[vertex].Color = overallColor; } //! Set the color of the top and bottom vertices of the billboard @@ -221,10 +224,11 @@ void CBillboardSceneNode::setColor(const video::SColor &overallColor) void CBillboardSceneNode::setColor(const video::SColor &topColor, const video::SColor &bottomColor) { - Buffer->Vertices[0].Color = bottomColor; - Buffer->Vertices[1].Color = topColor; - Buffer->Vertices[2].Color = topColor; - Buffer->Vertices[3].Color = bottomColor; + auto &vertices = Buffer->Vertices->Data; + vertices[0].Color = bottomColor; + vertices[1].Color = topColor; + vertices[2].Color = topColor; + vertices[3].Color = bottomColor; } //! Gets the color of the top and bottom vertices of the billboard @@ -233,8 +237,9 @@ void CBillboardSceneNode::setColor(const video::SColor &topColor, void CBillboardSceneNode::getColor(video::SColor &topColor, video::SColor &bottomColor) const { - bottomColor = Buffer->Vertices[0].Color; - topColor = Buffer->Vertices[1].Color; + auto &vertices = Buffer->Vertices->Data; + bottomColor = vertices[0].Color; + topColor = vertices[1].Color; } //! Creates a clone of this scene node and its children. diff --git a/irr/src/CMeshManipulator.cpp b/irr/src/CMeshManipulator.cpp index db6c39812..5abaffec8 100644 --- a/irr/src/CMeshManipulator.cpp +++ b/irr/src/CMeshManipulator.cpp @@ -133,7 +133,7 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const SMeshBuffer *buffer = new SMeshBuffer(); buffer->Material = mb->getMaterial(); auto *vt = static_cast(mb->getVertices()); - buffer->Vertices.insert(buffer->Vertices.end(), vt, vt + mb->getVertexCount()); + buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); auto *indices = mb->getIndices(); buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); @@ -143,7 +143,7 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const SMeshBufferLightMap *buffer = new SMeshBufferLightMap(); buffer->Material = mb->getMaterial(); auto *vt = static_cast(mb->getVertices()); - buffer->Vertices.insert(buffer->Vertices.end(), vt, vt + mb->getVertexCount()); + buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); auto *indices = mb->getIndices(); buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); @@ -153,7 +153,7 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const SMeshBufferTangents *buffer = new SMeshBufferTangents(); buffer->Material = mb->getMaterial(); auto *vt = static_cast(mb->getVertices()); - buffer->Vertices.insert(buffer->Vertices.end(), vt, vt + mb->getVertexCount()); + buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); auto *indices = mb->getIndices(); buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); diff --git a/irr/src/COBJMeshFileLoader.cpp b/irr/src/COBJMeshFileLoader.cpp index 4c5a5328d..ca382c418 100644 --- a/irr/src/COBJMeshFileLoader.cpp +++ b/irr/src/COBJMeshFileLoader.cpp @@ -228,8 +228,8 @@ IAnimatedMesh *COBJMeshFileLoader::createMesh(io::IReadFile *file) if (n != currMtl->VertMap.end()) { vertLocation = n->second; } else { - currMtl->Meshbuffer->Vertices.push_back(v); - vertLocation = currMtl->Meshbuffer->Vertices.size() - 1; + currMtl->Meshbuffer->VertexBuffer().push_back(v); + vertLocation = currMtl->Meshbuffer->VertexBuffer().size() - 1; currMtl->VertMap.emplace(v, vertLocation); } diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index ebb8b9000..4b91b04d3 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -178,13 +178,14 @@ void Clouds::updateMesh() auto *mb = m_meshbuffer.get(); + auto &vertices = mb->Vertices->Data; { const u32 vertex_count = num_faces_to_draw * 16 * m_cloud_radius_i * m_cloud_radius_i; const u32 quad_count = vertex_count / 4; const u32 index_count = quad_count * 6; // reserve memory - mb->Vertices.reserve(vertex_count); + vertices.reserve(vertex_count); mb->Indices.reserve(index_count); } @@ -192,7 +193,7 @@ void Clouds::updateMesh() #define INAREA(x, z, radius) \ ((x) >= -(radius) && (x) < (radius) && (z) >= -(radius) && (z) < (radius)) - mb->Vertices.clear(); + vertices.clear(); for (s16 zi0= -m_cloud_radius_i; zi0 < m_cloud_radius_i; zi0++) for (s16 xi0= -m_cloud_radius_i; xi0 < m_cloud_radius_i; xi0++) { @@ -312,7 +313,7 @@ void Clouds::updateMesh() for (video::S3DVertex &vertex : v) { vertex.Pos += pos; - mb->Vertices.push_back(vertex); + vertices.push_back(vertex); } } } diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 1521e62f8..128c3f9d9 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -134,16 +134,17 @@ Hud::Hud(Client *client, LocalPlayer *player, // Prepare mesh for compass drawing auto &b = m_rotation_mesh_buffer; - b.Vertices.resize(4); + auto &vertices = b.Vertices->Data; + vertices.resize(4); b.Indices.resize(6); video::SColor white(255, 255, 255, 255); v3f normal(0.f, 0.f, 1.f); - b.Vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f)); - b.Vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f)); - b.Vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f)); - b.Vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f)); + vertices[0] = video::S3DVertex(v3f(-1.f, -1.f, 0.f), normal, white, v2f(0.f, 1.f)); + vertices[1] = video::S3DVertex(v3f(-1.f, 1.f, 0.f), normal, white, v2f(0.f, 0.f)); + vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f)); + vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f)); b.Indices[0] = 0; b.Indices[1] = 1; diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 9f1f04359..2368057fc 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -555,14 +555,15 @@ v3f Minimap::getYawVec() scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() { scene::SMeshBuffer *buf = new scene::SMeshBuffer(); - buf->Vertices.resize(4); + auto &vertices = buf->Vertices->Data; + vertices.resize(4); buf->Indices.resize(6); static const video::SColor c(255, 255, 255, 255); - buf->Vertices[0] = video::S3DVertex(-1, -1, 0, 0, 0, 1, c, 0, 1); - buf->Vertices[1] = video::S3DVertex(-1, 1, 0, 0, 0, 1, c, 0, 0); - buf->Vertices[2] = video::S3DVertex( 1, 1, 0, 0, 0, 1, c, 1, 0); - buf->Vertices[3] = video::S3DVertex( 1, -1, 0, 0, 0, 1, c, 1, 1); + vertices[0] = video::S3DVertex(-1, -1, 0, 0, 0, 1, c, 0, 1); + vertices[1] = video::S3DVertex(-1, 1, 0, 0, 0, 1, c, 0, 0); + vertices[2] = video::S3DVertex( 1, 1, 0, 0, 0, 1, c, 1, 0); + vertices[3] = video::S3DVertex( 1, -1, 0, 0, 0, 1, c, 1, 1); buf->Indices[0] = 0; buf->Indices[1] = 1; diff --git a/src/client/sky.cpp b/src/client/sky.cpp index c68d13984..19b9765a5 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -830,7 +830,8 @@ void Sky::updateStars() warningstream << "Requested " << m_star_params.count << " stars but " << 0x4000 << " is the max\n"; m_star_params.count = 0x4000; } - m_stars->Vertices.reserve(4 * m_star_params.count); + auto &vertices = m_stars->Vertices->Data; + vertices.reserve(4 * m_star_params.count); m_stars->Indices.reserve(6 * m_star_params.count); video::SColor fallback_color = m_star_params.starcolor; // used on GLES 2 “without shaders” @@ -852,10 +853,10 @@ void Sky::updateStars() a.rotateVect(p1); a.rotateVect(p2); a.rotateVect(p3); - m_stars->Vertices.push_back(video::S3DVertex(p, {}, fallback_color, {})); - m_stars->Vertices.push_back(video::S3DVertex(p1, {}, fallback_color, {})); - m_stars->Vertices.push_back(video::S3DVertex(p2, {}, fallback_color, {})); - m_stars->Vertices.push_back(video::S3DVertex(p3, {}, fallback_color, {})); + vertices.push_back(video::S3DVertex(p, {}, fallback_color, {})); + vertices.push_back(video::S3DVertex(p1, {}, fallback_color, {})); + vertices.push_back(video::S3DVertex(p2, {}, fallback_color, {})); + vertices.push_back(video::S3DVertex(p3, {}, fallback_color, {})); } for (u16 i = 0; i < m_star_params.count; i++) { m_stars->Indices.push_back(i * 4 + 0); From 47e4c33a505baec177d3a6a3e8e3d234b549c08d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 27 Aug 2024 17:40:29 +0200 Subject: [PATCH 67/75] Split CIndexBuffer from CMeshBuffer --- irr/include/CIndexBuffer.h | 89 +++++++++++++++++++++++++++++++++ irr/include/CMeshBuffer.h | 38 ++++++++------ irr/include/IIndexBuffer.h | 39 +++++++-------- irr/src/CBillboardSceneNode.cpp | 15 +++--- irr/src/CMeshManipulator.cpp | 6 +-- irr/src/COBJMeshFileLoader.cpp | 7 +-- src/client/clientmap.cpp | 7 +-- src/client/clouds.cpp | 17 ++++--- src/client/hud.cpp | 15 +++--- src/client/mapblock_mesh.cpp | 18 +++---- src/client/mapblock_mesh.h | 20 ++++---- src/client/minimap.cpp | 15 +++--- src/client/sky.cpp | 15 +++--- 13 files changed, 197 insertions(+), 104 deletions(-) create mode 100644 irr/include/CIndexBuffer.h diff --git a/irr/include/CIndexBuffer.h b/irr/include/CIndexBuffer.h new file mode 100644 index 000000000..4701c2e34 --- /dev/null +++ b/irr/include/CIndexBuffer.h @@ -0,0 +1,89 @@ +// 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 +#include "IIndexBuffer.h" + +namespace irr +{ +namespace scene +{ +//! Template implementation of the IIndexBuffer interface +template +class CIndexBuffer : public IIndexBuffer +{ +public: + //! Default constructor for empty buffer + CIndexBuffer() + { +#ifdef _DEBUG + setDebugName("CIndexBuffer"); +#endif + } + + video::E_INDEX_TYPE getType() const + { + static_assert(sizeof(T) == 2 || sizeof(T) == 4, "invalid index type"); + return sizeof(T) == 2 ? video::EIT_16BIT : video::EIT_32BIT; + } + + const void *getData() const override + { + return Data.data(); + } + + void *getData() override + { + return Data.data(); + } + + u32 getCount() const override + { + return static_cast(Data.size()); + } + + E_HARDWARE_MAPPING getHardwareMappingHint() const override + { + return MappingHint; + } + + void setHardwareMappingHint(E_HARDWARE_MAPPING NewMappingHint) override + { + MappingHint = NewMappingHint; + } + + void setDirty() override + { + ++ChangedID; + } + + u32 getChangedID() const override { return ChangedID; } + + void setHWBuffer(void *ptr) const override + { + HWBuffer = ptr; + } + + void *getHWBuffer() const override + { + return HWBuffer; + } + + u32 ChangedID = 1; + + //! hardware mapping hint + E_HARDWARE_MAPPING MappingHint = EHM_NEVER; + mutable void *HWBuffer = nullptr; + + //! Indices of this buffer + std::vector Data; +}; + +//! Standard 16-bit buffer +typedef CIndexBuffer SIndexBuffer; + +} // end namespace scene +} // end namespace irr diff --git a/irr/include/CMeshBuffer.h b/irr/include/CMeshBuffer.h index a81d668bd..0b47494cd 100644 --- a/irr/include/CMeshBuffer.h +++ b/irr/include/CMeshBuffer.h @@ -7,6 +7,7 @@ #include #include "IMeshBuffer.h" #include "CVertexBuffer.h" +#include "CIndexBuffer.h" namespace irr { @@ -19,17 +20,19 @@ class CMeshBuffer : public IMeshBuffer public: //! Default constructor for empty meshbuffer CMeshBuffer() : - ChangedID_Index(1), MappingHint_Index(EHM_NEVER), HWBuffer(NULL), PrimitiveType(EPT_TRIANGLES) + HWBuffer(NULL), PrimitiveType(EPT_TRIANGLES) { #ifdef _DEBUG setDebugName("CMeshBuffer"); #endif Vertices = new CVertexBuffer(); + Indices = new SIndexBuffer(); } ~CMeshBuffer() { Vertices->drop(); + Indices->drop(); } //! Get material of this meshbuffer @@ -77,28 +80,34 @@ public: /** \return Index type of this buffer. */ video::E_INDEX_TYPE getIndexType() const override { - return video::EIT_16BIT; + return Indices->getType(); } //! Get pointer to indices /** \return Pointer to indices. */ const u16 *getIndices() const override { - return Indices.data(); + return static_cast(Indices->getData()); } //! Get pointer to indices /** \return Pointer to indices. */ u16 *getIndices() override { - return Indices.data(); + return static_cast(Indices->getData()); } //! Get number of indices /** \return Number of indices. */ u32 getIndexCount() const override { - return static_cast(Indices.size()); + return Indices->getCount(); + } + + // TEMPORARY helper for direct buffer acess + inline auto &IndexBuffer() + { + return Indices->Data; } //! Get the axis aligned bounding box @@ -186,10 +195,10 @@ public: for (u32 i = vertexCount; i < getVertexCount(); i++) BoundingBox.addInternalPoint(Vertices->getPosition(i)); - Indices.insert(Indices.end(), indices, indices + numIndices); + Indices->Data.insert(Indices->Data.end(), indices, indices + numIndices); if (vertexCount != 0) { for (u32 i = indexCount; i < getIndexCount(); i++) - Indices[i] += vertexCount; + Indices->Data[i] += vertexCount; } } @@ -202,7 +211,7 @@ public: //! get the current hardware mapping hint E_HARDWARE_MAPPING getHardwareMappingHint_Index() const override { - return MappingHint_Index; + return Indices->getHardwareMappingHint(); } //! set the hardware mapping hint, for driver @@ -211,7 +220,7 @@ public: if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_VERTEX) Vertices->setHardwareMappingHint(NewMappingHint); if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_INDEX) - MappingHint_Index = NewMappingHint; + Indices->setHardwareMappingHint(NewMappingHint); } //! Describe what kind of primitive geometry is used by the meshbuffer @@ -232,7 +241,7 @@ public: if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_VERTEX) Vertices->setDirty(); if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_INDEX) - ++ChangedID_Index; + Indices->setDirty(); } //! Get the currently used ID for identification of changes. @@ -241,7 +250,7 @@ public: //! Get the currently used ID for identification of changes. /** This shouldn't be used for anything outside the VideoDriver. */ - u32 getChangedID_Index() const override { return ChangedID_Index; } + u32 getChangedID_Index() const override { return Indices->getChangedID(); } void setHWBuffer(void *ptr) const override { @@ -253,17 +262,14 @@ public: return HWBuffer; } - u32 ChangedID_Index; - - E_HARDWARE_MAPPING MappingHint_Index; mutable void *HWBuffer; //! Material for this meshbuffer. video::SMaterial Material; //! Vertex buffer CVertexBuffer *Vertices; - //! Indices into the vertices of this buffer. - std::vector Indices; + //! Index buffer + SIndexBuffer *Indices; //! Bounding box of this meshbuffer. core::aabbox3d BoundingBox; //! Primitive type used for rendering (triangles, lines, ...) diff --git a/irr/include/IIndexBuffer.h b/irr/include/IIndexBuffer.h index 3d5b8e76a..bdbbb6dcc 100644 --- a/irr/include/IIndexBuffer.h +++ b/irr/include/IIndexBuffer.h @@ -12,40 +12,33 @@ namespace irr { -namespace video -{ - -} - namespace scene { class IIndexBuffer : public virtual IReferenceCounted { public: + //! Get type of index data which is stored in this meshbuffer. + /** \return Index type of this buffer. */ + virtual video::E_INDEX_TYPE getType() const = 0; + + //! Get access to indices. + /** \return Pointer to indices array. */ + virtual const void *getData() const = 0; + + //! Get access to indices. + /** \return Pointer to indices array. */ virtual void *getData() = 0; - virtual video::E_INDEX_TYPE getType() const = 0; - virtual void setType(video::E_INDEX_TYPE IndexType) = 0; - - virtual u32 stride() const = 0; - - virtual u32 size() const = 0; - virtual void push_back(const u32 &element) = 0; - virtual u32 operator[](u32 index) const = 0; - virtual u32 getLast() = 0; - virtual void setValue(u32 index, u32 value) = 0; - virtual void set_used(u32 usedNow) = 0; - virtual void reallocate(u32 new_size) = 0; - virtual u32 allocated_size() const = 0; - - virtual void *pointer() = 0; + //! Get amount of indices in this meshbuffer. + /** \return Number of indices in this buffer. */ + virtual u32 getCount() const = 0; //! get the current hardware mapping hint virtual E_HARDWARE_MAPPING getHardwareMappingHint() const = 0; //! set the hardware mapping hint, for driver - virtual void setHardwareMappingHint(E_HARDWARE_MAPPING NewMappingHint) = 0; + virtual void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint) = 0; //! flags the meshbuffer as changed, reloads hardware buffers virtual void setDirty() = 0; @@ -53,6 +46,10 @@ public: //! Get the currently used ID for identification of changes. /** This shouldn't be used for anything outside the VideoDriver. */ virtual u32 getChangedID() const = 0; + + //! Used by the VideoDriver to remember the buffer link. + virtual void setHWBuffer(void *ptr) const = 0; + virtual void *getHWBuffer() const = 0; }; } // end namespace scene diff --git a/irr/src/CBillboardSceneNode.cpp b/irr/src/CBillboardSceneNode.cpp index cf9a58818..db75a2af4 100644 --- a/irr/src/CBillboardSceneNode.cpp +++ b/irr/src/CBillboardSceneNode.cpp @@ -27,16 +27,17 @@ CBillboardSceneNode::CBillboardSceneNode(ISceneNode *parent, ISceneManager *mgr, setSize(size); auto &Vertices = Buffer->Vertices->Data; + auto &Indices = Buffer->Indices->Data; Vertices.resize(4); - Buffer->Indices.resize(6); + Indices.resize(6); - Buffer->Indices[0] = 0; - Buffer->Indices[1] = 2; - Buffer->Indices[2] = 1; - Buffer->Indices[3] = 0; - Buffer->Indices[4] = 3; - Buffer->Indices[5] = 2; + Indices[0] = 0; + Indices[1] = 2; + Indices[2] = 1; + Indices[3] = 0; + Indices[4] = 3; + Indices[5] = 2; Vertices[0].TCoords.set(1.0f, 1.0f); Vertices[0].Color = colorBottom; diff --git a/irr/src/CMeshManipulator.cpp b/irr/src/CMeshManipulator.cpp index 5abaffec8..9b2bd0cde 100644 --- a/irr/src/CMeshManipulator.cpp +++ b/irr/src/CMeshManipulator.cpp @@ -135,7 +135,7 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const auto *vt = static_cast(mb->getVertices()); buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); auto *indices = mb->getIndices(); - buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); + buffer->IndexBuffer().insert(buffer->IndexBuffer().end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); buffer->drop(); } break; @@ -145,7 +145,7 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const auto *vt = static_cast(mb->getVertices()); buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); auto *indices = mb->getIndices(); - buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); + buffer->IndexBuffer().insert(buffer->IndexBuffer().end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); buffer->drop(); } break; @@ -155,7 +155,7 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const auto *vt = static_cast(mb->getVertices()); buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); auto *indices = mb->getIndices(); - buffer->Indices.insert(buffer->Indices.end(), indices, indices + mb->getIndexCount()); + buffer->IndexBuffer().insert(buffer->IndexBuffer().end(), indices, indices + mb->getIndexCount()); clone->addMeshBuffer(buffer); buffer->drop(); } break; diff --git a/irr/src/COBJMeshFileLoader.cpp b/irr/src/COBJMeshFileLoader.cpp index ca382c418..064fc4186 100644 --- a/irr/src/COBJMeshFileLoader.cpp +++ b/irr/src/COBJMeshFileLoader.cpp @@ -247,15 +247,16 @@ IAnimatedMesh *COBJMeshFileLoader::createMesh(io::IReadFile *file) } // triangulate the face + auto &Indices = currMtl->Meshbuffer->IndexBuffer(); const int c = faceCorners[0]; for (u32 i = 1; i < faceCorners.size() - 1; ++i) { // Add a triangle const int a = faceCorners[i + 1]; const int b = faceCorners[i]; if (a != b && a != c && b != c) { // ignore degenerated faces. We can get them when we merge vertices above in the VertMap. - currMtl->Meshbuffer->Indices.push_back(a); - currMtl->Meshbuffer->Indices.push_back(b); - currMtl->Meshbuffer->Indices.push_back(c); + Indices.push_back(a); + Indices.push_back(b); + Indices.push_back(c); } else { ++degeneratedFaces; } diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index 5e4023569..b4356dc27 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -1358,11 +1358,8 @@ video::SMaterial &ClientMap::DrawDescriptor::getMaterial() u32 ClientMap::DrawDescriptor::draw(video::IVideoDriver* driver) { if (m_use_partial_buffer) { - m_partial_buffer->beforeDraw(); - driver->drawMeshBuffer(m_partial_buffer->getBuffer()); - auto count = m_partial_buffer->getBuffer()->getVertexCount(); - m_partial_buffer->afterDraw(); - return count; + m_partial_buffer->draw(driver); + return m_partial_buffer->getBuffer()->getVertexCount(); } else { driver->drawMeshBuffer(m_buffer); return m_buffer->getVertexCount(); diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index 4b91b04d3..64e4b775a 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -179,6 +179,7 @@ void Clouds::updateMesh() auto *mb = m_meshbuffer.get(); auto &vertices = mb->Vertices->Data; + auto &indices = mb->Indices->Data; { const u32 vertex_count = num_faces_to_draw * 16 * m_cloud_radius_i * m_cloud_radius_i; const u32 quad_count = vertex_count / 4; @@ -186,7 +187,7 @@ void Clouds::updateMesh() // reserve memory vertices.reserve(vertex_count); - mb->Indices.reserve(index_count); + indices.reserve(index_count); } #define GETINDEX(x, z, radius) (((z)+(radius))*(radius)*2 + (x)+(radius)) @@ -323,18 +324,18 @@ void Clouds::updateMesh() const u32 index_count = quad_count * 6; // rewrite index array as needed if (mb->getIndexCount() > index_count) { - mb->Indices.resize(index_count); + indices.resize(index_count); mb->setDirty(scene::EBT_INDEX); } else if (mb->getIndexCount() < index_count) { const u32 start = mb->getIndexCount() / 6; assert(start * 6 == mb->getIndexCount()); for (u32 k = start; k < quad_count; k++) { - mb->Indices.push_back(4 * k + 0); - mb->Indices.push_back(4 * k + 1); - mb->Indices.push_back(4 * k + 2); - mb->Indices.push_back(4 * k + 2); - mb->Indices.push_back(4 * k + 3); - mb->Indices.push_back(4 * k + 0); + indices.push_back(4 * k + 0); + indices.push_back(4 * k + 1); + indices.push_back(4 * k + 2); + indices.push_back(4 * k + 2); + indices.push_back(4 * k + 3); + indices.push_back(4 * k + 0); } mb->setDirty(scene::EBT_INDEX); } diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 128c3f9d9..52fbfb6b4 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -135,8 +135,9 @@ Hud::Hud(Client *client, LocalPlayer *player, // Prepare mesh for compass drawing auto &b = m_rotation_mesh_buffer; auto &vertices = b.Vertices->Data; + auto &indices = b.Indices->Data; vertices.resize(4); - b.Indices.resize(6); + indices.resize(6); video::SColor white(255, 255, 255, 255); v3f normal(0.f, 0.f, 1.f); @@ -146,12 +147,12 @@ Hud::Hud(Client *client, LocalPlayer *player, vertices[2] = video::S3DVertex(v3f( 1.f, 1.f, 0.f), normal, white, v2f(1.f, 0.f)); vertices[3] = video::S3DVertex(v3f( 1.f, -1.f, 0.f), normal, white, v2f(1.f, 1.f)); - b.Indices[0] = 0; - b.Indices[1] = 1; - b.Indices[2] = 2; - b.Indices[3] = 2; - b.Indices[4] = 3; - b.Indices[5] = 0; + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 2; + indices[4] = 3; + indices[5] = 0; b.getMaterial().Lighting = false; b.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 0638c34c9..59883d052 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -592,17 +592,14 @@ void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector &output PartialMeshBuffer */ -void PartialMeshBuffer::beforeDraw() const +void PartialMeshBuffer::draw(video::IVideoDriver *driver) const { - // Patch the indexes in the mesh buffer before draw - m_buffer->Indices = std::move(m_vertex_indexes); - m_buffer->setDirty(scene::EBT_INDEX); -} - -void PartialMeshBuffer::afterDraw() const -{ - // Take the data back - m_vertex_indexes = std::move(m_buffer->Indices); + // Swap out the index buffer before drawing the mesh + auto *old = m_buffer->Indices; + m_buffer->Indices = m_indices.get(); + m_buffer->setDirty(scene::EBT_INDEX); // TODO remove + driver->drawMeshBuffer(m_buffer); + m_buffer->Indices = old; } /* @@ -789,6 +786,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs } // Transparent parts have changing indices + // TODO: remove for (auto &it : m_transparent_triangles) it.buffer->setHardwareMappingHint(scene::EHM_STREAM, scene::EBT_INDEX); diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index 6ddd988aa..e958814f3 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "irrlichttypes_extrabloated.h" +#include "irr_ptr.h" #include "util/numeric.h" #include "client/tile.h" #include "voxel.h" @@ -145,25 +146,24 @@ private: * Attach alternate `Indices` to an existing mesh buffer, to make it possible to use different * indices with the same vertex buffer. * - * Irrlicht does not currently support this: `CMeshBuffer` ties together a single vertex buffer - * and a single index buffer. There's no way to share these between mesh buffers. - * */ class PartialMeshBuffer { public: - PartialMeshBuffer(scene::SMeshBuffer *buffer, std::vector &&vertex_indexes) : - m_buffer(buffer), m_vertex_indexes(std::move(vertex_indexes)) - {} + PartialMeshBuffer(scene::SMeshBuffer *buffer, std::vector &&vertex_indices) : + m_buffer(buffer) + { + m_indices.reset(new scene::SIndexBuffer()); + m_indices->Data = std::move(vertex_indices); + } scene::IMeshBuffer *getBuffer() const { return m_buffer; } - const std::vector &getVertexIndexes() const { return m_vertex_indexes; } - void beforeDraw() const; - void afterDraw() const; + void draw(video::IVideoDriver *driver) const; + private: scene::SMeshBuffer *m_buffer; - mutable std::vector m_vertex_indexes; + irr_ptr m_indices; }; /* diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 2368057fc..b2fc0882d 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -556,8 +556,9 @@ scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() { scene::SMeshBuffer *buf = new scene::SMeshBuffer(); auto &vertices = buf->Vertices->Data; + auto &indices = buf->Indices->Data; vertices.resize(4); - buf->Indices.resize(6); + indices.resize(6); static const video::SColor c(255, 255, 255, 255); vertices[0] = video::S3DVertex(-1, -1, 0, 0, 0, 1, c, 0, 1); @@ -565,12 +566,12 @@ scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() vertices[2] = video::S3DVertex( 1, 1, 0, 0, 0, 1, c, 1, 0); vertices[3] = video::S3DVertex( 1, -1, 0, 0, 0, 1, c, 1, 1); - buf->Indices[0] = 0; - buf->Indices[1] = 1; - buf->Indices[2] = 2; - buf->Indices[3] = 2; - buf->Indices[4] = 3; - buf->Indices[5] = 0; + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + indices[3] = 2; + indices[4] = 3; + indices[5] = 0; buf->setHardwareMappingHint(scene::EHM_STATIC); return buf; diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 19b9765a5..f309d444c 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -831,8 +831,9 @@ void Sky::updateStars() m_star_params.count = 0x4000; } auto &vertices = m_stars->Vertices->Data; + auto &indices = m_stars->Indices->Data; vertices.reserve(4 * m_star_params.count); - m_stars->Indices.reserve(6 * m_star_params.count); + indices.reserve(6 * m_star_params.count); video::SColor fallback_color = m_star_params.starcolor; // used on GLES 2 “without shaders” PcgRandom rgen(m_seed); @@ -859,12 +860,12 @@ void Sky::updateStars() vertices.push_back(video::S3DVertex(p3, {}, fallback_color, {})); } for (u16 i = 0; i < m_star_params.count; i++) { - m_stars->Indices.push_back(i * 4 + 0); - m_stars->Indices.push_back(i * 4 + 1); - m_stars->Indices.push_back(i * 4 + 2); - m_stars->Indices.push_back(i * 4 + 2); - m_stars->Indices.push_back(i * 4 + 3); - m_stars->Indices.push_back(i * 4 + 0); + indices.push_back(i * 4 + 0); + indices.push_back(i * 4 + 1); + indices.push_back(i * 4 + 2); + indices.push_back(i * 4 + 2); + indices.push_back(i * 4 + 3); + indices.push_back(i * 4 + 0); } m_stars->setHardwareMappingHint(scene::EHM_STATIC); } From 435a89b5a4b893cbcc2a9d417fd5ac84c2caad25 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 28 Aug 2024 17:59:53 +0200 Subject: [PATCH 68/75] Apply same changes to SSkinMeshBuffer --- irr/include/SSkinMeshBuffer.h | 248 +++++++++++++++------------------ irr/src/CB3DMeshFileLoader.cpp | 27 ++-- irr/src/CXMeshFileLoader.cpp | 18 +-- 3 files changed, 135 insertions(+), 158 deletions(-) diff --git a/irr/include/SSkinMeshBuffer.h b/irr/include/SSkinMeshBuffer.h index 2b71cf6c5..abe077076 100644 --- a/irr/include/SSkinMeshBuffer.h +++ b/irr/include/SSkinMeshBuffer.h @@ -5,6 +5,8 @@ #pragma once #include "IMeshBuffer.h" +#include "CVertexBuffer.h" +#include "CIndexBuffer.h" #include "S3DVertex.h" #include "irrArray.h" @@ -18,18 +20,32 @@ struct SSkinMeshBuffer : public IMeshBuffer { //! Default constructor SSkinMeshBuffer(video::E_VERTEX_TYPE vt = video::EVT_STANDARD) : - ChangedID_Vertex(1), ChangedID_Index(1), VertexType(vt), - PrimitiveType(EPT_TRIANGLES), HWBuffer(nullptr), - MappingHint_Vertex(EHM_NEVER), MappingHint_Index(EHM_NEVER), - BoundingBoxNeedsRecalculated(true) - {} + VertexType(vt), PrimitiveType(EPT_TRIANGLES), + HWBuffer(nullptr), BoundingBoxNeedsRecalculated(true) + { +#ifdef _DEBUG + setDebugName("SSkinMeshBuffer"); +#endif + Vertices_Tangents = new SVertexBufferTangents(); + Vertices_2TCoords = new SVertexBufferLightMap(); + Vertices_Standard = new SVertexBuffer(); + Indices = new SIndexBuffer(); + } //! Constructor for standard vertices SSkinMeshBuffer(std::vector &&vertices, std::vector &&indices) : SSkinMeshBuffer() { - Vertices_Standard = std::move(vertices); - Indices = std::move(indices); + Vertices_Standard->Data = std::move(vertices); + Indices->Data = std::move(indices); + } + + ~SSkinMeshBuffer() + { + Vertices_Tangents->drop(); + Vertices_2TCoords->drop(); + Vertices_Standard->drop(); + Indices->drop(); } //! Get Material of this buffer. @@ -44,81 +60,86 @@ struct SSkinMeshBuffer : public IMeshBuffer return Material; } +protected: + const scene::IVertexBuffer *getVertexBuffer() const + { + switch (VertexType) { + case video::EVT_2TCOORDS: + return Vertices_2TCoords; + case video::EVT_TANGENTS: + return Vertices_Tangents; + default: + return Vertices_Standard; + } + } + + scene::IVertexBuffer *getVertexBuffer() + { + switch (VertexType) { + case video::EVT_2TCOORDS: + return Vertices_2TCoords; + case video::EVT_TANGENTS: + return Vertices_Tangents; + default: + return Vertices_Standard; + } + } +public: + //! Get standard vertex at given index virtual video::S3DVertex *getVertex(u32 index) { switch (VertexType) { case video::EVT_2TCOORDS: - return (video::S3DVertex *)&Vertices_2TCoords[index]; + return &Vertices_2TCoords->Data[index]; case video::EVT_TANGENTS: - return (video::S3DVertex *)&Vertices_Tangents[index]; + return &Vertices_Tangents->Data[index]; default: - return &Vertices_Standard[index]; + return &Vertices_Standard->Data[index]; } } //! Get pointer to vertex array const void *getVertices() const override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return Vertices_2TCoords.data(); - case video::EVT_TANGENTS: - return Vertices_Tangents.data(); - default: - return Vertices_Standard.data(); - } + return getVertexBuffer()->getData(); } //! Get pointer to vertex array void *getVertices() override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return Vertices_2TCoords.data(); - case video::EVT_TANGENTS: - return Vertices_Tangents.data(); - default: - return Vertices_Standard.data(); - } + return getVertexBuffer()->getData(); } //! Get vertex count u32 getVertexCount() const override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return static_cast(Vertices_2TCoords.size()); - case video::EVT_TANGENTS: - return static_cast(Vertices_Tangents.size()); - default: - return static_cast(Vertices_Standard.size()); - } + return getVertexBuffer()->getCount(); } //! Get type of index data which is stored in this meshbuffer. /** \return Index type of this buffer. */ video::E_INDEX_TYPE getIndexType() const override { - return video::EIT_16BIT; + return Indices->getType(); } //! Get pointer to index array const u16 *getIndices() const override { - return Indices.data(); + return static_cast(Indices->getData()); } //! Get pointer to index array u16 *getIndices() override { - return Indices.data(); + return static_cast(Indices->getData()); } //! Get index count u32 getIndexCount() const override { - return static_cast(Indices.size()); + return Indices->getCount(); } //! Get bounding box @@ -143,32 +164,35 @@ struct SSkinMeshBuffer : public IMeshBuffer switch (VertexType) { case video::EVT_STANDARD: { - if (Vertices_Standard.empty()) + if (!Vertices_Standard->getCount()) BoundingBox.reset(0, 0, 0); else { - BoundingBox.reset(Vertices_Standard[0].Pos); - for (size_t i = 1; i < Vertices_Standard.size(); ++i) - BoundingBox.addInternalPoint(Vertices_Standard[i].Pos); + auto &vertices = Vertices_Standard->Data; + BoundingBox.reset(vertices[0].Pos); + for (size_t i = 1; i < vertices.size(); ++i) + BoundingBox.addInternalPoint(vertices[i].Pos); } break; } case video::EVT_2TCOORDS: { - if (Vertices_2TCoords.empty()) + if (!Vertices_2TCoords->getCount()) BoundingBox.reset(0, 0, 0); else { - BoundingBox.reset(Vertices_2TCoords[0].Pos); - for (size_t i = 1; i < Vertices_2TCoords.size(); ++i) - BoundingBox.addInternalPoint(Vertices_2TCoords[i].Pos); + auto &vertices = Vertices_2TCoords->Data; + BoundingBox.reset(vertices[0].Pos); + for (size_t i = 1; i < vertices.size(); ++i) + BoundingBox.addInternalPoint(vertices[i].Pos); } break; } case video::EVT_TANGENTS: { - if (Vertices_Tangents.empty()) + if (!Vertices_Tangents->getCount()) BoundingBox.reset(0, 0, 0); else { - BoundingBox.reset(Vertices_Tangents[0].Pos); - for (size_t i = 1; i < Vertices_Tangents.size(); ++i) - BoundingBox.addInternalPoint(Vertices_Tangents[i].Pos); + auto &vertices = Vertices_Tangents->Data; + BoundingBox.reset(vertices[0].Pos); + for (size_t i = 1; i < vertices.size(); ++i) + BoundingBox.addInternalPoint(vertices[i].Pos); } break; } @@ -185,15 +209,15 @@ struct SSkinMeshBuffer : public IMeshBuffer void convertTo2TCoords() { if (VertexType == video::EVT_STANDARD) { - for (const auto &Vertex_Standard : Vertices_Standard) { - video::S3DVertex2TCoords Vertex; + video::S3DVertex2TCoords Vertex; + for (const auto &Vertex_Standard : Vertices_Standard->Data) { Vertex.Color = Vertex_Standard.Color; Vertex.Pos = Vertex_Standard.Pos; Vertex.Normal = Vertex_Standard.Normal; Vertex.TCoords = Vertex_Standard.TCoords; - Vertices_2TCoords.push_back(Vertex); + Vertices_2TCoords->Data.push_back(Vertex); } - Vertices_Standard.clear(); + Vertices_Standard->Data.clear(); VertexType = video::EVT_2TCOORDS; } } @@ -202,26 +226,26 @@ struct SSkinMeshBuffer : public IMeshBuffer void convertToTangents() { if (VertexType == video::EVT_STANDARD) { - for (const auto &Vertex_Standard : Vertices_Standard) { video::S3DVertexTangents Vertex; + for (const auto &Vertex_Standard : Vertices_Standard->Data) { Vertex.Color = Vertex_Standard.Color; Vertex.Pos = Vertex_Standard.Pos; Vertex.Normal = Vertex_Standard.Normal; Vertex.TCoords = Vertex_Standard.TCoords; - Vertices_Tangents.push_back(Vertex); + Vertices_Tangents->Data.push_back(Vertex); } - Vertices_Standard.clear(); + Vertices_Standard->Data.clear(); VertexType = video::EVT_TANGENTS; } else if (VertexType == video::EVT_2TCOORDS) { - for (const auto &Vertex_2TCoords : Vertices_2TCoords) { - video::S3DVertexTangents Vertex; + video::S3DVertexTangents Vertex; + for (const auto &Vertex_2TCoords : Vertices_2TCoords->Data) { Vertex.Color = Vertex_2TCoords.Color; Vertex.Pos = Vertex_2TCoords.Pos; Vertex.Normal = Vertex_2TCoords.Normal; Vertex.TCoords = Vertex_2TCoords.TCoords; - Vertices_Tangents.push_back(Vertex); + Vertices_Tangents->Data.push_back(Vertex); } - Vertices_2TCoords.clear(); + Vertices_2TCoords->Data.clear(); VertexType = video::EVT_TANGENTS; } } @@ -229,79 +253,37 @@ struct SSkinMeshBuffer : public IMeshBuffer //! returns position of vertex i const core::vector3df &getPosition(u32 i) const override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return Vertices_2TCoords[i].Pos; - case video::EVT_TANGENTS: - return Vertices_Tangents[i].Pos; - default: - return Vertices_Standard[i].Pos; - } + return getVertexBuffer()->getPosition(i); } //! returns position of vertex i core::vector3df &getPosition(u32 i) override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return Vertices_2TCoords[i].Pos; - case video::EVT_TANGENTS: - return Vertices_Tangents[i].Pos; - default: - return Vertices_Standard[i].Pos; - } + return getVertexBuffer()->getPosition(i); } //! returns normal of vertex i const core::vector3df &getNormal(u32 i) const override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return Vertices_2TCoords[i].Normal; - case video::EVT_TANGENTS: - return Vertices_Tangents[i].Normal; - default: - return Vertices_Standard[i].Normal; - } + return getVertexBuffer()->getNormal(i); } //! returns normal of vertex i core::vector3df &getNormal(u32 i) override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return Vertices_2TCoords[i].Normal; - case video::EVT_TANGENTS: - return Vertices_Tangents[i].Normal; - default: - return Vertices_Standard[i].Normal; - } + return getVertexBuffer()->getNormal(i); } //! returns texture coords of vertex i const core::vector2df &getTCoords(u32 i) const override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return Vertices_2TCoords[i].TCoords; - case video::EVT_TANGENTS: - return Vertices_Tangents[i].TCoords; - default: - return Vertices_Standard[i].TCoords; - } + return getVertexBuffer()->getTCoords(i); } //! returns texture coords of vertex i core::vector2df &getTCoords(u32 i) override { - switch (VertexType) { - case video::EVT_2TCOORDS: - return Vertices_2TCoords[i].TCoords; - case video::EVT_TANGENTS: - return Vertices_Tangents[i].TCoords; - default: - return Vertices_Standard[i].TCoords; - } + return getVertexBuffer()->getTCoords(i); } //! append the vertices and indices to the current buffer @@ -313,26 +295,22 @@ struct SSkinMeshBuffer : public IMeshBuffer //! get the current hardware mapping hint for vertex buffers E_HARDWARE_MAPPING getHardwareMappingHint_Vertex() const override { - return MappingHint_Vertex; + return getVertexBuffer()->getHardwareMappingHint(); } //! get the current hardware mapping hint for index buffers E_HARDWARE_MAPPING getHardwareMappingHint_Index() const override { - return MappingHint_Index; + return Indices->getHardwareMappingHint(); } //! set the hardware mapping hint, for driver void setHardwareMappingHint(E_HARDWARE_MAPPING NewMappingHint, E_BUFFER_TYPE Buffer = EBT_VERTEX_AND_INDEX) override { - if (Buffer == EBT_VERTEX) - MappingHint_Vertex = NewMappingHint; - else if (Buffer == EBT_INDEX) - MappingHint_Index = NewMappingHint; - else if (Buffer == EBT_VERTEX_AND_INDEX) { - MappingHint_Vertex = NewMappingHint; - MappingHint_Index = NewMappingHint; - } + if (Buffer == EBT_VERTEX || Buffer == EBT_VERTEX_AND_INDEX) + getVertexBuffer()->setHardwareMappingHint(NewMappingHint); + if (Buffer == EBT_INDEX || Buffer == EBT_VERTEX_AND_INDEX) + Indices->setHardwareMappingHint(NewMappingHint); } //! Describe what kind of primitive geometry is used by the meshbuffer @@ -351,14 +329,20 @@ struct SSkinMeshBuffer : public IMeshBuffer void setDirty(E_BUFFER_TYPE Buffer = EBT_VERTEX_AND_INDEX) override { if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_VERTEX) - ++ChangedID_Vertex; + getVertexBuffer()->setDirty(); if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_INDEX) - ++ChangedID_Index; + Indices->setDirty(); } - u32 getChangedID_Vertex() const override { return ChangedID_Vertex; } + u32 getChangedID_Vertex() const override + { + return getVertexBuffer()->getChangedID(); + } - u32 getChangedID_Index() const override { return ChangedID_Index; } + u32 getChangedID_Index() const override + { + return Indices->getChangedID(); + } void setHWBuffer(void *ptr) const override { @@ -373,15 +357,11 @@ struct SSkinMeshBuffer : public IMeshBuffer //! Call this after changing the positions of any vertex. void boundingBoxNeedsRecalculated(void) { BoundingBoxNeedsRecalculated = true; } - std::vector Vertices_Tangents; - std::vector Vertices_2TCoords; - std::vector Vertices_Standard; - std::vector Indices; + SVertexBufferTangents *Vertices_Tangents; + SVertexBufferLightMap *Vertices_2TCoords; + SVertexBuffer *Vertices_Standard; + SIndexBuffer *Indices; - u32 ChangedID_Vertex; - u32 ChangedID_Index; - - // ISkinnedMesh::SJoint *AttachedJoint; core::matrix4 Transformation; video::SMaterial Material; @@ -394,11 +374,7 @@ struct SSkinMeshBuffer : public IMeshBuffer mutable void *HWBuffer; - // hardware mapping hint - E_HARDWARE_MAPPING MappingHint_Vertex : 3; - E_HARDWARE_MAPPING MappingHint_Index : 3; - - bool BoundingBoxNeedsRecalculated : 1; + bool BoundingBoxNeedsRecalculated; }; } // end namespace scene diff --git a/irr/src/CB3DMeshFileLoader.cpp b/irr/src/CB3DMeshFileLoader.cpp index 008169bd7..4cd7b1d82 100644 --- a/irr/src/CB3DMeshFileLoader.cpp +++ b/irr/src/CB3DMeshFileLoader.cpp @@ -259,14 +259,15 @@ bool CB3DMeshFileLoader::readChunkMESH(CSkinnedMesh::SJoint *inJoint) if (!NormalsInFile) { s32 i; - for (i = 0; i < (s32)meshBuffer->Indices.size(); i += 3) { - core::plane3df p(meshBuffer->getVertex(meshBuffer->Indices[i + 0])->Pos, - meshBuffer->getVertex(meshBuffer->Indices[i + 1])->Pos, - meshBuffer->getVertex(meshBuffer->Indices[i + 2])->Pos); + auto &indices = meshBuffer->Indices->Data; + for (i = 0; i < (s32)indices.size(); i += 3) { + core::plane3df p(meshBuffer->getVertex(indices[i + 0])->Pos, + meshBuffer->getVertex(indices[i + 1])->Pos, + meshBuffer->getVertex(indices[i + 2])->Pos); - meshBuffer->getVertex(meshBuffer->Indices[i + 0])->Normal += p.Normal; - meshBuffer->getVertex(meshBuffer->Indices[i + 1])->Normal += p.Normal; - meshBuffer->getVertex(meshBuffer->Indices[i + 2])->Normal += p.Normal; + meshBuffer->getVertex(indices[i + 0])->Normal += p.Normal; + meshBuffer->getVertex(indices[i + 1])->Normal += p.Normal; + meshBuffer->getVertex(indices[i + 2])->Normal += p.Normal; } for (i = 0; i < (s32)meshBuffer->getVertexCount(); ++i) { @@ -433,7 +434,7 @@ bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 m } const s32 memoryNeeded = B3dStack.getLast().length / sizeof(s32); - meshBuffer->Indices.reserve(memoryNeeded + meshBuffer->Indices.size() + 1); + meshBuffer->Indices->Data.reserve(memoryNeeded + meshBuffer->Indices->Data.size() + 1); while ((B3dStack.getLast().startposition + B3dStack.getLast().length) > B3DFile->getPos()) // this chunk repeats { @@ -471,9 +472,9 @@ bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 m // Add the vertex to the meshbuffer: if (meshBuffer->VertexType == video::EVT_STANDARD) - meshBuffer->Vertices_Standard.push_back(BaseVertices[vertex_id[i]]); + meshBuffer->Vertices_Standard->Data.push_back(BaseVertices[vertex_id[i]]); else - meshBuffer->Vertices_2TCoords.push_back(BaseVertices[vertex_id[i]]); + meshBuffer->Vertices_2TCoords->Data.push_back(BaseVertices[vertex_id[i]]); // create vertex id to meshbuffer index link: AnimatedVertices_VertexID[vertex_id[i]] = meshBuffer->getVertexCount() - 1; @@ -504,9 +505,9 @@ bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 m } } - meshBuffer->Indices.push_back(AnimatedVertices_VertexID[vertex_id[0]]); - meshBuffer->Indices.push_back(AnimatedVertices_VertexID[vertex_id[1]]); - meshBuffer->Indices.push_back(AnimatedVertices_VertexID[vertex_id[2]]); + meshBuffer->Indices->Data.push_back(AnimatedVertices_VertexID[vertex_id[0]]); + meshBuffer->Indices->Data.push_back(AnimatedVertices_VertexID[vertex_id[1]]); + meshBuffer->Indices->Data.push_back(AnimatedVertices_VertexID[vertex_id[2]]); } B3dStack.erase(B3dStack.size() - 1); diff --git a/irr/src/CXMeshFileLoader.cpp b/irr/src/CXMeshFileLoader.cpp index 4e11fe352..483955805 100644 --- a/irr/src/CXMeshFileLoader.cpp +++ b/irr/src/CXMeshFileLoader.cpp @@ -273,12 +273,12 @@ bool CXMeshFileLoader::load(io::IReadFile *file) } if (mesh->TCoords2.size()) { for (i = 0; i != mesh->Buffers.size(); ++i) { - mesh->Buffers[i]->Vertices_2TCoords.reserve(vCountArray[i]); + mesh->Buffers[i]->Vertices_2TCoords->Data.reserve(vCountArray[i]); mesh->Buffers[i]->VertexType = video::EVT_2TCOORDS; } } else { for (i = 0; i != mesh->Buffers.size(); ++i) - mesh->Buffers[i]->Vertices_Standard.reserve(vCountArray[i]); + mesh->Buffers[i]->Vertices_Standard->Data.reserve(vCountArray[i]); } verticesLinkIndex.set_used(mesh->Vertices.size()); @@ -290,14 +290,14 @@ bool CXMeshFileLoader::load(io::IReadFile *file) scene::SSkinMeshBuffer *buffer = mesh->Buffers[verticesLinkBuffer[i]]; if (mesh->TCoords2.size()) { - verticesLinkIndex[i] = buffer->Vertices_2TCoords.size(); - buffer->Vertices_2TCoords.emplace_back(mesh->Vertices[i]); + verticesLinkIndex[i] = buffer->Vertices_2TCoords->getCount(); + buffer->Vertices_2TCoords->Data.emplace_back(mesh->Vertices[i]); // We have a problem with correct tcoord2 handling here // crash fixed for now by checking the values - buffer->Vertices_2TCoords.back().TCoords2 = (i < mesh->TCoords2.size()) ? mesh->TCoords2[i] : mesh->Vertices[i].TCoords; + buffer->Vertices_2TCoords->Data.back().TCoords2 = (i < mesh->TCoords2.size()) ? mesh->TCoords2[i] : mesh->Vertices[i].TCoords; } else { - verticesLinkIndex[i] = buffer->Vertices_Standard.size(); - buffer->Vertices_Standard.push_back(mesh->Vertices[i]); + verticesLinkIndex[i] = buffer->Vertices_Standard->getCount(); + buffer->Vertices_Standard->Data.push_back(mesh->Vertices[i]); } } @@ -306,13 +306,13 @@ bool CXMeshFileLoader::load(io::IReadFile *file) for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) ++vCountArray[mesh->FaceMaterialIndices[i]]; for (i = 0; i != mesh->Buffers.size(); ++i) - mesh->Buffers[i]->Indices.reserve(vCountArray[i]); + mesh->Buffers[i]->Indices->Data.reserve(vCountArray[i]); delete[] vCountArray; // create indices per buffer for (i = 0; i < mesh->FaceMaterialIndices.size(); ++i) { scene::SSkinMeshBuffer *buffer = mesh->Buffers[mesh->FaceMaterialIndices[i]]; for (u32 id = i * 3 + 0; id != i * 3 + 3; ++id) { - buffer->Indices.push_back(verticesLinkIndex[mesh->Indices[id]]); + buffer->Indices->Data.push_back(verticesLinkIndex[mesh->Indices[id]]); } } } From be9aa19208a46ee3dccdad9890aed69656079489 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 28 Aug 2024 18:08:59 +0200 Subject: [PATCH 69/75] Propagate changes to IMeshBuffer parent class --- irr/include/CMeshBuffer.h | 132 ++--------------------- irr/include/IMeshBuffer.h | 198 ++++++++++++++++++++++++---------- irr/include/SSkinMeshBuffer.h | 139 ++---------------------- 3 files changed, 163 insertions(+), 306 deletions(-) diff --git a/irr/include/CMeshBuffer.h b/irr/include/CMeshBuffer.h index 0b47494cd..6b13a8982 100644 --- a/irr/include/CMeshBuffer.h +++ b/irr/include/CMeshBuffer.h @@ -49,25 +49,24 @@ public: return Material; } - //! Get pointer to vertices - /** \return Pointer to vertices. */ - const void *getVertices() const override + const scene::IVertexBuffer *getVertexBuffer() const override { - return Vertices->getData(); + return Vertices; } - //! Get pointer to vertices - /** \return Pointer to vertices. */ - void *getVertices() override + scene::IVertexBuffer *getVertexBuffer() override { - return Vertices->getData(); + return Vertices; } - //! Get number of vertices - /** \return Number of vertices. */ - u32 getVertexCount() const override + const scene::IIndexBuffer *getIndexBuffer() const override { - return Vertices->getCount(); + return Indices; + } + + scene::IIndexBuffer *getIndexBuffer() override + { + return Indices; } // TEMPORARY helper for direct buffer acess @@ -76,34 +75,6 @@ public: return Vertices->Data; } - //! Get type of index data which is stored in this meshbuffer. - /** \return Index type of this buffer. */ - video::E_INDEX_TYPE getIndexType() const override - { - return Indices->getType(); - } - - //! Get pointer to indices - /** \return Pointer to indices. */ - const u16 *getIndices() const override - { - return static_cast(Indices->getData()); - } - - //! Get pointer to indices - /** \return Pointer to indices. */ - u16 *getIndices() override - { - return static_cast(Indices->getData()); - } - - //! Get number of indices - /** \return Number of indices. */ - u32 getIndexCount() const override - { - return Indices->getCount(); - } - // TEMPORARY helper for direct buffer acess inline auto &IndexBuffer() { @@ -138,49 +109,6 @@ public: BoundingBox.reset(0, 0, 0); } - //! Get type of vertex data stored in this buffer. - /** \return Type of vertex data. */ - video::E_VERTEX_TYPE getVertexType() const override - { - return Vertices->getType(); - } - - //! returns position of vertex i - const core::vector3df &getPosition(u32 i) const override - { - return Vertices->getPosition(i); - } - - //! returns position of vertex i - core::vector3df &getPosition(u32 i) override - { - return Vertices->getPosition(i); - } - - //! returns normal of vertex i - const core::vector3df &getNormal(u32 i) const override - { - return Vertices->getNormal(i); - } - - //! returns normal of vertex i - core::vector3df &getNormal(u32 i) override - { - return Vertices->getNormal(i); - } - - //! returns texture coord of vertex i - const core::vector2df &getTCoords(u32 i) const override - { - return Vertices->getTCoords(i); - } - - //! returns texture coord of vertex i - core::vector2df &getTCoords(u32 i) override - { - return Vertices->getTCoords(i); - } - //! Append the vertices and indices to the current buffer void append(const void *const vertices, u32 numVertices, const u16 *const indices, u32 numIndices) override { @@ -202,27 +130,6 @@ public: } } - //! get the current hardware mapping hint - E_HARDWARE_MAPPING getHardwareMappingHint_Vertex() const override - { - return Vertices->getHardwareMappingHint(); - } - - //! get the current hardware mapping hint - E_HARDWARE_MAPPING getHardwareMappingHint_Index() const override - { - return Indices->getHardwareMappingHint(); - } - - //! set the hardware mapping hint, for driver - void setHardwareMappingHint(E_HARDWARE_MAPPING NewMappingHint, E_BUFFER_TYPE Buffer = EBT_VERTEX_AND_INDEX) override - { - if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_VERTEX) - Vertices->setHardwareMappingHint(NewMappingHint); - if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_INDEX) - Indices->setHardwareMappingHint(NewMappingHint); - } - //! Describe what kind of primitive geometry is used by the meshbuffer void setPrimitiveType(E_PRIMITIVE_TYPE type) override { @@ -235,23 +142,6 @@ public: return PrimitiveType; } - //! flags the mesh as changed, reloads hardware buffers - void setDirty(E_BUFFER_TYPE Buffer = EBT_VERTEX_AND_INDEX) override - { - if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_VERTEX) - Vertices->setDirty(); - if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_INDEX) - Indices->setDirty(); - } - - //! Get the currently used ID for identification of changes. - /** This shouldn't be used for anything outside the VideoDriver. */ - u32 getChangedID_Vertex() const override { return Vertices->getChangedID(); } - - //! Get the currently used ID for identification of changes. - /** This shouldn't be used for anything outside the VideoDriver. */ - u32 getChangedID_Index() const override { return Indices->getChangedID(); } - void setHWBuffer(void *ptr) const override { HWBuffer = ptr; diff --git a/irr/include/IMeshBuffer.h b/irr/include/IMeshBuffer.h index c69a12d1d..8a43db87e 100644 --- a/irr/include/IMeshBuffer.h +++ b/irr/include/IMeshBuffer.h @@ -7,8 +7,8 @@ #include "IReferenceCounted.h" #include "SMaterial.h" #include "aabbox3d.h" -#include "S3DVertex.h" -#include "SVertexIndex.h" +#include "IVertexBuffer.h" +#include "IIndexBuffer.h" #include "EHardwareBufferFlags.h" #include "EPrimitiveTypes.h" @@ -46,39 +46,17 @@ public: /** \return Material of this buffer. */ virtual const video::SMaterial &getMaterial() const = 0; - //! Get type of vertex data which is stored in this meshbuffer. - /** \return Vertex type of this buffer. */ - virtual video::E_VERTEX_TYPE getVertexType() const = 0; + /// Get the vertex buffer + virtual const scene::IVertexBuffer *getVertexBuffer() const = 0; - //! Get access to vertex data. The data is an array of vertices. - /** Which vertex type is used can be determined by getVertexType(). - \return Pointer to array of vertices. */ - virtual const void *getVertices() const = 0; + /// Get the vertex buffer + virtual scene::IVertexBuffer *getVertexBuffer() = 0; - //! Get access to vertex data. The data is an array of vertices. - /** Which vertex type is used can be determined by getVertexType(). - \return Pointer to array of vertices. */ - virtual void *getVertices() = 0; + /// Get the index buffer + virtual const scene::IIndexBuffer *getIndexBuffer() const = 0; - //! Get amount of vertices in meshbuffer. - /** \return Number of vertices in this buffer. */ - virtual u32 getVertexCount() const = 0; - - //! Get type of index data which is stored in this meshbuffer. - /** \return Index type of this buffer. */ - virtual video::E_INDEX_TYPE getIndexType() const = 0; - - //! Get access to indices. - /** \return Pointer to indices array. */ - virtual const u16 *getIndices() const = 0; - - //! Get access to indices. - /** \return Pointer to indices array. */ - virtual u16 *getIndices() = 0; - - //! Get amount of indices in this meshbuffer. - /** \return Number of indices in this buffer. */ - virtual u32 getIndexCount() const = 0; + /// Get the index buffer + virtual scene::IIndexBuffer *getIndexBuffer() = 0; //! Get the axis aligned bounding box of this meshbuffer. /** \return Axis aligned bounding box of this buffer. */ @@ -92,24 +70,6 @@ public: //! Recalculates the bounding box. Should be called if the mesh changed. virtual void recalculateBoundingBox() = 0; - //! returns position of vertex i - virtual const core::vector3df &getPosition(u32 i) const = 0; - - //! returns position of vertex i - virtual core::vector3df &getPosition(u32 i) = 0; - - //! returns normal of vertex i - virtual const core::vector3df &getNormal(u32 i) const = 0; - - //! returns normal of vertex i - virtual core::vector3df &getNormal(u32 i) = 0; - - //! returns texture coord of vertex i - virtual const core::vector2df &getTCoords(u32 i) const = 0; - - //! returns texture coord of vertex i - virtual core::vector2df &getTCoords(u32 i) = 0; - //! Append the vertices and indices to the current buffer /** Only works for compatible vertex types. \param vertices Pointer to a vertex array. @@ -118,25 +78,149 @@ public: \param numIndices Number of indices in array. */ virtual void append(const void *const vertices, u32 numVertices, const u16 *const indices, u32 numIndices) = 0; - //! get the current hardware mapping hint - virtual E_HARDWARE_MAPPING getHardwareMappingHint_Vertex() const = 0; + /* Leftover functions that are now just helpers for accessing the respective buffer. */ + + //! Get type of vertex data which is stored in this meshbuffer. + /** \return Vertex type of this buffer. */ + inline video::E_VERTEX_TYPE getVertexType() const + { + return getVertexBuffer()->getType(); + } + + //! Get access to vertex data. The data is an array of vertices. + /** Which vertex type is used can be determined by getVertexType(). + \return Pointer to array of vertices. */ + inline const void *getVertices() const + { + return getVertexBuffer()->getData(); + } + + //! Get access to vertex data. The data is an array of vertices. + /** Which vertex type is used can be determined by getVertexType(). + \return Pointer to array of vertices. */ + inline void *getVertices() + { + return getVertexBuffer()->getData(); + } + + //! Get amount of vertices in meshbuffer. + /** \return Number of vertices in this buffer. */ + inline u32 getVertexCount() const + { + return getVertexBuffer()->getCount(); + } + + //! Get type of index data which is stored in this meshbuffer. + /** \return Index type of this buffer. */ + inline video::E_INDEX_TYPE getIndexType() const + { + return getIndexBuffer()->getType(); + } + + //! Get access to indices. + /** \return Pointer to indices array. */ + inline const u16 *getIndices() const + { + _IRR_DEBUG_BREAK_IF(getIndexBuffer()->getType() != video::EIT_16BIT); + return static_cast(getIndexBuffer()->getData()); + } + + //! Get access to indices. + /** \return Pointer to indices array. */ + inline u16 *getIndices() + { + _IRR_DEBUG_BREAK_IF(getIndexBuffer()->getType() != video::EIT_16BIT); + return static_cast(getIndexBuffer()->getData()); + } + + //! Get amount of indices in this meshbuffer. + /** \return Number of indices in this buffer. */ + inline u32 getIndexCount() const + { + return getIndexBuffer()->getCount(); + } + + //! returns position of vertex i + inline const core::vector3df &getPosition(u32 i) const + { + return getVertexBuffer()->getPosition(i); + } + + //! returns position of vertex i + inline core::vector3df &getPosition(u32 i) + { + return getVertexBuffer()->getPosition(i); + } + + //! returns normal of vertex i + inline const core::vector3df &getNormal(u32 i) const + { + return getVertexBuffer()->getNormal(i); + } + + //! returns normal of vertex i + inline core::vector3df &getNormal(u32 i) + { + return getVertexBuffer()->getNormal(i); + } + + //! returns texture coord of vertex i + inline const core::vector2df &getTCoords(u32 i) const + { + return getVertexBuffer()->getTCoords(i); + } + + //! returns texture coord of vertex i + inline core::vector2df &getTCoords(u32 i) + { + return getVertexBuffer()->getTCoords(i); + } //! get the current hardware mapping hint - virtual E_HARDWARE_MAPPING getHardwareMappingHint_Index() const = 0; + inline E_HARDWARE_MAPPING getHardwareMappingHint_Vertex() const + { + return getVertexBuffer()->getHardwareMappingHint(); + } + + //! get the current hardware mapping hint + inline E_HARDWARE_MAPPING getHardwareMappingHint_Index() const + { + return getIndexBuffer()->getHardwareMappingHint(); + } //! set the hardware mapping hint, for driver - virtual void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) = 0; + inline void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) + { + if (buffer == EBT_VERTEX_AND_INDEX || buffer == EBT_VERTEX) + getVertexBuffer()->setHardwareMappingHint(newMappingHint); + if (buffer == EBT_VERTEX_AND_INDEX || buffer == EBT_INDEX) + getIndexBuffer()->setHardwareMappingHint(newMappingHint); + } //! flags the meshbuffer as changed, reloads hardware buffers - virtual void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) = 0; + inline void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) + { + if (buffer == EBT_VERTEX_AND_INDEX || buffer == EBT_VERTEX) + getVertexBuffer()->setDirty(); + if (buffer == EBT_VERTEX_AND_INDEX || buffer == EBT_INDEX) + getIndexBuffer()->setDirty(); + } //! Get the currently used ID for identification of changes. /** This shouldn't be used for anything outside the VideoDriver. */ - virtual u32 getChangedID_Vertex() const = 0; + inline u32 getChangedID_Vertex() const + { + return getVertexBuffer()->getChangedID(); + } //! Get the currently used ID for identification of changes. /** This shouldn't be used for anything outside the VideoDriver. */ - virtual u32 getChangedID_Index() const = 0; + inline u32 getChangedID_Index() const + { + return getIndexBuffer()->getChangedID(); + } + + /* End helpers */ //! Used by the VideoDriver to remember the buffer link. virtual void setHWBuffer(void *ptr) const = 0; diff --git a/irr/include/SSkinMeshBuffer.h b/irr/include/SSkinMeshBuffer.h index abe077076..949551161 100644 --- a/irr/include/SSkinMeshBuffer.h +++ b/irr/include/SSkinMeshBuffer.h @@ -60,8 +60,7 @@ struct SSkinMeshBuffer : public IMeshBuffer return Material; } -protected: - const scene::IVertexBuffer *getVertexBuffer() const + const scene::IVertexBuffer *getVertexBuffer() const override { switch (VertexType) { case video::EVT_2TCOORDS: @@ -84,7 +83,16 @@ protected: return Vertices_Standard; } } -public: + + const scene::IIndexBuffer *getIndexBuffer() const override + { + return Indices; + } + + scene::IIndexBuffer *getIndexBuffer() override + { + return Indices; + } //! Get standard vertex at given index virtual video::S3DVertex *getVertex(u32 index) @@ -99,49 +107,6 @@ public: } } - //! Get pointer to vertex array - const void *getVertices() const override - { - return getVertexBuffer()->getData(); - } - - //! Get pointer to vertex array - void *getVertices() override - { - return getVertexBuffer()->getData(); - } - - //! Get vertex count - u32 getVertexCount() const override - { - return getVertexBuffer()->getCount(); - } - - //! Get type of index data which is stored in this meshbuffer. - /** \return Index type of this buffer. */ - video::E_INDEX_TYPE getIndexType() const override - { - return Indices->getType(); - } - - //! Get pointer to index array - const u16 *getIndices() const override - { - return static_cast(Indices->getData()); - } - - //! Get pointer to index array - u16 *getIndices() override - { - return static_cast(Indices->getData()); - } - - //! Get index count - u32 getIndexCount() const override - { - return Indices->getCount(); - } - //! Get bounding box const core::aabbox3d &getBoundingBox() const override { @@ -199,12 +164,6 @@ public: } } - //! Get vertex type - video::E_VERTEX_TYPE getVertexType() const override - { - return VertexType; - } - //! Convert to 2tcoords vertex type void convertTo2TCoords() { @@ -250,69 +209,12 @@ public: } } - //! returns position of vertex i - const core::vector3df &getPosition(u32 i) const override - { - return getVertexBuffer()->getPosition(i); - } - - //! returns position of vertex i - core::vector3df &getPosition(u32 i) override - { - return getVertexBuffer()->getPosition(i); - } - - //! returns normal of vertex i - const core::vector3df &getNormal(u32 i) const override - { - return getVertexBuffer()->getNormal(i); - } - - //! returns normal of vertex i - core::vector3df &getNormal(u32 i) override - { - return getVertexBuffer()->getNormal(i); - } - - //! returns texture coords of vertex i - const core::vector2df &getTCoords(u32 i) const override - { - return getVertexBuffer()->getTCoords(i); - } - - //! returns texture coords of vertex i - core::vector2df &getTCoords(u32 i) override - { - return getVertexBuffer()->getTCoords(i); - } - //! append the vertices and indices to the current buffer void append(const void *const vertices, u32 numVertices, const u16 *const indices, u32 numIndices) override { _IRR_DEBUG_BREAK_IF(true); } - //! get the current hardware mapping hint for vertex buffers - E_HARDWARE_MAPPING getHardwareMappingHint_Vertex() const override - { - return getVertexBuffer()->getHardwareMappingHint(); - } - - //! get the current hardware mapping hint for index buffers - E_HARDWARE_MAPPING getHardwareMappingHint_Index() const override - { - return Indices->getHardwareMappingHint(); - } - - //! set the hardware mapping hint, for driver - void setHardwareMappingHint(E_HARDWARE_MAPPING NewMappingHint, E_BUFFER_TYPE Buffer = EBT_VERTEX_AND_INDEX) override - { - if (Buffer == EBT_VERTEX || Buffer == EBT_VERTEX_AND_INDEX) - getVertexBuffer()->setHardwareMappingHint(NewMappingHint); - if (Buffer == EBT_INDEX || Buffer == EBT_VERTEX_AND_INDEX) - Indices->setHardwareMappingHint(NewMappingHint); - } - //! Describe what kind of primitive geometry is used by the meshbuffer void setPrimitiveType(E_PRIMITIVE_TYPE type) override { @@ -325,25 +227,6 @@ public: return PrimitiveType; } - //! flags the mesh as changed, reloads hardware buffers - void setDirty(E_BUFFER_TYPE Buffer = EBT_VERTEX_AND_INDEX) override - { - if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_VERTEX) - getVertexBuffer()->setDirty(); - if (Buffer == EBT_VERTEX_AND_INDEX || Buffer == EBT_INDEX) - Indices->setDirty(); - } - - u32 getChangedID_Vertex() const override - { - return getVertexBuffer()->getChangedID(); - } - - u32 getChangedID_Index() const override - { - return Indices->getChangedID(); - } - void setHWBuffer(void *ptr) const override { HWBuffer = ptr; From 6b7fc1e9fe68960f76f3a5142a994a5dd36a90a6 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 28 Aug 2024 19:35:28 +0200 Subject: [PATCH 70/75] Handle vertex & index VBOs separately in GL drivers --- irr/include/IVideoDriver.h | 18 ++- irr/src/CNullDriver.cpp | 83 ++++++++++---- irr/src/CNullDriver.h | 86 +++++++++----- irr/src/COpenGLDriver.cpp | 165 ++++++++++++++------------- irr/src/COpenGLDriver.h | 23 ++-- irr/src/OpenGL/Driver.cpp | 224 ++++++++++++++++--------------------- irr/src/OpenGL/Driver.h | 30 +++-- 7 files changed, 346 insertions(+), 283 deletions(-) diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index 6d2182e8a..b3312160c 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -31,6 +31,8 @@ class IWriteFile; namespace scene { class IMeshBuffer; +class IVertexBuffer; +class IIndexBuffer; class IMesh; class IMeshManipulator; class ISceneNode; @@ -299,7 +301,10 @@ public: virtual void removeAllTextures() = 0; //! Remove hardware buffer - virtual void removeHardwareBuffer(const scene::IMeshBuffer *mb) = 0; + virtual void removeHardwareBuffer(const scene::IVertexBuffer *vb) = 0; + + //! Remove hardware buffer + virtual void removeHardwareBuffer(const scene::IIndexBuffer *ib) = 0; //! Remove all hardware buffers virtual void removeAllHardwareBuffers() = 0; @@ -738,6 +743,17 @@ public: /** \param mb Buffer to draw */ virtual void drawMeshBuffer(const scene::IMeshBuffer *mb) = 0; + /** + * Draws a mesh from individual vertex and index buffers. + * @param vb vertices to use + * @param ib indices to use + * @param primCount amount of primitives + * @param pType primitive type + */ + virtual void drawBuffers(const scene::IVertexBuffer *vb, + const scene::IIndexBuffer *ib, u32 primCount, + scene::E_PRIMITIVE_TYPE pType = scene::EPT_TRIANGLES) = 0; + //! Draws normals of a mesh buffer /** \param mb Buffer to draw the normals of \param length length scale factor of the normals diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index 054267711..6f261cef1 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -1109,19 +1109,20 @@ void CNullDriver::getFog(SColor &color, E_FOG_TYPE &fogType, f32 &start, f32 &en rangeFog = RangeFog; } -//! Draws a mesh buffer -void CNullDriver::drawMeshBuffer(const scene::IMeshBuffer *mb) +void CNullDriver::drawBuffers(const scene::IVertexBuffer *vb, + const scene::IIndexBuffer *ib, u32 primCount, + scene::E_PRIMITIVE_TYPE pType) { - if (!mb) + if (!vb || !ib) return; - // IVertexBuffer and IIndexBuffer later - SHWBufferLink *HWBuffer = getBufferLink(mb); + if (vb->getHWBuffer() || ib->getHWBuffer()) { + // subclass is supposed to override this if it supports hw buffers + _IRR_DEBUG_BREAK_IF(1); + } - if (HWBuffer) - drawHardwareBuffer(HWBuffer); - else - drawVertexPrimitiveList(mb->getVertices(), mb->getVertexCount(), mb->getIndices(), mb->getPrimitiveCount(), mb->getVertexType(), mb->getPrimitiveType(), mb->getIndexType()); + drawVertexPrimitiveList(vb->getData(), vb->getCount(), ib->getData(), + primCount, vb->getType(), pType, ib->getType()); } //! Draws the normals of a mesh buffer @@ -1140,17 +1141,30 @@ void CNullDriver::drawMeshBufferNormals(const scene::IMeshBuffer *mb, f32 length } } -CNullDriver::SHWBufferLink *CNullDriver::getBufferLink(const scene::IMeshBuffer *mb) +CNullDriver::SHWBufferLink *CNullDriver::getBufferLink(const scene::IVertexBuffer *vb) { - if (!mb || !isHardwareBufferRecommend(mb)) + if (!vb || !isHardwareBufferRecommend(vb)) return 0; // search for hardware links - SHWBufferLink *HWBuffer = reinterpret_cast(mb->getHWBuffer()); + SHWBufferLink *HWBuffer = reinterpret_cast(vb->getHWBuffer()); if (HWBuffer) return HWBuffer; - return createHardwareBuffer(mb); // no hardware links, and mesh wants one, create it + return createHardwareBuffer(vb); // no hardware links, and mesh wants one, create it +} + +CNullDriver::SHWBufferLink *CNullDriver::getBufferLink(const scene::IIndexBuffer *ib) +{ + if (!ib || !isHardwareBufferRecommend(ib)) + return 0; + + // search for hardware links + SHWBufferLink *HWBuffer = reinterpret_cast(ib->getHWBuffer()); + if (HWBuffer) + return HWBuffer; + + return createHardwareBuffer(ib); // no hardware links, and mesh wants one, create it } //! Update all hardware buffers, remove unused ones @@ -1161,8 +1175,13 @@ void CNullDriver::updateAllHardwareBuffers() SHWBufferLink *Link = *it; ++it; - if (!Link->MeshBuffer || Link->MeshBuffer->getReferenceCount() == 1) - deleteHardwareBuffer(Link); + if (Link->IsVertex) { + if (!Link->VertexBuffer || Link->VertexBuffer->getReferenceCount() == 1) + deleteHardwareBuffer(Link); + } else { + if (!Link->IndexBuffer || Link->IndexBuffer->getReferenceCount() == 1) + deleteHardwareBuffer(Link); + } } } @@ -1174,12 +1193,20 @@ void CNullDriver::deleteHardwareBuffer(SHWBufferLink *HWBuffer) delete HWBuffer; } -//! Remove hardware buffer -void CNullDriver::removeHardwareBuffer(const scene::IMeshBuffer *mb) +void CNullDriver::removeHardwareBuffer(const scene::IVertexBuffer *vb) { - if (!mb) + if (!vb) return; - SHWBufferLink *HWBuffer = reinterpret_cast(mb->getHWBuffer()); + SHWBufferLink *HWBuffer = reinterpret_cast(vb->getHWBuffer()); + if (HWBuffer) + deleteHardwareBuffer(HWBuffer); +} + +void CNullDriver::removeHardwareBuffer(const scene::IIndexBuffer *ib) +{ + if (!ib) + return; + SHWBufferLink *HWBuffer = reinterpret_cast(ib->getHWBuffer()); if (HWBuffer) deleteHardwareBuffer(HWBuffer); } @@ -1191,12 +1218,24 @@ void CNullDriver::removeAllHardwareBuffers() deleteHardwareBuffer(HWBufferList.front()); } -bool CNullDriver::isHardwareBufferRecommend(const scene::IMeshBuffer *mb) +bool CNullDriver::isHardwareBufferRecommend(const scene::IVertexBuffer *vb) { - if (!mb || (mb->getHardwareMappingHint_Index() == scene::EHM_NEVER && mb->getHardwareMappingHint_Vertex() == scene::EHM_NEVER)) + if (!vb || vb->getHardwareMappingHint() == scene::EHM_NEVER) return false; - if (mb->getVertexCount() < MinVertexCountForVBO) + if (vb->getCount() < MinVertexCountForVBO) + return false; + + return true; +} + +bool CNullDriver::isHardwareBufferRecommend(const scene::IIndexBuffer *ib) +{ + if (!ib || ib->getHardwareMappingHint() == scene::EHM_NEVER) + return false; + + // This is a bit stupid + if (ib->getCount() < MinVertexCountForVBO * 3) return false; return true; diff --git a/irr/src/CNullDriver.h b/irr/src/CNullDriver.h index 302108a1b..b8d45118f 100644 --- a/irr/src/CNullDriver.h +++ b/irr/src/CNullDriver.h @@ -269,8 +269,18 @@ public: const core::position2d &pos, const core::dimension2d &size) override; - //! Draws a mesh buffer - void drawMeshBuffer(const scene::IMeshBuffer *mb) override; + void drawMeshBuffer(const scene::IMeshBuffer *mb) override + { + if (!mb) + return; + drawBuffers(mb->getVertexBuffer(), mb->getIndexBuffer(), + mb->getPrimitiveCount(), mb->getPrimitiveType()); + } + + // Note: this should handle hw buffers + virtual void drawBuffers(const scene::IVertexBuffer *vb, + const scene::IIndexBuffer *ib, u32 primCount, + scene::E_PRIMITIVE_TYPE pType = scene::EPT_TRIANGLES) override; //! Draws the normals of a mesh buffer virtual void drawMeshBufferNormals(const scene::IMeshBuffer *mb, f32 length = 10.f, @@ -283,53 +293,70 @@ public: } protected: + /// Links a hardware buffer to either a vertex or index buffer struct SHWBufferLink { - SHWBufferLink(const scene::IMeshBuffer *_MeshBuffer) : - MeshBuffer(_MeshBuffer), - ChangedID_Vertex(0), ChangedID_Index(0), - Mapped_Vertex(scene::EHM_NEVER), Mapped_Index(scene::EHM_NEVER) + SHWBufferLink(const scene::IVertexBuffer *vb) : + VertexBuffer(vb), ChangedID(0), IsVertex(true) { - if (MeshBuffer) { - MeshBuffer->grab(); - MeshBuffer->setHWBuffer(reinterpret_cast(this)); + if (VertexBuffer) { + VertexBuffer->grab(); + VertexBuffer->setHWBuffer(this); + } + } + SHWBufferLink(const scene::IIndexBuffer *ib) : + IndexBuffer(ib), ChangedID(0), IsVertex(false) + { + if (IndexBuffer) { + IndexBuffer->grab(); + IndexBuffer->setHWBuffer(this); } } virtual ~SHWBufferLink() { - if (MeshBuffer) { - MeshBuffer->setHWBuffer(NULL); - MeshBuffer->drop(); + if (IsVertex && VertexBuffer) { + VertexBuffer->setHWBuffer(nullptr); + VertexBuffer->drop(); + } else if (!IsVertex && IndexBuffer) { + IndexBuffer->setHWBuffer(nullptr); + IndexBuffer->drop(); } } - const scene::IMeshBuffer *MeshBuffer; - u32 ChangedID_Vertex; - u32 ChangedID_Index; - scene::E_HARDWARE_MAPPING Mapped_Vertex; - scene::E_HARDWARE_MAPPING Mapped_Index; - std::list::iterator listPosition; + union { + const scene::IVertexBuffer *VertexBuffer; + const scene::IIndexBuffer *IndexBuffer; + }; + u32 ChangedID; + bool IsVertex; + std::list::iterator listPosition; }; - //! Gets hardware buffer link from a meshbuffer (may create or update buffer) - virtual SHWBufferLink *getBufferLink(const scene::IMeshBuffer *mb); + //! Gets hardware buffer link from a vertex buffer (may create or update buffer) + virtual SHWBufferLink *getBufferLink(const scene::IVertexBuffer *mb); + + //! Gets hardware buffer link from a index buffer (may create or update buffer) + virtual SHWBufferLink *getBufferLink(const scene::IIndexBuffer *mb); //! updates hardware buffer if needed (only some drivers can) virtual bool updateHardwareBuffer(SHWBufferLink *HWBuffer) { return false; } - //! Draw hardware buffer (only some drivers can) - virtual void drawHardwareBuffer(SHWBufferLink *HWBuffer) {} - //! Delete hardware buffer virtual void deleteHardwareBuffer(SHWBufferLink *HWBuffer); - //! Create hardware buffer from mesh (only some drivers can) - virtual SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer *mb) { return 0; } + //! Create hardware buffer from vertex buffer + virtual SHWBufferLink *createHardwareBuffer(const scene::IVertexBuffer *vb) { return 0; } + + //! Create hardware buffer from index buffer + virtual SHWBufferLink *createHardwareBuffer(const scene::IIndexBuffer *ib) { return 0; } public: //! Remove hardware buffer - void removeHardwareBuffer(const scene::IMeshBuffer *mb) override; + void removeHardwareBuffer(const scene::IVertexBuffer *vb) override; + + //! Remove hardware buffer + void removeHardwareBuffer(const scene::IIndexBuffer *ib) override; //! Remove all hardware buffers void removeAllHardwareBuffers() override; @@ -337,8 +364,11 @@ public: //! Update all hardware buffers, remove unused ones virtual void updateAllHardwareBuffers(); - //! is vbo recommended on this mesh? - virtual bool isHardwareBufferRecommend(const scene::IMeshBuffer *mb); + //! is vbo recommended? + virtual bool isHardwareBufferRecommend(const scene::IVertexBuffer *mb); + + //! is vbo recommended? + virtual bool isHardwareBufferRecommend(const scene::IIndexBuffer *mb); //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index 875534a4e..837dc05d3 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -260,10 +260,10 @@ bool COpenGLDriver::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) return false; #if defined(GL_ARB_vertex_buffer_object) - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; - const void *vertices = mb->getVertices(); - const u32 vertexCount = mb->getVertexCount(); - const E_VERTEX_TYPE vType = mb->getVertexType(); + const auto *vb = HWBuffer->VertexBuffer; + const void *vertices = vb->getData(); + const u32 vertexCount = vb->getCount(); + const E_VERTEX_TYPE vType = vb->getType(); const u32 vertexSize = getVertexPitchFromType(vType); accountHWBufferUpload(vertexSize * vertexCount); @@ -307,26 +307,26 @@ bool COpenGLDriver::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) // get or create buffer bool newBuffer = false; - if (!HWBuffer->vbo_verticesID) { - extGlGenBuffers(1, &HWBuffer->vbo_verticesID); - if (!HWBuffer->vbo_verticesID) + if (!HWBuffer->vbo_ID) { + extGlGenBuffers(1, &HWBuffer->vbo_ID); + if (!HWBuffer->vbo_ID) return false; newBuffer = true; - } else if (HWBuffer->vbo_verticesSize < vertexCount * vertexSize) { + } else if (HWBuffer->vbo_Size < vertexCount * vertexSize) { newBuffer = true; } - extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID); + extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_ID); // copy data to graphics card if (!newBuffer) extGlBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * vertexSize, vbuf); else { - HWBuffer->vbo_verticesSize = vertexCount * vertexSize; + HWBuffer->vbo_Size = vertexCount * vertexSize; - if (HWBuffer->Mapped_Vertex == scene::EHM_STATIC) + if (vb->getHardwareMappingHint() == scene::EHM_STATIC) extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, vbuf, GL_STATIC_DRAW); - else if (HWBuffer->Mapped_Vertex == scene::EHM_DYNAMIC) + else if (vb->getHardwareMappingHint() == scene::EHM_DYNAMIC) extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, vbuf, GL_DYNAMIC_DRAW); else // scene::EHM_STREAM extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, vbuf, GL_STREAM_DRAW); @@ -349,13 +349,13 @@ bool COpenGLDriver::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) return false; #if defined(GL_ARB_vertex_buffer_object) - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; + const auto *ib = HWBuffer->IndexBuffer; - const void *indices = mb->getIndices(); - u32 indexCount = mb->getIndexCount(); + const void *indices = ib->getData(); + u32 indexCount = ib->getCount(); GLenum indexSize; - switch (mb->getIndexType()) { + switch (ib->getType()) { case EIT_16BIT: { indexSize = sizeof(u16); break; @@ -373,26 +373,26 @@ bool COpenGLDriver::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) // get or create buffer bool newBuffer = false; - if (!HWBuffer->vbo_indicesID) { - extGlGenBuffers(1, &HWBuffer->vbo_indicesID); - if (!HWBuffer->vbo_indicesID) + if (!HWBuffer->vbo_ID) { + extGlGenBuffers(1, &HWBuffer->vbo_ID); + if (!HWBuffer->vbo_ID) return false; newBuffer = true; - } else if (HWBuffer->vbo_indicesSize < indexCount * indexSize) { + } else if (HWBuffer->vbo_Size < indexCount * indexSize) { newBuffer = true; } - extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID); + extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_ID); // copy data to graphics card if (!newBuffer) extGlBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexCount * indexSize, indices); else { - HWBuffer->vbo_indicesSize = indexCount * indexSize; + HWBuffer->vbo_Size = indexCount * indexSize; - if (HWBuffer->Mapped_Index == scene::EHM_STATIC) + if (ib->getHardwareMappingHint() == scene::EHM_STATIC) extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STATIC_DRAW); - else if (HWBuffer->Mapped_Index == scene::EHM_DYNAMIC) + else if (ib->getHardwareMappingHint() == scene::EHM_DYNAMIC) extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_DYNAMIC_DRAW); else // scene::EHM_STREAM extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STREAM_DRAW); @@ -412,51 +412,62 @@ bool COpenGLDriver::updateHardwareBuffer(SHWBufferLink *HWBuffer) if (!HWBuffer) return false; - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) { - if (HWBuffer->ChangedID_Vertex != HWBuffer->MeshBuffer->getChangedID_Vertex() || !((SHWBufferLink_opengl *)HWBuffer)->vbo_verticesID) { + auto *b = static_cast(HWBuffer); - HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex(); - - if (!updateVertexHardwareBuffer((SHWBufferLink_opengl *)HWBuffer)) + if (b->IsVertex) { + assert(b->VertexBuffer); + if (b->ChangedID != b->VertexBuffer->getChangedID() || !b->vbo_ID) { + if (!updateVertexHardwareBuffer(b)) return false; + b->ChangedID = b->VertexBuffer->getChangedID(); + } + } else { + assert(b->IndexBuffer); + if (b->ChangedID != b->IndexBuffer->getChangedID() || !b->vbo_ID) { + if (!updateIndexHardwareBuffer(b)) + return false; + b->ChangedID = b->IndexBuffer->getChangedID(); } } - - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) { - if (HWBuffer->ChangedID_Index != HWBuffer->MeshBuffer->getChangedID_Index() || !((SHWBufferLink_opengl *)HWBuffer)->vbo_indicesID) { - - HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index(); - - if (!updateIndexHardwareBuffer((SHWBufferLink_opengl *)HWBuffer)) - return false; - } - } - return true; } //! Create hardware buffer from meshbuffer -COpenGLDriver::SHWBufferLink *COpenGLDriver::createHardwareBuffer(const scene::IMeshBuffer *mb) +COpenGLDriver::SHWBufferLink *COpenGLDriver::createHardwareBuffer(const scene::IVertexBuffer *vb) { #if defined(GL_ARB_vertex_buffer_object) - if (!mb || (mb->getHardwareMappingHint_Index() == scene::EHM_NEVER && mb->getHardwareMappingHint_Vertex() == scene::EHM_NEVER)) + if (!vb || vb->getHardwareMappingHint() == scene::EHM_NEVER) return 0; - SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(mb); + SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(vb); // add to map HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer); - HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex(); - HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index(); - HWBuffer->Mapped_Vertex = mb->getHardwareMappingHint_Vertex(); - HWBuffer->Mapped_Index = mb->getHardwareMappingHint_Index(); - HWBuffer->vbo_verticesID = 0; - HWBuffer->vbo_indicesID = 0; - HWBuffer->vbo_verticesSize = 0; - HWBuffer->vbo_indicesSize = 0; + if (!updateVertexHardwareBuffer(HWBuffer)) { + deleteHardwareBuffer(HWBuffer); + return 0; + } - if (!updateHardwareBuffer(HWBuffer)) { + return HWBuffer; +#else + return 0; +#endif +} + +//! Create hardware buffer from meshbuffer +COpenGLDriver::SHWBufferLink *COpenGLDriver::createHardwareBuffer(const scene::IIndexBuffer *ib) +{ +#if defined(GL_ARB_vertex_buffer_object) + if (!ib || ib->getHardwareMappingHint() == scene::EHM_NEVER) + return 0; + + SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(ib); + + // add to map + HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer); + + if (!updateIndexHardwareBuffer(HWBuffer)) { deleteHardwareBuffer(HWBuffer); return 0; } @@ -473,51 +484,51 @@ void COpenGLDriver::deleteHardwareBuffer(SHWBufferLink *_HWBuffer) return; #if defined(GL_ARB_vertex_buffer_object) - SHWBufferLink_opengl *HWBuffer = (SHWBufferLink_opengl *)_HWBuffer; - if (HWBuffer->vbo_verticesID) { - extGlDeleteBuffers(1, &HWBuffer->vbo_verticesID); - HWBuffer->vbo_verticesID = 0; - } - if (HWBuffer->vbo_indicesID) { - extGlDeleteBuffers(1, &HWBuffer->vbo_indicesID); - HWBuffer->vbo_indicesID = 0; + auto *HWBuffer = (SHWBufferLink_opengl *)_HWBuffer; + if (HWBuffer->vbo_ID) { + extGlDeleteBuffers(1, &HWBuffer->vbo_ID); + HWBuffer->vbo_ID = 0; } #endif CNullDriver::deleteHardwareBuffer(_HWBuffer); } -//! Draw hardware buffer -void COpenGLDriver::drawHardwareBuffer(SHWBufferLink *_HWBuffer) +void COpenGLDriver::drawBuffers(const scene::IVertexBuffer *vb, + const scene::IIndexBuffer *ib, u32 PrimitiveCount, + scene::E_PRIMITIVE_TYPE PrimitiveType) { - if (!_HWBuffer) + if (!vb || !ib) return; - updateHardwareBuffer(_HWBuffer); // check if update is needed - #if defined(GL_ARB_vertex_buffer_object) - SHWBufferLink_opengl *HWBuffer = (SHWBufferLink_opengl *)_HWBuffer; + auto *hwvert = (SHWBufferLink_opengl *) getBufferLink(vb); + auto *hwidx = (SHWBufferLink_opengl *) getBufferLink(ib); + updateHardwareBuffer(hwvert); + updateHardwareBuffer(hwidx); - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; - const void *vertices = mb->getVertices(); - const void *indexList = mb->getIndices(); - - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) { - extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID); + const void *vertices = vb->getData(); + if (hwvert) { + extGlBindBuffer(GL_ARRAY_BUFFER, hwvert->vbo_ID); vertices = 0; } - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) { - extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID); + const void *indexList = ib->getData(); + if (hwidx) { + extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, hwidx->vbo_ID); indexList = 0; } - drawVertexPrimitiveList(vertices, mb->getVertexCount(), indexList, mb->getPrimitiveCount(), mb->getVertexType(), mb->getPrimitiveType(), mb->getIndexType()); + drawVertexPrimitiveList(vertices, vb->getCount(), indexList, + PrimitiveCount, vb->getType(), PrimitiveType, ib->getType()); - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) + if (hwvert) extGlBindBuffer(GL_ARRAY_BUFFER, 0); - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) + if (hwidx) extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +#else + drawVertexPrimitiveList(vb->getData(), vb->getCount(), ib->getData(), + PrimitiveCount, vb->getType(), PrimitiveType, ib->getType()); #endif } diff --git a/irr/src/COpenGLDriver.h b/irr/src/COpenGLDriver.h index 3c0a718fc..9c4ecd3b3 100644 --- a/irr/src/COpenGLDriver.h +++ b/irr/src/COpenGLDriver.h @@ -58,27 +58,28 @@ public: struct SHWBufferLink_opengl : public SHWBufferLink { - SHWBufferLink_opengl(const scene::IMeshBuffer *_MeshBuffer) : - SHWBufferLink(_MeshBuffer), vbo_verticesID(0), vbo_indicesID(0) {} + SHWBufferLink_opengl(const scene::IVertexBuffer *vb) : SHWBufferLink(vb) {} + SHWBufferLink_opengl(const scene::IIndexBuffer *ib) : SHWBufferLink(ib) {} - GLuint vbo_verticesID; // tmp - GLuint vbo_indicesID; // tmp - - GLuint vbo_verticesSize; // tmp - GLuint vbo_indicesSize; // tmp + GLuint vbo_ID = 0; + u32 vbo_Size = 0; }; //! updates hardware buffer if needed bool updateHardwareBuffer(SHWBufferLink *HWBuffer) override; - //! Create hardware buffer from mesh - SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer *mb) override; + //! Create hardware buffer from vertex buffer + SHWBufferLink *createHardwareBuffer(const scene::IVertexBuffer *vb) override; + + //! Create hardware buffer from index buffer + SHWBufferLink *createHardwareBuffer(const scene::IIndexBuffer *ib) override; //! Delete hardware buffer (only some drivers can) void deleteHardwareBuffer(SHWBufferLink *HWBuffer) override; - //! Draw hardware buffer - void drawHardwareBuffer(SHWBufferLink *HWBuffer) override; + void drawBuffers(const scene::IVertexBuffer *vb, + const scene::IIndexBuffer *ib, u32 primCount, + scene::E_PRIMITIVE_TYPE pType = scene::EPT_TRIANGLES) override; //! Create occlusion query. /** Use node for identification and mesh for occlusion test. */ diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index f681a3de9..46aa36d5c 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -477,44 +477,36 @@ void COpenGL3DriverBase::setTransform(E_TRANSFORMATION_STATE state, const core:: Transformation3DChanged = true; } -bool COpenGL3DriverBase::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) +bool COpenGL3DriverBase::updateHardwareBuffer(SHWBufferLink_opengl *HWBuffer, + const void *buffer, size_t bufferSize, scene::E_HARDWARE_MAPPING hint) { - if (!HWBuffer) - return false; + assert(HWBuffer); - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; - const void *vertices = mb->getVertices(); - const u32 vertexCount = mb->getVertexCount(); - const E_VERTEX_TYPE vType = mb->getVertexType(); - const u32 vertexSize = getVertexPitchFromType(vType); - - const void *buffer = vertices; - size_t bufferSize = vertexSize * vertexCount; accountHWBufferUpload(bufferSize); // get or create buffer bool newBuffer = false; - if (!HWBuffer->vbo_verticesID) { - GL.GenBuffers(1, &HWBuffer->vbo_verticesID); - if (!HWBuffer->vbo_verticesID) + if (!HWBuffer->vbo_ID) { + GL.GenBuffers(1, &HWBuffer->vbo_ID); + if (!HWBuffer->vbo_ID) return false; newBuffer = true; - } else if (HWBuffer->vbo_verticesSize < bufferSize) { + } else if (HWBuffer->vbo_Size < bufferSize) { newBuffer = true; } - GL.BindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID); + GL.BindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_ID); // copy data to graphics card if (!newBuffer) GL.BufferSubData(GL_ARRAY_BUFFER, 0, bufferSize, buffer); else { - HWBuffer->vbo_verticesSize = bufferSize; + HWBuffer->vbo_Size = bufferSize; GLenum usage = GL_STATIC_DRAW; - if (HWBuffer->Mapped_Index == scene::EHM_STREAM) + if (hint == scene::EHM_STREAM) usage = GL_STREAM_DRAW; - else if (HWBuffer->Mapped_Index == scene::EHM_DYNAMIC) + else if (hint == scene::EHM_DYNAMIC) usage = GL_DYNAMIC_DRAW; GL.BufferData(GL_ARRAY_BUFFER, bufferSize, buffer, usage); } @@ -524,67 +516,47 @@ bool COpenGL3DriverBase::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuff return (!TEST_GL_ERROR(this)); } +bool COpenGL3DriverBase::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) +{ + if (!HWBuffer) + return false; + + assert(HWBuffer->IsVertex); + const auto *vb = HWBuffer->VertexBuffer; + assert(vb); + + const u32 vertexSize = getVertexPitchFromType(vb->getType()); + const size_t bufferSize = vertexSize * vb->getCount(); + + return updateHardwareBuffer(HWBuffer, vb->getData(), bufferSize, vb->getHardwareMappingHint()); +} + bool COpenGL3DriverBase::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer) { if (!HWBuffer) return false; - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; + assert(!HWBuffer->IsVertex); + const auto *ib = HWBuffer->IndexBuffer; + assert(ib); - const void *indices = mb->getIndices(); - u32 indexCount = mb->getIndexCount(); - - GLenum indexSize; - switch (mb->getIndexType()) { - case (EIT_16BIT): { + u32 indexSize; + switch (ib->getType()) { + case EIT_16BIT: indexSize = sizeof(u16); break; - } - case (EIT_32BIT): { + case EIT_32BIT: indexSize = sizeof(u32); break; - } - default: { + default: return false; } - } - const size_t bufferSize = indexCount * indexSize; - accountHWBufferUpload(bufferSize); + const size_t bufferSize = ib->getCount() * indexSize; - // get or create buffer - bool newBuffer = false; - if (!HWBuffer->vbo_indicesID) { - GL.GenBuffers(1, &HWBuffer->vbo_indicesID); - if (!HWBuffer->vbo_indicesID) - return false; - newBuffer = true; - } else if (HWBuffer->vbo_indicesSize < bufferSize) { - newBuffer = true; - } - - GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID); - - // copy data to graphics card - if (!newBuffer) - GL.BufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, bufferSize, indices); - else { - HWBuffer->vbo_indicesSize = bufferSize; - - GLenum usage = GL_STATIC_DRAW; - if (HWBuffer->Mapped_Index == scene::EHM_STREAM) - usage = GL_STREAM_DRAW; - else if (HWBuffer->Mapped_Index == scene::EHM_DYNAMIC) - usage = GL_DYNAMIC_DRAW; - GL.BufferData(GL_ELEMENT_ARRAY_BUFFER, bufferSize, indices, usage); - } - - GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - - return (!TEST_GL_ERROR(this)); + return updateHardwareBuffer(HWBuffer, ib->getData(), bufferSize, ib->getHardwareMappingHint()); } -//! updates hardware buffer if needed bool COpenGL3DriverBase::updateHardwareBuffer(SHWBufferLink *HWBuffer) { if (!HWBuffer) @@ -592,50 +564,35 @@ bool COpenGL3DriverBase::updateHardwareBuffer(SHWBufferLink *HWBuffer) auto *b = static_cast(HWBuffer); - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) { - if (HWBuffer->ChangedID_Vertex != HWBuffer->MeshBuffer->getChangedID_Vertex() || !b->vbo_verticesID) { - - HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex(); - + if (b->IsVertex) { + assert(b->VertexBuffer); + if (b->ChangedID != b->VertexBuffer->getChangedID() || !b->vbo_ID) { if (!updateVertexHardwareBuffer(b)) return false; + b->ChangedID = b->VertexBuffer->getChangedID(); } - } - - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) { - if (HWBuffer->ChangedID_Index != HWBuffer->MeshBuffer->getChangedID_Index() || !b->vbo_indicesID) { - - HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index(); - + } else { + assert(b->IndexBuffer); + if (b->ChangedID != b->IndexBuffer->getChangedID() || !b->vbo_ID) { if (!updateIndexHardwareBuffer(b)) return false; + b->ChangedID = b->IndexBuffer->getChangedID(); } } - return true; } -//! Create hardware buffer from meshbuffer -COpenGL3DriverBase::SHWBufferLink *COpenGL3DriverBase::createHardwareBuffer(const scene::IMeshBuffer *mb) +COpenGL3DriverBase::SHWBufferLink *COpenGL3DriverBase::createHardwareBuffer(const scene::IVertexBuffer *vb) { - if (!mb || (mb->getHardwareMappingHint_Index() == scene::EHM_NEVER && mb->getHardwareMappingHint_Vertex() == scene::EHM_NEVER)) + if (!vb || vb->getHardwareMappingHint() == scene::EHM_NEVER) return 0; - SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(mb); + SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(vb); // add to map HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer); - HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex(); - HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index(); - HWBuffer->Mapped_Vertex = mb->getHardwareMappingHint_Vertex(); - HWBuffer->Mapped_Index = mb->getHardwareMappingHint_Index(); - HWBuffer->vbo_verticesID = 0; - HWBuffer->vbo_indicesID = 0; - HWBuffer->vbo_verticesSize = 0; - HWBuffer->vbo_indicesSize = 0; - - if (!updateHardwareBuffer(HWBuffer)) { + if (!updateVertexHardwareBuffer(HWBuffer)) { deleteHardwareBuffer(HWBuffer); return 0; } @@ -643,57 +600,70 @@ COpenGL3DriverBase::SHWBufferLink *COpenGL3DriverBase::createHardwareBuffer(cons return HWBuffer; } -void COpenGL3DriverBase::deleteHardwareBuffer(SHWBufferLink *_HWBuffer) +COpenGL3DriverBase::SHWBufferLink *COpenGL3DriverBase::createHardwareBuffer(const scene::IIndexBuffer *ib) { - if (!_HWBuffer) - return; + if (!ib || ib->getHardwareMappingHint() == scene::EHM_NEVER) + return 0; - SHWBufferLink_opengl *HWBuffer = static_cast(_HWBuffer); - if (HWBuffer->vbo_verticesID) { - GL.DeleteBuffers(1, &HWBuffer->vbo_verticesID); - HWBuffer->vbo_verticesID = 0; - } - if (HWBuffer->vbo_indicesID) { - GL.DeleteBuffers(1, &HWBuffer->vbo_indicesID); - HWBuffer->vbo_indicesID = 0; + SHWBufferLink_opengl *HWBuffer = new SHWBufferLink_opengl(ib); + + // add to map + HWBuffer->listPosition = HWBufferList.insert(HWBufferList.end(), HWBuffer); + + if (!updateIndexHardwareBuffer(HWBuffer)) { + deleteHardwareBuffer(HWBuffer); + return 0; } - CNullDriver::deleteHardwareBuffer(_HWBuffer); + return HWBuffer; } -//! Draw hardware buffer -void COpenGL3DriverBase::drawHardwareBuffer(SHWBufferLink *_HWBuffer) +void COpenGL3DriverBase::deleteHardwareBuffer(SHWBufferLink *HWBuffer) { - if (!_HWBuffer) + if (!HWBuffer) return; - SHWBufferLink_opengl *HWBuffer = static_cast(_HWBuffer); - - updateHardwareBuffer(HWBuffer); // check if update is needed - - const scene::IMeshBuffer *mb = HWBuffer->MeshBuffer; - const void *vertices = mb->getVertices(); - const void *indexList = mb->getIndices(); - - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) { - GL.BindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID); - vertices = 0; + auto *b = static_cast(HWBuffer); + if (b->vbo_ID) { + GL.DeleteBuffers(1, &b->vbo_ID); + b->vbo_ID = 0; } - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) { - GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID); - indexList = 0; + CNullDriver::deleteHardwareBuffer(HWBuffer); +} + +void COpenGL3DriverBase::drawBuffers(const scene::IVertexBuffer *vb, + const scene::IIndexBuffer *ib, u32 PrimitiveCount, + scene::E_PRIMITIVE_TYPE PrimitiveType) +{ + if (!vb || !ib) + return; + + auto *hwvert = static_cast(getBufferLink(vb)); + auto *hwidx = static_cast(getBufferLink(ib)); + updateHardwareBuffer(hwvert); + updateHardwareBuffer(hwidx); + + const void *vertices = vb->getData(); + if (hwvert) { + assert(hwvert->IsVertex); + GL.BindBuffer(GL_ARRAY_BUFFER, hwvert->vbo_ID); + vertices = nullptr; } - drawVertexPrimitiveList(vertices, mb->getVertexCount(), - indexList, mb->getPrimitiveCount(), - mb->getVertexType(), mb->getPrimitiveType(), - mb->getIndexType()); + const void *indexList = ib->getData(); + if (hwidx) { + assert(!hwidx->IsVertex); + GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, hwidx->vbo_ID); + indexList = nullptr; + } - if (HWBuffer->Mapped_Vertex != scene::EHM_NEVER) + drawVertexPrimitiveList(vertices, vb->getCount(), indexList, + PrimitiveCount, vb->getType(), PrimitiveType, ib->getType()); + + if (hwvert) GL.BindBuffer(GL_ARRAY_BUFFER, 0); - - if (HWBuffer->Mapped_Index != scene::EHM_NEVER) + if (hwidx) GL.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } diff --git a/irr/src/OpenGL/Driver.h b/irr/src/OpenGL/Driver.h index be4e4db9e..3104b164d 100644 --- a/irr/src/OpenGL/Driver.h +++ b/irr/src/OpenGL/Driver.h @@ -46,16 +46,11 @@ public: struct SHWBufferLink_opengl : public SHWBufferLink { - SHWBufferLink_opengl(const scene::IMeshBuffer *meshBuffer) : - SHWBufferLink(meshBuffer), vbo_verticesID(0), vbo_indicesID(0), vbo_verticesSize(0), vbo_indicesSize(0) - { - } + SHWBufferLink_opengl(const scene::IVertexBuffer *vb) : SHWBufferLink(vb) {} + SHWBufferLink_opengl(const scene::IIndexBuffer *ib) : SHWBufferLink(ib) {} - u32 vbo_verticesID; // tmp - u32 vbo_indicesID; // tmp - - u32 vbo_verticesSize; // tmp - u32 vbo_indicesSize; // tmp + GLuint vbo_ID = 0; + u32 vbo_Size = 0; }; bool updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer); @@ -64,14 +59,18 @@ public: //! updates hardware buffer if needed bool updateHardwareBuffer(SHWBufferLink *HWBuffer) override; - //! Create hardware buffer from mesh - SHWBufferLink *createHardwareBuffer(const scene::IMeshBuffer *mb) override; + //! Create hardware buffer from vertex buffer + SHWBufferLink *createHardwareBuffer(const scene::IVertexBuffer *vb) override; + + //! Create hardware buffer from index buffer + SHWBufferLink *createHardwareBuffer(const scene::IIndexBuffer *ib) override; //! Delete hardware buffer (only some drivers can) void deleteHardwareBuffer(SHWBufferLink *HWBuffer) override; - //! Draw hardware buffer - void drawHardwareBuffer(SHWBufferLink *HWBuffer) override; + void drawBuffers(const scene::IVertexBuffer *vb, + const scene::IIndexBuffer *ib, u32 primCount, + scene::E_PRIMITIVE_TYPE pType = scene::EPT_TRIANGLES) override; IRenderTarget *addRenderTarget() override; @@ -291,10 +290,7 @@ protected: LockRenderStateMode = false; } - void draw2D3DVertexPrimitiveList(const void *vertices, - u32 vertexCount, const void *indexList, u32 primitiveCount, - E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, - E_INDEX_TYPE iType, bool is3D); + bool updateHardwareBuffer(SHWBufferLink_opengl *b, const void *buffer, size_t bufferSize, scene::E_HARDWARE_MAPPING hint); void createMaterialRenderers(); From 62131fe2954d5e79651f9e47611675ba43da4d31 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 28 Aug 2024 21:46:52 +0200 Subject: [PATCH 71/75] Put all pieces together and clean up leftover code --- irr/include/CIndexBuffer.h | 2 +- irr/include/CMeshBuffer.h | 26 +---------- irr/include/IIndexBuffer.h | 26 +++++++++++ irr/include/IMeshBuffer.h | 55 ++--------------------- irr/include/SSkinMeshBuffer.h | 82 +++++++++++++--------------------- irr/src/CMeshManipulator.cpp | 35 +++++++++------ irr/src/COBJMeshFileLoader.cpp | 7 +-- src/client/hud.cpp | 17 +++---- src/client/hud.h | 3 +- src/client/mapblock_mesh.cpp | 14 ++---- src/client/mapblock_mesh.h | 7 ++- 11 files changed, 105 insertions(+), 169 deletions(-) diff --git a/irr/include/CIndexBuffer.h b/irr/include/CIndexBuffer.h index 4701c2e34..676408e65 100644 --- a/irr/include/CIndexBuffer.h +++ b/irr/include/CIndexBuffer.h @@ -24,7 +24,7 @@ public: #endif } - video::E_INDEX_TYPE getType() const + video::E_INDEX_TYPE getType() const override { static_assert(sizeof(T) == 2 || sizeof(T) == 4, "invalid index type"); return sizeof(T) == 2 ? video::EIT_16BIT : video::EIT_32BIT; diff --git a/irr/include/CMeshBuffer.h b/irr/include/CMeshBuffer.h index 6b13a8982..6eb325889 100644 --- a/irr/include/CMeshBuffer.h +++ b/irr/include/CMeshBuffer.h @@ -20,7 +20,7 @@ class CMeshBuffer : public IMeshBuffer public: //! Default constructor for empty meshbuffer CMeshBuffer() : - HWBuffer(NULL), PrimitiveType(EPT_TRIANGLES) + PrimitiveType(EPT_TRIANGLES) { #ifdef _DEBUG setDebugName("CMeshBuffer"); @@ -69,18 +69,6 @@ public: return Indices; } - // TEMPORARY helper for direct buffer acess - inline auto &VertexBuffer() - { - return Vertices->Data; - } - - // TEMPORARY helper for direct buffer acess - inline auto &IndexBuffer() - { - return Indices->Data; - } - //! Get the axis aligned bounding box /** \return Axis aligned bounding box of this buffer. */ const core::aabbox3d &getBoundingBox() const override @@ -142,18 +130,6 @@ public: return PrimitiveType; } - void setHWBuffer(void *ptr) const override - { - HWBuffer = ptr; - } - - void *getHWBuffer() const override - { - return HWBuffer; - } - - mutable void *HWBuffer; - //! Material for this meshbuffer. video::SMaterial Material; //! Vertex buffer diff --git a/irr/include/IIndexBuffer.h b/irr/include/IIndexBuffer.h index bdbbb6dcc..01282f0c8 100644 --- a/irr/include/IIndexBuffer.h +++ b/irr/include/IIndexBuffer.h @@ -7,6 +7,7 @@ #include "IReferenceCounted.h" #include "irrArray.h" #include "EHardwareBufferFlags.h" +#include "EPrimitiveTypes.h" #include "SVertexIndex.h" namespace irr @@ -50,6 +51,31 @@ public: //! Used by the VideoDriver to remember the buffer link. virtual void setHWBuffer(void *ptr) const = 0; virtual void *getHWBuffer() const = 0; + + //! Calculate how many geometric primitives would be drawn + u32 getPrimitiveCount(E_PRIMITIVE_TYPE primitiveType) const + { + const u32 indexCount = getCount(); + switch (primitiveType) { + case scene::EPT_POINTS: + return indexCount; + case scene::EPT_LINE_STRIP: + return indexCount - 1; + case scene::EPT_LINE_LOOP: + return indexCount; + case scene::EPT_LINES: + return indexCount / 2; + case scene::EPT_TRIANGLE_STRIP: + return (indexCount - 2); + case scene::EPT_TRIANGLE_FAN: + return (indexCount - 2); + case scene::EPT_TRIANGLES: + return indexCount / 3; + case scene::EPT_POINT_SPRITES: + return indexCount; + } + return 0; + } }; } // end namespace scene diff --git a/irr/include/IMeshBuffer.h b/irr/include/IMeshBuffer.h index 8a43db87e..55c05211a 100644 --- a/irr/include/IMeshBuffer.h +++ b/irr/include/IMeshBuffer.h @@ -176,18 +176,6 @@ public: return getVertexBuffer()->getTCoords(i); } - //! get the current hardware mapping hint - inline E_HARDWARE_MAPPING getHardwareMappingHint_Vertex() const - { - return getVertexBuffer()->getHardwareMappingHint(); - } - - //! get the current hardware mapping hint - inline E_HARDWARE_MAPPING getHardwareMappingHint_Index() const - { - return getIndexBuffer()->getHardwareMappingHint(); - } - //! set the hardware mapping hint, for driver inline void setHardwareMappingHint(E_HARDWARE_MAPPING newMappingHint, E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) { @@ -206,26 +194,8 @@ public: getIndexBuffer()->setDirty(); } - //! Get the currently used ID for identification of changes. - /** This shouldn't be used for anything outside the VideoDriver. */ - inline u32 getChangedID_Vertex() const - { - return getVertexBuffer()->getChangedID(); - } - - //! Get the currently used ID for identification of changes. - /** This shouldn't be used for anything outside the VideoDriver. */ - inline u32 getChangedID_Index() const - { - return getIndexBuffer()->getChangedID(); - } - /* End helpers */ - //! Used by the VideoDriver to remember the buffer link. - virtual void setHWBuffer(void *ptr) const = 0; - virtual void *getHWBuffer() const = 0; - //! Describe what kind of primitive geometry is used by the meshbuffer /** Note: Default is EPT_TRIANGLES. Using other types is fine for rendering. But meshbuffer manipulation functions might expect type EPT_TRIANGLES @@ -237,32 +207,13 @@ public: virtual E_PRIMITIVE_TYPE getPrimitiveType() const = 0; //! Calculate how many geometric primitives are used by this meshbuffer - virtual u32 getPrimitiveCount() const + u32 getPrimitiveCount() const { - const u32 indexCount = getIndexCount(); - switch (getPrimitiveType()) { - case scene::EPT_POINTS: - return indexCount; - case scene::EPT_LINE_STRIP: - return indexCount - 1; - case scene::EPT_LINE_LOOP: - return indexCount; - case scene::EPT_LINES: - return indexCount / 2; - case scene::EPT_TRIANGLE_STRIP: - return (indexCount - 2); - case scene::EPT_TRIANGLE_FAN: - return (indexCount - 2); - case scene::EPT_TRIANGLES: - return indexCount / 3; - case scene::EPT_POINT_SPRITES: - return indexCount; - } - return 0; + return getIndexBuffer()->getPrimitiveCount(getPrimitiveType()); } //! Calculate size of vertices and indices in memory - virtual size_t getSize() const + size_t getSize() const { size_t ret = 0; switch (getVertexType()) { diff --git a/irr/include/SSkinMeshBuffer.h b/irr/include/SSkinMeshBuffer.h index 949551161..fa6eae63a 100644 --- a/irr/include/SSkinMeshBuffer.h +++ b/irr/include/SSkinMeshBuffer.h @@ -21,7 +21,7 @@ struct SSkinMeshBuffer : public IMeshBuffer //! Default constructor SSkinMeshBuffer(video::E_VERTEX_TYPE vt = video::EVT_STANDARD) : VertexType(vt), PrimitiveType(EPT_TRIANGLES), - HWBuffer(nullptr), BoundingBoxNeedsRecalculated(true) + BoundingBoxNeedsRecalculated(true) { #ifdef _DEBUG setDebugName("SSkinMeshBuffer"); @@ -72,7 +72,7 @@ struct SSkinMeshBuffer : public IMeshBuffer } } - scene::IVertexBuffer *getVertexBuffer() + scene::IVertexBuffer *getVertexBuffer() override { switch (VertexType) { case video::EVT_2TCOORDS: @@ -119,6 +119,28 @@ struct SSkinMeshBuffer : public IMeshBuffer BoundingBox = box; } +private: + template void recalculateBoundingBox(const CVertexBuffer *buf) + { + if (!buf->getCount()) { + BoundingBox.reset(0, 0, 0); + } else { + auto &vertices = buf->Data; + BoundingBox.reset(vertices[0].Pos); + for (size_t i = 1; i < vertices.size(); ++i) + BoundingBox.addInternalPoint(vertices[i].Pos); + } + } + + template static void copyVertex(const T1 &src, T2 &dst) + { + dst.Pos = src.Pos; + dst.Normal = src.Normal; + dst.Color = src.Color; + dst.TCoords = src.TCoords; + } +public: + //! Recalculate bounding box void recalculateBoundingBox() override { @@ -129,36 +151,15 @@ struct SSkinMeshBuffer : public IMeshBuffer switch (VertexType) { case video::EVT_STANDARD: { - if (!Vertices_Standard->getCount()) - BoundingBox.reset(0, 0, 0); - else { - auto &vertices = Vertices_Standard->Data; - BoundingBox.reset(vertices[0].Pos); - for (size_t i = 1; i < vertices.size(); ++i) - BoundingBox.addInternalPoint(vertices[i].Pos); - } + recalculateBoundingBox(Vertices_Standard); break; } case video::EVT_2TCOORDS: { - if (!Vertices_2TCoords->getCount()) - BoundingBox.reset(0, 0, 0); - else { - auto &vertices = Vertices_2TCoords->Data; - BoundingBox.reset(vertices[0].Pos); - for (size_t i = 1; i < vertices.size(); ++i) - BoundingBox.addInternalPoint(vertices[i].Pos); - } + recalculateBoundingBox(Vertices_2TCoords); break; } case video::EVT_TANGENTS: { - if (!Vertices_Tangents->getCount()) - BoundingBox.reset(0, 0, 0); - else { - auto &vertices = Vertices_Tangents->Data; - BoundingBox.reset(vertices[0].Pos); - for (size_t i = 1; i < vertices.size(); ++i) - BoundingBox.addInternalPoint(vertices[i].Pos); - } + recalculateBoundingBox(Vertices_Tangents); break; } } @@ -170,10 +171,7 @@ struct SSkinMeshBuffer : public IMeshBuffer if (VertexType == video::EVT_STANDARD) { video::S3DVertex2TCoords Vertex; for (const auto &Vertex_Standard : Vertices_Standard->Data) { - Vertex.Color = Vertex_Standard.Color; - Vertex.Pos = Vertex_Standard.Pos; - Vertex.Normal = Vertex_Standard.Normal; - Vertex.TCoords = Vertex_Standard.TCoords; + copyVertex(Vertex_Standard, Vertex); Vertices_2TCoords->Data.push_back(Vertex); } Vertices_Standard->Data.clear(); @@ -185,12 +183,9 @@ struct SSkinMeshBuffer : public IMeshBuffer void convertToTangents() { if (VertexType == video::EVT_STANDARD) { - video::S3DVertexTangents Vertex; + video::S3DVertexTangents Vertex; for (const auto &Vertex_Standard : Vertices_Standard->Data) { - Vertex.Color = Vertex_Standard.Color; - Vertex.Pos = Vertex_Standard.Pos; - Vertex.Normal = Vertex_Standard.Normal; - Vertex.TCoords = Vertex_Standard.TCoords; + copyVertex(Vertex_Standard, Vertex); Vertices_Tangents->Data.push_back(Vertex); } Vertices_Standard->Data.clear(); @@ -198,10 +193,7 @@ struct SSkinMeshBuffer : public IMeshBuffer } else if (VertexType == video::EVT_2TCOORDS) { video::S3DVertexTangents Vertex; for (const auto &Vertex_2TCoords : Vertices_2TCoords->Data) { - Vertex.Color = Vertex_2TCoords.Color; - Vertex.Pos = Vertex_2TCoords.Pos; - Vertex.Normal = Vertex_2TCoords.Normal; - Vertex.TCoords = Vertex_2TCoords.TCoords; + copyVertex(Vertex_2TCoords, Vertex); Vertices_Tangents->Data.push_back(Vertex); } Vertices_2TCoords->Data.clear(); @@ -227,16 +219,6 @@ struct SSkinMeshBuffer : public IMeshBuffer return PrimitiveType; } - void setHWBuffer(void *ptr) const override - { - HWBuffer = ptr; - } - - void *getHWBuffer() const override - { - return HWBuffer; - } - //! Call this after changing the positions of any vertex. void boundingBoxNeedsRecalculated(void) { BoundingBoxNeedsRecalculated = true; } @@ -255,8 +237,6 @@ struct SSkinMeshBuffer : public IMeshBuffer //! Primitive type used for rendering (triangles, lines, ...) E_PRIMITIVE_TYPE PrimitiveType; - mutable void *HWBuffer; - bool BoundingBoxNeedsRecalculated; }; diff --git a/irr/src/CMeshManipulator.cpp b/irr/src/CMeshManipulator.cpp index 9b2bd0cde..2c9d05336 100644 --- a/irr/src/CMeshManipulator.cpp +++ b/irr/src/CMeshManipulator.cpp @@ -67,7 +67,7 @@ void recalculateNormalsT(IMeshBuffer *buffer, bool smooth, bool angleWeighted) core::vector3df weight(1.f, 1.f, 1.f); if (angleWeighted) - weight = irr::scene::getAngleWeight(v1, v2, v3); // writing irr::scene:: necessary for borland + weight = getAngleWeight(v1, v2, v3); buffer->getNormal(idx[i + 0]) += weight.X * normal; buffer->getNormal(idx[i + 1]) += weight.Y * normal; @@ -115,6 +115,21 @@ void CMeshManipulator::recalculateNormals(scene::IMesh *mesh, bool smooth, bool } } +template +void copyVertices(const scene::IVertexBuffer *src, scene::CVertexBuffer *dst) +{ + _IRR_DEBUG_BREAK_IF(T::getType() != src->getType()); + auto *data = static_cast(src->getData()); + dst->Data.assign(data, data + src->getCount()); +} + +static void copyIndices(const scene::IIndexBuffer *src, scene::SIndexBuffer *dst) +{ + _IRR_DEBUG_BREAK_IF(src->getType() != video::EIT_16BIT); + auto *data = static_cast(src->getData()); + dst->Data.assign(data, data + src->getCount()); +} + //! Clones a static IMesh into a modifyable SMesh. // not yet 32bit SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const @@ -132,30 +147,24 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const case video::EVT_STANDARD: { SMeshBuffer *buffer = new SMeshBuffer(); buffer->Material = mb->getMaterial(); - auto *vt = static_cast(mb->getVertices()); - buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); - auto *indices = mb->getIndices(); - buffer->IndexBuffer().insert(buffer->IndexBuffer().end(), indices, indices + mb->getIndexCount()); + copyVertices(mb->getVertexBuffer(), buffer->Vertices); + copyIndices(mb->getIndexBuffer(), buffer->Indices); clone->addMeshBuffer(buffer); buffer->drop(); } break; case video::EVT_2TCOORDS: { SMeshBufferLightMap *buffer = new SMeshBufferLightMap(); buffer->Material = mb->getMaterial(); - auto *vt = static_cast(mb->getVertices()); - buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); - auto *indices = mb->getIndices(); - buffer->IndexBuffer().insert(buffer->IndexBuffer().end(), indices, indices + mb->getIndexCount()); + copyVertices(mb->getVertexBuffer(), buffer->Vertices); + copyIndices(mb->getIndexBuffer(), buffer->Indices); clone->addMeshBuffer(buffer); buffer->drop(); } break; case video::EVT_TANGENTS: { SMeshBufferTangents *buffer = new SMeshBufferTangents(); buffer->Material = mb->getMaterial(); - auto *vt = static_cast(mb->getVertices()); - buffer->VertexBuffer().insert(buffer->VertexBuffer().end(), vt, vt + mb->getVertexCount()); - auto *indices = mb->getIndices(); - buffer->IndexBuffer().insert(buffer->IndexBuffer().end(), indices, indices + mb->getIndexCount()); + copyVertices(mb->getVertexBuffer(), buffer->Vertices); + copyIndices(mb->getIndexBuffer(), buffer->Indices); clone->addMeshBuffer(buffer); buffer->drop(); } break; diff --git a/irr/src/COBJMeshFileLoader.cpp b/irr/src/COBJMeshFileLoader.cpp index 064fc4186..bceba6a90 100644 --- a/irr/src/COBJMeshFileLoader.cpp +++ b/irr/src/COBJMeshFileLoader.cpp @@ -192,6 +192,7 @@ IAnimatedMesh *COBJMeshFileLoader::createMesh(io::IReadFile *file) faceCorners.set_used(0); // fast clear // read in all vertices + auto &Vertices = currMtl->Meshbuffer->Vertices->Data; linePtr = goNextWord(linePtr, endPtr); while (0 != linePtr[0]) { // Array to communicate with retrieveVertexIndices() @@ -228,8 +229,8 @@ IAnimatedMesh *COBJMeshFileLoader::createMesh(io::IReadFile *file) if (n != currMtl->VertMap.end()) { vertLocation = n->second; } else { - currMtl->Meshbuffer->VertexBuffer().push_back(v); - vertLocation = currMtl->Meshbuffer->VertexBuffer().size() - 1; + Vertices.push_back(v); + vertLocation = Vertices.size() - 1; currMtl->VertMap.emplace(v, vertLocation); } @@ -247,7 +248,7 @@ IAnimatedMesh *COBJMeshFileLoader::createMesh(io::IReadFile *file) } // triangulate the face - auto &Indices = currMtl->Meshbuffer->IndexBuffer(); + auto &Indices = currMtl->Meshbuffer->Indices->Data; const int c = faceCorners[0]; for (u32 i = 1; i < faceCorners.size() - 1; ++i) { // Add a triangle diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 52fbfb6b4..3ff83bdae 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -133,9 +133,10 @@ Hud::Hud(Client *client, LocalPlayer *player, rangelim(g_settings->getS16("selectionbox_width"), 1, 5); // Prepare mesh for compass drawing - auto &b = m_rotation_mesh_buffer; - auto &vertices = b.Vertices->Data; - auto &indices = b.Indices->Data; + m_rotation_mesh_buffer.reset(new scene::SMeshBuffer()); + auto *b = m_rotation_mesh_buffer.get(); + auto &vertices = b->Vertices->Data; + auto &indices = b->Indices->Data; vertices.resize(4); indices.resize(6); @@ -154,9 +155,9 @@ Hud::Hud(Client *client, LocalPlayer *player, indices[4] = 3; indices[5] = 0; - b.getMaterial().Lighting = false; - b.getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; - //b.setHardwareMappingHint(scene::EHM_STATIC); // FIXME: incorrectly stack allocated, not safe! + b->getMaterial().Lighting = false; + b->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; + b->setHardwareMappingHint(scene::EHM_STATIC); } void Hud::readScalingSetting() @@ -658,10 +659,10 @@ void Hud::drawCompassRotate(HudElement *e, video::ITexture *texture, driver->setTransform(video::ETS_VIEW, core::matrix4()); driver->setTransform(video::ETS_WORLD, Matrix); - video::SMaterial &material = m_rotation_mesh_buffer.getMaterial(); + auto &material = m_rotation_mesh_buffer->getMaterial(); material.TextureLayers[0].Texture = texture; driver->setMaterial(material); - driver->drawMeshBuffer(&m_rotation_mesh_buffer); + driver->drawMeshBuffer(m_rotation_mesh_buffer.get()); driver->setTransform(video::ETS_WORLD, core::matrix4()); driver->setTransform(video::ETS_VIEW, oldViewMat); diff --git a/src/client/hud.h b/src/client/hud.h index 55b24abd3..120215f45 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "irr_ptr.h" #include "irr_aabb3d.h" #include "../hud.h" @@ -152,7 +153,7 @@ private: video::SMaterial m_selection_material; video::SMaterial m_block_bounds_material; - scene::SMeshBuffer m_rotation_mesh_buffer; + irr_ptr m_rotation_mesh_buffer; enum { diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 59883d052..5b47a32ff 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -594,12 +594,9 @@ void MapBlockBspTree::traverse(s32 node, v3f viewpoint, std::vector &output void PartialMeshBuffer::draw(video::IVideoDriver *driver) const { - // Swap out the index buffer before drawing the mesh - auto *old = m_buffer->Indices; - m_buffer->Indices = m_indices.get(); - m_buffer->setDirty(scene::EBT_INDEX); // TODO remove - driver->drawMeshBuffer(m_buffer); - m_buffer->Indices = old; + const auto pType = m_buffer->getPrimitiveType(); + driver->drawBuffers(m_buffer->getVertexBuffer(), m_indices.get(), + m_indices->getPrimitiveCount(pType), pType); } /* @@ -785,11 +782,6 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs } } - // Transparent parts have changing indices - // TODO: remove - for (auto &it : m_transparent_triangles) - it.buffer->setHardwareMappingHint(scene::EHM_STREAM, scene::EBT_INDEX); - m_bsp_tree.buildTree(&m_transparent_triangles, data->side_length); // Check if animation is required for this mesh diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index e958814f3..d2c651525 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -145,19 +145,18 @@ private: * * Attach alternate `Indices` to an existing mesh buffer, to make it possible to use different * indices with the same vertex buffer. - * */ class PartialMeshBuffer { public: PartialMeshBuffer(scene::SMeshBuffer *buffer, std::vector &&vertex_indices) : - m_buffer(buffer) + m_buffer(buffer), m_indices(make_irr()) { - m_indices.reset(new scene::SIndexBuffer()); m_indices->Data = std::move(vertex_indices); + m_indices->setHardwareMappingHint(scene::EHM_STATIC); } - scene::IMeshBuffer *getBuffer() const { return m_buffer; } + auto *getBuffer() const { return m_buffer; } void draw(video::IVideoDriver *driver) const; From 3fb404961240eed6a3d97eaa0dcdda71241d098f Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 29 Aug 2024 16:13:30 +0200 Subject: [PATCH 72/75] Prevent accidentally copy/move of refcounted objects --- irr/include/IReferenceCounted.h | 4 ++++ src/util/pointer.h | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/irr/include/IReferenceCounted.h b/irr/include/IReferenceCounted.h index 6f85bb904..e1000d389 100644 --- a/irr/include/IReferenceCounted.h +++ b/irr/include/IReferenceCounted.h @@ -51,6 +51,10 @@ public: { } + // Reference counted objects can be neither copied nor moved. + IReferenceCounted(const IReferenceCounted &) = delete; + IReferenceCounted &operator=(const IReferenceCounted &) = delete; + //! Grabs the object. Increments the reference counter by one. /** Someone who calls grab() to an object, should later also call drop() to it. If an object never gets as much drop() as diff --git a/src/util/pointer.h b/src/util/pointer.h index 528897a1c..b7fad4fe1 100644 --- a/src/util/pointer.h +++ b/src/util/pointer.h @@ -273,9 +273,7 @@ public: void grab() noexcept { ++m_refcount; } void drop() noexcept { if (--m_refcount == 0) delete this; } - // Preserve own reference count. - IntrusiveReferenceCounted(const IntrusiveReferenceCounted &) {} - IntrusiveReferenceCounted &operator=(const IntrusiveReferenceCounted &) { return *this; } + DISABLE_CLASS_COPY(IntrusiveReferenceCounted) private: u32 m_refcount = 1; }; From e55fb6da71ff311cd6aba4314304f78053a62db1 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 1 Sep 2024 15:17:54 +0200 Subject: [PATCH 73/75] Mark a bunch of classes as final --- irr/include/CIndexBuffer.h | 2 +- irr/include/CMeshBuffer.h | 2 +- irr/include/CVertexBuffer.h | 2 +- irr/include/SAnimatedMesh.h | 2 +- irr/include/SMesh.h | 2 +- irr/include/SSkinMeshBuffer.h | 2 +- irr/src/CFileSystem.h | 2 +- irr/src/CImage.h | 2 +- irr/src/CLimitReadFile.h | 2 +- irr/src/CMemoryFile.h | 2 +- irr/src/CReadFile.h | 2 +- irr/src/CSceneManager.h | 2 +- src/client/inputhandler.h | 7 ++++--- src/client/texturesource.cpp | 2 +- src/network/mtp/impl.h | 2 +- 15 files changed, 18 insertions(+), 17 deletions(-) diff --git a/irr/include/CIndexBuffer.h b/irr/include/CIndexBuffer.h index 676408e65..4318ddaab 100644 --- a/irr/include/CIndexBuffer.h +++ b/irr/include/CIndexBuffer.h @@ -13,7 +13,7 @@ namespace scene { //! Template implementation of the IIndexBuffer interface template -class CIndexBuffer : public IIndexBuffer +class CIndexBuffer final : public IIndexBuffer { public: //! Default constructor for empty buffer diff --git a/irr/include/CMeshBuffer.h b/irr/include/CMeshBuffer.h index 6eb325889..9a6d4426f 100644 --- a/irr/include/CMeshBuffer.h +++ b/irr/include/CMeshBuffer.h @@ -15,7 +15,7 @@ namespace scene { //! Template implementation of the IMeshBuffer interface template -class CMeshBuffer : public IMeshBuffer +class CMeshBuffer final : public IMeshBuffer { public: //! Default constructor for empty meshbuffer diff --git a/irr/include/CVertexBuffer.h b/irr/include/CVertexBuffer.h index 6861913bc..3559bbddb 100644 --- a/irr/include/CVertexBuffer.h +++ b/irr/include/CVertexBuffer.h @@ -13,7 +13,7 @@ namespace scene { //! Template implementation of the IVertexBuffer interface template -class CVertexBuffer : public IVertexBuffer +class CVertexBuffer final : public IVertexBuffer { public: //! Default constructor for empty buffer diff --git a/irr/include/SAnimatedMesh.h b/irr/include/SAnimatedMesh.h index 42ba6b952..8fe14b41f 100644 --- a/irr/include/SAnimatedMesh.h +++ b/irr/include/SAnimatedMesh.h @@ -15,7 +15,7 @@ namespace scene { //! Simple implementation of the IAnimatedMesh interface. -struct SAnimatedMesh : public IAnimatedMesh +struct SAnimatedMesh final : public IAnimatedMesh { //! constructor SAnimatedMesh(scene::IMesh *mesh = 0, scene::E_ANIMATED_MESH_TYPE type = scene::EAMT_UNKNOWN) : diff --git a/irr/include/SMesh.h b/irr/include/SMesh.h index 66e6ecc08..15fa65115 100644 --- a/irr/include/SMesh.h +++ b/irr/include/SMesh.h @@ -14,7 +14,7 @@ namespace irr namespace scene { //! Simple implementation of the IMesh interface. -struct SMesh : public IMesh +struct SMesh final : public IMesh { //! constructor SMesh() diff --git a/irr/include/SSkinMeshBuffer.h b/irr/include/SSkinMeshBuffer.h index fa6eae63a..303207d93 100644 --- a/irr/include/SSkinMeshBuffer.h +++ b/irr/include/SSkinMeshBuffer.h @@ -16,7 +16,7 @@ namespace scene { //! A mesh buffer able to choose between S3DVertex2TCoords, S3DVertex and S3DVertexTangents at runtime -struct SSkinMeshBuffer : public IMeshBuffer +struct SSkinMeshBuffer final : public IMeshBuffer { //! Default constructor SSkinMeshBuffer(video::E_VERTEX_TYPE vt = video::EVT_STANDARD) : diff --git a/irr/src/CFileSystem.h b/irr/src/CFileSystem.h index 208a1ac41..9400d85a3 100644 --- a/irr/src/CFileSystem.h +++ b/irr/src/CFileSystem.h @@ -17,7 +17,7 @@ class CZipReader; /*! FileSystem which uses normal files and one zipfile */ -class CFileSystem : public IFileSystem +class CFileSystem final : public IFileSystem { public: //! constructor diff --git a/irr/src/CImage.h b/irr/src/CImage.h index 955f85705..33a34386e 100644 --- a/irr/src/CImage.h +++ b/irr/src/CImage.h @@ -21,7 +21,7 @@ inline bool checkImageDimensions(u32 width, u32 height) //! IImage implementation with a lot of special image operations for //! 16 bit A1R5G5B5/32 Bit A8R8G8B8 images, which are used by the SoftwareDevice. -class CImage : public IImage +class CImage final : public IImage { public: //! constructor from raw image data diff --git a/irr/src/CLimitReadFile.h b/irr/src/CLimitReadFile.h index 1594135e6..6b02cfdc0 100644 --- a/irr/src/CLimitReadFile.h +++ b/irr/src/CLimitReadFile.h @@ -20,7 +20,7 @@ namespace io This can be useful, for example for reading uncompressed files in an archive (zip, tar). !*/ -class CLimitReadFile : public IReadFile +class CLimitReadFile final : public IReadFile { public: CLimitReadFile(IReadFile *alreadyOpenedFile, long pos, long areaSize, const io::path &name); diff --git a/irr/src/CMemoryFile.h b/irr/src/CMemoryFile.h index 3ce0e8460..83d77cb77 100644 --- a/irr/src/CMemoryFile.h +++ b/irr/src/CMemoryFile.h @@ -17,7 +17,7 @@ namespace io /*! Class for reading from memory. */ -class CMemoryReadFile : public IMemoryReadFile +class CMemoryReadFile final : public IMemoryReadFile { public: //! Constructor diff --git a/irr/src/CReadFile.h b/irr/src/CReadFile.h index 14f674772..c9231be7c 100644 --- a/irr/src/CReadFile.h +++ b/irr/src/CReadFile.h @@ -17,7 +17,7 @@ namespace io /*! Class for reading a real file from disk. */ -class CReadFile : public IReadFile +class CReadFile final : public IReadFile { public: CReadFile(const io::path &fileName); diff --git a/irr/src/CSceneManager.h b/irr/src/CSceneManager.h index 32df145ec..4ef6d64b0 100644 --- a/irr/src/CSceneManager.h +++ b/irr/src/CSceneManager.h @@ -25,7 +25,7 @@ class IMeshCache; /*! The Scene Manager manages scene nodes, mesh resources, cameras and all the other stuff. */ -class CSceneManager : public ISceneManager, public ISceneNode +class CSceneManager final : public ISceneManager, public ISceneNode { public: //! constructor diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index ba85b30ad..daf01c488 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -269,11 +269,12 @@ public: JoystickController joystick; KeyCache keycache; }; + /* - Separated input handler + Separated input handler implementations */ -class RealInputHandler : public InputHandler +class RealInputHandler final : public InputHandler { public: RealInputHandler(MyEventReceiver *receiver) : m_receiver(receiver) @@ -372,7 +373,7 @@ private: v2s32 m_mousepos; }; -class RandomInputHandler : public InputHandler +class RandomInputHandler final : public InputHandler { public: RandomInputHandler() = default; diff --git a/src/client/texturesource.cpp b/src/client/texturesource.cpp index 12a21771a..a4222f414 100644 --- a/src/client/texturesource.cpp +++ b/src/client/texturesource.cpp @@ -39,7 +39,7 @@ struct TextureInfo }; // TextureSource -class TextureSource : public IWritableTextureSource +class TextureSource final : public IWritableTextureSource { public: TextureSource(); diff --git a/src/network/mtp/impl.h b/src/network/mtp/impl.h index cc1d4c2ed..7105bac6d 100644 --- a/src/network/mtp/impl.h +++ b/src/network/mtp/impl.h @@ -234,7 +234,7 @@ class Peer : public IPeer { class UDPPeer; -class Connection : public IConnection +class Connection final : public IConnection { public: friend class ConnectionSendThread; From 2bc9dc54ff9974c5fb04613022f8527e010bd1a9 Mon Sep 17 00:00:00 2001 From: Zughy <63455151+Zughy@users.noreply.github.com> Date: Mon, 2 Sep 2024 21:50:28 +0200 Subject: [PATCH 74/75] Windows/vcpkg instructions: enable i18n by default --- doc/compiling/windows.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/compiling/windows.md b/doc/compiling/windows.md index c63a7b319..eeaf2e4fd 100644 --- a/doc/compiling/windows.md +++ b/doc/compiling/windows.md @@ -14,7 +14,7 @@ It is highly recommended to use vcpkg as package manager. After you successfully built vcpkg you can easily install the required libraries: ```powershell -vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp gettext sdl2 --triplet x64-windows +vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp gettext[tools] sdl2 --triplet x64-windows ``` - `curl` is optional, but required to read the serverlist, `curl[winssl]` is required to use the content store. @@ -52,7 +52,7 @@ Use `--triplet` to specify the target triplet, e.g. `x64-windows` or `x86-window Run the following script in PowerShell: ```powershell -cmake . -G"Visual Studio 15 2017 Win64" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_GETTEXT=OFF -DENABLE_CURSES=OFF +cmake . -G"Visual Studio 16 2019" -DCMAKE_TOOLCHAIN_FILE=D:/vcpkg/scripts/buildsystems/vcpkg.cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_CURSES=OFF cmake --build . --config Release ``` Make sure that the right compiler is selected and the path to the vcpkg toolchain is correct. From d5d8fb629b69df57573dc2ef4043b287f6a0c5f0 Mon Sep 17 00:00:00 2001 From: red-001 Date: Mon, 2 Sep 2024 20:50:43 +0100 Subject: [PATCH 75/75] Simplify `TOSERVER_INIT` and `TOCLIENT_HELLO` - Network compression support was never added. - Client hasn't used the returned playername since at least 0.4-stable. --- src/client/client.cpp | 5 +---- src/network/clientpackethandler.cpp | 13 ++++--------- src/network/networkprotocol.h | 13 +++---------- src/network/serverpackethandler.cpp | 16 +++++----------- src/server/clientiface.h | 5 ----- 5 files changed, 13 insertions(+), 39 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index d3ad62512..dedbebbb4 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -1141,10 +1141,7 @@ void Client::sendInit(const std::string &playerName) { NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size())); - // we don't support network compression yet - u16 supp_comp_modes = NETPROTO_COMPRESSION_NONE; - - pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) supp_comp_modes; + pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u16) 0; pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX; pkt << playerName; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 4b9e8ceea..4eefd1c59 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -78,11 +78,11 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) u8 serialization_ver; u16 proto_ver; - u16 compression_mode; + u16 unused_compression_mode; u32 auth_mechs; - std::string username_legacy; // for case insensitivity - *pkt >> serialization_ver >> compression_mode >> proto_ver - >> auth_mechs >> username_legacy; + std::string unused; + *pkt >> serialization_ver >> unused_compression_mode >> proto_ver + >> auth_mechs >> unused; // Chose an auth method we support AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs); @@ -91,7 +91,6 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) << "serialization_ver=" << (u32)serialization_ver << ", auth_mechs=" << auth_mechs << ", proto_ver=" << proto_ver - << ", compression_mode=" << compression_mode << ". Doing auth with mech " << chosen_auth_mechanism << std::endl; if (!ser_ver_supported(serialization_ver)) { @@ -103,10 +102,6 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) m_server_ser_ver = serialization_ver; m_proto_ver = proto_ver; - //TODO verify that username_legacy matches sent username, only - // differs in casing (make both uppercase and compare) - // This is only necessary though when we actually want to add casing support - if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) { // we received a TOCLIENT_HELLO while auth was already going on errorstream << "Client: TOCLIENT_HELLO while auth was already going on" diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 467124f60..5af9859ae 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -243,9 +243,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #define CLIENT_PROTOCOL_VERSION_MIN 37 #define CLIENT_PROTOCOL_VERSION_MAX LATEST_PROTOCOL_VERSION -#define PASSWORD_SIZE 28 // Maximum password length. Allows for - // base64-encoded SHA-1 (27+\0). - // See also formspec [Version History] in doc/lua_api.md #define FORMSPEC_API_VERSION 7 @@ -260,10 +257,10 @@ enum ToClientCommand : u16 Sent after TOSERVER_INIT. u8 deployed serialization version - u16 deployed network compression mode + u16 unused (network compression, never implemeneted) u16 deployed protocol version u32 supported auth methods - std::string username that should be used for legacy hash (for proper casing) + std::string unused (used to be username) */ TOCLIENT_AUTH_ACCEPT = 0x03, /* @@ -914,7 +911,7 @@ enum ToServerCommand : u16 Sent first after connected. u8 serialization version (=SER_FMT_VER_HIGHEST_READ) - u16 supported network compression modes + u16 unused (supported network compression modes, never implemeneted) u16 minimum supported network protocol version u16 maximum supported network protocol version std::string player name @@ -1149,10 +1146,6 @@ enum AccessDeniedCode : u8 { SERVER_ACCESSDENIED_MAX, }; -enum NetProtoCompressionMode { - NETPROTO_COMPRESSION_NONE = 0, -}; - enum PlayerListModifer : u8 { PLAYER_LIST_INIT, diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index c0718d43b..7d8555c8e 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -101,12 +101,12 @@ void Server::handleCommand_Init(NetworkPacket* pkt) // First byte after command is maximum supported // serialization version u8 client_max; - u16 supp_compr_modes; + u16 unused; u16 min_net_proto_version = 0; u16 max_net_proto_version; std::string playerName; - *pkt >> client_max >> supp_compr_modes >> min_net_proto_version + *pkt >> client_max >> unused >> min_net_proto_version >> max_net_proto_version >> playerName; u8 our_max = SER_FMT_VER_HIGHEST_READ; @@ -190,9 +190,6 @@ void Server::handleCommand_Init(NetworkPacket* pkt) } m_clients.setPlayerName(peer_id, playername); - //TODO (later) case insensitivity - - std::string legacyPlayerNameCasing = playerName; if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) { actionstream << "Server: Player with the name \"singleplayer\" tried " @@ -279,17 +276,14 @@ void Server::handleCommand_Init(NetworkPacket* pkt) verbosestream << "Sending TOCLIENT_HELLO with auth method field: " << auth_mechs << std::endl; - NetworkPacket resp_pkt(TOCLIENT_HELLO, - 1 + 4 + legacyPlayerNameCasing.size(), peer_id); + NetworkPacket resp_pkt(TOCLIENT_HELLO, 0, peer_id); - u16 depl_compress_mode = NETPROTO_COMPRESSION_NONE; - resp_pkt << depl_serial_v << depl_compress_mode << net_proto_version - << auth_mechs << legacyPlayerNameCasing; + resp_pkt << depl_serial_v << u16(0) << net_proto_version + << auth_mechs << std::string_view(); Send(&resp_pkt); client->allowed_auth_mechs = auth_mechs; - client->setDeployedCompressionMode(depl_compress_mode); m_clients.event(peer_id, CSE_Hello); } diff --git a/src/server/clientiface.h b/src/server/clientiface.h index d4d21ceb2..f930e7f3e 100644 --- a/src/server/clientiface.h +++ b/src/server/clientiface.h @@ -321,9 +321,6 @@ public: void setPendingSerializationVersion(u8 version) { m_pending_serialization_version = version; } - void setDeployedCompressionMode(u16 byteFlag) - { m_deployed_compression = byteFlag; } - void confirmSerializationVersion() { serialization_version = m_pending_serialization_version; } @@ -449,8 +446,6 @@ private: std::string m_full_version = "unknown"; - u16 m_deployed_compression = 0; - /* time this client was created */