diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index e5c98a901..14d63f4fb 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -132,16 +132,16 @@ jobs: run: | ./util/test_multiplayer.sh - # Build with prometheus-cpp (server-only) - clang_11_prometheus: - name: "clang_11 (PROMETHEUS=1)" - runs-on: ubuntu-22.04 + # Build with prometheus-cpp (server-only), also runs on ARM64 + clang_prometheus_arm: + name: "clang (with Prometheus, ARM64)" + runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - name: Install deps run: | source ./util/ci/common.sh - install_linux_deps clang-11 + install_linux_deps --headless clang libluajit-5.1-dev - name: Build prometheus-cpp run: ./util/ci/build_prometheus_cpp.sh @@ -150,8 +150,8 @@ jobs: run: | ./util/ci/build.sh env: - CC: clang-11 - CXX: clang++-11 + CC: clang + CXX: clang++ CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0 -DENABLE_CURSES=0" - name: Test diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bf2effd1..70a027f57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ set(CLANG_MINIMUM_VERSION "7.0.1") # You should not need to edit these manually, use util/bump_version.sh set(VERSION_MAJOR 5) -set(VERSION_MINOR 12) +set(VERSION_MINOR 13) set(VERSION_PATCH 0) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") diff --git a/android/build.gradle b/android/build.gradle index 61637c2ec..d2a3b3b2f 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,7 +1,7 @@ // 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", 12) // Version Minor +project.ext.set("versionMinor", 13) // Version Minor project.ext.set("versionPatch", 0) // Version Patch // ^ keep in sync with cmake diff --git a/builtin/common/settings/generate_from_settingtypes.lua b/builtin/common/settings/generate_from_settingtypes.lua index 60422e74d..1c1535a17 100644 --- a/builtin/common/settings/generate_from_settingtypes.lua +++ b/builtin/common/settings/generate_from_settingtypes.lua @@ -102,7 +102,7 @@ end local translation_file_header = [[ // This file is automatically generated // It contains a bunch of fake gettext calls, to tell xgettext about the strings in config files -// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua +// To update it, refer to the bottom of builtin/common/settings/init.lua fake_function() {]] @@ -110,15 +110,15 @@ local function create_translation_file(settings) local result = { translation_file_header } for _, entry in ipairs(settings) do if entry.type == "category" then - insert(result, sprintf("\tgettext(%q);", entry.name)) + insert(result, sprintf("\t/* xgettext:no-c-format */ gettext(%q);", entry.name)) else if entry.readable_name then - insert(result, sprintf("\tgettext(%q);", entry.readable_name)) + insert(result, sprintf("\t/* xgettext:no-c-format */ gettext(%q);", entry.readable_name)) end if entry.comment ~= "" then local comment_escaped = entry.comment:gsub("\n", "\\n") comment_escaped = comment_escaped:gsub("\"", "\\\"") - insert(result, "\tgettext(\"" .. comment_escaped .. "\");") + insert(result, "\t/* xgettext:no-c-format */ gettext(\"" .. comment_escaped .. "\");") end end end diff --git a/builtin/common/strict.lua b/builtin/common/strict.lua index 9bfa8d7a2..b3c4ccce4 100644 --- a/builtin/common/strict.lua +++ b/builtin/common/strict.lua @@ -19,12 +19,14 @@ function meta:__newindex(name, value) return end local info = getinfo(2, "Sl") - local desc = ("%s:%d"):format(info.short_src, info.currentline) - local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name) - if not warned[warn_key] and info.what ~= "main" and info.what ~= "C" then - core.log("warning", ("Assignment to undeclared global %q inside a function at %s.") - :format(name, desc)) - warned[warn_key] = true + if info ~= nil then + local desc = ("%s:%d"):format(info.short_src, info.currentline) + local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name) + if not warned[warn_key] and info.what ~= "main" and info.what ~= "C" then + core.log("warning", ("Assignment to undeclared global %q inside a function at %s.") + :format(name, desc)) + warned[warn_key] = true + end end declared[name] = true end @@ -35,6 +37,9 @@ function meta:__index(name) return end local info = getinfo(2, "Sl") + if info == nil then + return + end local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name) if not warned[warn_key] and info.what ~= "C" then core.log("warning", ("Undeclared global variable %q accessed at %s:%s") diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua index 47b9d086c..31b104ed3 100644 --- a/builtin/fstk/ui.lua +++ b/builtin/fstk/ui.lua @@ -118,7 +118,7 @@ function ui.update() if (active_toplevel_ui_elements > 1) then core.log("warning", "more than one active ui ".. - "element, self most likely isn't intended") + "element, this most likely isn't intended") end if (active_toplevel_ui_elements == 0) then @@ -166,6 +166,10 @@ end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- core.button_handler = function(fields) + if fields["try_quit"] and not fields["key_enter"] then + core.event_handler("MenuQuit") + return + end if fields["btn_reconnect_yes"] then gamedata.reconnect_requested = false gamedata.errormessage = nil diff --git a/builtin/mainmenu/content/contentdb.lua b/builtin/mainmenu/content/contentdb.lua index be148841a..fbd94376d 100644 --- a/builtin/mainmenu/content/contentdb.lua +++ b/builtin/mainmenu/content/contentdb.lua @@ -170,14 +170,16 @@ function contentdb.get_package_by_id(id) end -function contentdb.calculate_package_id(type, author, name) - local id = author:lower() .. "/" +local function strip_game_suffix(type, name) if (type == nil or type == "game") and #name > 5 and name:sub(#name - 4) == "_game" then - id = id .. name:sub(1, #name - 5) + return name:sub(1, #name - 5) else - id = id .. name + return name end - return id +end + +function contentdb.calculate_package_id(type, author, name) + return author:lower() .. "/" .. strip_game_suffix(type, name) end @@ -427,7 +429,7 @@ function contentdb.set_packages_from_api(packages) -- We currently don't support name changing local suffix = "/" .. package.name if alias:sub(-#suffix) == suffix then - contentdb.aliases[alias:lower()] = package.id + contentdb.aliases[strip_game_suffix(packages.type, alias:lower())] = package.id end end end diff --git a/builtin/mainmenu/credits.json b/builtin/mainmenu/credits.json index bd4f0591f..61e53d3ba 100644 --- a/builtin/mainmenu/credits.json +++ b/builtin/mainmenu/credits.json @@ -47,22 +47,20 @@ ], "#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py", "contributors": [ - "JosiahWI", "Erich Schubert", "wrrrzr", - "1F616EMO", - "red-001 ", - "veprogames", - "paradust7", - "AFCMS", "siliconsniffer", - "Wuzzy", - "Zemtzov7" + "JosiahWI", + "veprogames", + "Miguel P.L", + "AFCMS" ], "previous_contributors": [ "Ælla Chiana Moskopp (erle) [Logo]", "numzero", + "red-001 ", "Giuseppe Bilotta", + "HybridDog", "ClobberXD", "Dániel Juhász (juhdanad) ", "MirceaKitsune ", @@ -75,6 +73,7 @@ "stujones11", "Rogier ", "Gregory Currie (gregorycu)", + "paradust7", "JacobF", "Jeija " ] diff --git a/builtin/mainmenu/dlg_rebind_keys.lua b/builtin/mainmenu/dlg_rebind_keys.lua index d1b442004..ec4d1357f 100644 --- a/builtin/mainmenu/dlg_rebind_keys.lua +++ b/builtin/mainmenu/dlg_rebind_keys.lua @@ -33,13 +33,13 @@ end local function buttonhandler(this, fields) if fields.reconfigure then + local parent = this.parent + close_dialog(this) - local maintab = ui.find_by_name("maintab") - local dlg = create_settings_dlg("controls_keyboard_and_mouse") - dlg:set_parent(maintab) - maintab:hide() + dlg:set_parent(parent) + parent:hide() dlg:show() return true @@ -74,7 +74,7 @@ local function create_rebind_keys_dlg() return dlg end -function migrate_keybindings() +function migrate_keybindings(parent) -- Show migration dialog if the user upgraded from an earlier version -- and this has not yet been shown before, *or* if keys settings had to be changed if core.is_first_run then @@ -95,14 +95,14 @@ function migrate_keybindings() end if not has_migration then - return + return parent end - local maintab = ui.find_by_name("maintab") - local dlg = create_rebind_keys_dlg() - dlg:set_parent(maintab) - maintab:hide() + dlg:set_parent(parent) + parent:hide() dlg:show() ui.update() + + return dlg end diff --git a/builtin/mainmenu/dlg_reinstall_mtg.lua b/builtin/mainmenu/dlg_reinstall_mtg.lua index c167b2656..6c512ea03 100644 --- a/builtin/mainmenu/dlg_reinstall_mtg.lua +++ b/builtin/mainmenu/dlg_reinstall_mtg.lua @@ -11,7 +11,7 @@ local SETTING_NAME = "no_mtg_notification" -function check_reinstall_mtg() +function check_reinstall_mtg(parent) -- used to be in minetest.conf if core.settings:get_bool(SETTING_NAME) then cache_settings:set_bool(SETTING_NAME, true) @@ -19,14 +19,14 @@ function check_reinstall_mtg() end if cache_settings:get_bool(SETTING_NAME) then - return + return parent end local games = core.get_games() for _, game in ipairs(games) do if game.id == "minetest" then cache_settings:set_bool(SETTING_NAME, true) - return + return parent end end @@ -40,16 +40,16 @@ function check_reinstall_mtg() end if not mtg_world_found then cache_settings:set_bool(SETTING_NAME, true) - return + return parent end - local maintab = ui.find_by_name("maintab") - local dlg = create_reinstall_mtg_dlg() - dlg:set_parent(maintab) - maintab:hide() + dlg:set_parent(parent) + parent:hide() dlg:show() ui.update() + + return dlg end local function get_formspec(dialogdata) @@ -74,22 +74,22 @@ end local function buttonhandler(this, fields) if fields.reinstall then + local parent = this.parent + -- Don't set "no_mtg_notification" here so that the dialog will be shown -- again if downloading MTG fails for whatever reason. this:delete() - local maintab = ui.find_by_name("maintab") - local dlg = create_contentdb_dlg(nil, "minetest/minetest") - dlg:set_parent(maintab) - maintab:hide() + dlg:set_parent(parent) + parent:hide() dlg:show() return true end if fields.dismiss then - cache_settings:set_bool("no_mtg_notification", true) + cache_settings:set_bool(SETTING_NAME, true) this:delete() return true end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 14185a484..c1bbc0ae4 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -112,8 +112,12 @@ local function init_globals() tv_main:show() ui.update() - check_reinstall_mtg() - migrate_keybindings() + -- synchronous, chain parents to only show one at a time + local parent = tv_main + parent = migrate_keybindings(parent) + check_reinstall_mtg(parent) + + -- asynchronous, will only be shown if we're still on "maintab" check_new_version() end diff --git a/doc/breakages.md b/doc/breakages.md index 412cf2e41..6c9acbd95 100644 --- a/doc/breakages.md +++ b/doc/breakages.md @@ -25,3 +25,4 @@ This list is largely advisory and items may be reevaluated once the time comes. * remove built-in knockback and related functions entirely * remove `safe` parameter from `core.serialize`, always enforce `safe = true`. possibly error when `loadstring` calls are encountered in `core.deserialize`. +* introduce strict type checking for all instances of `v3s16` / `v3f` read from Lua diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index c19e22cdb..ff4af5ccc 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -1,12 +1,15 @@ -Luanti Lua Client Modding API Reference 5.12.0 +Luanti Lua Client Modding API Reference 5.13.0 ============================================== **WARNING**: if you're looking for the `minetest` namespace (e.g. `minetest.something`), it's now called `core` due to the renaming of Luanti (formerly Minetest). `minetest` will keep existing as an alias, so that old code won't break. +Note that `core` has already existed since version 0.4.10, so you can use it +safely without breaking backwards compatibility. + * More information at -* Luanti Documentation: +* Additional documentation: Introduction ------------ diff --git a/doc/lua_api.md b/doc/lua_api.md index 2411fecdd..6a5a54c47 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5,9 +5,12 @@ Luanti Lua Modding API Reference it's now called `core` due to the renaming of Luanti (formerly Minetest). `minetest` will keep existing as an alias, so that old code won't break. +Note that `core` has already existed since version 0.4.10, so you can use it +safely without breaking backwards compatibility. + * More information at -* Luanti Documentation: -* (Unofficial) Minetest Modding Book by rubenwardy: +* Additional documentation: +* (Unofficial) Luanti Modding Book by rubenwardy: * Modding tools: Introduction @@ -312,6 +315,9 @@ due to their space savings. Bone weights should be normalized, e.g. using ["normalize all" in Blender](https://docs.blender.org/manual/en/4.2/grease_pencil/modes/weight_paint/weights_menu.html#normalize-all). +Note that nodes using matrix transforms must not be animated. +This also extends to bone overrides, which must not be applied to them. + You can use the [Khronos glTF validator](https://github.com/KhronosGroup/glTF-Validator) to check whether a model is a valid glTF file. @@ -4581,11 +4587,13 @@ and offset the noise variation. The final fractal value noise variation is created as follows: +``` noise = offset + scale * (octave1 + octave2 * persistence + octave3 * persistence ^ 2 + octave4 * persistence ^ 3 + ...) +``` Noise Parameters ---------------- @@ -4699,11 +4707,13 @@ with restraint. The absolute value of each octave's noise variation is used when combining the octaves. The final value noise variation is created as follows: +``` noise = offset + scale * (abs(octave1) + abs(octave2) * persistence + abs(octave3) * persistence ^ 2 + abs(octave4) * persistence ^ 3 + ...) +``` ### Format example @@ -4998,7 +5008,8 @@ A VoxelManip object can be created any time using either: If the optional position parameters are present for either of these routines, the specified region will be pre-loaded into the VoxelManip object on creation. Otherwise, the area of map you wish to manipulate must first be loaded into the -VoxelManip object using `VoxelManip:read_from_map()`. +VoxelManip object using `VoxelManip:read_from_map()`, or an empty one created +with `VoxelManip:initialize()`. Note that `VoxelManip:read_from_map()` returns two position vectors. The region formed by these positions indicate the minimum and maximum (respectively) @@ -5009,14 +5020,14 @@ be queried any time after loading map data with `VoxelManip:get_emerged_area()`. Now that the VoxelManip object is populated with map data, your mod can fetch a copy of this data using either of two methods. `VoxelManip:get_node_at()`, which retrieves an individual node in a MapNode formatted table at the position -requested is the simplest method to use, but also the slowest. +requested. This is the simplest method to use, but also the slowest. Nodes in a VoxelManip object may also be read in bulk to a flat array table using: * `VoxelManip:get_data()` for node content (in Content ID form, see section [Content IDs]), -* `VoxelManip:get_light_data()` for node light levels, and +* `VoxelManip:get_light_data()` for node param (usually light levels), and * `VoxelManip:get_param2_data()` for the node type-dependent "param2" values. See section [Flat array format] for more details. @@ -5031,17 +5042,16 @@ internal state unless otherwise explicitly stated. Once the bulk data has been edited to your liking, the internal VoxelManip state can be set using: -* `VoxelManip:set_data()` for node content (in Content ID form, see section - [Content IDs]), -* `VoxelManip:set_light_data()` for node light levels, and -* `VoxelManip:set_param2_data()` for the node type-dependent `param2` values. +* `VoxelManip:set_data()` or +* `VoxelManip:set_light_data()` or +* `VoxelManip:set_param2_data()` The parameter to each of the above three functions can use any table at all in the same flat array format as produced by `get_data()` etc. and is not required to be a table retrieved from `get_data()`. Once the internal VoxelManip state has been modified to your liking, the -changes can be committed back to the map by calling `VoxelManip:write_to_map()` +changes can be committed back to the map by calling `VoxelManip:write_to_map()`. ### Flat array format @@ -5173,15 +5183,22 @@ inside the VoxelManip. Methods ------- -* `read_from_map(p1, p2)`: Loads a chunk of map into the VoxelManip object +* `read_from_map(p1, p2)`: Loads a part of the map into the VoxelManip object containing the region formed by `p1` and `p2`. - * returns actual emerged `pmin`, actual emerged `pmax` + * returns actual emerged `pmin`, actual emerged `pmax` (MapBlock-aligned) * Note that calling this multiple times will *add* to the area loaded in the VoxelManip, and not reset it. +* `initialize(p1, p2, [node])`: Clears and resizes the VoxelManip object to + comprise the region formed by `p1` and `p2`. + * **No data** is read from the map, so you can use this to treat `VoxelManip` + objects as general containers of node data. + * `node`: if present the data will be filled with this node; if not it will + be uninitialized + * returns actual emerged `pmin`, actual emerged `pmax` (MapBlock-aligned) + * (introduced in 5.13.0) * `write_to_map([light])`: Writes the data loaded from the `VoxelManip` back to the map. - * **important**: data must be set using `VoxelManip:set_data()` before - calling this. + * **important**: you should call `set_data()` before this, or nothing will change. * if `light` is true, then lighting is automatically recalculated. The default value is true. If `light` is false, no light calculations happen, and you should correct @@ -5242,6 +5259,15 @@ Methods where the engine will keep the map and the VM in sync automatically. * Note: this doesn't do what you think it does and is subject to removal. Don't use it! * `get_emerged_area()`: Returns actual emerged minimum and maximum positions. + * "Emerged" does not imply that this region was actually loaded from the map, + if `initialize()` has been used. +* `close()`: Frees the data buffers associated with the VoxelManip object. + It will become empty. + * Since Lua's garbage collector is not aware of the potentially significant + memory behind a VoxelManip, frequent VoxelManip usage can cause the server to + run out of RAM. Therefore it's recommend to call this method once you're done + with the VoxelManip. + * (introduced in 5.13.0) `VoxelArea` ----------- @@ -6562,13 +6588,10 @@ Environment access * The actual seed used is the noiseparams seed plus the world seed. * `core.get_value_noise(seeddiff, octaves, persistence, spread)` * Deprecated: use `core.get_value_noise(noiseparams)` instead. - * Return world-specific value noise * `core.get_perlin(noiseparams)` - * Deprecated: use `core.get_value_noise(noiseparams)` instead. - * Return world-specific value noise (was not Perlin noise) + * Deprecated: renamed to `core.get_value_noise` in version 5.12.0. * `core.get_perlin(seeddiff, octaves, persistence, spread)` - * Deprecated: use `core.get_value_noise(noiseparams)` instead. - * Return world-specific value noise (was not Perlin noise) + * Deprecated: renamed to `core.get_value_noise` in version 5.12.0. * `core.get_voxel_manip([pos1, pos2])` * Return voxel manipulator object. * Loads the manipulator from the map if positions are passed. @@ -9061,78 +9084,6 @@ offering very strong randomness. * `get_state()`: return generator state encoded in string * `set_state(state_string)`: restore generator state from encoded string -`ValueNoise` -------------- - -A value noise generator. -It can be created via `ValueNoise()` or `core.get_value_noise()`. -For legacy reasons, it can also be created via `PerlinNoise()` or `core.get_perlin()`, -but the implemented noise is not Perlin noise. -For `core.get_value_noise()`, the actual seed used is the noiseparams seed -plus the world seed, to create world-specific noise. - -* `ValueNoise(noiseparams) -* `ValueNoise(seed, octaves, persistence, spread)` (Deprecated) -* `PerlinNoise(noiseparams)` (Deprecated) -* `PerlinNoise(seed, octaves, persistence, spread)` (Deprecated) - -* `core.get_value_noise(noiseparams)` -* `core.get_value_noise(seeddiff, octaves, persistence, spread)` (Deprecated) -* `core.get_perlin(noiseparams)` (Deprecated) -* `core.get_perlin(seeddiff, octaves, persistence, spread)` (Deprecated) - -### Methods - -* `get_2d(pos)`: returns 2D noise value at `pos={x=,y=}` -* `get_3d(pos)`: returns 3D noise value at `pos={x=,y=,z=}` - -`ValueNoiseMap` ----------------- - -A fast, bulk noise generator. - -It can be created via `ValueNoiseMap(noiseparams, size)` or -`core.get_value_noise_map(noiseparams, size)`. -For legacy reasons, it can also be created via `PerlinNoiseMap(noiseparams, size)` -or `core.get_perlin_map(noiseparams, size)`, but it is not Perlin noise. -For `core.get_value_noise_map()`, the actual seed used is the noiseparams seed -plus the world seed, to create world-specific noise. - -Format of `size` is `{x=dimx, y=dimy, z=dimz}`. The `z` component is omitted -for 2D noise, and it must be larger than 1 for 3D noise (otherwise -`nil` is returned). - -For each of the functions with an optional `buffer` parameter: If `buffer` is -not nil, this table will be used to store the result instead of creating a new -table. - -### Methods - -* `get_2d_map(pos)`: returns a `` times `` 2D array of 2D noise - with values starting at `pos={x=,y=}` -* `get_3d_map(pos)`: returns a `` times `` times `` - 3D array of 3D noise with values starting at `pos={x=,y=,z=}`. -* `get_2d_map_flat(pos, buffer)`: returns a flat `` element - array of 2D noise with values starting at `pos={x=,y=}` -* `get_3d_map_flat(pos, buffer)`: Same as `get2dMap_flat`, but 3D noise -* `calc_2d_map(pos)`: Calculates the 2d noise map starting at `pos`. The result - is stored internally. -* `calc_3d_map(pos)`: Calculates the 3d noise map starting at `pos`. The result - is stored internally. -* `get_map_slice(slice_offset, slice_size, buffer)`: In the form of an array, - returns a slice of the most recently computed noise results. The result slice - begins at coordinates `slice_offset` and takes a chunk of `slice_size`. - E.g., to grab a 2-slice high horizontal 2d plane of noise starting at buffer - offset y = 20: - `noisevals = noise:get_map_slice({y=20}, {y=2})` - It is important to note that `slice_offset` offset coordinates begin at 1, - and are relative to the starting position of the most recently calculated - noise. - To grab a single vertical column of noise starting at map coordinates - x = 1023, y=1000, z = 1000: - `noise:calc_3d_map({x=1000, y=1000, z=1000})` - `noisevals = noise:get_map_slice({x=24, z=1}, {x=1, z=1})` - `PlayerMetaRef` --------------- @@ -9184,14 +9135,17 @@ end The map is loaded as the ray advances. If the map is modified after the `Raycast` is created, the changes may or may not have an effect on the object. -It can be created via `Raycast(pos1, pos2, objects, liquids)` or -`core.raycast(pos1, pos2, objects, liquids)` where: +It can be created via `Raycast(pos1, pos2, objects, liquids, pointabilities)` +or `core.raycast(pos1, pos2, objects, liquids, pointabilities)` where: * `pos1`: start of the ray * `pos2`: end of the ray -* `objects`: if false, only nodes will be returned. Default is true. +* `objects`: if false, only nodes will be returned. Default is `true`. * `liquids`: if false, liquid nodes (`liquidtype ~= "none"`) won't be - returned. Default is false. + returned. Default is `false`. +* `pointabilities`: Allows overriding the `pointable` property of + nodes and objects. Uses the same format as the `pointabilities` property + of item definitions. Default is `nil`. ### Limitations @@ -9307,6 +9261,81 @@ to restrictions of JSON. * All methods in MetaDataRef +`ValueNoise` +------------- + +A value noise generator. +It can be created via `ValueNoise()` or `core.get_value_noise()`. +For `core.get_value_noise()`, the actual seed used is the noiseparams seed +plus the world seed, to create world-specific noise. + +* `ValueNoise(noiseparams)` +* `ValueNoise(seed, octaves, persistence, spread)` (deprecated) +* `core.get_value_noise(noiseparams)` +* `core.get_value_noise(seeddiff, octaves, persistence, spread)` (deprecated) + +These were previously called `PerlinNoise()` and `core.get_perlin()`, but the +implemented noise was not Perlin noise. They were renamed in 5.12.0. The old +names still exist as aliases. + +### Methods + +* `get_2d(pos)`: returns 2D noise value at `pos={x=,y=}` +* `get_3d(pos)`: returns 3D noise value at `pos={x=,y=,z=}` + +`ValueNoiseMap` +---------------- + +A fast, bulk noise generator. + +It can be created via `ValueNoiseMap(noiseparams, size)` or +`core.get_value_noise_map(noiseparams, size)`. +For `core.get_value_noise_map()`, the actual seed used is the noiseparams seed +plus the world seed, to create world-specific noise. + +These were previously called `PerlinNoiseMap()` and `core.get_perlin_map()`, +but the implemented noise was not Perlin noise. They were renamed in 5.12.0. +The old names still exist as aliases. + +Format of `size` is `{x=dimx, y=dimy, z=dimz}`. The `z` component is omitted +for 2D noise, and it must be larger than 1 for 3D noise (otherwise +`nil` is returned). + +For each of the functions with an optional `buffer` parameter: If `buffer` is +not nil, this table will be used to store the result instead of creating a new +table. + +### Methods + +* `get_2d_map(pos)`: returns a `` times `` 2D array of 2D noise + with values starting at `pos={x=,y=}` +* `get_3d_map(pos)`: returns a `` times `` times `` + 3D array of 3D noise with values starting at `pos={x=,y=,z=}`. +* `get_2d_map_flat(pos, buffer)`: returns a flat `` element + array of 2D noise with values starting at `pos={x=,y=}` +* `get_3d_map_flat(pos, buffer)`: Same as `get2dMap_flat`, but 3D noise +* `calc_2d_map(pos)`: Calculates the 2d noise map starting at `pos`. The result + is stored internally. +* `calc_3d_map(pos)`: Calculates the 3d noise map starting at `pos`. The result + is stored internally. +* `get_map_slice(slice_offset, slice_size, buffer)`: In the form of an array, + returns a slice of the most recently computed noise results. The result slice + begins at coordinates `slice_offset` and takes a chunk of `slice_size`. + E.g., to grab a 2-slice high horizontal 2d plane of noise starting at buffer + offset `y = 20`: + ```lua + noisevals = noise:get_map_slice({y=20}, {y=2}) + ``` + It is important to note that `slice_offset` offset coordinates begin at 1, + and are relative to the starting position of the most recently calculated + noise. + To grab a single vertical column of noise starting at map coordinates + `x = 1023, y=1000, z = 1000`: + ```lua + noise:calc_3d_map({x=1000, y=1000, z=1000}) + noisevals = noise:get_map_slice({x=24, z=1}, {x=1, z=1}) + ``` + @@ -10172,9 +10201,12 @@ Used by `core.register_node`. mesh = "", -- File name of mesh when using "mesh" drawtype -- The center of the node is the model origin. - -- For legacy reasons, models in OBJ format use a scale of 1 node = 1 unit; - -- all other model file formats use a scale of 1 node = 10 units, - -- consistent with the scale used for entities. + -- For legacy reasons, this uses a different scale depending on the mesh: + -- 1. For glTF models: 10 units = 1 node (consistent with the scale for entities). + -- 2. For obj models: 1 unit = 1 node. + -- 3. For b3d and x models: 1 unit = 1 node if static, otherwise 10 units = 1 node. + -- Using static glTF or obj models is recommended. + -- You can use the `visual_scale` multiplier to achieve the expected scale. selection_box = { -- see [Node boxes] for possibilities diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index c0dcc9068..56b51f3b9 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -1,4 +1,4 @@ -Luanti Lua Mainmenu API Reference 5.12.0 +Luanti Lua Mainmenu API Reference 5.13.0 ======================================== Introduction @@ -23,8 +23,8 @@ Callbacks * `core.button_handler(fields)`: called when a button is pressed. * `fields` = `{name1 = value1, name2 = value2, ...}` * `core.event_handler(event)` - * `event`: `"MenuQuit"`, `"KeyEnter"`, `"ExitButton"`, `"EditBoxEnter"` or - `"FullscreenChange"` + * `event`: `"MenuQuit"` (derived from `quit`) or `"FullscreenChange"` + The main menu may issue custom events, such as `"Refresh"` (server list). * `core.on_before_close()`: called before the menu is closed, either to exit or to join a game diff --git a/games/devtest/mods/testentities/models/LICENSE.txt b/games/devtest/mods/testentities/models/LICENSE.txt index 19ffffc5c..6ec445db6 100644 --- a/games/devtest/mods/testentities/models/LICENSE.txt +++ b/games/devtest/mods/testentities/models/LICENSE.txt @@ -12,4 +12,10 @@ Jordach (CC BY-SA 3.0): Zeg9 (CC BY-SA 3.0): testentities_lava_flan.x - testentities_lava_flan.png \ No newline at end of file + testentities_lava_flan.png + +"Cool Guy": + +hecks (refer to irr/LICENSE): + testentities_cool_guy.x + testentities_cool_guy.png diff --git a/games/devtest/mods/testentities/models/testentities_cool_guy.png b/games/devtest/mods/testentities/models/testentities_cool_guy.png new file mode 100644 index 000000000..84cc12e44 Binary files /dev/null and b/games/devtest/mods/testentities/models/testentities_cool_guy.png differ diff --git a/games/devtest/mods/testentities/models/testentities_cool_guy.x b/games/devtest/mods/testentities/models/testentities_cool_guy.x new file mode 100755 index 000000000..e806d8315 --- /dev/null +++ b/games/devtest/mods/testentities/models/testentities_cool_guy.x @@ -0,0 +1,2 @@ +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/games/devtest/mods/testentities/visuals.lua b/games/devtest/mods/testentities/visuals.lua index dfbf655ea..26b538e7f 100644 --- a/games/devtest/mods/testentities/visuals.lua +++ b/games/devtest/mods/testentities/visuals.lua @@ -102,6 +102,19 @@ core.register_entity("testentities:lava_flan", { end, }) +core.register_entity("testentities:cool_guy", { + initial_properties = { + visual = "mesh", + mesh = "testentities_cool_guy.x", + textures = { + "testentities_cool_guy.png" + }, + }, + on_activate = function(self) + self.object:set_animation({x = 0, y = 29}, 30, 0, true) + end, +}) + -- Advanced visual tests -- An entity for testing animated and yaw-modulated sprites diff --git a/games/devtest/mods/unittests/misc.lua b/games/devtest/mods/unittests/misc.lua index 65dc3259e..28cc2c1eb 100644 --- a/games/devtest/mods/unittests/misc.lua +++ b/games/devtest/mods/unittests/misc.lua @@ -67,18 +67,6 @@ local function test_dynamic_media(cb, player) end unittests.register("test_dynamic_media", test_dynamic_media, {async=true, player=true}) -local function test_v3f_metatable(player) - assert(vector.check(player:get_pos())) -end -unittests.register("test_v3f_metatable", test_v3f_metatable, {player=true}) - -local function test_v3s16_metatable(player, pos) - local node = core.get_node(pos) - local found_pos = core.find_node_near(pos, 0, node.name, true) - assert(vector.check(found_pos)) -end -unittests.register("test_v3s16_metatable", test_v3s16_metatable, {map=true}) - local function test_clear_meta(_, pos) local ref = core.get_meta(pos) diff --git a/irr/include/IAnimatedMesh.h b/irr/include/IAnimatedMesh.h index 62568bf6b..0ef0971bd 100644 --- a/irr/include/IAnimatedMesh.h +++ b/irr/include/IAnimatedMesh.h @@ -13,8 +13,8 @@ namespace scene //! Interface for an animated mesh. /** There are already simple implementations of this interface available so you don't have to implement this interface on your own if you need to: -You might want to use irr::scene::SAnimatedMesh, irr::scene::SMesh, -irr::scene::SMeshBuffer etc. */ +You might want to use irr::scene::SMesh, irr::scene::SMeshBuffer etc. +*/ class IAnimatedMesh : public IMesh { public: @@ -34,22 +34,8 @@ public: scene node the mesh is instantiated in.*/ virtual void setAnimationSpeed(f32 fps) = 0; - //! Returns the IMesh interface for a frame. - /** \param frame: Frame number, >= 0, <= getMaxFrameNumber() - Linear interpolation is used if this is between two frames. - \return Returns the animated mesh for the given frame */ - virtual IMesh *getMesh(f32 frame) = 0; - - //! Returns the type of the animated mesh. - /** In most cases it is not necessary to use this method. - This is useful for making a safe downcast. For example, - if getMeshType() returns EAMT_MD2 it's safe to cast the - IAnimatedMesh to IAnimatedMeshMD2. - \returns Type of the mesh. */ - E_ANIMATED_MESH_TYPE getMeshType() const override - { - return EAMT_UNKNOWN; - } + //! Returns the type of the animated mesh. Useful for safe downcasts. + E_ANIMATED_MESH_TYPE getMeshType() const = 0; }; } // end namespace scene diff --git a/irr/include/IAnimatedMeshSceneNode.h b/irr/include/IAnimatedMeshSceneNode.h index 8f9f6d661..2df8da917 100644 --- a/irr/include/IAnimatedMeshSceneNode.h +++ b/irr/include/IAnimatedMeshSceneNode.h @@ -12,35 +12,8 @@ namespace irr { namespace scene { -enum E_JOINT_UPDATE_ON_RENDER -{ - //! do nothing - EJUOR_NONE = 0, - - //! get joints positions from the mesh (for attached nodes, etc) - EJUOR_READ, - - //! control joint positions in the mesh (eg. ragdolls, or set the animation from animateJoints() ) - EJUOR_CONTROL -}; - class IAnimatedMeshSceneNode; -//! Callback interface for catching events of ended animations. -/** Implement this interface and use -IAnimatedMeshSceneNode::setAnimationEndCallback to be able to -be notified if an animation playback has ended. -**/ -class IAnimationEndCallBack : public virtual IReferenceCounted -{ -public: - //! Will be called when the animation playback has ended. - /** See IAnimatedMeshSceneNode::setAnimationEndCallback for - more information. - \param node: Node of which the animation has ended. */ - virtual void OnAnimationEnd(IAnimatedMeshSceneNode *node) = 0; -}; - //! Scene node capable of displaying an animated mesh. class IAnimatedMeshSceneNode : public ISceneNode { @@ -120,11 +93,10 @@ public: /** When true the animations are played looped */ virtual bool getLoopMode() const = 0; - //! Sets a callback interface which will be called if an animation playback has ended. - /** Set this to 0 to disable the callback again. - Please note that this will only be called when in non looped - mode, see IAnimatedMeshSceneNode::setLoopMode(). */ - virtual void setAnimationEndCallback(IAnimationEndCallBack *callback = 0) = 0; + //! Will be called right after the joints have been animated, + //! but before the transforms have been propagated recursively to children. + virtual void setOnAnimateCallback( + const std::function &cb) = 0; //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style. /** In this way it is possible to change the materials a mesh @@ -139,20 +111,15 @@ public: virtual void setMesh(IAnimatedMesh *mesh) = 0; //! Returns the current mesh - virtual IAnimatedMesh *getMesh(void) = 0; - - //! Set how the joints should be updated on render - virtual void setJointMode(E_JOINT_UPDATE_ON_RENDER mode) = 0; + virtual IAnimatedMesh *getMesh() = 0; //! Sets the transition time in seconds - /** Note: This needs to enable joints, and setJointmode set to - EJUOR_CONTROL. You must call animateJoints(), or the mesh will - not animate. */ + /** Note: You must call animateJoints(), or the mesh will not animate. */ virtual void setTransitionTime(f32 Time) = 0; //! animates the joints in the mesh based on the current frame. /** Also takes in to account transitions. */ - virtual void animateJoints(bool CalculateAbsolutePositions = true) = 0; + virtual void animateJoints() = 0; //! render mesh ignoring its transformation. /** Culling is unaffected. */ diff --git a/irr/include/IBoneSceneNode.h b/irr/include/IBoneSceneNode.h index eef55f6e0..d169b8d30 100644 --- a/irr/include/IBoneSceneNode.h +++ b/irr/include/IBoneSceneNode.h @@ -11,85 +11,41 @@ namespace irr namespace scene { -//! Enumeration for different bone animation modes -enum E_BONE_ANIMATION_MODE -{ - //! The bone is usually animated, unless it's parent is not animated - EBAM_AUTOMATIC = 0, - - //! The bone is animated by the skin, if it's parent is not animated then animation will resume from this bone onward - EBAM_ANIMATED, - - //! The bone is not animated by the skin - EBAM_UNANIMATED, - - //! Not an animation mode, just here to count the available modes - EBAM_COUNT - -}; - -enum E_BONE_SKINNING_SPACE -{ - //! local skinning, standard - EBSS_LOCAL = 0, - - //! global skinning - EBSS_GLOBAL, - - EBSS_COUNT -}; - -//! Names for bone animation modes -const c8 *const BoneAnimationModeNames[] = { - "automatic", - "animated", - "unanimated", - 0, - }; - //! Interface for bones used for skeletal animation. /** Used with SkinnedMesh and IAnimatedMeshSceneNode. */ class IBoneSceneNode : public ISceneNode { public: - IBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id = -1) : - ISceneNode(parent, mgr, id), positionHint(-1), scaleHint(-1), rotationHint(-1) {} + IBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, + s32 id = -1, u32 boneIndex = 0, + const std::optional &boneName = std::nullopt) + : + ISceneNode(parent, mgr, id), + BoneIndex(boneIndex) + { + setName(boneName); + } - //! Get the index of the bone - virtual u32 getBoneIndex() const = 0; + //! Returns the index of the bone + u32 getBoneIndex() const + { + return BoneIndex; + } - //! Sets the animation mode of the bone. - /** \return True if successful. (Unused) */ - virtual bool setAnimationMode(E_BONE_ANIMATION_MODE mode) = 0; + //! returns the axis aligned bounding box of this node + const core::aabbox3d &getBoundingBox() const override + { + return Box; + } - //! Gets the current animation mode of the bone - virtual E_BONE_ANIMATION_MODE getAnimationMode() const = 0; + const u32 BoneIndex; - //! Get the axis aligned bounding box of this node - const core::aabbox3d &getBoundingBox() const override = 0; - - //! Returns the relative transformation of the scene node. - // virtual core::matrix4 getRelativeTransformation() const = 0; - - //! The animation method. - void OnAnimate(u32 timeMs) override = 0; + // Bogus box; bone scene nodes are not rendered anyways. + static constexpr core::aabbox3d Box = {{0, 0, 0}}; //! The render method. /** Does nothing as bones are not visible. */ void render() override {} - - //! How the relative transformation of the bone is used - virtual void setSkinningSpace(E_BONE_SKINNING_SPACE space) = 0; - - //! How the relative transformation of the bone is used - virtual E_BONE_SKINNING_SPACE getSkinningSpace() const = 0; - - //! Updates the absolute position based on the relative and the parents position - virtual void updateAbsolutePositionOfAllChildren() = 0; - - s32 positionHint; - s32 scaleHint; - s32 rotationHint; }; } // end namespace scene diff --git a/irr/include/IMesh.h b/irr/include/IMesh.h index 8ee180d5d..e8656e868 100644 --- a/irr/include/IMesh.h +++ b/irr/include/IMesh.h @@ -20,38 +20,6 @@ enum E_ANIMATED_MESH_TYPE //! Unknown animated mesh type. EAMT_UNKNOWN = 0, - //! Quake 2 MD2 model file - EAMT_MD2, - - //! Quake 3 MD3 model file - EAMT_MD3, - - //! Maya .obj static model - EAMT_OBJ, - - //! Quake 3 .bsp static Map - EAMT_BSP, - - //! 3D Studio .3ds file - EAMT_3DS, - - //! My3D Mesh, the file format by Zhuck Dimitry - EAMT_MY3D, - - //! Pulsar LMTools .lmts file. This Irrlicht loader was written by Jonas Petersen - EAMT_LMTS, - - //! Cartography Shop .csm file. This loader was created by Saurav Mohapatra. - EAMT_CSM, - - //! .oct file for Paul Nette's FSRad or from Murphy McCauley's Blender .oct exporter. - /** The oct file format contains 3D geometry and lightmaps and - can be loaded directly by Irrlicht */ - EAMT_OCT, - - //! Halflife MDL model file - EAMT_MDL_HALFLIFE, - //! generic skinned mesh EAMT_SKINNED, @@ -119,9 +87,7 @@ public: virtual void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) = 0; //! Returns the type of the meshes. - /** This is useful for making a safe downcast. For example, - if getMeshType() returns EAMT_MD2 it's safe to cast the - IMesh to IAnimatedMeshMD2. + /** This is useful for making a safe downcast. Note: It's no longer just about animated meshes, that name has just historical reasons. \returns Type of the mesh */ virtual E_ANIMATED_MESH_TYPE getMeshType() const diff --git a/irr/include/IMeshManipulator.h b/irr/include/IMeshManipulator.h index c9d989cae..0312a38c8 100644 --- a/irr/include/IMeshManipulator.h +++ b/irr/include/IMeshManipulator.h @@ -66,26 +66,6 @@ public: IReferenceCounted::drop() for more information. */ virtual SMesh *createMeshCopy(IMesh *mesh) const = 0; - //! Get amount of polygons in mesh. - /** \param mesh Input mesh - \return Number of polygons in mesh. */ - virtual s32 getPolyCount(IMesh *mesh) const = 0; - - //! Get amount of polygons in mesh. - /** \param mesh Input mesh - \return Number of polygons in mesh. */ - virtual s32 getPolyCount(IAnimatedMesh *mesh) const = 0; - - //! Create a new AnimatedMesh and adds the mesh to it - /** \param mesh Input mesh - \param type The type of the animated mesh to create. - \return Newly created animated mesh with mesh as its only - content. When you don't need the animated mesh anymore, you - should call IAnimatedMesh::drop(). See - IReferenceCounted::drop() for more information. */ - virtual IAnimatedMesh *createAnimatedMesh(IMesh *mesh, - scene::E_ANIMATED_MESH_TYPE type = scene::EAMT_UNKNOWN) const = 0; - //! Apply a manipulator on the Meshbuffer /** \param func A functor defining the mesh manipulation. \param buffer The Meshbuffer to apply the manipulator to. diff --git a/irr/include/IMeshSceneNode.h b/irr/include/IMeshSceneNode.h index 1fb005405..3065f4c07 100644 --- a/irr/include/IMeshSceneNode.h +++ b/irr/include/IMeshSceneNode.h @@ -32,7 +32,7 @@ public: //! Get the currently defined mesh for display. /** \return Pointer to mesh which is displayed by this node. */ - virtual IMesh *getMesh(void) = 0; + virtual IMesh *getMesh() = 0; //! Sets if the scene node should not copy the materials of the mesh but use them directly. /** In this way it is possible to change the materials of a mesh diff --git a/irr/include/ISceneNode.h b/irr/include/ISceneNode.h index f91fd6499..573715861 100644 --- a/irr/include/ISceneNode.h +++ b/irr/include/ISceneNode.h @@ -94,16 +94,12 @@ public: \param timeMs Current time in milliseconds. */ virtual void OnAnimate(u32 timeMs) { - if (IsVisible) { - // update absolute position - updateAbsolutePosition(); + if (!IsVisible && Children.empty()) + return; - // perform the post render process on all children - - ISceneNodeList::iterator it = Children.begin(); - for (; it != Children.end(); ++it) - (*it)->OnAnimate(timeMs); - } + updateAbsolutePosition(); + for (auto *child : Children) + child->OnAnimate(timeMs); } //! Renders the node. diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index f392eb636..02c9a2d4f 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -172,7 +172,7 @@ public: \return Pointer to the texture, or 0 if the texture could not be loaded. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual ITexture *getTexture(const io::path &filename) = 0; + [[deprecated]] virtual ITexture *getTexture(const io::path &filename) = 0; //! Get access to a named texture. /** Loads the texture from disk if it is not @@ -184,7 +184,7 @@ public: \return Pointer to the texture, or 0 if the texture could not be loaded. This pointer should not be dropped. See IReferenceCounted::drop() for more information. */ - virtual ITexture *getTexture(io::IReadFile *file) = 0; + [[deprecated]] virtual ITexture *getTexture(io::IReadFile *file) = 0; //! Returns amount of textures currently loaded /** \return Amount of textures currently loaded */ diff --git a/irr/include/SAnimatedMesh.h b/irr/include/SAnimatedMesh.h deleted file mode 100644 index dcc65410f..000000000 --- a/irr/include/SAnimatedMesh.h +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#pragma once - -#include -#include "IAnimatedMesh.h" -#include "IMesh.h" -#include "aabbox3d.h" - -namespace irr -{ -namespace scene -{ - -//! Simple implementation of the IAnimatedMesh interface. -struct SAnimatedMesh final : public IAnimatedMesh -{ - //! constructor - SAnimatedMesh(scene::IMesh *mesh = 0, scene::E_ANIMATED_MESH_TYPE type = scene::EAMT_UNKNOWN) : - IAnimatedMesh(), FramesPerSecond(25.f), Type(type) - { - addMesh(mesh); - recalculateBoundingBox(); - } - - //! destructor - virtual ~SAnimatedMesh() - { - // drop meshes - for (auto *mesh : Meshes) - mesh->drop(); - } - - f32 getMaxFrameNumber() const override - { - return static_cast(Meshes.size() - 1); - } - - //! Gets the default animation speed of the animated mesh. - /** \return Amount of frames per second. If the amount is 0, it is a static, non animated mesh. */ - f32 getAnimationSpeed() const override - { - return FramesPerSecond; - } - - //! Gets the frame count of the animated mesh. - /** \param fps Frames per second to play the animation with. If the amount is 0, it is not animated. - The actual speed is set in the scene node the mesh is instantiated in.*/ - void setAnimationSpeed(f32 fps) override - { - FramesPerSecond = fps; - } - - //! Returns the IMesh interface for a frame. - /** \param frame: Frame number as zero based index. - \return The animated mesh based for the given frame */ - IMesh *getMesh(f32 frame) override - { - if (Meshes.empty()) - return nullptr; - - return Meshes[static_cast(frame)]; - } - - //! adds a Mesh - void addMesh(IMesh *mesh) - { - if (mesh) { - mesh->grab(); - Meshes.push_back(mesh); - } - } - - //! Returns an axis aligned bounding box of the mesh. - /** \return A bounding box of this mesh is returned. */ - const core::aabbox3d &getBoundingBox() const override - { - return Box; - } - - //! set user axis aligned bounding box - void setBoundingBox(const core::aabbox3df &box) override - { - Box = box; - } - - //! Recalculates the bounding box. - void recalculateBoundingBox() - { - Box.reset(0, 0, 0); - - if (Meshes.empty()) - return; - - Box = Meshes[0]->getBoundingBox(); - - for (u32 i = 1; i < Meshes.size(); ++i) - Box.addInternalBox(Meshes[i]->getBoundingBox()); - } - - //! Returns the type of the animated mesh. - E_ANIMATED_MESH_TYPE getMeshType() const override - { - return Type; - } - - //! returns amount of mesh buffers. - u32 getMeshBufferCount() const override - { - if (Meshes.empty()) - return 0; - - return Meshes[0]->getMeshBufferCount(); - } - - //! returns pointer to a mesh buffer - IMeshBuffer *getMeshBuffer(u32 nr) const override - { - if (Meshes.empty()) - return 0; - - return Meshes[0]->getMeshBuffer(nr); - } - - //! Returns pointer to a mesh buffer which fits a material - /** \param material: material to search for - \return Returns the pointer to the mesh buffer or - NULL if there is no such mesh buffer. */ - IMeshBuffer *getMeshBuffer(const video::SMaterial &material) const override - { - if (Meshes.empty()) - return 0; - - return Meshes[0]->getMeshBuffer(material); - } - - //! 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 < Meshes.size(); ++i) - Meshes[i]->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 < Meshes.size(); ++i) - Meshes[i]->setDirty(buffer); - } - - //! All meshes defining the animated mesh - std::vector Meshes; - - //! The bounding box of this mesh - core::aabbox3d Box{{0.0f, 0.0f, 0.0f}}; - - //! Default animation speed of this mesh. - f32 FramesPerSecond; - - //! The type of the mesh. - E_ANIMATED_MESH_TYPE Type; -}; - -} // end namespace scene -} // end namespace irr diff --git a/irr/include/SMesh.h b/irr/include/SMesh.h index 5e76fafc5..b22bb7749 100644 --- a/irr/include/SMesh.h +++ b/irr/include/SMesh.h @@ -5,7 +5,7 @@ #pragma once #include -#include "IMesh.h" +#include "IAnimatedMesh.h" #include "IMeshBuffer.h" #include "aabbox3d.h" @@ -14,7 +14,7 @@ namespace irr namespace scene { //! Simple implementation of the IMesh interface. -struct SMesh final : public IMesh +struct SMesh final : public IAnimatedMesh { //! constructor SMesh() {} @@ -134,6 +134,15 @@ struct SMesh final : public IMesh //! The bounding box of this mesh core::aabbox3d BoundingBox{{0, 0, 0}}; + + // Implement animated mesh interface as a static mesh. + // Slightly hacky: Eventually should be consolidated with SSkinnedMesh, + // with all the animation-related parts behind an optional. + + virtual f32 getMaxFrameNumber() const override { return 0.0f; } + virtual f32 getAnimationSpeed() const override { return 0.0f; } + virtual void setAnimationSpeed(f32 fps) override {} + E_ANIMATED_MESH_TYPE getMeshType() const override { return EAMT_STATIC; } }; } // end namespace scene diff --git a/irr/include/SkinnedMesh.h b/irr/include/SkinnedMesh.h index c9ea99365..aa718c882 100644 --- a/irr/include/SkinnedMesh.h +++ b/irr/include/SkinnedMesh.h @@ -8,11 +8,18 @@ #include "ISceneManager.h" #include "SMeshBuffer.h" #include "SSkinMeshBuffer.h" +#include "aabbox3d.h" +#include "irrMath.h" +#include "irrTypes.h" +#include "matrix4.h" #include "quaternion.h" #include "vector3d.h" +#include "Transform.h" #include #include +#include +#include namespace irr { @@ -26,12 +33,20 @@ class ISceneManager; class SkinnedMesh : public IAnimatedMesh { public: + + enum class SourceFormat { + B3D, + X, + GLTF, + OTHER, + }; + //! constructor - SkinnedMesh() : + SkinnedMesh(SourceFormat src_format) : EndFrame(0.f), FramesPerSecond(25.f), - LastAnimatedFrame(-1), SkinnedLastFrame(false), HasAnimation(false), PreparedForSkinning(false), - AnimateNormals(true), HardwareSkinning(false) + AnimateNormals(true), + SrcFormat(src_format) { SkinningBuffers = &LocalBuffers; } @@ -39,6 +54,10 @@ public: //! destructor virtual ~SkinnedMesh(); + //! The source (file) format the mesh was loaded from. + //! Important for legacy reasons pertaining to different mesh loader behavior. + SourceFormat getSourceFormat() const { return SrcFormat; } + //! If the duration is 0, it is a static (=non animated) mesh. f32 getMaxFrameNumber() const override; @@ -51,14 +70,12 @@ public: The actual speed is set in the scene node the mesh is instantiated in.*/ void setAnimationSpeed(f32 fps) override; - //! returns the animated mesh for the given frame - IMesh *getMesh(f32) override; + //! Turns the given array of local matrices into an array of global matrices + //! by multiplying with respective parent matrices. + void calculateGlobalMatrices(std::vector &matrices) const; - //! Animates joints based on frame input - void animateMesh(f32 frame); - - //! Performs a software skin on this mesh based of joint positions - void skinMesh(); + //! Performs a software skin on this mesh based on the given joint matrices + void skinMesh(const std::vector &animated_transforms); //! returns amount of mesh buffers. u32 getMeshBufferCount() const override; @@ -76,14 +93,15 @@ public: void setTextureSlot(u32 meshbufNr, u32 textureSlot); - //! returns an axis aligned bounding box + //! Returns bounding box of the mesh *in static pose*. const core::aabbox3d &getBoundingBox() const override { - return BoundingBox; + // TODO ideally we shouldn't be forced to implement this + return StaticPoseBox; } - //! set user axis aligned bounding box + //! Set bounding box of the mesh *in static pose*. void setBoundingBox(const core::aabbox3df &box) override { - BoundingBox = box; + StaticPoseBox = box; } //! set the hardware mapping hint, for driver @@ -127,28 +145,15 @@ public: return !HasAnimation; } - //! Allows to enable hardware skinning. - /* This feature is not implemented in Irrlicht yet */ - bool setHardwareSkinning(bool on); - //! Refreshes vertex data cached in joints such as positions and normals void refreshJointCache(); //! Moves the mesh into static position. void resetAnimation(); - void updateBoundingBox(); - - //! Recovers the joints from the mesh - void recoverJointsFromMesh(std::vector &jointChildSceneNodes); - - //! Transfers the joint data to the mesh - void transferJointsToMesh(const std::vector &jointChildSceneNodes); - //! Creates an array of joints from this mesh as children of node - void addJoints(std::vector &jointChildSceneNodes, - IAnimatedMeshSceneNode *node, - ISceneManager *smgr); + std::vector addJoints( + IAnimatedMeshSceneNode *node, ISceneManager *smgr); //! A vertex weight struct SWeight @@ -223,7 +228,7 @@ public: static core::quaternion interpolateValue(core::quaternion from, core::quaternion to, f32 time) { core::quaternion result; - result.slerp(from, to, time, 0.001f); + result.slerp(from, to, time); return result; } @@ -275,15 +280,14 @@ public: }); } - void updateTransform(f32 frame, - core::vector3df &t, core::quaternion &r, core::vector3df &s) const + void updateTransform(f32 frame, core::Transform &transform) const { if (auto pos = position.get(frame)) - t = *pos; + transform.translation = *pos; if (auto rot = rotation.get(frame)) - r = *rot; + transform.rotation = *rot; if (auto scl = scale.get(frame)) - s = *scl; + transform.scale = *scl; } void cleanup() { @@ -296,16 +300,34 @@ public: //! Joints struct SJoint { - SJoint() : GlobalSkinningSpace(false) {} + SJoint() {} //! The name of this joint std::optional Name; - //! Local matrix of this joint - core::matrix4 LocalMatrix; + //! Local transformation to be set by loaders. Mutated by animation. + using VariantTransform = std::variant; + VariantTransform transform{core::Transform{}}; + + VariantTransform animate(f32 frame) const { + if (keys.empty()) + return transform; + + if (std::holds_alternative(transform)) { + // .x lets animations override matrix transforms entirely, + // which is what we implement here. + // .gltf does not allow animation of nodes using matrix transforms. + // Note that a decomposition into a TRS transform need not exist! + core::Transform trs; + keys.updateTransform(frame, trs); + return {trs}; + } + + auto trs = std::get(transform); + keys.updateTransform(frame, trs); + return {trs}; + } - //! List of child joints - std::vector Children; //! List of attached meshes std::vector AttachedMeshes; @@ -316,42 +338,49 @@ public: //! Skin weights std::vector Weights; + //! Bounding box of all affected vertices, in local space + core::aabbox3df LocalBoundingBox{{0, 0, 0}}; + //! Unnecessary for loaders, will be overwritten on finalize core::matrix4 GlobalMatrix; // loaders may still choose to set this (temporarily) to calculate absolute vertex data. - core::matrix4 GlobalAnimatedMatrix; - core::matrix4 LocalAnimatedMatrix; - - //! These should be set by loaders. - core::vector3df Animatedposition; - core::vector3df Animatedscale; - core::quaternion Animatedrotation; // The .x and .gltf formats pre-calculate this std::optional GlobalInversedMatrix; - private: - //! Internal members used by SkinnedMesh - friend class SkinnedMesh; - bool GlobalSkinningSpace; + void setParent(SJoint *parent) { + ParentJointID = parent ? parent->JointID : std::optional{}; + } + + u16 JointID; // TODO refactor away: pointers -> IDs (problem: .x loader abuses SJoint) + std::optional ParentJointID; }; + //! Animates joints based on frame input + std::vector animateMesh(f32 frame); + + //! Calculates a bounding box given an animation in the form of global joint transforms. + core::aabbox3df calculateBoundingBox( + const std::vector &global_transforms); + + void recalculateBaseBoundingBoxes(); + const std::vector &getAllJoints() const { return AllJoints; } protected: - void checkForAnimation(); + bool checkForAnimation() const; + + void topoSortJoints(); + + void prepareForSkinning(); + + void calculateStaticBoundingBox(); + void calculateJointBoundingBoxes(); + void calculateBufferBoundingBoxes(); void normalizeWeights(); - void buildAllLocalAnimatedMatrices(); - - void buildAllGlobalAnimatedMatrices(SJoint *Joint = 0, SJoint *ParentJoint = 0); - - void calculateGlobalMatrices(SJoint *Joint, SJoint *ParentJoint); - - void skinJoint(SJoint *Joint, SJoint *ParentJoint); - void calculateTangents(core::vector3df &normal, core::vector3df &tangent, core::vector3df &binormal, const core::vector3df &vt1, const core::vector3df &vt2, const core::vector3df &vt3, @@ -363,31 +392,33 @@ protected: //! Mapping from meshbuffer number to bindable texture slot std::vector TextureSlots; + //! Joints, topologically sorted (parents come before their children). std::vector AllJoints; - std::vector RootJoints; // bool can't be used here because std::vector // doesn't allow taking a reference to individual elements. std::vector> Vertices_Moved; - core::aabbox3d BoundingBox{{0, 0, 0}}; + //! Bounding box of just the static parts of the mesh + core::aabbox3df StaticPartsBox{{0, 0, 0}}; + + //! Bounding box of the mesh in static pose + core::aabbox3df StaticPoseBox{{0, 0, 0}}; f32 EndFrame; f32 FramesPerSecond; - f32 LastAnimatedFrame; - bool SkinnedLastFrame; - bool HasAnimation; bool PreparedForSkinning; bool AnimateNormals; - bool HardwareSkinning; + + SourceFormat SrcFormat; }; // Interface for mesh loaders class SkinnedMeshBuilder : public SkinnedMesh { public: - SkinnedMeshBuilder() : SkinnedMesh() {} + SkinnedMeshBuilder(SourceFormat src_format) : SkinnedMesh(src_format) {} //! loaders should call this after populating the mesh // returns *this, so do not try to drop the mesh builder instance diff --git a/irr/include/Transform.h b/irr/include/Transform.h new file mode 100644 index 000000000..1e96e183d --- /dev/null +++ b/irr/include/Transform.h @@ -0,0 +1,42 @@ +#pragma once + +#include "irrMath.h" +#include +#include +#include + +namespace irr +{ +namespace core +{ + +struct Transform { + vector3df translation; + quaternion rotation; + vector3df scale{1}; + + Transform interpolate(Transform to, f32 time) const + { + core::quaternion interpolated_rotation; + interpolated_rotation.slerp(rotation, to.rotation, time); + return { + to.translation.getInterpolated(translation, time), + interpolated_rotation, + to.scale.getInterpolated(scale, time), + }; + } + + matrix4 buildMatrix() const + { + matrix4 T; + T.setTranslation(translation); + matrix4 R; + rotation.getMatrix_transposed(R); + matrix4 S; + S.setScale(scale); + return T * R * S; + } +}; + +} // end namespace core +} // end namespace irr diff --git a/irr/include/quaternion.h b/irr/include/quaternion.h index e23b1317d..42e0428a9 100644 --- a/irr/include/quaternion.h +++ b/irr/include/quaternion.h @@ -180,7 +180,7 @@ public: linear interpolation. */ quaternion &slerp(quaternion q1, quaternion q2, - f32 time, f32 threshold = .05f); + f32 time, f32 threshold = .001f); //! Set this quaternion to represent a rotation from angle and axis. /** Axis must be unit length. diff --git a/irr/src/CAnimatedMeshSceneNode.cpp b/irr/src/CAnimatedMeshSceneNode.cpp index 09d83038b..7b1d9053a 100644 --- a/irr/src/CAnimatedMeshSceneNode.cpp +++ b/irr/src/CAnimatedMeshSceneNode.cpp @@ -3,9 +3,13 @@ // For conditions of distribution and use, see copyright notice in irrlicht.h #include "CAnimatedMeshSceneNode.h" +#include "CBoneSceneNode.h" #include "IVideoDriver.h" #include "ISceneManager.h" #include "S3DVertex.h" +#include "Transform.h" +#include "irrTypes.h" +#include "matrix4.h" #include "os.h" #include "SkinnedMesh.h" #include "IDummyTransformationSceneNode.h" @@ -17,6 +21,9 @@ #include "IFileSystem.h" #include "quaternion.h" #include +#include +#include +#include namespace irr { @@ -30,13 +37,13 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh *mesh, const core::vector3df &rotation, const core::vector3df &scale) : IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale), - Mesh(0), + Mesh(nullptr), StartFrame(0), EndFrame(0), FramesPerSecond(0.025f), CurrentFrameNr(0.f), LastTimeMs(0), TransitionTime(0), Transiting(0.f), TransitingBlend(0.f), - JointMode(EJUOR_NONE), JointsUsed(false), + JointsUsed(false), Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false), - LoopCallBack(0), PassCount(0) + PassCount(0) { setMesh(mesh); } @@ -44,8 +51,6 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh *mesh, //! destructor CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode() { - if (LoopCallBack) - LoopCallBack->drop(); if (Mesh) Mesh->drop(); } @@ -87,8 +92,7 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs) if (FramesPerSecond > 0.f) { // forwards... if (CurrentFrameNr > EndFrame) CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, EndFrame - StartFrame); - } else // backwards... - { + } else { // backwards... if (CurrentFrameNr < StartFrame) CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, EndFrame - StartFrame); } @@ -97,18 +101,9 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs) CurrentFrameNr += timeMs * FramesPerSecond; if (FramesPerSecond > 0.f) { // forwards... - if (CurrentFrameNr > EndFrame) { - CurrentFrameNr = EndFrame; - if (LoopCallBack) - LoopCallBack->OnAnimationEnd(this); - } - } else // backwards... - { - if (CurrentFrameNr < StartFrame) { - CurrentFrameNr = StartFrame; - if (LoopCallBack) - LoopCallBack->OnAnimationEnd(this); - } + CurrentFrameNr = std::min(CurrentFrameNr, EndFrame); + } else { // backwards... + CurrentFrameNr = std::max(CurrentFrameNr, StartFrame); } } } @@ -156,38 +151,18 @@ void CAnimatedMeshSceneNode::OnRegisterSceneNode() IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame() { if (Mesh->getMeshType() != EAMT_SKINNED) { - return Mesh->getMesh(getFrameNr()); - } else { - // As multiple scene nodes may be sharing the same skinned mesh, we have to - // re-animate it every frame to ensure that this node gets the mesh that it needs. - - SkinnedMesh *skinnedMesh = static_cast(Mesh); - - if (JointMode == EJUOR_CONTROL) // write to mesh - skinnedMesh->transferJointsToMesh(JointChildSceneNodes); - else - skinnedMesh->animateMesh(getFrameNr()); - - // Update the skinned mesh for the current joint transforms. - skinnedMesh->skinMesh(); - - if (JointMode == EJUOR_READ) { // read from mesh - skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes); - - //---slow--- - for (u32 n = 0; n < JointChildSceneNodes.size(); ++n) - if (JointChildSceneNodes[n]->getParent() == this) { - JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); // temp, should be an option - } - } - - if (JointMode == EJUOR_CONTROL) { - // For meshes other than EJUOR_CONTROL, this is done by calling animateMesh() - skinnedMesh->updateBoundingBox(); - } - - return skinnedMesh; + return Mesh; } + + // As multiple scene nodes may be sharing the same skinned mesh, we have to + // re-animate it every frame to ensure that this node gets the mesh that it needs. + + auto *skinnedMesh = static_cast(Mesh); + + // Matrices have already been calculated in OnAnimate + skinnedMesh->skinMesh(PerJoint.GlobalMatrices); + + return skinnedMesh; } //! OnAnimate() is called just before rendering the whole scene. @@ -201,7 +176,28 @@ void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs) buildFrameNr(timeMs - LastTimeMs); LastTimeMs = timeMs; + // This needs to be done on animate, which is called recursively *before* + // anything is rendered so that the transformations of children are up to date + animateJoints(); + + // Copy old transforms *before* bone overrides have been applied. + // TODO if there are no bone overrides or no animation blending, this is unnecessary. + copyOldTransforms(); + + if (OnAnimateCallback) + OnAnimateCallback(timeMs / 1000.0f); + IAnimatedMeshSceneNode::OnAnimate(timeMs); + + if (auto *skinnedMesh = dynamic_cast(Mesh)) { + for (u16 i = 0; i < PerJoint.SceneNodes.size(); ++i) + PerJoint.GlobalMatrices[i] = PerJoint.SceneNodes[i]->getRelativeTransformation(); + assert(PerJoint.GlobalMatrices.size() == skinnedMesh->getJointCount()); + skinnedMesh->calculateGlobalMatrices(PerJoint.GlobalMatrices); + Box = skinnedMesh->calculateBoundingBox(PerJoint.GlobalMatrices); + } else { + Box = Mesh->getBoundingBox(); + } } //! renders the node. @@ -218,15 +214,7 @@ void CAnimatedMeshSceneNode::render() ++PassCount; scene::IMesh *m = getMeshForCurrentFrame(); - - if (m) { - Box = m->getBoundingBox(); - } else { -#ifdef _DEBUG - os::Printer::log("Animated Mesh returned no mesh to render.", ELL_WARNING); -#endif - return; - } + assert(m); driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); @@ -294,11 +282,12 @@ void CAnimatedMeshSceneNode::render() if (DebugDataVisible & scene::EDS_SKELETON) { if (Mesh->getMeshType() == EAMT_SKINNED) { // draw skeleton - - for (auto *joint : ((SkinnedMesh *)Mesh)->getAllJoints()) { - for (const auto *childJoint : joint->Children) { - driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(), - childJoint->GlobalAnimatedMatrix.getTranslation(), + const auto &joints = (static_cast(Mesh))->getAllJoints(); + for (u16 i = 0; i < PerJoint.GlobalMatrices.size(); ++i) { + const auto translation = PerJoint.GlobalMatrices[i].getTranslation(); + if (auto pjid = joints[i]->ParentJointID) { + const auto parent_translation = PerJoint.GlobalMatrices[*pjid].getTranslation(); + driver->draw3DLine(parent_translation, translation, video::SColor(255, 51, 66, 255)); } } @@ -407,12 +396,12 @@ IBoneSceneNode *CAnimatedMeshSceneNode::getJointNode(const c8 *jointName) return 0; } - if (JointChildSceneNodes.size() <= *number) { + if (PerJoint.SceneNodes.size() <= *number) { os::Printer::log("Joint was found in mesh, but is not loaded into node", jointName, ELL_WARNING); return 0; } - return JointChildSceneNodes[*number]; + return PerJoint.SceneNodes[*number]; } //! Returns a pointer to a child node, which has the same transformation as @@ -426,12 +415,12 @@ IBoneSceneNode *CAnimatedMeshSceneNode::getJointNode(u32 jointID) checkJoints(); - if (JointChildSceneNodes.size() <= jointID) { + if (PerJoint.SceneNodes.size() <= jointID) { os::Printer::log("Joint not loaded into node", ELL_WARNING); return 0; } - return JointChildSceneNodes[jointID]; + return PerJoint.SceneNodes[jointID]; } //! Gets joint count. @@ -452,9 +441,9 @@ bool CAnimatedMeshSceneNode::removeChild(ISceneNode *child) { if (ISceneNode::removeChild(child)) { if (JointsUsed) { // stop weird bugs caused while changing parents as the joints are being created - for (u32 i = 0; i < JointChildSceneNodes.size(); ++i) { - if (JointChildSceneNodes[i] == child) { - JointChildSceneNodes[i] = 0; // remove link to child + for (u32 i = 0; i < PerJoint.SceneNodes.size(); ++i) { + if (PerJoint.SceneNodes[i] == child) { + PerJoint.SceneNodes[i] = 0; // remove link to child break; } } @@ -478,22 +467,6 @@ bool CAnimatedMeshSceneNode::getLoopMode() const return Looping; } -//! Sets a callback interface which will be called if an animation -//! playback has ended. Set this to 0 to disable the callback again. -void CAnimatedMeshSceneNode::setAnimationEndCallback(IAnimationEndCallBack *callback) -{ - if (callback == LoopCallBack) - return; - - if (LoopCallBack) - LoopCallBack->drop(); - - LoopCallBack = callback; - - if (LoopCallBack) - LoopCallBack->grab(); -} - //! Sets if the scene node should not copy the materials of the mesh but use them in a read only style. void CAnimatedMeshSceneNode::setReadOnlyMaterials(bool readonly) { @@ -525,18 +498,15 @@ void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh *mesh) // get materials and bounding box Box = Mesh->getBoundingBox(); - IMesh *m = Mesh->getMesh(0); - if (m) { - Materials.clear(); - Materials.reallocate(m->getMeshBufferCount()); + Materials.clear(); + Materials.reallocate(Mesh->getMeshBufferCount()); - for (u32 i = 0; i < m->getMeshBufferCount(); ++i) { - IMeshBuffer *mb = m->getMeshBuffer(i); - if (mb) - Materials.push_back(mb->getMaterial()); - else - Materials.push_back(video::SMaterial()); - } + for (u32 i = 0; i < Mesh->getMeshBufferCount(); ++i) { + IMeshBuffer *mb = Mesh->getMeshBuffer(i); + if (mb) + Materials.push_back(mb->getMaterial()); + else + Materials.push_back(video::SMaterial()); } // clean up joint nodes @@ -556,14 +526,7 @@ void CAnimatedMeshSceneNode::updateAbsolutePosition() IAnimatedMeshSceneNode::updateAbsolutePosition(); } -//! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set) -void CAnimatedMeshSceneNode::setJointMode(E_JOINT_UPDATE_ON_RENDER mode) -{ - checkJoints(); - JointMode = mode; -} - -//! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2) +//! Sets the transition time in seconds (note: This needs to enable joints) //! you must call animateJoints(), or the mesh will not animate void CAnimatedMeshSceneNode::setTransitionTime(f32 time) { @@ -571,10 +534,6 @@ void CAnimatedMeshSceneNode::setTransitionTime(f32 time) if (TransitionTime == ttime) return; TransitionTime = ttime; - if (ttime != 0) - setJointMode(EJUOR_CONTROL); - else - setJointMode(EJUOR_NONE); } //! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected) @@ -583,120 +542,104 @@ void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable) RenderFromIdentity = enable; } -//! updates the joint positions of this mesh -void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions) +void CAnimatedMeshSceneNode::addJoints() { - if (Mesh && Mesh->getMeshType() == EAMT_SKINNED) { - checkJoints(); - const f32 frame = getFrameNr(); // old? + const auto &joints = static_cast(Mesh)->getAllJoints(); + PerJoint.setN(joints.size()); + PerJoint.SceneNodes.clear(); + PerJoint.SceneNodes.reserve(joints.size()); + for (size_t i = 0; i < joints.size(); ++i) { + const auto *joint = joints[i]; + ISceneNode *parent = this; + if (joint->ParentJointID) + parent = PerJoint.SceneNodes.at(*joint->ParentJointID); // exists because of topo. order + assert(parent); + const auto *matrix = std::get_if(&joint->transform); + PerJoint.SceneNodes.push_back(new CBoneSceneNode( + parent, SceneManager, 0, i, joint->Name, + matrix ? core::Transform{} : std::get(joint->transform), + matrix ? *matrix : std::optional{})); + } +} - SkinnedMesh *skinnedMesh = static_cast(Mesh); - - skinnedMesh->animateMesh(frame); - skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes); - - //----------------------------------------- - // Transition - //----------------------------------------- - - if (Transiting != 0.f) { - // Init additional matrices - if (PretransitingSave.size() < JointChildSceneNodes.size()) { - for (u32 n = PretransitingSave.size(); n < JointChildSceneNodes.size(); ++n) - PretransitingSave.push_back(core::matrix4()); - } - - for (u32 n = 0; n < JointChildSceneNodes.size(); ++n) { - //------Position------ - - JointChildSceneNodes[n]->setPosition( - core::lerp( - PretransitingSave[n].getTranslation(), - JointChildSceneNodes[n]->getPosition(), - TransitingBlend)); - - //------Rotation------ - - // Code is slow, needs to be fixed up - - const core::quaternion RotationStart(PretransitingSave[n].getRotationRadians()); - const core::quaternion RotationEnd(JointChildSceneNodes[n]->getRotation() * core::DEGTORAD); - - core::quaternion QRotation; - QRotation.slerp(RotationStart, RotationEnd, TransitingBlend); - - core::vector3df tmpVector; - QRotation.toEuler(tmpVector); - tmpVector *= core::RADTODEG; // convert from radians back to degrees - JointChildSceneNodes[n]->setRotation(tmpVector); - - //------Scale------ - - // JointChildSceneNodes[n]->setScale( - // core::lerp( - // PretransitingSave[n].getScale(), - // JointChildSceneNodes[n]->getScale(), - // TransitingBlend)); - } +void CAnimatedMeshSceneNode::updateJointSceneNodes( + const std::vector &transforms) +{ + for (size_t i = 0; i < transforms.size(); ++i) { + const auto &transform = transforms[i]; + auto *node = static_cast(PerJoint.SceneNodes[i]); + if (const auto *trs = std::get_if(&transform)) { + node->setTransform(*trs); + // .x lets animations override matrix transforms entirely. + node->Matrix = std::nullopt; + } else { + node->Matrix = std::get(transform); } + } +} - if (CalculateAbsolutePositions) { - //---slow--- - for (u32 n = 0; n < JointChildSceneNodes.size(); ++n) { - if (JointChildSceneNodes[n]->getParent() == this) { - JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); // temp, should be an option - } +//! updates the joint positions of this mesh +void CAnimatedMeshSceneNode::animateJoints() +{ + if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) + return; + + checkJoints(); + + SkinnedMesh *skinnedMesh = static_cast(Mesh); + if (!skinnedMesh->isStatic()) + updateJointSceneNodes(skinnedMesh->animateMesh(getFrameNr())); + + //----------------------------------------- + // Transition + //----------------------------------------- + + if (Transiting != 0.f) { + for (u32 i = 0; i < PerJoint.SceneNodes.size(); ++i) { + if (PerJoint.PreTransSaves[i]) { + PerJoint.SceneNodes[i]->setTransform(PerJoint.PreTransSaves[i]->interpolate( + PerJoint.SceneNodes[i]->getTransform(), TransitingBlend)); } } } } -/*! - */ void CAnimatedMeshSceneNode::checkJoints() { if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED) return; if (!JointsUsed) { - for (u32 i = 0; i < JointChildSceneNodes.size(); ++i) - removeChild(JointChildSceneNodes[i]); - JointChildSceneNodes.clear(); - - // Create joints for SkinnedMesh - ((SkinnedMesh *)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager); - ((SkinnedMesh *)Mesh)->recoverJointsFromMesh(JointChildSceneNodes); + for (u32 i = 0; i < PerJoint.SceneNodes.size(); ++i) + removeChild(PerJoint.SceneNodes[i]); + addJoints(); JointsUsed = true; - JointMode = EJUOR_READ; } } -/*! - */ +void CAnimatedMeshSceneNode::copyOldTransforms() +{ + for (u32 i = 0; i < PerJoint.SceneNodes.size(); ++i) { + if (!PerJoint.SceneNodes[i]->Matrix) { + PerJoint.PreTransSaves[i] = PerJoint.SceneNodes[i]->getTransform(); + } else { + PerJoint.PreTransSaves[i] = std::nullopt; + } + } +} + void CAnimatedMeshSceneNode::beginTransition() { if (!JointsUsed) return; if (TransitionTime != 0) { - // Check the array is big enough - if (PretransitingSave.size() < JointChildSceneNodes.size()) { - for (u32 n = PretransitingSave.size(); n < JointChildSceneNodes.size(); ++n) - PretransitingSave.push_back(core::matrix4()); - } - - // Copy the position of joints - for (u32 n = 0; n < JointChildSceneNodes.size(); ++n) - PretransitingSave[n] = JointChildSceneNodes[n]->getRelativeTransformation(); - Transiting = core::reciprocal((f32)TransitionTime); } TransitingBlend = 0.f; } -/*! - */ ISceneNode *CAnimatedMeshSceneNode::clone(ISceneNode *newParent, ISceneManager *newManager) { if (!newParent) @@ -722,19 +665,15 @@ ISceneNode *CAnimatedMeshSceneNode::clone(ISceneNode *newParent, ISceneManager * newNode->EndFrame = EndFrame; newNode->FramesPerSecond = FramesPerSecond; newNode->CurrentFrameNr = CurrentFrameNr; - newNode->JointMode = JointMode; newNode->JointsUsed = JointsUsed; newNode->TransitionTime = TransitionTime; newNode->Transiting = Transiting; newNode->TransitingBlend = TransitingBlend; newNode->Looping = Looping; newNode->ReadOnlyMaterials = ReadOnlyMaterials; - newNode->LoopCallBack = LoopCallBack; - if (newNode->LoopCallBack) - newNode->LoopCallBack->grab(); newNode->PassCount = PassCount; - newNode->JointChildSceneNodes = JointChildSceneNodes; - newNode->PretransitingSave = PretransitingSave; + newNode->PerJoint.SceneNodes = PerJoint.SceneNodes; + newNode->PerJoint.PreTransSaves = PerJoint.PreTransSaves; newNode->RenderFromIdentity = RenderFromIdentity; return newNode; diff --git a/irr/src/CAnimatedMeshSceneNode.h b/irr/src/CAnimatedMeshSceneNode.h index 5149f7618..c67479481 100644 --- a/irr/src/CAnimatedMeshSceneNode.h +++ b/irr/src/CAnimatedMeshSceneNode.h @@ -4,9 +4,12 @@ #pragma once +#include "CBoneSceneNode.h" #include "IAnimatedMeshSceneNode.h" #include "IAnimatedMesh.h" +#include "SkinnedMesh.h" +#include "Transform.h" #include "matrix4.h" namespace irr @@ -54,9 +57,11 @@ public: //! returns the current loop mode bool getLoopMode() const override; - //! Sets a callback interface which will be called if an animation - //! playback has ended. Set this to 0 to disable the callback again. - void setAnimationEndCallback(IAnimationEndCallBack *callback = 0) override; + void setOnAnimateCallback( + const std::function &cb) override + { + OnAnimateCallback = cb; + } //! sets the speed with which the animation is played //! NOTE: setMesh will also change this value and set it to the default speed of the mesh @@ -117,15 +122,16 @@ public: //! updates the absolute position based on the relative and the parents position void updateAbsolutePosition() override; - //! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set) - void setJointMode(E_JOINT_UPDATE_ON_RENDER mode) override; - - //! Sets the transition time in seconds (note: This needs to enable joints, and setJointmode maybe set to 2) + //! Sets the transition time in seconds (note: This needs to enable joints) //! you must call animateJoints(), or the mesh will not animate void setTransitionTime(f32 Time) override; + void updateJointSceneNodes(const std::vector &transforms); + //! updates the joint positions of this mesh - void animateJoints(bool CalculateAbsolutePositions = true) override; + void animateJoints() override; + + void addJoints(); //! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected) void setRenderFromIdentity(bool On) override; @@ -142,6 +148,7 @@ private: void buildFrameNr(u32 timeMs); void checkJoints(); + void copyOldTransforms(); void beginTransition(); core::array Materials; @@ -158,19 +165,30 @@ private: f32 Transiting; // is mesh transiting (plus cache of TransitionTime) f32 TransitingBlend; // 0-1, calculated on buildFrameNr - // 0-unused, 1-get joints only, 2-set joints only, 3-move and set - E_JOINT_UPDATE_ON_RENDER JointMode; bool JointsUsed; bool Looping; bool ReadOnlyMaterials; bool RenderFromIdentity; - IAnimationEndCallBack *LoopCallBack; s32 PassCount; + std::function OnAnimateCallback; - std::vector JointChildSceneNodes; - core::array PretransitingSave; + struct PerJointData { + std::vector SceneNodes; + std::vector GlobalMatrices; + std::vector> PreTransSaves; + void setN(u16 n) { + SceneNodes.clear(); + SceneNodes.resize(n); + GlobalMatrices.clear(); + GlobalMatrices.resize(n); + PreTransSaves.clear(); + PreTransSaves.resize(n); + } + }; + + PerJointData PerJoint; }; } // end namespace scene diff --git a/irr/src/CB3DMeshFileLoader.cpp b/irr/src/CB3DMeshFileLoader.cpp index e99bd2eed..6e3e14791 100644 --- a/irr/src/CB3DMeshFileLoader.cpp +++ b/irr/src/CB3DMeshFileLoader.cpp @@ -48,7 +48,7 @@ IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file) return 0; B3DFile = file; - AnimatedMesh = new scene::SkinnedMeshBuilder(); + AnimatedMesh = new scene::SkinnedMeshBuilder(SkinnedMesh::SourceFormat::B3D); ShowWarning = true; // If true a warning is issued if too many textures are used VerticesStart = 0; @@ -143,31 +143,25 @@ bool CB3DMeshFileLoader::readChunkNODE(SkinnedMesh::SJoint *inJoint) os::Printer::log(logStr.c_str(), joint->Name.value_or("").c_str(), ELL_DEBUG); #endif - f32 position[3], scale[3], rotation[4]; + core::Transform transform; + { + f32 t[3], s[3], r[4]; - readFloats(position, 3); - readFloats(scale, 3); - readFloats(rotation, 4); + readFloats(t, 3); + readFloats(s, 3); + readFloats(r, 4); - joint->Animatedposition = core::vector3df(position[0], position[1], position[2]); - joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]); - joint->Animatedrotation = core::quaternion(rotation[1], rotation[2], rotation[3], rotation[0]); - - // Build LocalMatrix: - - core::matrix4 positionMatrix; - positionMatrix.setTranslation(joint->Animatedposition); - core::matrix4 scaleMatrix; - scaleMatrix.setScale(joint->Animatedscale); - core::matrix4 rotationMatrix; - joint->Animatedrotation.getMatrix_transposed(rotationMatrix); - - joint->LocalMatrix = positionMatrix * rotationMatrix * scaleMatrix; + joint->transform = transform = { + {t[0], t[1], t[2]}, + {r[1], r[2], r[3], r[0]}, + {s[0], s[1], s[2]}, + }; + } if (inJoint) - joint->GlobalMatrix = inJoint->GlobalMatrix * joint->LocalMatrix; + joint->GlobalMatrix = inJoint->GlobalMatrix * transform.buildMatrix(); else - joint->GlobalMatrix = joint->LocalMatrix; + joint->GlobalMatrix = transform.buildMatrix(); while (B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats { diff --git a/irr/src/CBoneSceneNode.cpp b/irr/src/CBoneSceneNode.cpp deleted file mode 100644 index 7aa637094..000000000 --- a/irr/src/CBoneSceneNode.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include "CBoneSceneNode.h" - -#include - -namespace irr -{ -namespace scene -{ - -//! constructor -CBoneSceneNode::CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id, - u32 boneIndex, const std::optional &boneName) : - IBoneSceneNode(parent, mgr, id), - BoneIndex(boneIndex), - AnimationMode(EBAM_AUTOMATIC), SkinningSpace(EBSS_LOCAL) -{ - setName(boneName); -} - -//! Returns the index of the bone -u32 CBoneSceneNode::getBoneIndex() const -{ - return BoneIndex; -} - -//! Sets the animation mode of the bone. Returns true if successful. -bool CBoneSceneNode::setAnimationMode(E_BONE_ANIMATION_MODE mode) -{ - AnimationMode = mode; - return true; -} - -//! Gets the current animation mode of the bone -E_BONE_ANIMATION_MODE CBoneSceneNode::getAnimationMode() const -{ - return AnimationMode; -} - -//! returns the axis aligned bounding box of this node -const core::aabbox3d &CBoneSceneNode::getBoundingBox() const -{ - return Box; -} - -/* -//! Returns the relative transformation of the scene node. -core::matrix4 CBoneSceneNode::getRelativeTransformation() const -{ - return core::matrix4(); // RelativeTransformation; -} -*/ - -void CBoneSceneNode::OnAnimate(u32 timeMs) -{ - if (IsVisible) { - // update absolute position - // updateAbsolutePosition(); - - // perform the post render process on all children - ISceneNodeList::iterator it = Children.begin(); - for (; it != Children.end(); ++it) - (*it)->OnAnimate(timeMs); - } -} - -void CBoneSceneNode::helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node) -{ - Node->updateAbsolutePosition(); - - ISceneNodeList::const_iterator it = Node->getChildren().begin(); - for (; it != Node->getChildren().end(); ++it) { - helper_updateAbsolutePositionOfAllChildren((*it)); - } -} - -void CBoneSceneNode::updateAbsolutePositionOfAllChildren() -{ - helper_updateAbsolutePositionOfAllChildren(this); -} - -} // namespace scene -} // namespace irr diff --git a/irr/src/CBoneSceneNode.h b/irr/src/CBoneSceneNode.h index d900570db..4151f7372 100644 --- a/irr/src/CBoneSceneNode.h +++ b/irr/src/CBoneSceneNode.h @@ -7,6 +7,8 @@ // Used with SkinnedMesh and IAnimatedMeshSceneNode, for boned meshes #include "IBoneSceneNode.h" +#include "Transform.h" +#include "matrix4.h" #include @@ -21,49 +23,48 @@ public: //! constructor CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id = -1, u32 boneIndex = 0, - const std::optional &boneName = std::nullopt); - - //! Returns the index of the bone - u32 getBoneIndex() const override; - - //! Sets the animation mode of the bone. Returns true if successful. - bool setAnimationMode(E_BONE_ANIMATION_MODE mode) override; - - //! Gets the current animation mode of the bone - E_BONE_ANIMATION_MODE getAnimationMode() const override; - - //! returns the axis aligned bounding box of this node - const core::aabbox3d &getBoundingBox() const override; - - /* - //! Returns the relative transformation of the scene node. - //core::matrix4 getRelativeTransformation() const override; - */ - - void OnAnimate(u32 timeMs) override; - - void updateAbsolutePositionOfAllChildren() override; - - //! How the relative transformation of the bone is used - void setSkinningSpace(E_BONE_SKINNING_SPACE space) override + const std::optional &boneName = std::nullopt, + const core::Transform &transform = {}, + const std::optional &matrix = std::nullopt) : + IBoneSceneNode(parent, mgr, id, boneIndex, boneName), + Matrix(matrix) { - SkinningSpace = space; + setTransform(transform); } - E_BONE_SKINNING_SPACE getSkinningSpace() const override + void setTransform(const core::Transform &transform) { - return SkinningSpace; + setPosition(transform.translation); + { + core::vector3df euler; + auto rot = transform.rotation; + // Invert to be consistent with setRotationDegrees + rot.makeInverse(); + rot.toEuler(euler); + setRotation(euler * core::RADTODEG); + } + setScale(transform.scale); } -private: - void helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node); + core::Transform getTransform() const + { + return { + getPosition(), + core::quaternion(getRotation() * core::DEGTORAD).makeInverse(), + getScale() + }; + } - u32 BoneIndex; + core::matrix4 getRelativeTransformation() const override + { + if (Matrix) + return *Matrix; + return IBoneSceneNode::getRelativeTransformation(); + } - core::aabbox3d Box{-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}; - - E_BONE_ANIMATION_MODE AnimationMode; - E_BONE_SKINNING_SPACE SkinningSpace; + //! Some file formats alternatively let bones specify a transformation matrix. + //! If this is set, it overrides the TRS properties. + std::optional Matrix; }; } // end namespace scene diff --git a/irr/src/CGLTFMeshFileLoader.cpp b/irr/src/CGLTFMeshFileLoader.cpp index f70f6692b..53ae1b580 100644 --- a/irr/src/CGLTFMeshFileLoader.cpp +++ b/irr/src/CGLTFMeshFileLoader.cpp @@ -347,7 +347,8 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file) const char *filename = file->getFileName().c_str(); try { tiniergltf::GlTF model = parseGLTF(file); - irr_ptr mesh(new SkinnedMeshBuilder()); + irr_ptr mesh(new SkinnedMeshBuilder( + SkinnedMesh::SourceFormat::GLTF)); MeshExtractor extractor(std::move(model), mesh.get()); try { extractor.load(); @@ -538,34 +539,25 @@ static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, SkinnedMes mat[i] = static_cast(m[i]); mat = convertHandedness(mat); - // Decompose the matrix into translation, scale, and rotation. - joint->Animatedposition = mat.getTranslation(); - - auto scale = mat.getScale(); - joint->Animatedscale = scale; - joint->Animatedrotation = mat.getRotationRadians(scale); - // Invert the rotation because it is applied using `getMatrix_transposed`, - // which again inverts. - joint->Animatedrotation.makeInverse(); - + // Note: "When a node is targeted for animation [...], + // only TRS properties MAY be present; matrix MUST NOT be present." + // Thus we MUST NOT do any decomposition, which in general need not exist. + joint->transform = mat; return mat; } static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, SkinnedMesh::SJoint *joint) { - const auto &trans = trs.translation; - const auto &rot = trs.rotation; - const auto &scale = trs.scale; - core::matrix4 transMat; - joint->Animatedposition = convertHandedness(core::vector3df(trans[0], trans[1], trans[2])); - transMat.setTranslation(joint->Animatedposition); - core::matrix4 rotMat; - joint->Animatedrotation = convertHandedness(core::quaternion(rot[0], rot[1], rot[2], rot[3])); - core::quaternion(joint->Animatedrotation).getMatrix_transposed(rotMat); - joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]); - core::matrix4 scaleMat; - scaleMat.setScale(joint->Animatedscale); - return transMat * rotMat * scaleMat; + const auto &t = trs.translation; + const auto &r = trs.rotation; + const auto &s = trs.scale; + core::Transform transform{ + convertHandedness(core::vector3df(t[0], t[1], t[2])), + convertHandedness(core::quaternion(r[0], r[1], r[2], r[3])), + core::vector3df(s[0], s[1], s[2]), + }; + joint->transform = transform; + return transform.buildMatrix(); } static core::matrix4 loadTransform(std::optional> transform, @@ -583,8 +575,7 @@ void SelfType::MeshExtractor::loadNode( const auto &node = m_gltf_model.nodes->at(nodeIdx); auto *joint = m_irr_model->addJoint(parent); const core::matrix4 transform = loadTransform(node.transform, joint); - joint->LocalMatrix = transform; - joint->GlobalMatrix = parent ? parent->GlobalMatrix * joint->LocalMatrix : joint->LocalMatrix; + joint->GlobalMatrix = parent ? parent->GlobalMatrix * transform : transform; if (node.name.has_value()) { joint->Name = node.name->c_str(); } @@ -641,7 +632,6 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx) { const auto &anim = m_gltf_model.animations->at(animIdx); for (const auto &channel : anim.channels) { - const auto &sampler = anim.samplers.at(channel.sampler); bool interpolate = ([&]() { @@ -662,6 +652,11 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx) throw std::runtime_error("no animated node"); auto *joint = m_loaded_nodes.at(*channel.target.node); + if (std::holds_alternative(joint->transform)) { + warn("nodes using matrix transforms must not be animated"); + continue; + } + switch (channel.target.path) { case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: { const auto outputAccessor = Accessor::make(m_gltf_model, sampler.output); diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index 83da89c11..c752d7d31 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -320,7 +320,6 @@ set(IRRMESHLOADER add_library(IRRMESHOBJ OBJECT SkinnedMesh.cpp - CBoneSceneNode.cpp CMeshSceneNode.cpp CAnimatedMeshSceneNode.cpp ${IRRMESHLOADER} diff --git a/irr/src/CMeshCache.cpp b/irr/src/CMeshCache.cpp index 4f7e1203c..9a41aa582 100644 --- a/irr/src/CMeshCache.cpp +++ b/irr/src/CMeshCache.cpp @@ -35,7 +35,7 @@ void CMeshCache::removeMesh(const IMesh *const mesh) if (!mesh) return; for (u32 i = 0; i < Meshes.size(); ++i) { - if (Meshes[i].Mesh == mesh || (Meshes[i].Mesh && Meshes[i].Mesh->getMesh(0) == mesh)) { + if (Meshes[i].Mesh == mesh) { Meshes[i].Mesh->drop(); Meshes.erase(i); return; @@ -53,7 +53,7 @@ u32 CMeshCache::getMeshCount() const s32 CMeshCache::getMeshIndex(const IMesh *const mesh) const { for (u32 i = 0; i < Meshes.size(); ++i) { - if (Meshes[i].Mesh == mesh || (Meshes[i].Mesh && Meshes[i].Mesh->getMesh(0) == mesh)) + if (Meshes[i].Mesh == mesh) return (s32)i; } @@ -93,7 +93,7 @@ const io::SNamedPath &CMeshCache::getMeshName(const IMesh *const mesh) const return emptyNamedPath; for (u32 i = 0; i < Meshes.size(); ++i) { - if (Meshes[i].Mesh == mesh || (Meshes[i].Mesh && Meshes[i].Mesh->getMesh(0) == mesh)) + if (Meshes[i].Mesh == mesh) return Meshes[i].NamedPath; } @@ -115,7 +115,7 @@ bool CMeshCache::renameMesh(u32 index, const io::path &name) bool CMeshCache::renameMesh(const IMesh *const mesh, const io::path &name) { for (u32 i = 0; i < Meshes.size(); ++i) { - if (Meshes[i].Mesh == mesh || (Meshes[i].Mesh && Meshes[i].Mesh->getMesh(0) == mesh)) { + if (Meshes[i].Mesh == mesh) { Meshes[i].NamedPath.setPath(name); Meshes.sort(); return true; diff --git a/irr/src/CMeshManipulator.cpp b/irr/src/CMeshManipulator.cpp index 659e8dff8..535b74686 100644 --- a/irr/src/CMeshManipulator.cpp +++ b/irr/src/CMeshManipulator.cpp @@ -6,7 +6,6 @@ #include "SkinnedMesh.h" #include "SMesh.h" #include "CMeshBuffer.h" -#include "SAnimatedMesh.h" #include "os.h" #include @@ -178,34 +177,5 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const return clone; } -//! Returns amount of polygons in mesh. -s32 CMeshManipulator::getPolyCount(scene::IMesh *mesh) const -{ - if (!mesh) - return 0; - - s32 trianglecount = 0; - - for (u32 g = 0; g < mesh->getMeshBufferCount(); ++g) - trianglecount += mesh->getMeshBuffer(g)->getIndexCount() / 3; - - return trianglecount; -} - -//! Returns amount of polygons in mesh. -s32 CMeshManipulator::getPolyCount(scene::IAnimatedMesh *mesh) const -{ - if (mesh && mesh->getMaxFrameNumber() != 0) - return getPolyCount(mesh->getMesh(0)); - - return 0; -} - -//! create a new AnimatedMesh and adds the mesh to it -IAnimatedMesh *CMeshManipulator::createAnimatedMesh(scene::IMesh *mesh, scene::E_ANIMATED_MESH_TYPE type) const -{ - return new SAnimatedMesh(mesh, type); -} - } // end namespace scene } // end namespace irr diff --git a/irr/src/CMeshManipulator.h b/irr/src/CMeshManipulator.h index 0377d6a3a..c5632512d 100644 --- a/irr/src/CMeshManipulator.h +++ b/irr/src/CMeshManipulator.h @@ -31,15 +31,6 @@ public: //! Clones a static IMesh into a modifiable SMesh. SMesh *createMeshCopy(scene::IMesh *mesh) const override; - - //! Returns amount of polygons in mesh. - s32 getPolyCount(scene::IMesh *mesh) const override; - - //! Returns amount of polygons in mesh. - s32 getPolyCount(scene::IAnimatedMesh *mesh) const override; - - //! create a new AnimatedMesh and adds the mesh to it - IAnimatedMesh *createAnimatedMesh(scene::IMesh *mesh, scene::E_ANIMATED_MESH_TYPE type) const override; }; } // end namespace scene diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index 246f414de..f7d012913 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -1238,7 +1238,7 @@ void CNullDriver::addOcclusionQuery(scene::ISceneNode *node, const scene::IMesh else if (node->getType() == scene::ESNT_MESH) mesh = static_cast(node)->getMesh(); else - mesh = static_cast(node)->getMesh()->getMesh(0); + mesh = static_cast(node)->getMesh(); if (!mesh) return; } diff --git a/irr/src/COBJMeshFileLoader.cpp b/irr/src/COBJMeshFileLoader.cpp index 5c7f38950..bc51c10f2 100644 --- a/irr/src/COBJMeshFileLoader.cpp +++ b/irr/src/COBJMeshFileLoader.cpp @@ -7,7 +7,6 @@ #include "IVideoDriver.h" #include "SMesh.h" #include "SMeshBuffer.h" -#include "SAnimatedMesh.h" #include "IReadFile.h" #include "fast_atof.h" #include "coreutil.h" @@ -272,23 +271,19 @@ IAnimatedMesh *COBJMeshFileLoader::createMesh(io::IReadFile *file) } } - // Create the Animated mesh if there's anything in the mesh - SAnimatedMesh *animMesh = 0; - if (0 != mesh->getMeshBufferCount()) { - mesh->recalculateBoundingBox(); - animMesh = new SAnimatedMesh(); - animMesh->Type = EAMT_OBJ; - animMesh->addMesh(mesh); - animMesh->recalculateBoundingBox(); - } - // Clean up the allocate obj file contents delete[] buf; // more cleaning up cleanUp(); - mesh->drop(); - return animMesh; + // Nothing in the mesh + if (mesh->getMeshBufferCount() == 0) { + mesh->drop(); + return nullptr; + } + + mesh->recalculateBoundingBox(); + return mesh; } //! Read RGB color diff --git a/irr/src/CSceneManager.cpp b/irr/src/CSceneManager.cpp index b5a310287..b0b82b772 100644 --- a/irr/src/CSceneManager.cpp +++ b/irr/src/CSceneManager.cpp @@ -8,7 +8,6 @@ #include "CSceneManager.h" #include "IVideoDriver.h" #include "IFileSystem.h" -#include "SAnimatedMesh.h" #include "CMeshCache.h" #include "IGUIEnvironment.h" #include "IMaterialRenderer.h" @@ -762,7 +761,7 @@ ISceneManager *CSceneManager::createNewSceneManager(bool cloneContent) //! Get a skinned mesh, which is not available as header-only code SkinnedMesh *CSceneManager::createSkinnedMesh() { - return new SkinnedMesh(); + return new SkinnedMesh(SkinnedMesh::SourceFormat::OTHER); } // creates a scenemanager diff --git a/irr/src/CXMeshFileLoader.cpp b/irr/src/CXMeshFileLoader.cpp index ed8c18350..f2c4e94e6 100644 --- a/irr/src/CXMeshFileLoader.cpp +++ b/irr/src/CXMeshFileLoader.cpp @@ -4,6 +4,7 @@ #include "CXMeshFileLoader.h" #include "SkinnedMesh.h" +#include "Transform.h" #include "os.h" #include "fast_atof.h" @@ -54,7 +55,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file) u32 time = os::Timer::getRealTime(); #endif - AnimatedMesh = new SkinnedMeshBuilder(); + AnimatedMesh = new SkinnedMeshBuilder(SkinnedMesh::SourceFormat::X); SkinnedMesh *res = nullptr; if (load(file)) { @@ -513,6 +514,7 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent) if (n.has_value()) { JointID = *n; joint = AnimatedMesh->getAllJoints()[JointID]; + joint->setParent(Parent); } } @@ -527,8 +529,6 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent) #ifdef _XREADER_DEBUG os::Printer::log("using joint ", name.c_str(), ELL_DEBUG); #endif - if (Parent) - Parent->Children.push_back(joint); } // Now inside a frame. @@ -552,12 +552,10 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent) if (!parseDataObjectFrame(joint)) return false; } else if (objectName == "FrameTransformMatrix") { - if (!parseDataObjectTransformationMatrix(joint->LocalMatrix)) + core::matrix4 matrix; + if (!parseDataObjectTransformationMatrix(matrix)) return false; - - // joint->LocalAnimatedMatrix - // joint->LocalAnimatedMatrix.makeInverse(); - // joint->LocalMatrix=tmp*joint->LocalAnimatedMatrix; + joint->transform = matrix; } else if (objectName == "Mesh") { /* frame.Meshes.push_back(SXMesh()); diff --git a/irr/src/SkinnedMesh.cpp b/irr/src/SkinnedMesh.cpp index 938a50e17..cf58121ba 100644 --- a/irr/src/SkinnedMesh.cpp +++ b/irr/src/SkinnedMesh.cpp @@ -7,7 +7,15 @@ #include "CBoneSceneNode.h" #include "IAnimatedMeshSceneNode.h" #include "SSkinMeshBuffer.h" +#include "Transform.h" +#include "aabbox3d.h" +#include "irrMath.h" +#include "matrix4.h" #include "os.h" +#include "vector3d.h" +#include +#include +#include #include #include @@ -48,183 +56,77 @@ void SkinnedMesh::setAnimationSpeed(f32 fps) FramesPerSecond = fps; } -//! returns the animated mesh based -IMesh *SkinnedMesh::getMesh(f32 frame) -{ - // animate(frame,startFrameLoop, endFrameLoop); - if (frame == -1) - return this; +// Keyframe Animation - animateMesh(frame); - skinMesh(); - return this; + +using VariantTransform = SkinnedMesh::SJoint::VariantTransform; +std::vector SkinnedMesh::animateMesh(f32 frame) +{ + assert(HasAnimation); + std::vector result; + result.reserve(AllJoints.size()); + for (auto *joint : AllJoints) + result.push_back(joint->animate(frame)); + return result; } -//-------------------------------------------------------------------------- -// Keyframe Animation -//-------------------------------------------------------------------------- - -//! Animates joints based on frame input -void SkinnedMesh::animateMesh(f32 frame) +core::aabbox3df SkinnedMesh::calculateBoundingBox( + const std::vector &global_transforms) { - if (!HasAnimation || LastAnimatedFrame == frame) - return; - - LastAnimatedFrame = frame; - SkinnedLastFrame = false; - - for (auto *joint : AllJoints) { - // The joints can be animated here with no input from their - // parents, but for setAnimationMode extra checks are needed - // to their parents - joint->keys.updateTransform(frame, - joint->Animatedposition, - joint->Animatedrotation, - joint->Animatedscale); + assert(global_transforms.size() == AllJoints.size()); + core::aabbox3df result = StaticPartsBox; + // skeletal animation + for (u16 i = 0; i < AllJoints.size(); ++i) { + auto box = AllJoints[i]->LocalBoundingBox; + global_transforms[i].transformBoxEx(box); + result.addInternalBox(box); } - - // Note: - // LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for - // one render (to play two animations at the same time) LocalAnimatedMatrix only needs to be built once. - // a call to buildAllLocalAnimatedMatrices is needed before skinning the mesh, and before the user gets the joints to move - - //---------------- - // Temp! - buildAllLocalAnimatedMatrices(); - //----------------- - - updateBoundingBox(); -} - -void SkinnedMesh::buildAllLocalAnimatedMatrices() -{ - for (auto *joint : AllJoints) { - // Could be faster: - - if (!joint->keys.empty()) { - joint->GlobalSkinningSpace = false; - - // IRR_TEST_BROKEN_QUATERNION_USE: TODO - switched to getMatrix_transposed instead of getMatrix for downward compatibility. - // Not tested so far if this was correct or wrong before quaternion fix! - // Note that using getMatrix_transposed inverts the rotation. - joint->Animatedrotation.getMatrix_transposed(joint->LocalAnimatedMatrix); - - // --- joint->LocalAnimatedMatrix *= joint->Animatedrotation.getMatrix() --- - f32 *m1 = joint->LocalAnimatedMatrix.pointer(); - core::vector3df &Pos = joint->Animatedposition; - m1[0] += Pos.X * m1[3]; - m1[1] += Pos.Y * m1[3]; - m1[2] += Pos.Z * m1[3]; - m1[4] += Pos.X * m1[7]; - m1[5] += Pos.Y * m1[7]; - m1[6] += Pos.Z * m1[7]; - m1[8] += Pos.X * m1[11]; - m1[9] += Pos.Y * m1[11]; - m1[10] += Pos.Z * m1[11]; - m1[12] += Pos.X * m1[15]; - m1[13] += Pos.Y * m1[15]; - m1[14] += Pos.Z * m1[15]; - // ----------------------------------- - - if (!joint->keys.scale.empty()) { - /* - core::matrix4 scaleMatrix; - scaleMatrix.setScale(joint->Animatedscale); - joint->LocalAnimatedMatrix *= scaleMatrix; - */ - - // -------- joint->LocalAnimatedMatrix *= scaleMatrix ----------------- - core::matrix4 &mat = joint->LocalAnimatedMatrix; - mat[0] *= joint->Animatedscale.X; - mat[1] *= joint->Animatedscale.X; - mat[2] *= joint->Animatedscale.X; - mat[3] *= joint->Animatedscale.X; - mat[4] *= joint->Animatedscale.Y; - mat[5] *= joint->Animatedscale.Y; - mat[6] *= joint->Animatedscale.Y; - mat[7] *= joint->Animatedscale.Y; - mat[8] *= joint->Animatedscale.Z; - mat[9] *= joint->Animatedscale.Z; - mat[10] *= joint->Animatedscale.Z; - mat[11] *= joint->Animatedscale.Z; - // ----------------------------------- - } - } else { - joint->LocalAnimatedMatrix = joint->LocalMatrix; + // rigid animation + for (u16 i = 0; i < AllJoints.size(); ++i) { + for (u32 j : AllJoints[i]->AttachedMeshes) { + auto box = (*SkinningBuffers)[j]->BoundingBox; + global_transforms[i].transformBoxEx(box); + result.addInternalBox(box); } } - SkinnedLastFrame = false; + return result; } -void SkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint) +// Software Skinning + +void SkinnedMesh::skinMesh(const std::vector &global_matrices) { - if (!joint) { - for (auto *rootJoint : RootJoints) - buildAllGlobalAnimatedMatrices(rootJoint, 0); - return; - } else { - // Find global matrix... - if (!parentJoint || joint->GlobalSkinningSpace) - joint->GlobalAnimatedMatrix = joint->LocalAnimatedMatrix; - else - joint->GlobalAnimatedMatrix = parentJoint->GlobalAnimatedMatrix * joint->LocalAnimatedMatrix; - } - - for (auto *childJoint : joint->Children) - buildAllGlobalAnimatedMatrices(childJoint, joint); -} - -//-------------------------------------------------------------------------- -// Software Skinning -//-------------------------------------------------------------------------- - -//! Preforms a software skin on this mesh based of joint positions -void SkinnedMesh::skinMesh() -{ - if (!HasAnimation || SkinnedLastFrame) + if (!HasAnimation) return; - //---------------- - // This is marked as "Temp!". A shiny dubloon to whomever can tell me why. - buildAllGlobalAnimatedMatrices(); - //----------------- - - SkinnedLastFrame = true; - if (!HardwareSkinning) { - // rigid animation - for (auto *joint : AllJoints) { - for (u32 attachedMeshIdx : joint->AttachedMeshes) { - SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx]; - Buffer->Transformation = joint->GlobalAnimatedMatrix; - } + // rigid animation + for (size_t i = 0; i < AllJoints.size(); ++i) { + auto *joint = AllJoints[i]; + for (u32 attachedMeshIdx : joint->AttachedMeshes) { + SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx]; + Buffer->Transformation = global_matrices[i]; } - - // clear skinning helper array - for (std::vector &buf : Vertices_Moved) - std::fill(buf.begin(), buf.end(), false); - - // skin starting with the root joints - for (auto *rootJoint : RootJoints) - skinJoint(rootJoint, 0); - - for (auto *buffer : *SkinningBuffers) - buffer->setDirty(EBT_VERTEX); } - updateBoundingBox(); -} -void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) -{ - if (joint->Weights.size()) { - // Find this joints pull on vertices... + // clear skinning helper array + for (std::vector &buf : Vertices_Moved) + std::fill(buf.begin(), buf.end(), false); + + // skin starting with the root joints + for (size_t i = 0; i < AllJoints.size(); ++i) { + auto *joint = AllJoints[i]; + if (joint->Weights.empty()) + continue; + + // Find this joints pull on vertices // Note: It is assumed that the global inversed matrix has been calculated at this point. - core::matrix4 jointVertexPull = joint->GlobalAnimatedMatrix * joint->GlobalInversedMatrix.value(); + core::matrix4 jointVertexPull = global_matrices[i] * joint->GlobalInversedMatrix.value(); core::vector3df thisVertexMove, thisNormalMove; auto &buffersUsed = *SkinningBuffers; - // Skin Vertices Positions and Normals... + // Skin Vertices, Positions and Normals for (const auto &weight : joint->Weights) { // Pull this vertex... jointVertexPull.transformVect(thisVertexMove, weight.StaticPos); @@ -251,14 +153,11 @@ void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint) //*(weight._Pos) += thisVertexMove * weight.strength; } - - buffersUsed[weight.buffer_id]->boundingBoxNeedsRecalculated(); } } - // Skin all children - for (auto *childJoint : joint->Children) - skinJoint(childJoint, joint); + for (auto *buffer : *SkinningBuffers) + buffer->setDirty(EBT_VERTEX); } //! Gets joint count. @@ -310,7 +209,7 @@ IMeshBuffer *SkinnedMesh::getMeshBuffer(const video::SMaterial &material) const if (LocalBuffers[i]->getMaterial() == material) return LocalBuffers[i]; } - return 0; + return nullptr; } u32 SkinnedMesh::getTextureSlot(u32 meshbufNr) const @@ -337,29 +236,6 @@ void SkinnedMesh::setDirty(E_BUFFER_TYPE buffer) LocalBuffers[i]->setDirty(buffer); } -//! (This feature is not implemented in irrlicht yet) -bool SkinnedMesh::setHardwareSkinning(bool on) -{ - if (HardwareSkinning != on) { - if (on) { - - // set mesh to static pose... - for (auto *joint : AllJoints) { - for (const auto &weight : joint->Weights) { - const u16 buffer_id = weight.buffer_id; - const u32 vertex_id = weight.vertex_id; - LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos = weight.StaticPos; - LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = weight.StaticNormal; - LocalBuffers[buffer_id]->boundingBoxNeedsRecalculated(); - } - } - } - - HardwareSkinning = on; - } - return HardwareSkinning; -} - void SkinnedMesh::refreshJointCache() { // copy cache from the mesh... @@ -384,113 +260,192 @@ void SkinnedMesh::resetAnimation() LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = weight.StaticNormal; } } - SkinnedLastFrame = false; - LastAnimatedFrame = -1; } -void SkinnedMesh::calculateGlobalMatrices(SJoint *joint, SJoint *parentJoint) +//! Turns the given array of local matrices into an array of global matrices +//! by multiplying with respective parent matrices. +void SkinnedMesh::calculateGlobalMatrices(std::vector &matrices) const { - if (!joint && parentJoint) // bit of protection from endless loops - return; - - // Go through the root bones - if (!joint) { - for (auto *rootJoint : RootJoints) - calculateGlobalMatrices(rootJoint, nullptr); - return; + // Note that the joints are topologically sorted. + for (u16 i = 0; i < AllJoints.size(); ++i) { + if (auto parent_id = AllJoints[i]->ParentJointID) { + matrices[i] = matrices[*parent_id] * matrices[i]; + } } - - if (!parentJoint) - joint->GlobalMatrix = joint->LocalMatrix; - else - joint->GlobalMatrix = parentJoint->GlobalMatrix * joint->LocalMatrix; - - joint->LocalAnimatedMatrix = joint->LocalMatrix; - joint->GlobalAnimatedMatrix = joint->GlobalMatrix; - - if (!joint->GlobalInversedMatrix.has_value()) { // might be pre calculated - joint->GlobalInversedMatrix = joint->GlobalMatrix; - joint->GlobalInversedMatrix->makeInverse(); // slow - } - - for (auto *childJoint : joint->Children) - calculateGlobalMatrices(childJoint, joint); - SkinnedLastFrame = false; } -void SkinnedMesh::checkForAnimation() +bool SkinnedMesh::checkForAnimation() const { - // Check for animation... - HasAnimation = false; for (auto *joint : AllJoints) { if (!joint->keys.empty()) { - HasAnimation = true; - break; + return true; } } - // meshes with weights, are still counted as animated for ragdolls, etc - if (!HasAnimation) { - for (auto *joint : AllJoints) { - if (joint->Weights.size()) { - HasAnimation = true; - break; + // meshes with weights are animatable + for (auto *joint : AllJoints) { + if (!joint->Weights.empty()) { + return true; + } + } + + return false; +} + +void SkinnedMesh::prepareForSkinning() +{ + HasAnimation = checkForAnimation(); + if (!HasAnimation || PreparedForSkinning) + return; + + PreparedForSkinning = true; + + EndFrame = 0.0f; + for (const auto *joint : AllJoints) { + EndFrame = std::max(EndFrame, joint->keys.getEndFrame()); + } + + for (auto *joint : AllJoints) { + for (auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; + + // check for invalid ids + if (buffer_id >= LocalBuffers.size()) { + os::Printer::log("Skinned Mesh: Weight buffer id too large", ELL_WARNING); + weight.buffer_id = weight.vertex_id = 0; + } else if (vertex_id >= LocalBuffers[buffer_id]->getVertexCount()) { + os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING); + weight.buffer_id = weight.vertex_id = 0; } } } - if (HasAnimation) { - EndFrame = 0.0f; - for (const auto *joint : AllJoints) { - EndFrame = std::max(EndFrame, joint->keys.getEndFrame()); + for (u32 i = 0; i < Vertices_Moved.size(); ++i) + for (u32 j = 0; j < Vertices_Moved[i].size(); ++j) + Vertices_Moved[i][j] = false; + + // For skinning: cache weight values for speed + for (auto *joint : AllJoints) { + for (auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; + + weight.Moved = &Vertices_Moved[buffer_id][vertex_id]; + weight.StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos; + weight.StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal; } } - if (HasAnimation && !PreparedForSkinning) { - PreparedForSkinning = true; + normalizeWeights(); - // check for bugs: - for (auto *joint : AllJoints) { - for (auto &weight : joint->Weights) { - const u16 buffer_id = weight.buffer_id; - const u32 vertex_id = weight.vertex_id; + for (auto *joint : AllJoints) { + joint->keys.cleanup(); + } +} - // check for invalid ids - if (buffer_id >= LocalBuffers.size()) { - os::Printer::log("Skinned Mesh: Weight buffer id too large", ELL_WARNING); - weight.buffer_id = weight.vertex_id = 0; - } else if (vertex_id >= LocalBuffers[buffer_id]->getVertexCount()) { - os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING); - weight.buffer_id = weight.vertex_id = 0; +void SkinnedMesh::calculateStaticBoundingBox() +{ + std::vector> animated(getMeshBufferCount()); + for (u32 mb = 0; mb < getMeshBufferCount(); mb++) + animated[mb] = std::vector(getMeshBuffer(mb)->getVertexCount()); + + for (auto *joint : AllJoints) { + for (auto &weight : joint->Weights) { + const u16 buffer_id = weight.buffer_id; + const u32 vertex_id = weight.vertex_id; + animated[buffer_id][vertex_id] = true; + } + } + + bool first = true; + for (u16 mb = 0; mb < getMeshBufferCount(); mb++) { + for (u32 v = 0; v < getMeshBuffer(mb)->getVertexCount(); v++) { + if (!animated[mb][v]) { + auto pos = getMeshBuffer(mb)->getVertexBuffer()->getPosition(v); + if (!first) { + StaticPartsBox.addInternalPoint(pos); + } else { + StaticPartsBox.reset(pos); + first = false; } } } + } +} - // An array used in skinning - - for (u32 i = 0; i < Vertices_Moved.size(); ++i) - for (u32 j = 0; j < Vertices_Moved[i].size(); ++j) - Vertices_Moved[i][j] = false; - - // For skinning: cache weight values for speed - - for (auto *joint : AllJoints) { - for (auto &weight : joint->Weights) { - const u16 buffer_id = weight.buffer_id; - const u32 vertex_id = weight.vertex_id; - - weight.Moved = &Vertices_Moved[buffer_id][vertex_id]; - weight.StaticPos = LocalBuffers[buffer_id]->getVertex(vertex_id)->Pos; - weight.StaticNormal = LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal; - - // weight._Pos=&Buffers[buffer_id]->getVertex(vertex_id)->Pos; +void SkinnedMesh::calculateJointBoundingBoxes() +{ + for (auto *joint : AllJoints) { + bool first = true; + for (auto &weight : joint->Weights) { + if (weight.strength < 1e-6) + continue; + auto pos = weight.StaticPos; + joint->GlobalInversedMatrix.value().transformVect(pos); + if (!first) { + joint->LocalBoundingBox.addInternalPoint(pos); + } else { + joint->LocalBoundingBox.reset(pos); + first = false; } } - - // normalize weights - normalizeWeights(); } - SkinnedLastFrame = false; +} + +void SkinnedMesh::calculateBufferBoundingBoxes() +{ + for (u32 j = 0; j < LocalBuffers.size(); ++j) { + // If we use skeletal animation, this will just be a bounding box of the static pose; + // if we use rigid animation, this will correctly transform the points first. + LocalBuffers[j]->recalculateBoundingBox(); + } +} + +void SkinnedMesh::recalculateBaseBoundingBoxes() { + calculateStaticBoundingBox(); + calculateJointBoundingBoxes(); + calculateBufferBoundingBoxes(); +} + +void SkinnedMesh::topoSortJoints() +{ + size_t n = AllJoints.size(); + + std::vector new_to_old_id; + + std::vector> children(n); + for (u16 i = 0; i < n; ++i) { + if (auto parentId = AllJoints[i]->ParentJointID) + children[*parentId].push_back(i); + else + new_to_old_id.push_back(i); + } + + // Levelorder + for (u16 i = 0; i < n; ++i) { + new_to_old_id.insert(new_to_old_id.end(), + children[new_to_old_id[i]].begin(), + children[new_to_old_id[i]].end()); + } + + std::vector old_to_new_id(n); + for (u16 i = 0; i < n; ++i) + old_to_new_id[new_to_old_id[i]] = i; + + std::vector joints(n); + for (u16 i = 0; i < n; ++i) { + joints[i] = AllJoints[new_to_old_id[i]]; + joints[i]->JointID = i; + if (auto parentId = joints[i]->ParentJointID) + joints[i]->ParentJointID = old_to_new_id[*parentId]; + } + AllJoints = std::move(joints); + + for (u16 i = 0; i < n; ++i) { + if (auto pjid = AllJoints[i]->ParentJointID) + assert(*pjid < i); + } } //! called by loader after populating with mesh and bone data @@ -498,98 +453,44 @@ SkinnedMesh *SkinnedMeshBuilder::finalize() { os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG); - // Make sure we recalc the next frame - LastAnimatedFrame = -1; - SkinnedLastFrame = false; - - // calculate bounding box - for (auto *buffer : LocalBuffers) { - buffer->recalculateBoundingBox(); - } - - if (AllJoints.size() || RootJoints.size()) { - // populate AllJoints or RootJoints, depending on which is empty - if (RootJoints.empty()) { - - for (auto *joint : AllJoints) { - - bool foundParent = false; - for (const auto *parentJoint : AllJoints) { - for (const auto *childJoint : parentJoint->Children) { - if (childJoint == joint) - foundParent = true; - } - } - - if (!foundParent) - RootJoints.push_back(joint); - } - } else { - AllJoints = RootJoints; - } - } - - // Set array sizes... + topoSortJoints(); + // Set array sizes for (u32 i = 0; i < LocalBuffers.size(); ++i) { Vertices_Moved.emplace_back(LocalBuffers[i]->getVertexCount()); } - checkForAnimation(); + prepareForSkinning(); - if (HasAnimation) { - for (auto *joint : AllJoints) { - joint->keys.cleanup(); - } - } - - // Needed for animation and skinning... - - calculateGlobalMatrices(0, 0); - - // rigid animation for non animated meshes + std::vector matrices; + matrices.reserve(AllJoints.size()); for (auto *joint : AllJoints) { + if (const auto *matrix = std::get_if(&joint->transform)) + matrices.push_back(*matrix); + else + matrices.push_back(std::get(joint->transform).buildMatrix()); + } + calculateGlobalMatrices(matrices); + + for (size_t i = 0; i < AllJoints.size(); ++i) { + auto *joint = AllJoints[i]; + if (!joint->GlobalInversedMatrix) { + joint->GlobalInversedMatrix = matrices[i]; + joint->GlobalInversedMatrix->makeInverse(); + } + // rigid animation for non animated meshes for (u32 attachedMeshIdx : joint->AttachedMeshes) { SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx]; - Buffer->Transformation = joint->GlobalAnimatedMatrix; + Buffer->Transformation = matrices[i]; } } - // calculate bounding box - if (LocalBuffers.empty()) - BoundingBox.reset(0, 0, 0); - else { - irr::core::aabbox3df bb(LocalBuffers[0]->BoundingBox); - LocalBuffers[0]->Transformation.transformBoxEx(bb); - BoundingBox.reset(bb); - - for (u32 j = 1; j < LocalBuffers.size(); ++j) { - bb = LocalBuffers[j]->BoundingBox; - LocalBuffers[j]->Transformation.transformBoxEx(bb); - - BoundingBox.addInternalBox(bb); - } - } + recalculateBaseBoundingBoxes(); + StaticPoseBox = calculateBoundingBox(matrices); return this; } -void SkinnedMesh::updateBoundingBox() -{ - if (!SkinningBuffers) - return; - - BoundingBox.reset(0, 0, 0); - - for (auto *buffer : *SkinningBuffers) { - buffer->recalculateBoundingBox(); - core::aabbox3df bb = buffer->BoundingBox; - buffer->Transformation.transformBoxEx(bb); - - BoundingBox.addInternalBox(bb); - } -} - scene::SSkinMeshBuffer *SkinnedMeshBuilder::addMeshBuffer() { scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer(); @@ -607,14 +508,10 @@ void SkinnedMeshBuilder::addMeshBuffer(SSkinMeshBuffer *meshbuf) SkinnedMesh::SJoint *SkinnedMeshBuilder::addJoint(SJoint *parent) { SJoint *joint = new SJoint; + joint->setParent(parent); + joint->JointID = AllJoints.size(); AllJoints.push_back(joint); - if (!parent) { - // Add root joints to array in finalize() - } else { - // Set parent (Be careful of the mesh loader also setting the parent) - parent->Children.push_back(joint); - } return joint; } @@ -684,73 +581,6 @@ void SkinnedMesh::normalizeWeights() } } -void SkinnedMesh::recoverJointsFromMesh(std::vector &jointChildSceneNodes) -{ - for (u32 i = 0; i < AllJoints.size(); ++i) { - IBoneSceneNode *node = jointChildSceneNodes[i]; - SJoint *joint = AllJoints[i]; - node->setPosition(joint->LocalAnimatedMatrix.getTranslation()); - node->setRotation(joint->LocalAnimatedMatrix.getRotationDegrees()); - node->setScale(joint->LocalAnimatedMatrix.getScale()); - - node->updateAbsolutePosition(); - } -} - -void SkinnedMesh::transferJointsToMesh(const std::vector &jointChildSceneNodes) -{ - for (u32 i = 0; i < AllJoints.size(); ++i) { - const IBoneSceneNode *const node = jointChildSceneNodes[i]; - SJoint *joint = AllJoints[i]; - - joint->LocalAnimatedMatrix.setRotationDegrees(node->getRotation()); - joint->LocalAnimatedMatrix.setTranslation(node->getPosition()); - joint->LocalAnimatedMatrix *= core::matrix4().setScale(node->getScale()); - - joint->GlobalSkinningSpace = (node->getSkinningSpace() == EBSS_GLOBAL); - } - // Make sure we recalc the next frame - LastAnimatedFrame = -1; - SkinnedLastFrame = false; -} - -void SkinnedMesh::addJoints(std::vector &jointChildSceneNodes, - IAnimatedMeshSceneNode *node, ISceneManager *smgr) -{ - // Create new joints - for (u32 i = 0; i < AllJoints.size(); ++i) { - jointChildSceneNodes.push_back(new CBoneSceneNode(0, smgr, 0, i, AllJoints[i]->Name)); - } - - // Match up parents - for (u32 i = 0; i < jointChildSceneNodes.size(); ++i) { - const SJoint *const joint = AllJoints[i]; // should be fine - - s32 parentID = -1; - - for (u32 j = 0; (parentID == -1) && (j < AllJoints.size()); ++j) { - if (i != j) { - const SJoint *const parentTest = AllJoints[j]; - for (u32 n = 0; n < parentTest->Children.size(); ++n) { - if (parentTest->Children[n] == joint) { - parentID = j; - break; - } - } - } - } - - IBoneSceneNode *bone = jointChildSceneNodes[i]; - if (parentID != -1) - bone->setParent(jointChildSceneNodes[parentID]); - else - bone->setParent(node); - - bone->drop(); - } - SkinnedLastFrame = false; -} - void SkinnedMesh::convertMeshToTangents() { // now calculate tangents diff --git a/lib/tiniergltf/tiniergltf.hpp b/lib/tiniergltf/tiniergltf.hpp index 06e2f5356..81ac10ec8 100644 --- a/lib/tiniergltf/tiniergltf.hpp +++ b/lib/tiniergltf/tiniergltf.hpp @@ -916,12 +916,7 @@ struct Node { 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 - }) + : transform(TRS{}) { check(o.isObject()); if (o.isMember("camera")) { diff --git a/minetest.conf.example b/minetest.conf.example index d9b7a58ff..29cbb207b 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -95,366 +95,366 @@ ### Keybindings # Key for moving the player forward. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_forward = KEY_KEY_W +# keymap_forward = SYSTEM_SCANCODE_26 # Key for moving the player backward. # Will also disable autoforward, when active. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_backward = KEY_KEY_S +# keymap_backward = SYSTEM_SCANCODE_22 # Key for moving the player left. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_left = KEY_KEY_A +# keymap_left = SYSTEM_SCANCODE_4 # Key for moving the player right. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_right = KEY_KEY_D +# keymap_right = SYSTEM_SCANCODE_7 # Key for jumping. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_jump = KEY_SPACE +# keymap_jump = SYSTEM_SCANCODE_44 # Key for sneaking. # Also used for climbing down and descending in water if aux1_descends is disabled. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_sneak = KEY_LSHIFT +# keymap_sneak = SYSTEM_SCANCODE_225 # Key for digging, punching or using something. # (Note: The actual meaning might vary on a per-game basis.) -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_dig = KEY_LBUTTON # Key for placing an item/block or for using something. # (Note: The actual meaning might vary on a per-game basis.) -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_place = KEY_RBUTTON # Key for opening the inventory. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_inventory = KEY_KEY_I +# keymap_inventory = SYSTEM_SCANCODE_12 # Key for moving fast in fast mode. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_aux1 = KEY_KEY_E +# keymap_aux1 = SYSTEM_SCANCODE_8 # Key for opening the chat window. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_chat = KEY_KEY_T +# keymap_chat = SYSTEM_SCANCODE_23 # Key for opening the chat window to type commands. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_cmd = / +# keymap_cmd = SYSTEM_SCANCODE_56 # Key for opening the chat window to type local commands. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_cmd_local = . +# keymap_cmd_local = SYSTEM_SCANCODE_55 # Key for toggling unlimited view range. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_rangeselect = # Key for toggling flying. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_freemove = KEY_KEY_K +# keymap_freemove = SYSTEM_SCANCODE_14 # Key for toggling pitch move mode. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_pitchmove = # Key for toggling fast mode. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_fastmove = KEY_KEY_J +# keymap_fastmove = SYSTEM_SCANCODE_13 # Key for toggling noclip mode. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_noclip = KEY_KEY_H +# keymap_noclip = SYSTEM_SCANCODE_11 # Key for selecting the next item in the hotbar. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_hotbar_next = KEY_KEY_N +# keymap_hotbar_next = SYSTEM_SCANCODE_17 # Key for selecting the previous item in the hotbar. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_hotbar_previous = KEY_KEY_B +# keymap_hotbar_previous = SYSTEM_SCANCODE_5 # Key for muting the game. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_mute = KEY_KEY_M +# keymap_mute = SYSTEM_SCANCODE_16 # Key for increasing the volume. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_increase_volume = # Key for decreasing the volume. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_decrease_volume = # Key for toggling autoforward. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_autoforward = # Key for toggling cinematic mode. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_cinematic = # Key for toggling display of minimap. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_minimap = KEY_KEY_V +# keymap_minimap = SYSTEM_SCANCODE_25 # Key for taking screenshots. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_screenshot = KEY_F12 +# keymap_screenshot = SYSTEM_SCANCODE_69 # Key for toggling fullscreen mode. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_fullscreen = KEY_F11 +# keymap_fullscreen = SYSTEM_SCANCODE_68 # Key for dropping the currently selected item. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_drop = KEY_KEY_Q +# keymap_drop = SYSTEM_SCANCODE_20 # Key to use view zoom when possible. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_zoom = KEY_KEY_Z +# keymap_zoom = SYSTEM_SCANCODE_29 # Key for toggling the display of the HUD. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_toggle_hud = KEY_F1 +# keymap_toggle_hud = SYSTEM_SCANCODE_58 # Key for toggling the display of chat. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_toggle_chat = KEY_F2 +# keymap_toggle_chat = SYSTEM_SCANCODE_59 # Key for toggling the display of the large chat console. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_console = KEY_F10 +# keymap_console = SYSTEM_SCANCODE_67 # Key for toggling the display of fog. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_toggle_fog = KEY_F3 +# keymap_toggle_fog = SYSTEM_SCANCODE_60 # Key for toggling the display of debug info. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_toggle_debug = KEY_F5 +# keymap_toggle_debug = SYSTEM_SCANCODE_62 # Key for toggling the display of the profiler. Used for development. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_toggle_profiler = KEY_F6 +# keymap_toggle_profiler = SYSTEM_SCANCODE_63 # Key for toggling the display of mapblock boundaries. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_toggle_block_bounds = # Key for switching between first- and third-person camera. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_camera_mode = KEY_KEY_C +# keymap_camera_mode = SYSTEM_SCANCODE_6 # Key for increasing the viewing range. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_increase_viewing_range_min = + +# keymap_increase_viewing_range_min = SYSTEM_SCANCODE_46 # Key for decreasing the viewing range. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_decrease_viewing_range_min = - +# keymap_decrease_viewing_range_min = SYSTEM_SCANCODE_45 # Key for selecting the first hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot1 = KEY_KEY_1 +# keymap_slot1 = SYSTEM_SCANCODE_30 # Key for selecting the second hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot2 = KEY_KEY_2 +# keymap_slot2 = SYSTEM_SCANCODE_31 # Key for selecting the third hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot3 = KEY_KEY_3 +# keymap_slot3 = SYSTEM_SCANCODE_32 # Key for selecting the fourth hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot4 = KEY_KEY_4 +# keymap_slot4 = SYSTEM_SCANCODE_33 # Key for selecting the fifth hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot5 = KEY_KEY_5 +# keymap_slot5 = SYSTEM_SCANCODE_34 # Key for selecting the sixth hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot6 = KEY_KEY_6 +# keymap_slot6 = SYSTEM_SCANCODE_35 # Key for selecting the seventh hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot7 = KEY_KEY_7 +# keymap_slot7 = SYSTEM_SCANCODE_36 # Key for selecting the eighth hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot8 = KEY_KEY_8 +# keymap_slot8 = SYSTEM_SCANCODE_37 # Key for selecting the ninth hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot9 = KEY_KEY_9 +# keymap_slot9 = SYSTEM_SCANCODE_38 # Key for selecting the tenth hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key -# keymap_slot10 = KEY_KEY_0 +# keymap_slot10 = SYSTEM_SCANCODE_39 # Key for selecting the 11th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot11 = # Key for selecting the 12th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot12 = # Key for selecting the 13th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot13 = # Key for selecting the 14th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot14 = # Key for selecting the 15th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot15 = # Key for selecting the 16th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot16 = # Key for selecting the 17th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot17 = # Key for selecting the 18th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot18 = # Key for selecting the 19th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot19 = # Key for selecting the 20th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot20 = # Key for selecting the 21st hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot21 = # Key for selecting the 22nd hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot22 = # Key for selecting the 23rd hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot23 = # Key for selecting the 24th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot24 = # Key for selecting the 25th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot25 = # Key for selecting the 26th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot26 = # Key for selecting the 27th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot27 = # Key for selecting the 28th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot28 = # Key for selecting the 29th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot29 = # Key for selecting the 30th hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot30 = # Key for selecting the 31st hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot31 = # Key for selecting the 32nd hotbar slot. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_slot32 = @@ -925,10 +925,6 @@ # type: bool # enable_translucent_foliage = false -# Apply specular shading to nodes. -# type: bool -# enable_node_specular = false - # When enabled, liquid reflections are simulated. # type: bool # enable_water_reflections = false @@ -3554,27 +3550,27 @@ ### Client Debugging # Key for toggling the camera update. Only usable with 'debug' privilege. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_toggle_update_camera = # Key for switching to the previous entry in Quicktune. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_quicktune_prev = # Key for switching to the next entry in Quicktune. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_quicktune_next = # Key for decrementing the selected value in Quicktune. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_quicktune_dec = # Key for incrementing the selected value in Quicktune. -# See https://github.com/luanti-org/luanti/blob/master/irr/include/Keycodes.h +# See https://docs.luanti.org/for-players/controls/ # type: key # keymap_quicktune_inc = diff --git a/misc/AppImageBuilder.yml b/misc/AppImageBuilder.yml index 4862e58f1..b69b88a17 100644 --- a/misc/AppImageBuilder.yml +++ b/misc/AppImageBuilder.yml @@ -56,5 +56,3 @@ script: | # Is a backup icon location in case mkdir -p AppDir/usr/share/luanti/misc cp AppDir/usr/share/icons/hicolor/128x128/apps/luanti.png AppDir/usr/share/luanti/misc/luanti-xorg-icon-128.png - # Validation issues - sed -i '/PrefersNonDefaultGPU/d' AppDir/usr/share/applications/org.luanti.luanti.desktop diff --git a/misc/org.luanti.luanti.desktop b/misc/org.luanti.luanti.desktop index 325bd59d6..eab76aabb 100644 --- a/misc/org.luanti.luanti.desktop +++ b/misc/org.luanti.luanti.desktop @@ -7,7 +7,7 @@ Comment[fr]=Plate-forme de jeu multijoueurs à base de blocs Exec=luanti Icon=luanti Terminal=false -PrefersNonDefaultGPU=true +# Note: don't add PrefersNonDefaultGPU here, see #16095 Type=Application Categories=Game;Simulation; StartupNotify=false diff --git a/misc/org.luanti.luanti.metainfo.xml b/misc/org.luanti.luanti.metainfo.xml index 361e4f40f..6759cdc1f 100644 --- a/misc/org.luanti.luanti.metainfo.xml +++ b/misc/org.luanti.luanti.metainfo.xml @@ -174,6 +174,6 @@ celeron55@gmail.com - + diff --git a/po/de/luanti.po b/po/de/luanti.po index d8cba07fb..2f37c2ec7 100644 --- a/po/de/luanti.po +++ b/po/de/luanti.po @@ -3,8 +3,8 @@ msgstr "" "Project-Id-Version: German (Minetest)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-14 23:02+0200\n" -"PO-Revision-Date: 2025-04-25 19:50+0000\n" -"Last-Translator: Wuzzy \n" +"PO-Revision-Date: 2025-05-16 10:31+0000\n" +"Last-Translator: sfan5 \n" "Language-Team: German \n" "Language: de\n" @@ -937,29 +937,27 @@ msgstr "Die Welt „$1“ löschen?" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "As a result, your keybindings may have been changed." -msgstr "" +msgstr "Aufgrund dessen könnte sich Ihre Tastenbelegung geändert haben." #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Check out the key settings or refer to the documentation:" -msgstr "" +msgstr "Schauen Sie sich die Einstellungen oder die Dokumentation an:" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Close" -msgstr "" +msgstr "Schließen" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Keybindings changed" -msgstr "Tastenbelegung" +msgstr "Geänderte Tastenbelegung" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Open settings" -msgstr "Einstellungen" +msgstr "Einstellungen öffnen" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "The input handling system was reworked in Luanti 5.12.0." -msgstr "" +msgstr "Das Steuerungssystem wurde in Luanti 5.12.0 überarbeitet." #: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp msgid "Confirm Password" @@ -4660,7 +4658,7 @@ msgid "" "Instrument global callback functions on registration.\n" "(anything you pass to a core.register_*() function)" msgstr "" -"Globale Rückruffunktionen bei ihrer Registrierung instrumentieren\n" +"Globale Rückruffunktionen bei ihrer Registrierung instrumentieren.\n" "(alles, was man einer Funktion wie core.register_*() übergibt)." #: src/settings_translation_file.cpp diff --git a/po/eu/luanti.po b/po/eu/luanti.po index 39e391e21..5e8dc52e7 100644 --- a/po/eu/luanti.po +++ b/po/eu/luanti.po @@ -3,8 +3,8 @@ msgstr "" "Project-Id-Version: minetest\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-14 23:02+0200\n" -"PO-Revision-Date: 2022-04-29 20:12+0000\n" -"Last-Translator: JonAnder Oier \n" +"PO-Revision-Date: 2025-05-21 10:48+0000\n" +"Last-Translator: Josu Igoa \n" "Language-Team: Basque \n" "Language: eu\n" @@ -12,7 +12,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.12.1\n" +"X-Generator: Weblate 5.12-dev\n" #: builtin/client/chatcommands.lua msgid "Clear the out chat queue" @@ -23,9 +23,8 @@ msgid "Empty command." msgstr "Agindu hutsa." #: builtin/client/chatcommands.lua -#, fuzzy msgid "Exit to main menu" -msgstr "Itzuli menu nagusira" +msgstr "Irten menu nagusira" #: builtin/client/chatcommands.lua msgid "Invalid command: " @@ -64,9 +63,8 @@ msgid "Command not available: " msgstr "Komandoa ez dago eskuragarri: " #: builtin/common/chatcommands.lua -#, fuzzy msgid "Get help for commands (-t: output in chat)" -msgstr "Eskuratu laguntza komandoetarako" +msgstr "Eskuratu laguntza komandoetarako (-t: irteera txat-ean)" #: builtin/common/chatcommands.lua msgid "" @@ -76,9 +74,8 @@ msgstr "" "zerrendatzeko." #: builtin/common/chatcommands.lua -#, fuzzy msgid "[all | ] [-t]" -msgstr "[guztia | ]" +msgstr "[all | ] [-t]" #: builtin/common/settings/components.lua msgid "Browse" diff --git a/po/fr/luanti.po b/po/fr/luanti.po index fa6fa8839..089ba13c3 100644 --- a/po/fr/luanti.po +++ b/po/fr/luanti.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: French (Minetest)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-14 23:02+0200\n" -"PO-Revision-Date: 2025-04-28 18:58+0000\n" +"PO-Revision-Date: 2025-05-18 17:31+0000\n" "Last-Translator: waxtatect \n" "Language-Team: French \n" @@ -938,29 +938,27 @@ msgstr "Supprimer le monde « $1 » ?" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "As a result, your keybindings may have been changed." -msgstr "" +msgstr "Ainsi, il est possible que certaines touches aient été modifiées." #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Check out the key settings or refer to the documentation:" -msgstr "" +msgstr "Vérifier les paramètres des touches ou consulter la documentation :" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Close" -msgstr "" +msgstr "Fermer" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Keybindings changed" -msgstr "Raccourcis clavier" +msgstr "Modification des raccourcis clavier" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Open settings" -msgstr "Paramètres" +msgstr "Ouvrir les paramètres" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "The input handling system was reworked in Luanti 5.12.0." -msgstr "" +msgstr "Le système d'affectation des touches a été remanié dans Luanti 5.12.0." #: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp msgid "Confirm Password" diff --git a/po/id/luanti.po b/po/id/luanti.po index 2b29b9cc1..801bdf42b 100644 --- a/po/id/luanti.po +++ b/po/id/luanti.po @@ -3,7 +3,7 @@ msgstr "" "Project-Id-Version: Indonesian (Minetest)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-14 23:02+0200\n" -"PO-Revision-Date: 2025-04-25 10:52+0000\n" +"PO-Revision-Date: 2025-05-15 23:01+0000\n" "Last-Translator: Linerly \n" "Language-Team: Indonesian \n" @@ -12,7 +12,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.11.1-dev\n" +"X-Generator: Weblate 5.12-dev\n" #: builtin/client/chatcommands.lua msgid "Clear the out chat queue" @@ -930,29 +930,27 @@ msgstr "Hapus dunia \"$1\"?" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "As a result, your keybindings may have been changed." -msgstr "" +msgstr "Oleh sebab itu, pengikatan tombol kamu dapat berubah." #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Check out the key settings or refer to the documentation:" -msgstr "" +msgstr "Periksa pengaturan tombol atau rujuk pada dokumentasi:" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Close" -msgstr "" +msgstr "Tutup" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Keybindings changed" -msgstr "Pengikatan tombol" +msgstr "Pengikatan tombol berubah" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Open settings" -msgstr "Pengaturan" +msgstr "Buka pengaturan" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "The input handling system was reworked in Luanti 5.12.0." -msgstr "" +msgstr "Sistem penanganan masukan telah dikerjakan ulang dalam Luanti 5.12.0." #: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp msgid "Confirm Password" diff --git a/po/it/luanti.po b/po/it/luanti.po index 6377cca82..cbbbd78cc 100644 --- a/po/it/luanti.po +++ b/po/it/luanti.po @@ -3,8 +3,8 @@ msgstr "" "Project-Id-Version: Italian (Minetest)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-14 23:02+0200\n" -"PO-Revision-Date: 2024-01-18 07:31+0000\n" -"Last-Translator: Filippo Alfieri \n" +"PO-Revision-Date: 2025-05-23 15:04+0000\n" +"Last-Translator: Francesco Rossi \n" "Language-Team: Italian \n" "Language: it\n" @@ -12,7 +12,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 5.4-dev\n" +"X-Generator: Weblate 5.12-dev\n" #: builtin/client/chatcommands.lua msgid "Clear the out chat queue" @@ -85,16 +85,15 @@ msgstr "Esplora" #: builtin/common/settings/components.lua msgid "Conflicts with \"$1\"" -msgstr "" +msgstr "Va in conflitto con \"$1\"" #: builtin/common/settings/components.lua msgid "Edit" msgstr "Modifica" #: builtin/common/settings/components.lua -#, fuzzy msgid "Remove keybinding" -msgstr "Mappatura dei tasti." +msgstr "Rimuovi questa mappatura." #: builtin/common/settings/components.lua msgid "Select directory" @@ -219,7 +218,7 @@ msgstr "Accessibilità" #: builtin/common/settings/dlg_settings.lua msgid "Auto" -msgstr "" +msgstr "Automatico" #: builtin/common/settings/dlg_settings.lua #: builtin/mainmenu/content/dlg_contentdb.lua @@ -229,7 +228,7 @@ msgstr "Indietro" #: builtin/common/settings/dlg_settings.lua msgid "Buttons with crosshair" -msgstr "" +msgstr "Pulsanti e mirino" #: builtin/common/settings/dlg_settings.lua src/gui/touchscreenlayout.cpp #: src/settings_translation_file.cpp @@ -259,7 +258,7 @@ msgstr "Generale" #: builtin/common/settings/dlg_settings.lua msgid "Long tap" -msgstr "" +msgstr "Tocco prolungato" #: builtin/common/settings/dlg_settings.lua msgid "Movement" @@ -284,7 +283,7 @@ msgstr "Ricerca" #: builtin/common/settings/dlg_settings.lua msgid "Short tap" -msgstr "" +msgstr "Tocco rapido" #: builtin/common/settings/dlg_settings.lua msgid "Show advanced settings" @@ -301,7 +300,7 @@ msgstr "Tab" #: builtin/common/settings/dlg_settings.lua msgid "Tap with crosshair" -msgstr "" +msgstr "Tocco con mirino" #: builtin/common/settings/dlg_settings.lua #, fuzzy @@ -436,7 +435,7 @@ msgstr "Scaricando $1..." #: builtin/mainmenu/content/dlg_contentdb.lua msgid "All" -msgstr "" +msgstr "Tutto" #: builtin/mainmenu/content/dlg_contentdb.lua #, fuzzy @@ -449,7 +448,7 @@ msgstr "Scaricamento..." #: builtin/mainmenu/content/dlg_contentdb.lua msgid "Featured" -msgstr "" +msgstr "In evidenza" #: builtin/mainmenu/content/dlg_contentdb.lua msgid "Games" @@ -522,7 +521,7 @@ msgstr "Dipendenze:" #: builtin/mainmenu/content/dlg_install.lua msgid "Error getting dependencies for package $1" -msgstr "" +msgstr "Errore nell'ottenere le dipendenze per il pacchetto $1" #: builtin/mainmenu/content/dlg_install.lua msgid "Install" @@ -546,7 +545,7 @@ msgstr "Per favore, controlla che il gioco di base sia corretto." #: builtin/mainmenu/content/dlg_install.lua msgid "You need to install a game before you can install a mod" -msgstr "Devi installare un gioco prima di poter installare un mod" +msgstr "Devi installare un gioco prima di poter installare un modulo" #: builtin/mainmenu/content/dlg_overwrite.lua msgid "\"$1\" already exists. Would you like to overwrite it?" @@ -568,20 +567,19 @@ msgstr "Descrizione del Server" #: builtin/mainmenu/content/dlg_package.lua msgid "Donate" -msgstr "" +msgstr "Dona" #: builtin/mainmenu/content/dlg_package.lua msgid "Error loading package information" -msgstr "" +msgstr "Errore nel caricare le informazioni del pacchetto" #: builtin/mainmenu/content/dlg_package.lua -#, fuzzy msgid "Error loading reviews" -msgstr "Errore di creazione del client: %s" +msgstr "Errore nel caricare le recensioni" #: builtin/mainmenu/content/dlg_package.lua msgid "Forum Topic" -msgstr "" +msgstr "Discussione sul forum" #: builtin/mainmenu/content/dlg_package.lua #, fuzzy @@ -595,19 +593,19 @@ msgstr "Installa $1" #: builtin/mainmenu/content/dlg_package.lua msgid "Issue Tracker" -msgstr "" +msgstr "Segnalazioni" #: builtin/mainmenu/content/dlg_package.lua msgid "Reviews" -msgstr "" +msgstr "Recensioni" #: builtin/mainmenu/content/dlg_package.lua msgid "Source" -msgstr "" +msgstr "Sorgente" #: builtin/mainmenu/content/dlg_package.lua msgid "Translate" -msgstr "" +msgstr "Traduci" #: builtin/mainmenu/content/dlg_package.lua builtin/mainmenu/tab_content.lua msgid "Uninstall" @@ -624,7 +622,7 @@ msgstr "Visita il sito Web" #: builtin/mainmenu/content/dlg_package.lua msgid "by $1 — $2 downloads — +$3 / $4 / -$5" -msgstr "" +msgstr "di $1 — $2 scaricamenti — +$3 / $4 / -$5" #: builtin/mainmenu/content/pkgmgr.lua msgid "$1 (Enabled)" @@ -632,7 +630,7 @@ msgstr "$1 (Attivato)" #: builtin/mainmenu/content/pkgmgr.lua msgid "$1 mods" -msgstr "$1 mod" +msgstr "$1 moduli" #: builtin/mainmenu/content/pkgmgr.lua msgid "Failed to install $1 to $2" @@ -646,11 +644,11 @@ msgstr "" #: builtin/mainmenu/content/pkgmgr.lua msgid "Unable to find a valid mod, modpack, or game" -msgstr "Impossibile trovare un mod o un pacchetto mod validi" +msgstr "Impossibile trovare un modulo o un pacchetto moduli validi" #: builtin/mainmenu/content/pkgmgr.lua msgid "Unable to install a $1 as a $2" -msgstr "Impossibile installare una mod come un $1" +msgstr "Impossibile installare $1 come se fosse $2" #: builtin/mainmenu/content/pkgmgr.lua msgid "Unable to install a $1 as a texture pack" @@ -661,6 +659,8 @@ msgid "" "Players connected to\n" "$1" msgstr "" +"Giocanti connessɜ a\n" +"$1" #: builtin/mainmenu/dlg_config_world.lua msgid "(Enabled, has error)" @@ -676,7 +676,7 @@ msgstr "Disattiva tutto" #: builtin/mainmenu/dlg_config_world.lua msgid "Disable modpack" -msgstr "Disattiva pacchetto mod" +msgstr "Disattiva pacchetto moduli" #: builtin/mainmenu/dlg_config_world.lua msgid "Enable all" @@ -684,14 +684,14 @@ msgstr "Attiva tutto" #: builtin/mainmenu/dlg_config_world.lua msgid "Enable modpack" -msgstr "Attiva pacchetto mod" +msgstr "Attiva pacchetto moduli" #: builtin/mainmenu/dlg_config_world.lua msgid "" "Failed to enable mod \"$1\" as it contains disallowed characters. Only " "characters [a-z0-9_] are allowed." msgstr "" -"Impossibile abilitare la mod \"$1\" poiché contiene caratteri non " +"Impossibile abilitare il modulo \"$1\" poiché contiene caratteri non " "consentiti. Sono ammessi solo caratteri [a-z0-9_]." #: builtin/mainmenu/dlg_config_world.lua @@ -716,7 +716,7 @@ msgstr "Nessuna dipendenza necessaria" #: builtin/mainmenu/dlg_config_world.lua msgid "No modpack description provided." -msgstr "Non è stata fornita alcuna descrizione per il pacchetto mod." +msgstr "Non è stata fornita alcuna descrizione per il pacchetto moduli." #: builtin/mainmenu/dlg_config_world.lua msgid "No optional dependencies" @@ -776,7 +776,7 @@ msgstr "Decorazioni" #: builtin/mainmenu/dlg_create_world.lua msgid "Desert temples" -msgstr "" +msgstr "Templi del deserto" #: builtin/mainmenu/dlg_create_world.lua msgid "Development Test is meant for developers." @@ -947,20 +947,19 @@ msgstr "Eliminare il mondo \"$1\"?" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "As a result, your keybindings may have been changed." -msgstr "" +msgstr "Di conseguenza, la mappatura dei tuoi tasti potrebbe essere cambiata." #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Check out the key settings or refer to the documentation:" -msgstr "" +msgstr "Dài un occhio alle impostazioni dei tasti o consulta la documentazione:" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Close" -msgstr "" +msgstr "Chiudi" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Keybindings changed" -msgstr "Mappatura dei tasti." +msgstr "Mappatura dei tasti cambiata" #: builtin/mainmenu/dlg_rebind_keys.lua #, fuzzy @@ -970,6 +969,7 @@ msgstr "Impostazioni" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "The input handling system was reworked in Luanti 5.12.0." msgstr "" +"Il sistema di gestione degli input è stato rivisitato nella 5.12.0 di Luanti." #: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp msgid "Confirm Password" @@ -1038,14 +1038,14 @@ msgstr "Conferma" #: builtin/mainmenu/dlg_rename_modpack.lua msgid "Rename Modpack:" -msgstr "Rinomina il pacchetto mod:" +msgstr "Rinomina il pacchetto moduli:" #: builtin/mainmenu/dlg_rename_modpack.lua msgid "" "This modpack has an explicit name given in its modpack.conf which will " "override any renaming here." msgstr "" -"Questo pacchetto mod esplicita un nome fornito in modpack.conf che " +"Questo pacchetto moduli esplicita un nome fornito in modpack.conf che " "sovrascriverà ogni modifica del nome qui effettuata." #: builtin/mainmenu/dlg_server_list_mods.lua @@ -1055,15 +1055,15 @@ msgstr "Attiva tutto" #: builtin/mainmenu/dlg_server_list_mods.lua msgid "Group by prefix" -msgstr "" +msgstr "Raggruppa per prefisso" #: builtin/mainmenu/dlg_server_list_mods.lua msgid "The $1 server uses a game called $2 and the following mods:" -msgstr "" +msgstr "Il server $1 usa un gioco chiamato $2 e i seguenti moduli:" #: builtin/mainmenu/dlg_server_list_mods.lua msgid "The $1 server uses the following mods:" -msgstr "" +msgstr "Il server $1 usa i seguenti moduli:" #: builtin/mainmenu/dlg_version_info.lua msgid "A new $1 version is available" @@ -1136,7 +1136,7 @@ msgid "" "Opens the directory that contains user-provided worlds, games, mods,\n" "and texture packs in a file manager / explorer." msgstr "" -"Apre la cartella contenente mondi, giochi, mod e pacchetti\n" +"Apre la cartella contenente mondi, giochi, moduli e pacchetti\n" "texture forniti dall'utente in un gestore / visualizzatore di file ." #: builtin/mainmenu/tab_about.lua @@ -1228,16 +1228,16 @@ msgid "Install games from ContentDB" msgstr "Installa giochi da ContentDB" #: builtin/mainmenu/tab_local.lua -#, fuzzy msgid "Luanti doesn't come with a game by default." -msgstr "" -"Il gioco Minetest Game non è più installato per impostazione predefinita" +msgstr "Di base, Luanti non viene fornito con giochi già installati." #: builtin/mainmenu/tab_local.lua msgid "" "Luanti is a game-creation platform that allows you to play many different " "games." msgstr "" +"Luanti è una piattaforma di gioco che permette di giocare a tanti titoli " +"differenti." #: builtin/mainmenu/tab_local.lua msgid "New" @@ -1272,9 +1272,8 @@ msgid "Start Game" msgstr "Gioca" #: builtin/mainmenu/tab_local.lua -#, fuzzy msgid "You need to install a game before you can create a world." -msgstr "Devi installare un gioco prima di poter installare un mod" +msgstr "Devi installare un gioco prima di poter creare un mondo." #: builtin/mainmenu/tab_online.lua #, fuzzy @@ -1316,9 +1315,8 @@ msgid "Login" msgstr "Accedi" #: builtin/mainmenu/tab_online.lua -#, fuzzy msgid "Number of mods: $1" -msgstr "Numero di thread emerge" +msgstr "Numero di moduli: $1" #: builtin/mainmenu/tab_online.lua #, fuzzy @@ -1330,11 +1328,12 @@ msgid "Ping" msgstr "Ping" #: builtin/mainmenu/tab_online.lua -#, fuzzy msgid "" "Players:\n" "$1" -msgstr "Client" +msgstr "" +"Giocatori:\n" +"$1" #: builtin/mainmenu/tab_online.lua msgid "" @@ -1343,6 +1342,10 @@ msgid "" "mod:\n" "player:" msgstr "" +"Possibili filtri\n" +"game:\n" +"mod:\n" +"player:" #: builtin/mainmenu/tab_online.lua msgid "Public Servers" @@ -1475,9 +1478,9 @@ msgid "Camera update enabled" msgstr "Aggiornamento telecamera abilitato" #: src/client/game.cpp -#, fuzzy msgid "Can't show block bounds (disabled by game or mod)" -msgstr "Impossibile mostrare i limiti del blocco (disabilitato da mod o gioco)" +msgstr "" +"Impossibile mostrare i limiti del blocco (disabilitato da modulo o gioco)" #: src/client/game.cpp msgid "Cinematic mode disabled" @@ -1489,11 +1492,11 @@ msgstr "Modalità cinematica attiva" #: src/client/game.cpp msgid "Client disconnected" -msgstr "Client disconnesso" +msgstr "Cliente disconnesso" #: src/client/game.cpp msgid "Client side scripting is disabled" -msgstr "Scripting su lato client disabilitato" +msgstr "Gli script lato cliente sono disabilitati" #: src/client/game.cpp msgid "Connecting to server..." @@ -1514,7 +1517,7 @@ msgstr "Impossibile risolvere l'indirizzo: %s" #: src/client/game.cpp msgid "Creating client..." -msgstr "Creazione del client..." +msgstr "Creazione del cliente..." #: src/client/game.cpp msgid "Creating server..." @@ -1527,7 +1530,7 @@ msgstr "Info debug mostrate" #: src/client/game.cpp #, c-format msgid "Error creating client: %s" -msgstr "Errore di creazione del client: %s" +msgstr "Errore di creazione del cliente: %s" #: src/client/game.cpp msgid "Fast mode disabled" @@ -1562,9 +1565,8 @@ msgid "Fog enabled" msgstr "Nebbia attivata" #: src/client/game.cpp -#, fuzzy msgid "Fog enabled by game or mod" -msgstr "Ingrandimento attualmente disabilitato dal gioco o da un mod" +msgstr "Nebbia abilitata dal gioco o da un modulo" #: src/client/game.cpp msgid "Item definitions..." @@ -1580,7 +1582,7 @@ msgstr "MiB/s" #: src/client/game.cpp msgid "Minimap currently disabled by game or mod" -msgstr "Minimappa attualmente disabilitata dal gioco o da una mod" +msgstr "Minimappa attualmente disabilitata dal gioco o da un modulo" #: src/client/game.cpp msgid "Multiplayer" @@ -1604,15 +1606,15 @@ msgstr "Definizione dei nodi..." #: src/client/game.cpp msgid "Pitch move mode disabled" -msgstr "Modalità inclinazione movimento disabilitata" +msgstr "Beccheggio disabilitato" #: src/client/game.cpp msgid "Pitch move mode enabled" -msgstr "Modalità inclinazione movimento abilitata" +msgstr "Beccheggio abilitato" #: src/client/game.cpp msgid "Profiler graph shown" -msgstr "Grafico profiler visualizzato" +msgstr "Profilatore visualizzato" #: src/client/game.cpp msgid "Resolving address..." @@ -1665,7 +1667,8 @@ msgstr "Raggio visivo illimitato abilitato" #: src/client/game.cpp msgid "Unlimited viewing range enabled, but forbidden by game or mod" -msgstr "Raggio visivo illimitato abilitato, ma vietato dal gioco o dal mod" +msgstr "" +"Raggio visivo illimitato abilitato, ma è bloccato dal gioco o da un modulo" #: src/client/game.cpp #, fuzzy, c-format @@ -1676,8 +1679,8 @@ msgstr "Il raggio visivo è al minimo: %d" #, c-format msgid "Viewing changed to %d (the minimum), but limited to %d by game or mod" msgstr "" -"Raggio visivo modificato a %d (il minimo), ma limitato a %d dal gioco o dal " -"mod" +"Raggio visivo modificato a %d (il minimo), ma limitato a %d dal gioco o da " +"un modulo" #: src/client/game.cpp #, c-format @@ -1694,13 +1697,13 @@ msgstr "Raggio visivo cambiato a %d" msgid "" "Viewing range changed to %d (the maximum), but limited to %d by game or mod" msgstr "" -"Raggio visivo modificato a %d (il massimo), ma limitato a %d dal gioco o dal " -"mod" +"Raggio visivo modificato a %d (il massimo), ma limitato a %d dal gioco o da " +"un modulo" #: src/client/game.cpp -#, fuzzy, c-format +#, c-format msgid "Viewing range changed to %d, but limited to %d by game or mod" -msgstr "Raggio visivo cambiato a %d" +msgstr "Raggio visivo cambiato a %d, ma limitato a %d dal gioco o da un modulo" #: src/client/game.cpp #, c-format @@ -1717,7 +1720,7 @@ msgstr "Struttura a fili visualizzata" #: src/client/game.cpp msgid "Zoom currently disabled by game or mod" -msgstr "Ingrandimento attualmente disabilitato dal gioco o da un mod" +msgstr "Zoom attualmente disabilitato dal gioco o da un modulo" #: src/client/game_formspec.cpp msgid "- Mode: " @@ -1819,7 +1822,7 @@ msgstr "Sei morto" #: src/client/gameui.cpp msgid "Chat currently disabled by game or mod" -msgstr "Chat attualmente disabilitata dal gioco o da un mod" +msgstr "Chat attualmente disabilitata dal gioco o da un modulo" #: src/client/gameui.cpp msgid "Chat hidden" @@ -2170,7 +2173,7 @@ msgstr "%s mancante:" msgid "" "Install and enable the required mods, or disable the mods causing errors." msgstr "" -"Installa e abilita le mod richieste o disabilita le mod che causano errori." +"Installa e abilita i moduli richiesti o disabilita quelli che causano errori." #: src/content/mod_configuration.cpp msgid "" @@ -2178,11 +2181,11 @@ msgid "" "the mods." msgstr "" "Nota: questo potrebbe essere causato da un ciclo di dipendenza, nel qual " -"caso prova ad aggiornare le mod." +"caso prova ad aggiornare i moduli." #: src/content/mod_configuration.cpp msgid "Some mods have unsatisfied dependencies:" -msgstr "Alcune mod hanno dipendenze non soddisfatte:" +msgstr "Alcuni moduli hanno dipendenze non soddisfatte:" #: src/gui/guiButtonKey.h #, fuzzy @@ -2203,11 +2206,11 @@ msgstr "Prosegui" #: src/gui/guiOpenURL.cpp msgid "Open" -msgstr "" +msgstr "Apri" #: src/gui/guiOpenURL.cpp msgid "Open URL?" -msgstr "" +msgstr "Apri URL?" #: src/gui/guiOpenURL.cpp #, fuzzy @@ -2250,13 +2253,12 @@ msgid "Done" msgstr "Fatto!" #: src/gui/touchscreeneditor.cpp -#, fuzzy msgid "Remove" -msgstr "Server remoto" +msgstr "Rimuovi" #: src/gui/touchscreeneditor.cpp msgid "Reset" -msgstr "" +msgstr "Ripristina" #: src/gui/touchscreeneditor.cpp msgid "Start dragging a button to add. Tap outside to cancel." @@ -2264,11 +2266,11 @@ msgstr "" #: src/gui/touchscreeneditor.cpp msgid "Tap a button to select it. Drag a button to move it." -msgstr "" +msgstr "Tocca un pulsante per selezionarlo. Trascinalo per spostarlo." #: src/gui/touchscreeneditor.cpp msgid "Tap outside to deselect." -msgstr "" +msgstr "Tocca fuori per deselezionare." #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Aux1" @@ -2280,7 +2282,7 @@ msgstr "Cambia vista" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Dig/punch/use" -msgstr "" +msgstr "Scava/colpisci/usa" #: src/gui/touchscreenlayout.cpp msgid "Drop" @@ -2301,16 +2303,15 @@ msgstr "ID del joystick" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Jump" -msgstr "Salta" +msgstr "Salto" #: src/gui/touchscreenlayout.cpp msgid "Overflow menu" msgstr "" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp -#, fuzzy msgid "Place/use" -msgstr "Tasto piazza" +msgstr "Piazza/usa" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Range select" @@ -2325,13 +2326,12 @@ msgid "Toggle chat log" msgstr "Log chat sì/no" #: src/gui/touchscreenlayout.cpp -#, fuzzy msgid "Toggle debug" -msgstr "Nebbia sì/no" +msgstr "Debug sì/no" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Toggle fast" -msgstr "Corsa sì/no" +msgstr "Veloce sì/no" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Toggle fly" @@ -2354,14 +2354,16 @@ msgid "" "Another client is connected with this name. If your client closed " "unexpectedly, try again in a minute." msgstr "" +"C'è già un altro cliente connesso con questo nome. Se è stato il tuo a " +"chiudersi di colpo, riprova tra un minuto." #: src/network/clientpackethandler.cpp msgid "Empty passwords are disallowed. Set a password and try again." -msgstr "" +msgstr "Non sono permesse password vuote. Imposta una password e riprova." #: src/network/clientpackethandler.cpp msgid "Internal server error" -msgstr "" +msgstr "Errore interno del server" #: src/network/clientpackethandler.cpp #, fuzzy @@ -2389,7 +2391,7 @@ msgstr "Nome già in uso. Scegli un altro nome, per favore" #: src/network/clientpackethandler.cpp msgid "Player name contains disallowed characters" -msgstr "" +msgstr "Il nome contiene caratteri non permessi" #: src/network/clientpackethandler.cpp #, fuzzy @@ -2404,31 +2406,35 @@ msgstr "Uscita dal gioco..." #: src/network/clientpackethandler.cpp msgid "" "The server has experienced an internal error. You will now be disconnected." -msgstr "" +msgstr "Il server ha avuto un errore interno. Sei statə scollegatə." #: src/network/clientpackethandler.cpp msgid "The server is running in singleplayer mode. You cannot connect." -msgstr "" +msgstr "Il server è in modalità giocatore singolo. Non puoi connetterti." #: src/network/clientpackethandler.cpp msgid "Too many users" -msgstr "" +msgstr "Troppɜ utenti" #: src/network/clientpackethandler.cpp msgid "Unknown disconnect reason." -msgstr "" +msgstr "Motivo della disconnessione: sconosciuto." #: src/network/clientpackethandler.cpp msgid "" "Your client sent something the server didn't expect. Try reconnecting or " "updating your client." msgstr "" +"Il server ha ricevuto qualcosa di inaspettato dal tuo cliente. Prova a " +"riconnetterti o ad aggiornare Luanti." #: src/network/clientpackethandler.cpp msgid "" "Your client's version is not supported.\n" "Please contact the server administrator." msgstr "" +"La versione del tuo cliente non è supportata.\n" +"Contatta un amministratore del server." #: src/server.cpp #, fuzzy, c-format @@ -2601,12 +2607,12 @@ msgstr "" #: src/settings_translation_file.cpp msgid "A message to be displayed to all clients when the server crashes." -msgstr "" -"Un messaggio da mostrare a tutti i client quando il server va in crash." +msgstr "Un messaggio da mostrare a tutti i clienti quando il server crasha." #: src/settings_translation_file.cpp msgid "A message to be displayed to all clients when the server shuts down." -msgstr "Un messaggio da mostrare a tutti i client quando il server chiude." +msgstr "" +"Un messaggio da mostrare a tutti i clienti quando il server viene spento." #: src/settings_translation_file.cpp msgid "ABM interval" @@ -2688,7 +2694,7 @@ msgstr "Usare l'aspetto 3D per le nuvole invece di quello piatto." #: src/settings_translation_file.cpp msgid "Allows liquids to be translucent." -msgstr "" +msgstr "Attiva la traslucenza sui liquidi." #: src/settings_translation_file.cpp msgid "" @@ -2741,11 +2747,11 @@ msgstr "Anti-Scalettatura:" #: src/settings_translation_file.cpp msgid "Anticheat flags" -msgstr "" +msgstr "Segnalini dell'anticheat" #: src/settings_translation_file.cpp msgid "Anticheat movement tolerance" -msgstr "" +msgstr "Tolleranza di movimento dell'anticheat" #: src/settings_translation_file.cpp msgid "Append item name" @@ -2791,7 +2797,6 @@ msgid "Ask to reconnect after crash" msgstr "Chiedi di riconnettersi dopo un crash" #: src/settings_translation_file.cpp -#, fuzzy msgid "" "At this distance the server will aggressively optimize which blocks are sent " "to\n" @@ -2804,17 +2809,14 @@ msgid "" "Stated in MapBlocks (16 nodes)." msgstr "" "A questa distanza il server ottimizzerà aggressivamente quali blocchi sono\n" -"inviati ai client.\n" +"inviati ai clienti.\n" "Potenzialmente valori piccoli migliorano molto le prestazioni, al costo di\n" -"difetti di disegno visibili (alcuni blocchi non saranno disegnati sott'acqua " -"e\n" -"nelle grotte, come a volte sul terreno).\n" +"difetti di renderizzazione (con alcuni blocchi nelle grotte).\n" "Impostarla a un valore maggiore di max_block_send_distance disabilita\n" "questa ottimizzazione.\n" "Fissata in blocchi mappa (16 nodi)." #: src/settings_translation_file.cpp -#, fuzzy msgid "" "At this distance the server will perform a simpler and cheaper occlusion " "check.\n" @@ -2824,14 +2826,10 @@ msgid "" "This is especially useful for very large viewing range (upwards of 500).\n" "Stated in MapBlocks (16 nodes)." msgstr "" -"A questa distanza il server ottimizzerà aggressivamente quali blocchi sono\n" -"inviati ai client.\n" +"A questa distanza il server ottimizzerà aggressivamente l'occlusione.\n" "Potenzialmente valori piccoli migliorano molto le prestazioni, al costo di\n" -"difetti di disegno visibili (alcuni blocchi non saranno disegnati sott'acqua " -"e\n" -"nelle grotte, come a volte sul terreno).\n" -"Impostarla a un valore maggiore di max_block_send_distance disabilita\n" -"questa ottimizzazione.\n" +"difetti di renderizzazione (blocchi mancanti).\n" +"Torna utile soprattutto con un raggio di visione molto alto (sopra i 500).\n" "Fissata in blocchi mappa (16 nodi)." #: src/settings_translation_file.cpp @@ -2844,7 +2842,7 @@ msgstr "Salto automatico" #: src/settings_translation_file.cpp msgid "Automatically jump up single-node obstacles." -msgstr "Salta automaticamente su ostacoli di un nodo singolo." +msgstr "Salta automaticamente su ostacoli alti un nodo." #: src/settings_translation_file.cpp msgid "Automatically report to the serverlist." @@ -2852,7 +2850,7 @@ msgstr "Fa rapporto automatico all'elenco dei server." #: src/settings_translation_file.cpp msgid "Autoscaling mode" -msgstr "Modalità scalamento automatico" +msgstr "Scalatura automatica" #: src/settings_translation_file.cpp msgid "Aux1 key for climbing/descending" @@ -2953,7 +2951,7 @@ msgstr "Fluidità della telecamera" #: src/settings_translation_file.cpp msgid "Camera smoothing in cinematic mode" -msgstr "Fluidità della telecamera in modalità cinematic" +msgstr "Fluidità della telecamera in modalità cinematica" #: src/settings_translation_file.cpp msgid "Cave noise" @@ -3059,7 +3057,7 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Client" -msgstr "Client" +msgstr "Cliente" #: src/settings_translation_file.cpp #, fuzzy @@ -3068,29 +3066,27 @@ msgstr "Debugging" #: src/settings_translation_file.cpp msgid "Client Mesh Chunksize" -msgstr "Grandezza Client della Mesh del Chunk" +msgstr "Grandezza dei pezzi di mappa del cliente" #: src/settings_translation_file.cpp msgid "Client and Server" -msgstr "Client e server" +msgstr "Cliente e server" #: src/settings_translation_file.cpp msgid "Client modding" -msgstr "Modifica del client" +msgstr "Moddaggio del cliente" #: src/settings_translation_file.cpp msgid "Client side modding restrictions" -msgstr "Restrizioni delle modifiche del client" +msgstr "Restrizioni del moddaggio del cliente" #: src/settings_translation_file.cpp -#, fuzzy msgid "Client-side Modding" -msgstr "Mod lato client" +msgstr "Mod lato cliente" #: src/settings_translation_file.cpp -#, fuzzy msgid "Client-side node lookup range restriction" -msgstr "Restrizione dell'area di ricerca dei nodi su lato client" +msgstr "Restrizione dell'area di ricerca dei nodi lato cliente" #: src/settings_translation_file.cpp msgid "Climbing speed" @@ -3153,17 +3149,19 @@ msgid "" "Comma-separated list of mods that are allowed to access HTTP APIs, which\n" "allow them to upload and download data to/from the internet." msgstr "" -"Elenco separato da virgole di mod a cui è permesso l'accesso alle API HTTP,\n" -"che gli permettono di caricare e scaricare dati su/da internet." +"Elenco, separato da virgole, di moduli a cui è permesso l'accesso alle API " +"HTTP,\n" +"le quali permettono a tali moduli di caricare e scaricare dati su/da " +"internet." #: src/settings_translation_file.cpp msgid "" "Comma-separated list of trusted mods that are allowed to access insecure\n" "functions even when mod security is on (via request_insecure_environment())." msgstr "" -"Elenco separato da virgole delle mod fidate ai quali è permesso l'accesso a " +"Elenco separato da virgole dei moduli fidati ai quali è permesso l'accesso a " "funzioni non sicure\n" -"anche quando la sicurezza mod è attiva (tramite " +"anche quando la sicurezza moduli è attiva (tramite " "request_insecure_environment())." #: src/settings_translation_file.cpp @@ -3191,7 +3189,7 @@ msgid "" "9 - best compression, slowest" msgstr "" "Livello di compressione da utilizzare per l'invio dei blocchi-mappa al " -"client.\n" +"cliente.\n" "-1 - utilizza il livello di compressione predefinito\n" "0 - compressione minima, più veloce\n" "9 - compressione migliore, più lento" @@ -3323,9 +3321,8 @@ msgid "" msgstr "" #: src/settings_translation_file.cpp -#, fuzzy msgid "Decrease view range" -msgstr "Diminuisci raggio" +msgstr "Diminuisci raggio visivo" #: src/settings_translation_file.cpp #, fuzzy @@ -3376,7 +3373,6 @@ msgstr "" "ma utilizza più risorse." #: src/settings_translation_file.cpp -#, fuzzy msgid "" "Define the oldest clients allowed to connect.\n" "Older clients are compatible in the sense that they will not crash when " @@ -3388,11 +3384,11 @@ msgid "" "Luanti still enforces its own internal minimum, and enabling\n" "strict_protocol_version_checking will effectively override this." msgstr "" -"Abilitare per impedire ai client obsoleti di connettersi.\n" -"I client più vecchi sono compatibili nel senso che non andranno in crash " -"alla connessione\n" +"Abilitare per impedire ai clienti obsoleti di connettersi.\n" +"I clienti più vecchi sono compatibili nel senso che non crasheranno al " +"connettersi\n" "ai nuovi server, ma potrebbero non supportare tutte le nuove caratteristiche " -"che ti aspetti." +"che ci si aspetta." #: src/settings_translation_file.cpp msgid "Defines areas where trees have apples." @@ -3466,9 +3462,9 @@ msgid "" "Delay between mesh updates on the client in ms. Increasing this will slow\n" "down the rate of mesh updates, thus reducing jitter on slower clients." msgstr "" -"Ritardo in ms tra gli aggiornamenti delle mesh sul client. Aumentandolo si\n" +"Ritardo in ms degli aggiornamenti delle mesh lato cliente. Aumentandolo si\n" "ritarderà il ritmo di aggiornamento delle mesh, riducendo così lo sfarfallio " -"sui client più lenti." +"sui clienti più lenti." #: src/settings_translation_file.cpp msgid "Delay in sending blocks after building" @@ -3539,12 +3535,11 @@ msgstr "Nome di dominio del server, da mostrarsi nell'elenco dei server." #: src/settings_translation_file.cpp msgid "Double tap jump for fly" -msgstr "Doppio \"salta\" per volare" +msgstr "Premi due volte salto per volare" #: src/settings_translation_file.cpp msgid "Double-tapping the jump key toggles fly mode." -msgstr "" -"Premendo due volte il tasto di salto si (dis)attiva la modalità di volo." +msgstr "Premendo due volte il tasto salto si (dis)attiva la modalità volo." #: src/settings_translation_file.cpp msgid "" @@ -3555,9 +3550,8 @@ msgid "" msgstr "" #: src/settings_translation_file.cpp -#, fuzzy msgid "Drop item" -msgstr "Tasto butta oggetto" +msgstr "Getta oggetto" #: src/settings_translation_file.cpp msgid "Dump the mapgen debug information." @@ -3602,7 +3596,7 @@ msgid "" "Enable IPv6 support (for both client and server).\n" "Required for IPv6 connections to work at all." msgstr "" -"Abilitare il supporto IPv6 (sia per il client che per il server).\n" +"Abilitare il supporto IPv6 (sia per il cliente che per il server).\n" "Necessario per il funzionamento delle connessioni IPv6." #: src/settings_translation_file.cpp @@ -3611,13 +3605,17 @@ msgid "" "Note that clients will be able to connect with both IPv4 and IPv6.\n" "Ignored if bind_address is set." msgstr "" +"Abilita il supporto per IPv6 sul server.\n" +"Notare che i clienti saranno in grado di connettersi sia tramite IPv4 che " +"IPv6.\n" +"Ignorato se bind_address è definito." #: src/settings_translation_file.cpp msgid "" "Enable Lua modding support on client.\n" "This support is experimental and API can change." msgstr "" -"Abilita il supporto per le modifiche tramite Lua sul client.\n" +"Abilita il supporto per il moddaggio tramite Lua sul cliente.\n" "Questo supporto è sperimentale e l'API potrebbe cambiare." #: src/settings_translation_file.cpp @@ -3674,24 +3672,23 @@ msgstr "Abilita i joystick. Richiede un riavvio del gioco per avere effetto" #: src/settings_translation_file.cpp msgid "Enable mod channels support." -msgstr "Abilita il supporto canali mod." +msgstr "Abilita il supporto canali per i moduli." #: src/settings_translation_file.cpp msgid "Enable mod security" -msgstr "Abilita la sicurezza mod" +msgstr "Abilita la sicurezza moduli" #: src/settings_translation_file.cpp msgid "Enable mouse wheel (scroll) for item selection in hotbar." msgstr "" "Abilita lo scorrimento della rotellina del mouse per la selezione degli " -"oggetti nella hotbar." +"oggetti nella barra delle azioni." #: src/settings_translation_file.cpp -#, fuzzy msgid "Enable random mod loading (mainly used for testing)." msgstr "" -"Abilita l'ingresso di dati casuali da parte dell'utente (utilizzato solo per " -"i test)." +"Abilita il caricamento casuale di moduli (usando principalmente per fare dei " +"test)." #: src/settings_translation_file.cpp msgid "Enable random user input (only used for testing)." @@ -3718,11 +3715,11 @@ msgid "" "to new servers, but they may not support all new features that you are " "expecting." msgstr "" -"Abilitare per impedire ai client obsoleti di connettersi.\n" -"I client più vecchi sono compatibili nel senso che non andranno in crash " -"alla connessione\n" +"Abilitare per impedire ai clienti obsoleti di connettersi.\n" +"I clienti più vecchi sono compatibili nel senso che non crasheranno al " +"connettersi\n" "ai nuovi server, ma potrebbero non supportare tutte le nuove caratteristiche " -"che ti aspetti." +"che ci si aspetta." #: src/settings_translation_file.cpp msgid "Enable updates available indicator on content tab" @@ -3852,11 +3849,11 @@ msgstr "Percorso del carattere di ripiego" #: src/settings_translation_file.cpp msgid "Fast mode acceleration" -msgstr "Accelerazione della modalità veloce" +msgstr "Accelerazione in modalità veloce" #: src/settings_translation_file.cpp msgid "Fast mode speed" -msgstr "Velocità della modalità veloce" +msgstr "Velocità in modalità veloce" #: src/settings_translation_file.cpp msgid "Field of view" @@ -3872,7 +3869,7 @@ msgid "" "the\n" "Multiplayer Tab." msgstr "" -"File in client/serverlist/ contenente i vostri server preferiti mostrati " +"File in client/serverlist/ contenente i tuoi server preferiti mostrati " "nella\n" "scheda di gioco in rete." @@ -4018,9 +4015,9 @@ msgstr "" "divisibili per questo\n" "valore, in pixel, in caso di caratteri in stile pixel che non vengono " "ridimensionati correttamente.\n" -"Per esempio, un carattere pixelato alto 16 pixels richiederebbe che questo " +"Per esempio, un carattere pixelato alto 16 pixel richiederebbe che questo " "sia 16, così sarà sempre\n" -"solo grande 16, 32, 48, etc., e una mod che richiede una grandezza di 25 " +"solo grande 16, 32, 48, etc., e un modulo che richiede una grandezza di 25 " "otterrà 32." #: src/settings_translation_file.cpp @@ -4074,15 +4071,15 @@ msgid "" "From how far blocks are generated for clients, stated in mapblocks (16 " "nodes)." msgstr "" -"Da che distanza vengono generati i blocchi per i client, fissata in blocchi " +"Da che distanza vengono generati i blocchi per i clienti, fissata in blocchi " "mappa (16 nodi)." #: src/settings_translation_file.cpp msgid "" "From how far blocks are sent to clients, stated in mapblocks (16 nodes)." msgstr "" -"Da che distanza i blocchi sono inviati ai client, fissata in blocchi mappa " -"(16 nodi)." +"Da che distanza i blocchi sono inviati ai clienti, fissata in blocchi mappa (" +"16 nodi)." #: src/settings_translation_file.cpp msgid "" @@ -4092,7 +4089,7 @@ msgid "" "to maintain active objects up to this distance in the direction the\n" "player is looking. (This can avoid mobs suddenly disappearing from view)" msgstr "" -"Da che distanza il client sa degli oggetti, fissata in blocchi mappa (16 " +"Da che distanza il cliente rileva gli oggetti, fissata in blocchi mappa (16 " "nodi).\n" "\n" "Impostarla maggiore di active_block_range provocherà il mantenimento\n" @@ -4106,7 +4103,7 @@ msgstr "Schermo intero" #: src/settings_translation_file.cpp msgid "Fullscreen mode." -msgstr "Modalità a schermo intero." +msgstr "Schermo intero." #: src/settings_translation_file.cpp #, fuzzy @@ -4179,7 +4176,7 @@ msgstr "Rumore del terreno" #: src/settings_translation_file.cpp msgid "HTTP mods" -msgstr "Mod HTTP" +msgstr "Moduli HTTP" #: src/settings_translation_file.cpp msgid "HUD" @@ -4198,10 +4195,10 @@ msgid "" msgstr "" "Gestione delle chiamate API Lua deprecate:\n" "- none (nessuno): non registra le chiamate obsolete\n" -"- log (registro): imita e registra il backtrace di una chiamata obsoleta " -"(impostazione predefinita).\n" -"- error (errore): interrompe l'utilizzo della chiamata deprecata " -"(consigliata per gli sviluppatori di mod)." +"- log (registro): imita e registra il backtrace di una chiamata obsoleta (" +"impostazione predefinita).\n" +"- error (errore): interrompe l'utilizzo della chiamata deprecata (" +"consigliata per chi sviluppa moduli)." #: src/settings_translation_file.cpp msgid "" @@ -4211,7 +4208,7 @@ msgid "" "call).\n" "* Instrument the sampler being used to update the statistics." msgstr "" -"Fare in modo che il generatore di profili si predisponga da sé:\n" +"Fare in modo che il profilatore si predisponga da sé:\n" "* Predisporre una funzione vuota.\n" "Ciò stima il sovraccarico che la predisposizione aggiunge (+1 chiamata di " "funzione).\n" @@ -4226,11 +4223,8 @@ msgid "Heat noise" msgstr "Rumore del calore" #: src/settings_translation_file.cpp -#, fuzzy msgid "Height component of the initial window size." -msgstr "" -"Componente altezza della dimensione iniziale della finestra. Ignorata in " -"modalità a schermo intero." +msgstr "Componente altezza della dimensione iniziale della finestra." #: src/settings_translation_file.cpp msgid "Height noise" @@ -4293,196 +4287,160 @@ msgstr "" "in nodi al secondo per secondo." #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 1" -msgstr "Tasto riquadro 1 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 1" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 10" -msgstr "Tasto riquadro 10 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 10" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 11" -msgstr "Tasto riquadro 11 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 11" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 12" -msgstr "Tasto riquadro 12 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 12" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 13" -msgstr "Tasto riquadro 13 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 13" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 14" -msgstr "Tasto riquadro 14 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 14" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 15" -msgstr "Tasto riquadro 15 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 15" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 16" -msgstr "Tasto riquadro 16 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 16" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 17" -msgstr "Tasto riquadro 17 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 17" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 18" -msgstr "Tasto riquadro 18 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 18" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 19" -msgstr "Tasto riquadro 19 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 19" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 2" -msgstr "Tasto riquadro 2 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 2" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 20" -msgstr "Tasto riquadro 20 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 20" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 21" -msgstr "Tasto riquadro 21 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 21" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 22" -msgstr "Tasto riquadro 22 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 22" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 23" -msgstr "Tasto riquadro 23 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 23" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 24" -msgstr "Tasto riquadro 24 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 24" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 25" -msgstr "Tasto riquadro 25 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 25" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 26" -msgstr "Tasto riquadro 26 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 26" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 27" -msgstr "Tasto riquadro 27 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 27" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 28" -msgstr "Tasto riquadro 28 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 28" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 29" -msgstr "Tasto riquadro 29 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 29" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 3" -msgstr "Tasto riquadro 3 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 3" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 30" -msgstr "Tasto riquadro 30 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 30" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 31" -msgstr "Tasto riquadro 31 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 31" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 32" -msgstr "Tasto riquadro 32 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 32" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 4" -msgstr "Tasto riquadro 4 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 4" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 5" -msgstr "Tasto riquadro 5 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 5" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 6" -msgstr "Tasto riquadro 6 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 6" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 7" -msgstr "Tasto riquadro 7 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 7" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 8" -msgstr "Tasto riquadro 8 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 8" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 9" -msgstr "Tasto riquadro 9 della barra di scelta rapida" +msgstr "Barra delle azioni, casella 9" #: src/settings_translation_file.cpp msgid "Hotbar: Enable mouse wheel for selection" -msgstr "Hotbar: attiva la rotellina del mouse per la selezione" +msgstr "Barra delle azioni: attiva la rotellina del mouse per la selezione" #: src/settings_translation_file.cpp msgid "Hotbar: Invert mouse wheel direction" -msgstr "Hotbar: inverti la direzione della rotellina del mouse" +msgstr "Barra delle azioni: inverti la direzione della rotellina del mouse" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar: select next item" -msgstr "Tasto successivo della barra di scelta rapida" +msgstr "Barra delle azioni: seleziona prossimo oggetto" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar: select previous item" -msgstr "Tasto precedente della barra di scelta rapida" +msgstr "Barra delle azioni: seleziona oggetto precedente" #: src/settings_translation_file.cpp msgid "How deep to make rivers." msgstr "Quanto fare profondi i fiumi." #: src/settings_translation_file.cpp -#, fuzzy msgid "" "How fast liquid waves will move. Higher = faster.\n" "If negative, liquid waves will move backwards." msgstr "" "A quale velocità si muovono le onde dei liquidi. Maggiore = più veloce.\n" -"Se negativa, le onde dei liquidi si sposteranno all'indietro.\n" -"Richiede l'abilitazione dei liquidi ondeggianti." +"Se negativa, le onde dei liquidi si sposteranno all'indietro." #: src/settings_translation_file.cpp msgid "" @@ -4500,7 +4458,7 @@ msgid "" "How much you are slowed down when moving inside a liquid.\n" "Decrease this to increase liquid resistance to movement." msgstr "" -"Quanto sei rallentato mentre ti muovi dentro un liquido.\n" +"Quanto sei rallentatə mentre ti muovi dentro un liquido.\n" "Riducila per aumentare la resistenza del liquido al movimento." #: src/settings_translation_file.cpp @@ -4550,6 +4508,10 @@ msgid "" "ContentDB to\n" "check for package updates when opening the mainmenu." msgstr "" +"Se abilitato e si hanno pacchetti provenienti da ContentDB installati, " +"Luanti potrebbe contattare\n" +"ContentDB per controllare eventuali aggiornamenti quando viene aperto il " +"menù principale." #: src/settings_translation_file.cpp msgid "" @@ -4600,16 +4562,17 @@ msgstr "" #: src/settings_translation_file.cpp msgid "If enabled, the \"Aux1\" key will toggle when pressed." -msgstr "" +msgstr "Se abilitato, il tasto \"Aux1\" si azionerà quando premuto." #: src/settings_translation_file.cpp msgid "" "If enabled, the \"Sneak\" key will toggle when pressed.\n" "This functionality is ignored when fly is enabled." msgstr "" +"Se abilitato, il tasto \"Furtivo\" si azionerà quando premuto.\n" +"Questa funzionalità è ignorata quando la modalità volo è abilitata." #: src/settings_translation_file.cpp -#, fuzzy msgid "" "If enabled, the server will perform map block occlusion culling based on\n" "on the eye position of the player. This can reduce the number of blocks\n" @@ -4620,7 +4583,7 @@ msgstr "" "basandosi\n" "sulla posizione degli occhi del giocatore. Questo può ridurre del 50-80% il " "numero dei blocchi\n" -"inviati al client. Il client non riceverà più la maggior parte degli " +"inviati al cliente. Il cliente non riceverà più la maggior parte degli " "invisibili\n" "cosicché l'utilità della modalità incorporea è ridotta." @@ -4661,7 +4624,7 @@ msgid "" "deleting an older debug.txt.1 if it exists.\n" "debug.txt is only moved if this setting is positive." msgstr "" -"Se la dimensione del file debug.txt supera il numero di Mb indicati in\n" +"Se la dimensione del file debug.txt supera il numero di MB indicati in\n" "questa impostazione quando viene aperto, il file viene rinominato in " "debug.txt.1,\n" "eliminando un eventuale debug.txt.1 più vecchio.\n" @@ -4690,9 +4653,8 @@ msgid "In-game chat console height, between 0.1 (10%) and 1.0 (100%)." msgstr "Altezza della console di chat nel gioco, tra 0.1 (10%) e 1.0 (100%)." #: src/settings_translation_file.cpp -#, fuzzy msgid "Increase view range" -msgstr "Aumenta raggio" +msgstr "Aumenta raggio visivo" #: src/settings_translation_file.cpp #, fuzzy @@ -4759,13 +4721,13 @@ msgstr "Animazioni degli oggetti dell'inventario" #: src/settings_translation_file.cpp msgid "Invert mouse" -msgstr "Invertire il mouse" +msgstr "Mouse invertito" #: src/settings_translation_file.cpp msgid "Invert mouse wheel (scroll) direction for item selection in hotbar." msgstr "" -"Inverte la direzione di scorrimento della rotellina del mouse per la " -"selezione degli oggetti nella hotbar." +"Inverte lo scorrimento della rotellina del mouse per la selezione degli " +"oggetti nella barra delle azioni." #: src/settings_translation_file.cpp msgid "Invert vertical mouse movement." @@ -4892,15 +4854,15 @@ msgstr "Velocità di salto" #: src/settings_translation_file.cpp msgid "Key for decreasing the viewing range." -msgstr "" +msgstr "Tasto per diminuire il raggio visivo." #: src/settings_translation_file.cpp msgid "Key for decreasing the volume." -msgstr "" +msgstr "Tasto per abbassare il volume." #: src/settings_translation_file.cpp msgid "Key for decrementing the selected value in Quicktune." -msgstr "" +msgstr "Tasto per diminuire il valore di Quicktune." #: src/settings_translation_file.cpp msgid "" @@ -4910,27 +4872,27 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Key for dropping the currently selected item." -msgstr "" +msgstr "Tasto per gettare l'oggetto che si ha in mano." #: src/settings_translation_file.cpp msgid "Key for increasing the viewing range." -msgstr "" +msgstr "Tasto per aumentare il raggio visivo." #: src/settings_translation_file.cpp msgid "Key for increasing the volume." -msgstr "" +msgstr "Tasto per alzare il volume." #: src/settings_translation_file.cpp msgid "Key for incrementing the selected value in Quicktune." -msgstr "" +msgstr "Tasto per aumentare il valore di Quicktune." #: src/settings_translation_file.cpp msgid "Key for jumping." -msgstr "" +msgstr "Tasto per saltare." #: src/settings_translation_file.cpp msgid "Key for moving fast in fast mode." -msgstr "" +msgstr "Tasto per muoversi rapidamente in modalità veloce." #: src/settings_translation_file.cpp #, fuzzy @@ -4945,35 +4907,36 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Key for moving the player forward." -msgstr "" +msgstr "Tasto per muovere il giocatore in avanti." #: src/settings_translation_file.cpp msgid "Key for moving the player left." -msgstr "" +msgstr "Tasto per muovere il giocatore a sinistra." #: src/settings_translation_file.cpp msgid "Key for moving the player right." -msgstr "" +msgstr "Tasto per muovere il giocatore a destra." #: src/settings_translation_file.cpp msgid "Key for muting the game." -msgstr "" +msgstr "Tasto per mutare il gioco." #: src/settings_translation_file.cpp msgid "Key for opening the chat window to type commands." -msgstr "" +msgstr "Tasto per aprire la finestra della chat, pronta per scrivere comandi." #: src/settings_translation_file.cpp msgid "Key for opening the chat window to type local commands." msgstr "" +"Tasto per aprire la finestra della chat, pronta per scrivere comandi locali." #: src/settings_translation_file.cpp msgid "Key for opening the chat window." -msgstr "" +msgstr "Tasto per aprire la finestra della chat." #: src/settings_translation_file.cpp msgid "Key for opening the inventory." -msgstr "" +msgstr "Tasto per aprire l'inventario." #: src/settings_translation_file.cpp msgid "" @@ -4983,139 +4946,139 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Key for selecting the 11th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare l'11° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 12th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 12° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 13th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 13° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 14th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 14° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 15th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 15° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 16th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 16° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 17th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 17° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 18th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 18° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 19th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 19° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 20th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 20° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 21st hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 21° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 22nd hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 22° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 23rd hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 23° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 24th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 24° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 25th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 25° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 26th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 26° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 27th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 27° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 28th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 28° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 29th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 29° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 30th hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 30° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 31st hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 31° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the 32nd hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 32° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the eighth hotbar slot." -msgstr "" +msgstr "Tasto per selezionare l'8° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the fifth hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 5° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the first hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 1° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the fourth hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 4° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the next item in the hotbar." -msgstr "" +msgstr "Tasto per selezionare il prossimo oggetto nella barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the ninth hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 9° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the previous item in the hotbar." -msgstr "" +msgstr "Tasto per selezionare l'oggetto precedente nella barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the second hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 2° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the seventh hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 7° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the sixth hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 6° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the tenth hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 10° casella della barra delle azioni." #: src/settings_translation_file.cpp msgid "Key for selecting the third hotbar slot." -msgstr "" +msgstr "Tasto per selezionare la 3° casella della barra delle azioni." #: src/settings_translation_file.cpp #, fuzzy @@ -5132,7 +5095,7 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Key for switching between first- and third-person camera." -msgstr "" +msgstr "Tasto per ruotare tra prima e terza persona." #: src/settings_translation_file.cpp msgid "Key for switching to the next entry in Quicktune." @@ -5149,37 +5112,35 @@ msgstr "Formato degli screenshot." #: src/settings_translation_file.cpp msgid "Key for toggling autoforward." -msgstr "" +msgstr "Tasto per (dis)attivare l'avanzamento automatico." #: src/settings_translation_file.cpp -#, fuzzy msgid "Key for toggling cinematic mode." -msgstr "Fluidità della telecamera in modalità cinematic" +msgstr "Tasto per attivare e disattivare la modalità cinematica." #: src/settings_translation_file.cpp msgid "Key for toggling display of minimap." -msgstr "" +msgstr "Tasto per (dis)attivare la minimappa." #: src/settings_translation_file.cpp msgid "Key for toggling fast mode." -msgstr "" +msgstr "Tasto per attivare e disattivare la modalità veloce." #: src/settings_translation_file.cpp msgid "Key for toggling flying." -msgstr "" +msgstr "Tasto per (dis)attivare il volo." #: src/settings_translation_file.cpp -#, fuzzy msgid "Key for toggling fullscreen mode." -msgstr "Modalità a schermo intero." +msgstr "Tasto per attivare e disattivare la modalità a schermo intero." #: src/settings_translation_file.cpp msgid "Key for toggling noclip mode." -msgstr "" +msgstr "Tasto per attivare e disattivare la modalità incorporea." #: src/settings_translation_file.cpp msgid "Key for toggling pitch move mode." -msgstr "" +msgstr "Tasto per attivare e disattivare il beccheggio." #: src/settings_translation_file.cpp msgid "Key for toggling the camera update. Only usable with 'debug' privilege." @@ -5212,20 +5173,20 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Key for toggling the display of the profiler. Used for development." msgstr "" +"Tasto per alternare la visualizzazione del profilatore. Usato per fini di " +"sviluppo." #: src/settings_translation_file.cpp -#, fuzzy msgid "Key for toggling unlimited view range." -msgstr "Raggio visivo illimitato disabilitato" +msgstr "Tasto per (dis)attivare il raggio visivo illimitato." #: src/settings_translation_file.cpp msgid "Key to use view zoom when possible." msgstr "" #: src/settings_translation_file.cpp -#, fuzzy msgid "Keybindings" -msgstr "Mappatura dei tasti." +msgstr "Mappatura dei tasti" #: src/settings_translation_file.cpp msgid "Keyboard and Mouse" @@ -5265,9 +5226,8 @@ msgid "Large cave proportion flooded" msgstr "Proporzione inondata della grotta grande" #: src/settings_translation_file.cpp -#, fuzzy msgid "Large chat console" -msgstr "Tasto console grande di chat" +msgstr "Console" #: src/settings_translation_file.cpp msgid "Leaves style" @@ -5401,7 +5361,8 @@ msgstr "" "- Recupero dei file multimediali se il server usa l'impostazione " "remote_media.\n" "- Scaricamento dell'elenco dei server e annuncio del server.\n" -"- Scaricamenti effettuati dal menu principale (per es. il gestore mod.).\n" +"- Scaricamenti effettuati dal menu principale (per es. il gestore " +"moduli.).\n" "Ha effetto solo se compilato con cURL." #: src/settings_translation_file.cpp @@ -5439,7 +5400,7 @@ msgstr "Scatto di aggiornamento del liquido" #: src/settings_translation_file.cpp msgid "Load the game profiler" -msgstr "Caricare il generatore di profili del gioco" +msgstr "Caricare il profilatore del gioco" #: src/settings_translation_file.cpp msgid "" @@ -5447,10 +5408,9 @@ msgid "" "Provides a /profiler command to access the compiled profile.\n" "Useful for mod developers and server operators." msgstr "" -"Carica il generatore di profili per raccogliere i dati di profilazione del " -"gioco.\n" +"Carica il profilatore per raccogliere i dati di profilazione del gioco.\n" "Fornisce un comando /profiler per accedere al profilo compilato.\n" -"Utile per sviluppatori di moduli e operatori di server." +"Utile per chi sviluppa moduli e chi opera dei server." #: src/settings_translation_file.cpp msgid "Loading Block Modifiers" @@ -5699,7 +5659,7 @@ msgstr "Numero massimo di blocchi caricati a forza" #: src/settings_translation_file.cpp msgid "Maximum hotbar width" -msgstr "Larghezza massima della barra di scelta rapida" +msgstr "Larghezza massima della barra delle azioni" #: src/settings_translation_file.cpp msgid "Maximum limit of random number of large caves per mapchunk." @@ -5724,9 +5684,9 @@ msgid "" "The maximum total count is calculated dynamically:\n" "max_total = ceil((#clients + max_users) * per_client / 4)" msgstr "" -"Numero massimo di blocchi inviati simultaneamente per client.\n" +"Numero massimo di blocchi inviati simultaneamente per clientecliente.\n" "Il conto totale massimo è calcolato dinamicamente:\n" -"tot_max = arrotonda((N°client + max_utenti) * per_client / 4)" +"tot_max = arrotonda((N°clienti + max_utenti) * per_cliente / 4)" #: src/settings_translation_file.cpp msgid "Maximum number of blocks that can be queued for loading." @@ -5797,13 +5757,13 @@ msgid "" "Maximum proportion of current window to be used for hotbar.\n" "Useful if there's something to be displayed right or left of hotbar." msgstr "" -"Porzione massima della finestra attuale da usarsi per la barra di scelta " -"rapida.\n" +"Porzione massima della finestra attuale da usarsi per la barra delle azioni." +"\n" "Utile se c'è qualcosa da mostrare a destra o sinistra della barra." #: src/settings_translation_file.cpp msgid "Maximum simultaneous block sends per client" -msgstr "Invii simultanei massimi di blocchi per client" +msgstr "Invii simultanei massimi di blocchi per cliente" #: src/settings_translation_file.cpp #, fuzzy @@ -5825,8 +5785,8 @@ msgid "" "Maximum time a file download (e.g. a mod download) may take, stated in " "milliseconds." msgstr "" -"Tempo massimo in millisecondi che può richiedere lo scaricamento di un file " -"(es. un mod)." +"Tempo massimo in millisecondi che può richiedere lo scaricamento di un file (" +"es. un modulo)." #: src/settings_translation_file.cpp msgid "" @@ -5896,7 +5856,7 @@ msgstr "Sicurezza Mod" #: src/settings_translation_file.cpp msgid "Mod channels" -msgstr "Canali mod" +msgstr "Canali per i moduli" #: src/settings_translation_file.cpp msgid "Modifies the size of the HUD elements." @@ -5939,28 +5899,24 @@ msgid "Mouse sensitivity multiplier." msgstr "Moltiplicatore della sensibilità del mouse." #: src/settings_translation_file.cpp -#, fuzzy msgid "Move backward" -msgstr "Indietreggia" +msgstr "Indietro" #: src/settings_translation_file.cpp -#, fuzzy msgid "Move forward" -msgstr "Avanzam. autom." +msgstr "Avanti" #: src/settings_translation_file.cpp -#, fuzzy msgid "Move left" -msgstr "Movimento" +msgstr "Sinistra" #: src/settings_translation_file.cpp msgid "Move right" -msgstr "" +msgstr "Destra" #: src/settings_translation_file.cpp -#, fuzzy msgid "Movement threshold" -msgstr "Soglia della caverna" +msgstr "Soglia di movimento" #: src/settings_translation_file.cpp msgid "Mud noise" @@ -5987,14 +5943,13 @@ msgstr "" "- 'floatlands' in v7 (disabilitato per impostazione predefinita)." #: src/settings_translation_file.cpp -#, fuzzy msgid "" "Name of the player.\n" "When running a server, a client connecting with this name is admin.\n" "When starting from the main menu, this is overridden." msgstr "" "Nome del giocatore.\n" -"Quando si esegue un server, i client che si connettono con questo nome sono " +"Quando si esegue un server, i clienti che si connettono con questo nome sono " "amministratori.\n" "Quando si avvia dal menu principale, questo viene scavalcato." @@ -6118,7 +6073,7 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Open chat" -msgstr "" +msgstr "Apri chat" #: src/settings_translation_file.cpp #, fuzzy @@ -6149,9 +6104,8 @@ msgid "Optional override for chat weblink color." msgstr "Sovrascrittura opzionale per i colori dei link web in chat." #: src/settings_translation_file.cpp -#, fuzzy msgid "Other Effects" -msgstr "Effetti grafici" +msgstr "Altri effetti" #: src/settings_translation_file.cpp msgid "" @@ -6247,7 +6201,7 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Prevent mods from doing insecure things like running shell commands." msgstr "" -"Impedisce che i mod facciano cose non sicure come eseguire comandi della " +"Impedisce che i moduli facciano cose non sicure come eseguire comandi della " "shell." #: src/settings_translation_file.cpp @@ -6265,7 +6219,7 @@ msgstr "Privilegi che i giocatori con basic_privs possono concedere" #: src/settings_translation_file.cpp msgid "Profiler" -msgstr "Generatore di profili" +msgstr "Profilatore" #: src/settings_translation_file.cpp msgid "Prometheus listener address" @@ -6339,7 +6293,7 @@ msgstr "Dati in ingresso casuali" #: src/settings_translation_file.cpp msgid "Random mod load order" -msgstr "" +msgstr "Ordine casuale di caricamento moduli" #: src/settings_translation_file.cpp msgid "Recent Chat Messages" @@ -6363,7 +6317,7 @@ msgid "" "Remove color codes from incoming chat messages\n" "Use this to stop players from being able to use color in their messages" msgstr "" -"Leva i codici di colore dai messaggi di chat in arrivo\n" +"Rimuove i codici di colore dai messaggi di chat in arrivo\n" "Usalo per impedire ai giocatori di usare i colori nei loro messaggi" #: src/settings_translation_file.cpp @@ -6387,18 +6341,17 @@ msgid "" "csm_restriction_noderange)\n" "READ_PLAYERINFO: 32 (disable get_player_names call client-side)" msgstr "" -"Restringe l'accesso di certe funzioni lato-client sui server.\n" +"Restringe l'accesso di certe funzioni lato cliente sui server.\n" "Combina i valori byte sottostanti per restringere le caratteristiche\n" -"lato-client, o imposta a 0 per nessuna restrizione:\n" -"LOAD_CLIENT_MODS: 1 (disabilita il caricamento di mod forniti dal client)\n" -"CHAT_MESSAGES: 2 (disabilita la chiamata di send_chat_message su lato-" -"client)\n" -"READ_ITEMDEFS: 4 (disabilita la chiamata di get_item_def su lato-client)\n" -"READ_NODEDEFS: 8 (disabilita la chiamata di get_node_def su lato-client)\n" -"LOOKUP_NODES_LIMIT: 16 (limita la chiamata get_node su lato-client a\n" +"lato cliente, o imposta a 0 per nessuna restrizione:\n" +"LOAD_CLIENT_MODS: 1 (disabilita il caricamento di moduli forniti dal cliente)" +"\n" +"CHAT_MESSAGES: 2 (disabilita la chiamata di send_chat_message lato cliente)\n" +"READ_ITEMDEFS: 4 (disabilita la chiamata di get_item_def lato cliente)\n" +"READ_NODEDEFS: 8 (disabilita la chiamata di get_node_def lato cliente)\n" +"LOOKUP_NODES_LIMIT: 16 (limita la chiamata get_node lato cliente a\n" "csm_restriction_noderange)\n" -"READ_PLAYERINFO: 32 (disabilita la chiamata di get_player_names su lato-" -"client)" +"READ_PLAYERINFO: 32 (disabilita la chiamata di get_player_names lato cliente)" #: src/settings_translation_file.cpp msgid "Ridge mountain spread noise" @@ -6463,7 +6416,7 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Save the map received by the client on disk." -msgstr "Salvare su disco la mappa ricevuta dal client." +msgstr "Salvare su disco la mappa ricevuta dal cliente." #: src/settings_translation_file.cpp msgid "" @@ -6765,7 +6718,7 @@ msgid "" "Set the maximum length of a chat message (in characters) sent by clients." msgstr "" "Imposta la lunghezza massima (in caratteri) di un messaggio di chat inviato " -"dai client." +"dai clienti." #: src/settings_translation_file.cpp msgid "" @@ -6919,7 +6872,7 @@ msgid "" "draw calls, benefiting especially high-end GPUs.\n" "Systems with a low-end GPU (or no GPU) would benefit from smaller values." msgstr "" -"Lunghezza del lato di un cubo di blocchi mappa che il client considererà " +"Lunghezza del lato di un cubo di blocchi mappa che il cliente considererà " "assieme\n" "durante la generazione delle mesh.\n" "Valori maggiori aumentano l'utilizzo della GPU riducendo il numero di\n" @@ -7040,7 +6993,7 @@ msgid "" "(obviously, remote_media should end with a slash).\n" "Files that are not present will be fetched the usual way." msgstr "" -"Specifica l'URL da cui il client recupera i file multimediali invece di " +"Specifica l'URL da cui il cliente recupera i file multimediali invece di " "usare UDP.\n" "$filename dovrebbe essere accessibile da $remote_media$filename tramite\n" "cURL (ovviamente, remote_media dovrebbe finire con una barra).\n" @@ -7053,7 +7006,7 @@ msgid "" "items." msgstr "" "Fissa la dimensione predefinita della pila di nodi, oggetti e strumenti.\n" -"Si noti che mod o giochi possono impostare esplicitamente una pila per " +"Si noti che moduli o giochi possono impostare esplicitamente una pila per " "alcuni (o tutti) gli oggetti." #: src/settings_translation_file.cpp @@ -7271,6 +7224,16 @@ msgid "" "Known from the classic Luanti mobile controls.\n" "Combat is more or less impossible." msgstr "" +"L'azione per prendere a pugni giocanti/entità.\n" +"Può essere sovrascritto da giochi e moduli.\n" +"\n" +"* Tocco rapido\n" +"Facile da usare e diffuso su altri giochi che non possono essere nominati.\n" +"\n" +"* Tocco prolungato\n" +"Conosciuto dalla comunità di Luanti in quanto è stato il controllo " +"predefinito da telefono.\n" +"Combattere è pressoché impossibile." #: src/settings_translation_file.cpp msgid "The identifier of the joystick to use" @@ -7294,13 +7257,11 @@ msgid "" msgstr "" #: src/settings_translation_file.cpp -#, fuzzy msgid "" "The length in pixels after which a touch interaction is considered movement." -msgstr "La distanza in pixel richiesta per avviare l'interazione touch screen." +msgstr "La distanza in pixel dopo la quale un tocco è considerato movimento." #: src/settings_translation_file.cpp -#, fuzzy msgid "" "The maximum height of the surface of waving liquids.\n" "4.0 = Wave height is two nodes.\n" @@ -7310,8 +7271,7 @@ msgstr "" "L'altezza massima della superficie dei liquidi ondulanti.\n" "4.0 = L'altezza dell'onda è di due nodi.\n" "0.0 = L'onda non si muove affatto.\n" -"Il valore predefinito è 1.0 (1/2 nodo).\n" -"Richiede l'abilitazione dei liquidi ondulanti." +"Il valore predefinito è 1.0 (1/2 nodo)." #: src/settings_translation_file.cpp #, fuzzy @@ -7334,7 +7294,7 @@ msgid "" msgstr "" "I privilegi ricevuti automaticamente dai nuovi utenti.\n" "Si veda /privs in gioco per un elenco completo sul vostro server e la " -"configurazione dei mod." +"configurazione dei moduli." #: src/settings_translation_file.cpp msgid "" @@ -7458,7 +7418,7 @@ msgid "" "Setting it to -1 disables the feature." msgstr "" "Tempo di vita in secondi per le entità oggetto (oggetti buttati).\n" -"Impostandola a -1 disabilita la caratteristica." +"Imposta a -1 per disabilitare la funzionalità." #: src/settings_translation_file.cpp msgid "Time of day when a new world is started, in millihours (0-23999)." @@ -7473,8 +7433,8 @@ msgstr "Velocità del tempo" #: src/settings_translation_file.cpp msgid "Timeout for client to remove unused map data from memory, in seconds." msgstr "" -"Scadenza per il client per rimuovere dalla memoria dati mappa inutilizzati, " -"in secondi." +"Scadenza per il cliente per rimuovere dalla memoria i dati della mappa " +"inutilizzati, in secondi." #: src/settings_translation_file.cpp msgid "" @@ -7489,72 +7449,64 @@ msgstr "" "rimosso un nodo." #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle Aux1 key" -msgstr "Tasto Speciale" +msgstr "Taso Aux1 sì/no" #: src/settings_translation_file.cpp msgid "Toggle HUD" msgstr "HUD sì/no" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle Sneak key" -msgstr "Tasto di (dis)attivazione della modalità telecamera" +msgstr "Furtivo sì/no" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle automatic forward" -msgstr "Tasto di avanzamento automatico" +msgstr "Avanzamento automatico sì/no" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle block bounds" -msgstr "Limiti del blocco nascosto" +msgstr "Limiti del blocco sì/no" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle camera mode" -msgstr "Tasto di (dis)attivazione della modalità telecamera" +msgstr "Cambia inquadratura" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle camera update" -msgstr "Tasto di (dis)attivazione della modalità telecamera" +msgstr "Aggiornamento telecamera sì/no" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle cinematic mode" -msgstr "Scegli cinematica" +msgstr "Cinematica sì/no" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle debug info" -msgstr "Nebbia sì/no" +msgstr "Mostra informazioni di debug" #: src/settings_translation_file.cpp msgid "Toggle fog" msgstr "Nebbia sì/no" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle fullscreen" -msgstr "Volo sì/no" +msgstr "Schermo intero sì/no" #: src/settings_translation_file.cpp msgid "Toggle pitchmove" msgstr "Beccheggio sì/no" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle profiler" -msgstr "Volo sì/no" +msgstr "Profilatore sì/no" #: src/settings_translation_file.cpp msgid "" "Tolerance of movement cheat detector.\n" "Increase the value if players experience stuttery movement." msgstr "" +"Tolleranza del rilevatore di cheat di movimento.\n" +"Aumentane il valore se i giocatori vivono un'esperienza a scatti." #: src/settings_translation_file.cpp msgid "Tooltip delay" @@ -7622,7 +7574,7 @@ msgstr "" #: src/settings_translation_file.cpp msgid "Trusted mods" -msgstr "Mod fidate" +msgstr "Moduli fidati" #: src/settings_translation_file.cpp msgid "" @@ -8010,14 +7962,14 @@ msgid "" msgstr "" "Se lo sfondo dell'etichetta del nome debba essere mostrato per impostazione " "predefinita.\n" -"Le mod possono comunque impostare uno sfondo." +"I moduli possono comunque impostare uno sfondo." #: src/settings_translation_file.cpp msgid "" "Whether players are shown to clients without any range limit.\n" "Deprecated, use the setting player_transfer_distance instead." msgstr "" -"Se i giocatori vengono mostrati ai client senza alcun limite di raggio.\n" +"Se i giocatori vengono mostrati ai clienti senza alcun limite di raggio.\n" "Deprecata, usa invece l'impostazione player_transfer_distance." #: src/settings_translation_file.cpp @@ -8029,7 +7981,7 @@ msgid "" "Whether to ask clients to reconnect after a (Lua) crash.\n" "Set this to true if your server is set up to restart automatically." msgstr "" -"Se chiedere ai client di riconnettersi dopo un crash (Lua).\n" +"Se chiedere ai clienti di riconnettersi dopo un crash (Lua).\n" "Impostatela su Vero se il vostro server è configurato per riavviarsi " "automaticamente." @@ -8038,23 +7990,21 @@ msgid "Whether to fog out the end of the visible area." msgstr "Se annebbiare o meno la fine dell'area visibile." #: src/settings_translation_file.cpp -#, fuzzy msgid "" "Whether to mute sounds. You can unmute sounds at any time.\n" "In-game, you can toggle the mute state with the mute key or by using the\n" "pause menu." msgstr "" "Se silenziare i suoni. È possibile de-silenziare i suoni in qualsiasi " -"momento, a meno che\n" -"il sistema audio non sia disabilitato (enable_sound=false).\n" -"Nel gioco, puoi alternare lo stato silenziato col tasto muta o usando\n" +"momento.\n" +"In partita puoi mutare e smutare il gioco col tasto muta o usando\n" "il menu di pausa." #: src/settings_translation_file.cpp msgid "" "Whether to show the client debug info (has the same effect as hitting F5)." msgstr "" -"Se mostrare le informazioni di debug del client (ha lo stesso effetto di " +"Se mostrare le informazioni di debug del cliente (ha lo stesso effetto di " "premere F5)." #: src/settings_translation_file.cpp @@ -8096,7 +8046,6 @@ msgid "World start time" msgstr "Ora di avvio del mondo" #: src/settings_translation_file.cpp -#, fuzzy msgid "" "World-aligned textures may be scaled to span several nodes. However,\n" "the server may not send the scale you want, especially if you use\n" @@ -8110,7 +8059,7 @@ msgstr "" "Tuttavia, il server potrebbe non inviare le dimensioni desiderate, " "specialmente se è in uso un\n" "pacchetto texture progettato in modo specifico; con questa opzione, il " -"client prova a stabilire\n" +"cliente prova a stabilire\n" "automaticamente le dimensioni basandosi sulla grandezza della texture.\n" "Si veda anche texture_min_size.\n" "Avviso: questa opzione è SPERIMENTALE!" diff --git a/po/pt/luanti.po b/po/pt/luanti.po index 86a3f1344..974f76346 100644 --- a/po/pt/luanti.po +++ b/po/pt/luanti.po @@ -3,8 +3,8 @@ msgstr "" "Project-Id-Version: Portuguese (Minetest)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-14 23:02+0200\n" -"PO-Revision-Date: 2025-05-12 16:41+0000\n" -"Last-Translator: Felipe Amaral \n" +"PO-Revision-Date: 2025-05-18 15:01+0000\n" +"Last-Translator: Ian Pedras \n" "Language-Team: Portuguese \n" "Language: pt\n" @@ -934,30 +934,29 @@ msgid "Delete World \"$1\"?" msgstr "Eliminar mundo \"$1\"?" #: builtin/mainmenu/dlg_rebind_keys.lua +#, fuzzy msgid "As a result, your keybindings may have been changed." -msgstr "" +msgstr "Como resultado, os seus comandos foram mudados." #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Check out the key settings or refer to the documentation:" -msgstr "" +msgstr "Veja as configurações ou refera a decumentação:" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Close" -msgstr "" +msgstr "Fechar" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Keybindings changed" -msgstr "Combinações de teclas." +msgstr "Combinações de teclas mudadas" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Open settings" -msgstr "Definições" +msgstr "Abrir Definições" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "The input handling system was reworked in Luanti 5.12.0." -msgstr "" +msgstr "O sistema de combinações de teclas foi remodelado no Luanti 5.12.0." #: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp msgid "Confirm Password" @@ -2058,7 +2057,7 @@ msgstr "Scroll Lock" #. ~ Key name #: src/client/keycode.cpp msgid "Select" -msgstr "Seleccionar" +msgstr "Selecionar" #: src/client/keycode.cpp msgid "Shift Key" @@ -2115,14 +2114,14 @@ msgid "Minimap in texture mode" msgstr "Minimapa em modo de textura" #: src/client/shader.cpp -#, fuzzy, c-format +#, c-format msgid "Failed to compile the \"%s\" shader." -msgstr "Falha ao abrir página da web" +msgstr "Falha ao compilar o \"%s\" shader." #: src/client/shader.cpp #, fuzzy msgid "GLSL is not supported by the driver" -msgstr "Som do sistema não é suportado nesta versão" +msgstr "GLSL não é suportado pelo seu sistema" #. ~ Error when a mod is missing dependencies. Ex: "Mod Title is missing: mod1, mod2, mod3" #: src/content/mod_configuration.cpp @@ -2168,11 +2167,11 @@ msgstr "Continuar" #: src/gui/guiOpenURL.cpp msgid "Open" -msgstr "" +msgstr "Abrir" #: src/gui/guiOpenURL.cpp msgid "Open URL?" -msgstr "" +msgstr "Abrir URL?" #: src/gui/guiOpenURL.cpp #, fuzzy @@ -2207,33 +2206,34 @@ msgstr "Volume do som: %d%%" #: src/gui/touchscreeneditor.cpp #, fuzzy msgid "Add button" -msgstr "Roda do Rato" +msgstr "Adicionar botão" #: src/gui/touchscreeneditor.cpp -#, fuzzy msgid "Done" -msgstr "Feito!" +msgstr "Feito" #: src/gui/touchscreeneditor.cpp #, fuzzy msgid "Remove" -msgstr "Servidor remoto" +msgstr "Remover" #: src/gui/touchscreeneditor.cpp +#, fuzzy msgid "Reset" -msgstr "" +msgstr "Refazer" #: src/gui/touchscreeneditor.cpp +#, fuzzy msgid "Start dragging a button to add. Tap outside to cancel." -msgstr "" +msgstr "Começe a arrastar um botão para adicionar. Clique fora para cancelar." #: src/gui/touchscreeneditor.cpp msgid "Tap a button to select it. Drag a button to move it." -msgstr "" +msgstr "Clique num botão para selecioná-lo. Arraste o botão para movê-lo." #: src/gui/touchscreeneditor.cpp msgid "Tap outside to deselect." -msgstr "" +msgstr "Click fora para desselecionar." #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Aux1" @@ -2245,7 +2245,7 @@ msgstr "Mudar camera" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Dig/punch/use" -msgstr "" +msgstr "Escavar/Attacar/Usar" #: src/gui/touchscreenlayout.cpp msgid "Drop" @@ -2370,27 +2370,32 @@ msgstr "" #: src/network/clientpackethandler.cpp msgid "The server is running in singleplayer mode. You cannot connect." -msgstr "" +msgstr "Este servidor está a correr no modo uni jogador. Não se pode connectar." #: src/network/clientpackethandler.cpp msgid "Too many users" -msgstr "" +msgstr "Demasiados utilizadores" #: src/network/clientpackethandler.cpp msgid "Unknown disconnect reason." -msgstr "" +msgstr "Razão de desconecto desconhecido." #: src/network/clientpackethandler.cpp +#, fuzzy msgid "" "Your client sent something the server didn't expect. Try reconnecting or " "updating your client." msgstr "" +"O seu cliente mandou algo que o servidor não esperava. Tente reconectar ou " +"atualizar o seu cliente." #: src/network/clientpackethandler.cpp msgid "" "Your client's version is not supported.\n" "Please contact the server administrator." msgstr "" +"A versão do seu cliente não é supportado.\n" +"Por favor contacte o administrador do servidor." #: src/server.cpp #, c-format @@ -2465,7 +2470,7 @@ msgstr "Ruído 2D que localiza os vales e canais dos rios." #: src/settings_translation_file.cpp msgid "3D" -msgstr "" +msgstr "3D" #: src/settings_translation_file.cpp msgid "3D clouds" @@ -2638,13 +2643,13 @@ msgid "" msgstr "" #: src/settings_translation_file.cpp -#, fuzzy msgid "Allow clouds to look 3D instead of flat." msgstr "Usar nuvens 3D em vez de planas." #: src/settings_translation_file.cpp +#, fuzzy msgid "Allows liquids to be translucent." -msgstr "" +msgstr "Permitir liquidos semi transparentes." #: src/settings_translation_file.cpp msgid "" @@ -2697,8 +2702,9 @@ msgid "Anticheat flags" msgstr "" #: src/settings_translation_file.cpp +#, fuzzy msgid "Anticheat movement tolerance" -msgstr "" +msgstr "Tolerância de movimentos anti-batota" #: src/settings_translation_file.cpp msgid "Append item name" @@ -2724,8 +2730,9 @@ msgid "" msgstr "" #: src/settings_translation_file.cpp +#, fuzzy msgid "Apply specular shading to nodes." -msgstr "" +msgstr "Aplicar shading especular aos nodes." #: src/settings_translation_file.cpp msgid "Arm inertia" @@ -2824,9 +2831,8 @@ msgid "Base terrain height." msgstr "Altura base do terreno." #: src/settings_translation_file.cpp -#, fuzzy msgid "Base texture size" -msgstr "Tamanho mínimo da textura" +msgstr "Tamanho base da textura" #: src/settings_translation_file.cpp msgid "Basic privileges" @@ -2849,9 +2855,8 @@ msgid "Bind address" msgstr "Endereço de bind" #: src/settings_translation_file.cpp -#, fuzzy msgid "Biome API" -msgstr "Biomas" +msgstr "API de Biomas" #: src/settings_translation_file.cpp msgid "Biome noise" @@ -3016,13 +3021,12 @@ msgid "Client" msgstr "Cliente" #: src/settings_translation_file.cpp -#, fuzzy msgid "Client Debugging" -msgstr "Debugging" +msgstr "Debugging de cliente" #: src/settings_translation_file.cpp msgid "Client Mesh Chunksize" -msgstr "" +msgstr "Tamhãnho do Client Mesh Chunksize" #: src/settings_translation_file.cpp msgid "Client and Server" @@ -3063,7 +3067,7 @@ msgstr "Nuvens no menu" #: src/settings_translation_file.cpp msgid "Color depth for post-processing texture" -msgstr "" +msgstr "Profundidade da cor para a textura post-processing" #: src/settings_translation_file.cpp msgid "Colored fog" diff --git a/po/ru/luanti.po b/po/ru/luanti.po index a9109e95d..c31366bc0 100644 --- a/po/ru/luanti.po +++ b/po/ru/luanti.po @@ -3,8 +3,8 @@ msgstr "" "Project-Id-Version: Russian (Minetest)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-14 23:02+0200\n" -"PO-Revision-Date: 2025-05-03 05:01+0000\n" -"Last-Translator: Nana_M \n" +"PO-Revision-Date: 2025-05-16 16:16+0000\n" +"Last-Translator: BlackImpostor \n" "Language-Team: Russian \n" "Language: ru\n" @@ -934,29 +934,27 @@ msgstr "Удалить мир «$1»?" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "As a result, your keybindings may have been changed." -msgstr "" +msgstr "В результате ваши привязки клавиш, возможно, были изменены." #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Check out the key settings or refer to the documentation:" -msgstr "" +msgstr "Ознакомьтесь с настройками клавиш или обратитесь к документации:" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Close" -msgstr "" +msgstr "Закрыть" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Keybindings changed" -msgstr "Привязки клавиш" +msgstr "Изменены привязки клавиш" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Open settings" -msgstr "Настройки" +msgstr "Открыть настройки" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "The input handling system was reworked in Luanti 5.12.0." -msgstr "" +msgstr "Система обработки входных данных была переработана в Luanti 5.12.0." #: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp msgid "Confirm Password" @@ -2250,7 +2248,7 @@ msgstr "Бросить" #: src/gui/touchscreenlayout.cpp msgid "Exit" -msgstr "Закрыть" +msgstr "Выйти" #: src/gui/touchscreenlayout.cpp msgid "Inventory" diff --git a/po/zh_CN/luanti.po b/po/zh_CN/luanti.po index 9d85accc8..201169c0a 100644 --- a/po/zh_CN/luanti.po +++ b/po/zh_CN/luanti.po @@ -3,8 +3,8 @@ msgstr "" "Project-Id-Version: Chinese (Simplified) (Minetest)\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2025-05-14 23:02+0200\n" -"PO-Revision-Date: 2025-02-24 16:36+0000\n" -"Last-Translator: BX Zhang \n" +"PO-Revision-Date: 2025-05-15 23:02+0000\n" +"Last-Translator: y5nw \n" "Language-Team: Chinese (Simplified Han script) \n" "Language: zh_CN\n" @@ -12,7 +12,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.10.1-dev\n" +"X-Generator: Weblate 5.12-dev\n" #: builtin/client/chatcommands.lua msgid "Clear the out chat queue" @@ -82,16 +82,15 @@ msgstr "浏览" #: builtin/common/settings/components.lua msgid "Conflicts with \"$1\"" -msgstr "" +msgstr "与“$1”冲突" #: builtin/common/settings/components.lua msgid "Edit" msgstr "编辑" #: builtin/common/settings/components.lua -#, fuzzy msgid "Remove keybinding" -msgstr "按键绑定。" +msgstr "移除按键绑定" #: builtin/common/settings/components.lua msgid "Select directory" @@ -253,7 +252,7 @@ msgstr "通用" #: builtin/common/settings/dlg_settings.lua msgid "Long tap" -msgstr "" +msgstr "长按" #: builtin/common/settings/dlg_settings.lua msgid "Movement" @@ -558,12 +557,11 @@ msgstr "捐赠" #: builtin/mainmenu/content/dlg_package.lua msgid "Error loading package information" -msgstr "" +msgstr "无法获取软件包信息" #: builtin/mainmenu/content/dlg_package.lua -#, fuzzy msgid "Error loading reviews" -msgstr "创建客户端出错:%s" +msgstr "无法加载评价" #: builtin/mainmenu/content/dlg_package.lua msgid "Forum Topic" @@ -583,7 +581,7 @@ msgstr "问题跟踪器" #: builtin/mainmenu/content/dlg_package.lua msgid "Reviews" -msgstr "" +msgstr "评价" #: builtin/mainmenu/content/dlg_package.lua msgid "Source" @@ -638,10 +636,11 @@ msgid "Unable to install a $1 as a texture pack" msgstr "无法将$1安装为材质包" #: builtin/mainmenu/dlg_clients_list.lua +#, fuzzy msgid "" "Players connected to\n" "$1" -msgstr "" +msgstr "连接到$1的玩家" #: builtin/mainmenu/dlg_config_world.lua msgid "(Enabled, has error)" @@ -924,25 +923,23 @@ msgstr "删除世界“$1”?" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "As a result, your keybindings may have been changed." -msgstr "" +msgstr "您的一些按键绑定有可能已被更改。" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Check out the key settings or refer to the documentation:" -msgstr "" +msgstr "请查看按键绑定设置或参考文档:" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "Close" -msgstr "" +msgstr "关闭" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Keybindings changed" -msgstr "按键绑定。" +msgstr "按键绑定已被更改" #: builtin/mainmenu/dlg_rebind_keys.lua -#, fuzzy msgid "Open settings" -msgstr "设置" +msgstr "打开设置" #: builtin/mainmenu/dlg_rebind_keys.lua msgid "The input handling system was reworked in Luanti 5.12.0." @@ -1292,12 +1289,11 @@ msgid "Ping" msgstr "ping值" #: builtin/mainmenu/tab_online.lua -#, fuzzy msgid "" "Players:\n" "$1" msgstr "" -"客户端:\n" +"玩家::\n" "$1" #: builtin/mainmenu/tab_online.lua @@ -2247,7 +2243,7 @@ msgstr "溢出菜单" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp #, fuzzy msgid "Place/use" -msgstr "放置键" +msgstr "放置或使用" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Range select" @@ -2262,9 +2258,8 @@ msgid "Toggle chat log" msgstr "启用/禁用聊天记录" #: src/gui/touchscreenlayout.cpp -#, fuzzy msgid "Toggle debug" -msgstr "启用/禁用雾" +msgstr "显示/隐藏调试信息" #: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp msgid "Toggle fast" @@ -2301,9 +2296,8 @@ msgid "Internal server error" msgstr "内部服务器错误" #: src/network/clientpackethandler.cpp -#, fuzzy msgid "Invalid password" -msgstr "旧密码" +msgstr "密码错误" #. ~ DO NOT TRANSLATE THIS LITERALLY! #. This is a special string which needs to contain the translation's @@ -2977,9 +2971,8 @@ msgid "Client" msgstr "客户端" #: src/settings_translation_file.cpp -#, fuzzy msgid "Client Debugging" -msgstr "调试" +msgstr "客户端调试" #: src/settings_translation_file.cpp msgid "Client Mesh Chunksize" @@ -3233,7 +3226,6 @@ msgid "Decrease view range" msgstr "减少可视范围" #: src/settings_translation_file.cpp -#, fuzzy msgid "Decrease volume" msgstr "减小音量" @@ -3446,9 +3438,8 @@ msgid "" msgstr "" #: src/settings_translation_file.cpp -#, fuzzy msgid "Drop item" -msgstr "丢弃物品键" +msgstr "丢弃物品" #: src/settings_translation_file.cpp msgid "Dump the mapgen debug information." @@ -4133,164 +4124,132 @@ msgstr "" "单位为方块每二次方秒。" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 1" -msgstr "快捷栏1键" +msgstr "快捷栏第1项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 10" -msgstr "快捷栏10键" +msgstr "快捷栏第10项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 11" -msgstr "快捷栏11键" +msgstr "快捷栏第11项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 12" -msgstr "快捷栏12键" +msgstr "快捷栏第12项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 13" -msgstr "快捷栏13键" +msgstr "快捷栏第13项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 14" -msgstr "快捷栏14键" +msgstr "快捷栏第14项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 15" -msgstr "快捷栏15键" +msgstr "快捷栏第15项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 16" -msgstr "快捷栏16键" +msgstr "快捷栏第16项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 17" -msgstr "快捷栏17键" +msgstr "快捷栏第17项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 18" -msgstr "快捷栏18键" +msgstr "快捷栏第18项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 19" -msgstr "快捷栏19键" +msgstr "快捷栏第19项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 2" -msgstr "快捷栏2键" +msgstr "快捷栏第2项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 20" -msgstr "快捷栏20键" +msgstr "快捷栏第20项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 21" -msgstr "快捷栏21键" +msgstr "快捷栏第21项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 22" -msgstr "快捷栏22键" +msgstr "快捷栏第22项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 23" -msgstr "快捷栏23键" +msgstr "快捷栏第23项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 24" -msgstr "快捷栏24键" +msgstr "快捷栏第24项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 25" -msgstr "快捷栏25键" +msgstr "快捷栏第25项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 26" -msgstr "快捷栏26键" +msgstr "快捷栏第26项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 27" -msgstr "快捷栏27键" +msgstr "快捷栏第27项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 28" -msgstr "快捷栏28键" +msgstr "快捷栏第28项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 29" -msgstr "快捷栏29键" +msgstr "快捷栏第29项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 3" -msgstr "快捷栏3键" +msgstr "快捷栏第3项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 30" -msgstr "快捷栏30键" +msgstr "快捷栏第30项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 31" -msgstr "快捷栏31键" +msgstr "快捷栏第31项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 32" -msgstr "快捷栏32键" +msgstr "快捷栏第32项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 4" -msgstr "快捷栏4键" +msgstr "快捷栏第4项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 5" -msgstr "快捷栏5键" +msgstr "快捷栏第5项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 6" -msgstr "快捷栏6键" +msgstr "快捷栏第6项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 7" -msgstr "快捷栏7键" +msgstr "快捷栏第7项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 8" -msgstr "快捷栏8键" +msgstr "快捷栏第8项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar slot 9" -msgstr "快捷栏9键" +msgstr "快捷栏第9项" #: src/settings_translation_file.cpp msgid "Hotbar: Enable mouse wheel for selection" @@ -4301,14 +4260,12 @@ msgid "Hotbar: Invert mouse wheel direction" msgstr "快捷栏:反转鼠标滚轮方向" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar: select next item" -msgstr "快捷栏下一个键" +msgstr "选择快捷栏下一项" #: src/settings_translation_file.cpp -#, fuzzy msgid "Hotbar: select previous item" -msgstr "快捷栏上一个键" +msgstr "选择快捷栏上一项" #: src/settings_translation_file.cpp msgid "How deep to make rivers." @@ -4513,7 +4470,6 @@ msgid "Increase view range" msgstr "增加可视范围" #: src/settings_translation_file.cpp -#, fuzzy msgid "Increase volume" msgstr "增大音量" @@ -5031,9 +4987,8 @@ msgid "Key to use view zoom when possible." msgstr "" #: src/settings_translation_file.cpp -#, fuzzy msgid "Keybindings" -msgstr "按键绑定。" +msgstr "按键绑定" #: src/settings_translation_file.cpp msgid "Keyboard and Mouse" @@ -5072,9 +5027,8 @@ msgid "Large cave proportion flooded" msgstr "大型洞穴淹没比" #: src/settings_translation_file.cpp -#, fuzzy msgid "Large chat console" -msgstr "大型聊天控制台键" +msgstr "大型聊天控制台" #: src/settings_translation_file.cpp msgid "Leaves style" @@ -5714,23 +5668,20 @@ msgid "Mouse sensitivity multiplier." msgstr "鼠标灵敏度倍数。" #: src/settings_translation_file.cpp -#, fuzzy msgid "Move backward" -msgstr "向后" +msgstr "后退" #: src/settings_translation_file.cpp -#, fuzzy msgid "Move forward" -msgstr "自动向前" +msgstr "前进" #: src/settings_translation_file.cpp -#, fuzzy msgid "Move left" -msgstr "移动" +msgstr "往左移动" #: src/settings_translation_file.cpp msgid "Move right" -msgstr "" +msgstr "往右移动" #: src/settings_translation_file.cpp #, fuzzy @@ -5879,14 +5830,12 @@ msgid "" msgstr "默认字体后阴影的透明度(alpha),取值范围0~255。" #: src/settings_translation_file.cpp -#, fuzzy msgid "Open chat" -msgstr "打开" +msgstr "打开聊天室" #: src/settings_translation_file.cpp -#, fuzzy msgid "Open inventory" -msgstr "物品栏" +msgstr "打开物品栏" #: src/settings_translation_file.cpp msgid "" @@ -7174,14 +7123,12 @@ msgid "Toggle Sneak key" msgstr "启用/禁用拍照模式键" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle automatic forward" -msgstr "自动前进键" +msgstr "启用/禁用自动前进" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle block bounds" -msgstr "地图块边界" +msgstr "显示/隐藏地图块边界" #: src/settings_translation_file.cpp #, fuzzy @@ -7199,18 +7146,16 @@ msgid "Toggle cinematic mode" msgstr "切换电影模式" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle debug info" -msgstr "启用/禁用雾" +msgstr "显示/隐藏调试信息" #: src/settings_translation_file.cpp msgid "Toggle fog" msgstr "启用/禁用雾" #: src/settings_translation_file.cpp -#, fuzzy msgid "Toggle fullscreen" -msgstr "启用/禁用飞行模式" +msgstr "切换全屏模式" #: src/settings_translation_file.cpp msgid "Toggle pitchmove" @@ -7238,9 +7183,8 @@ msgid "Touchscreen" msgstr "触摸屏" #: src/settings_translation_file.cpp -#, fuzzy msgid "Touchscreen controls" -msgstr "触屏阈值" +msgstr "触屏控制" #: src/settings_translation_file.cpp msgid "Touchscreen sensitivity" diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index a770dbcae..4c8eaf060 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -147,8 +147,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) /* Menu-game loop */ - bool retval = true; - bool *kill = porting::signal_handler_killstatus(); + bool retval = true; + volatile auto *kill = porting::signal_handler_killstatus(); while (m_rendering_engine->run() && !*kill && !g_gamecallback->shutdown_requested) { @@ -326,6 +326,18 @@ void ClientLauncher::setting_changed_callback(const std::string &name, void *dat static_cast(data)->config_guienv(); } +static video::ITexture *loadTexture(video::IVideoDriver *driver, const char *path) +{ + // FIXME?: it would be cleaner to do this through a ITextureSource, but we don't have one + video::ITexture *texture = nullptr; + verbosestream << "Loading texture " << path << std::endl; + if (auto *image = driver->createImageFromFile(path); image) { + texture = driver->addTexture(fs::GetFilenameFromPath(path), image); + image->drop(); + } + return texture; +} + void ClientLauncher::config_guienv() { gui::IGUISkin *skin = guienv->getSkin(); @@ -364,10 +376,9 @@ void ClientLauncher::config_guienv() if (cached_id != sprite_ids.end()) { skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, cached_id->second); } else { - gui::IGUISpriteBank *sprites = skin->getSpriteBank(); - video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); - video::ITexture *texture = driver->getTexture(path.c_str()); - s32 id = sprites->addTextureAsSprite(texture); + auto *driver = m_rendering_engine->get_video_driver(); + auto *texture = loadTexture(driver, path.c_str()); + s32 id = skin->getSpriteBank()->addTextureAsSprite(texture); if (id != -1) { skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, id); sprite_ids.emplace(path, id); @@ -529,9 +540,9 @@ bool ClientLauncher::launch_game(std::string &error_message, void ClientLauncher::main_menu(MainMenuData *menudata) { - bool *kill = porting::signal_handler_killstatus(); + volatile auto *kill = porting::signal_handler_killstatus(); video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); - auto *device = m_rendering_engine->get_raw_device(); + auto *device = m_rendering_engine->get_raw_device(); // Wait until app is in foreground because of #15883 infostream << "Waiting for app to be in foreground" << std::endl; diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index e0ac5fff0..ecfe0f2de 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -178,15 +178,6 @@ static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill, matrix.setTextureScale(txs, tys); } -// Evaluate transform chain recursively; irrlicht does not do this for us -static void updatePositionRecursive(scene::ISceneNode *node) -{ - scene::ISceneNode *parent = node->getParent(); - if (parent) - updatePositionRecursive(parent); - node->updateAbsolutePosition(); -} - static bool logOnce(const std::ostringstream &from, std::ostream &log_to) { thread_local std::vector logged; @@ -682,7 +673,6 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) m_animated_meshnode = m_smgr->addAnimatedMeshSceneNode(mesh, m_matrixnode); m_animated_meshnode->grab(); mesh->drop(); // The scene node took hold of it - m_animated_meshnode->animateJoints(); // Needed for some animations m_animated_meshnode->setScale(m_prop.visual_size); // set vertex colors to ensure alpha is set @@ -693,6 +683,21 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) m_animated_meshnode->forEachMaterial([this] (auto &mat) { mat.BackfaceCulling = m_prop.backface_culling; }); + + m_animated_meshnode->setOnAnimateCallback([&](f32 dtime) { + for (auto &it : m_bone_override) { + auto* bone = m_animated_meshnode->getJointNode(it.first.c_str()); + if (!bone) + continue; + + BoneOverride &props = it.second; + props.dtime_passed += dtime; + + bone->setPosition(props.getPosition(bone->getPosition())); + bone->setRotation(props.getRotationEulerDeg(bone->getRotation())); + bone->setScale(props.getScale(bone->getScale())); + } + }); } else errorstream<<"GenericCAO::addToScene(): Could not load mesh "<updateAbsolutePosition(); - m_animated_meshnode->animateJoints(); - updateBones(dtime); - } } static void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count) @@ -1394,44 +1386,6 @@ void GenericCAO::updateAnimationSpeed() m_animated_meshnode->setAnimationSpeed(m_animation_speed); } -void GenericCAO::updateBones(f32 dtime) -{ - if (!m_animated_meshnode) - return; - if (m_bone_override.empty()) { - m_animated_meshnode->setJointMode(scene::EJUOR_NONE); - return; - } - - m_animated_meshnode->setJointMode(scene::EJUOR_CONTROL); // To write positions to the mesh on render - for (auto &it : m_bone_override) { - std::string bone_name = it.first; - scene::IBoneSceneNode* bone = m_animated_meshnode->getJointNode(bone_name.c_str()); - if (!bone) - continue; - - BoneOverride &props = it.second; - props.dtime_passed += dtime; - - bone->setPosition(props.getPosition(bone->getPosition())); - bone->setRotation(props.getRotationEulerDeg(bone->getRotation())); - bone->setScale(props.getScale(bone->getScale())); - } - - // The following is needed for set_bone_pos to propagate to - // attached objects correctly. - // Irrlicht ought to do this, but doesn't when using EJUOR_CONTROL. - for (u32 i = 0; i < m_animated_meshnode->getJointCount(); ++i) { - auto bone = m_animated_meshnode->getJointNode(i); - // Look for the root bone. - if (bone && bone->getParent() == m_animated_meshnode) { - // Update entire skeleton. - bone->updateAbsolutePositionOfAllChildren(); - break; - } - } -} - void GenericCAO::updateAttachments() { ClientActiveObject *parent = getParent(); @@ -1747,7 +1701,6 @@ void GenericCAO::processMessage(const std::string &data) } else { m_bone_override[bone] = props; } - // updateBones(); now called every step } else if (cmd == AO_CMD_ATTACH_TO) { u16 parent_id = readS16(is); std::string bone = deSerializeString16(is); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 9762684f6..2e765ab6e 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -286,8 +286,6 @@ public: void updateAnimationSpeed(); - void updateBones(f32 dtime); - void processMessage(const std::string &data) override; bool directReportPunch(v3f dir, const ItemStack *punchitem, diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index f5b528287..c07a0a3b1 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -137,11 +137,20 @@ void MapblockMeshGenerator::drawQuad(const TileSpec &tile, v3f *coords, const v3 } static std::array setupCuboidVertices(const aabb3f &box, - const f32 *txc, const TileSpec *tiles, int tilecount) + const f32 *txc, const TileSpec *tiles, int tilecount, v3s16 alignment) { v3f min = box.MinEdge; v3f max = box.MaxEdge; + // Texture coords are [0,1] if not specified otherwise + f32 uniform_txc[24]; + if (!txc) { + for (int i = 0; i != 24; ++i) { + uniform_txc[i] = (i % 4 < 2) ? 0.0f : 1.0f; + } + txc = uniform_txc; + } + std::array vertices = {{ // top video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, {}, txc[0], txc[1]), @@ -185,15 +194,47 @@ static std::array setupCuboidVertices(const aabb3f &box, case TileRotation::None: break; case TileRotation::R90: - tcoords.set(-tcoords.Y, tcoords.X); + tcoords.set(1 - tcoords.Y, tcoords.X); break; case TileRotation::R180: - tcoords.set(-tcoords.X, -tcoords.Y); + tcoords.set(1 - tcoords.X, 1 - tcoords.Y); break; case TileRotation::R270: - tcoords.set(tcoords.Y, -tcoords.X); + tcoords.set(tcoords.Y, 1 - tcoords.X); break; } + + if (tile.world_aligned) { + // Maps uv dimension of every face to world dimension xyz + constexpr int coord_dim[12] = { + 0, 2, // up + 0, 2, // down + 2, 1, // right + 2, 1, // left + 0, 1, // back + 0, 1, // front + }; + + auto scale = tile.layers[0].scale; + f32 scale_factor = 1.0f / scale; + + float x = alignment[coord_dim[face*2]] % scale; + float y = alignment[coord_dim[face*2 + 1]] % scale; + + // Faces grow in different directions + if (face != 1) { + y = tcoords.Y + ((scale-1)-y); + } else { + y = tcoords.Y + y; + } + if (face == 3 || face == 4) { + x = tcoords.X + ((scale-1)-x); + } else { + x = tcoords.X + x; + } + + tcoords.set(x * scale_factor, y * scale_factor); + } } } @@ -212,6 +253,7 @@ enum class QuadDiagonal { // for the opposite corners of each face - therefore, there // should be (2+2)*6=24 values in the list. The order of // the faces in the list is up-down-right-left-back-front +// if nullptr use standard [0,1] coords // (compatible with ContentFeatures). // mask - a bit mask that suppresses drawing of tiles. // tile i will not be drawn if mask & (1 << i) is 1 @@ -224,7 +266,7 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box, { assert(tilecount >= 1 && tilecount <= 6); // pre-condition - auto vertices = setupCuboidVertices(box, txc, tiles, tilecount); + auto vertices = setupCuboidVertices(box, txc, tiles, tilecount, cur_node.p); for (int k = 0; k < 6; ++k) { if (mask & (1 << k)) @@ -301,12 +343,13 @@ video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos, void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords) { - f32 tx1 = (box.MinEdge.X / BS) + 0.5; - f32 ty1 = (box.MinEdge.Y / BS) + 0.5; - f32 tz1 = (box.MinEdge.Z / BS) + 0.5; - f32 tx2 = (box.MaxEdge.X / BS) + 0.5; - f32 ty2 = (box.MaxEdge.Y / BS) + 0.5; - f32 tz2 = (box.MaxEdge.Z / BS) + 0.5; + // Generate texture coords which are aligned to coords of a solid nodes + f32 tx1 = (box.MinEdge.X / BS) + 0.5f; + f32 ty1 = (box.MinEdge.Y / BS) + 0.5f; + f32 tz1 = (box.MinEdge.Z / BS) + 0.5f; + f32 tx2 = (box.MaxEdge.X / BS) + 0.5f; + f32 ty2 = (box.MaxEdge.Y / BS) + 0.5f; + f32 tz2 = (box.MaxEdge.Z / BS) + 0.5f; f32 txc[24] = { tx1, 1 - tz2, tx2, 1 - tz1, // up tx1, tz1, tx2, tz2, // down @@ -334,7 +377,6 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const TileSpec *tiles, int tile_count, const f32 *txc, u8 mask) { bool scale = std::fabs(cur_node.f->visual_scale - 1.0f) > 1e-3f; - f32 texture_coord_buf[24]; f32 dx1 = box.MinEdge.X; f32 dy1 = box.MinEdge.Y; f32 dz1 = box.MinEdge.Z; @@ -342,19 +384,11 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, f32 dy2 = box.MaxEdge.Y; f32 dz2 = box.MaxEdge.Z; if (scale) { - if (!txc) { // generate texture coords before scaling - generateCuboidTextureCoords(box, texture_coord_buf); - txc = texture_coord_buf; - } box.MinEdge *= cur_node.f->visual_scale; box.MaxEdge *= cur_node.f->visual_scale; } box.MinEdge += cur_node.origin; box.MaxEdge += cur_node.origin; - if (!txc) { - generateCuboidTextureCoords(box, texture_coord_buf); - txc = texture_coord_buf; - } if (data->m_smooth_lighting) { LightInfo lights[8]; for (int j = 0; j < 8; ++j) { @@ -442,10 +476,8 @@ void MapblockMeshGenerator::drawSolidNode() u8 mask = faces ^ 0b0011'1111; // k-th bit is set if k-th face is to be *omitted*, as expected by cuboid drawing functions. cur_node.origin = intToFloat(cur_node.p, BS); auto box = aabb3f(v3f(-0.5 * BS), v3f(0.5 * BS)); - f32 texture_coord_buf[24]; box.MinEdge += cur_node.origin; box.MaxEdge += cur_node.origin; - generateCuboidTextureCoords(box, texture_coord_buf); if (data->m_smooth_lighting) { LightPair lights[6][4]; for (int face = 0; face < 6; ++face) { @@ -458,7 +490,7 @@ void MapblockMeshGenerator::drawSolidNode() } } - drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) { + drawCuboid(box, tiles, 6, nullptr, mask, [&] (int face, video::S3DVertex vertices[4]) { auto final_lights = lights[face]; for (int j = 0; j < 4; j++) { video::S3DVertex &vertex = vertices[j]; @@ -471,7 +503,7 @@ void MapblockMeshGenerator::drawSolidNode() return QuadDiagonal::Diag02; }); } else { - drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) { + drawCuboid(box, tiles, 6, nullptr, mask, [&] (int face, video::S3DVertex vertices[4]) { video::SColor color = encode_light(lights[face], cur_node.f->light_source); if (!cur_node.f->light_source) applyFacesShading(color, vertices[0].Normal); @@ -952,7 +984,10 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]]; if (edge_invisible) continue; - drawAutoLightedCuboid(frame_edges[edge], tiles[1]); + + f32 txc[24]; + generateCuboidTextureCoords(frame_edges[edge], txc); + drawAutoLightedCuboid(frame_edges[edge], tiles[1], txc); } for (int face = 0; face < 6; face++) { @@ -996,16 +1031,17 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() float vlev = (param2 / 63.0f) * 2.0f - 1.0f; TileSpec tile; getSpecialTile(0, &tile); - drawAutoLightedCuboid( - aabb3f( - -(nb[5] ? g : b), - -(nb[4] ? g : b), - -(nb[3] ? g : b), - (nb[2] ? g : b), - (nb[1] ? g : b) * vlev, - (nb[0] ? g : b) - ), - tile); + aabb3f box( + -(nb[5] ? g : b), + -(nb[4] ? g : b), + -(nb[3] ? g : b), + (nb[2] ? g : b), + (nb[1] ? g : b) * vlev, + (nb[0] ? g : b) + ); + f32 txc[24]; + generateCuboidTextureCoords(box, txc); + drawAutoLightedCuboid(box, tile, txc); } } @@ -1649,7 +1685,10 @@ void MapblockMeshGenerator::drawNodeboxNode() for (auto &box : boxes) { u8 mask = getNodeBoxMask(box, solid_neighbors, sametype_neighbors); - drawAutoLightedCuboid(box, tiles, 6, nullptr, mask); + + f32 txc[24]; + generateCuboidTextureCoords(box, txc); + drawAutoLightedCuboid(box, tiles, 6, txc, mask); } } @@ -1676,7 +1715,7 @@ void MapblockMeshGenerator::drawMeshNode() if (cur_node.f->mesh_ptr) { // clone and rotate mesh - mesh = cloneMesh(cur_node.f->mesh_ptr); + mesh = cloneStaticMesh(cur_node.f->mesh_ptr); bool modified = true; if (facedir) rotateMeshBy6dFacedir(mesh, facedir); diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index a4ed6a0fc..9d51ba2bc 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -86,7 +86,7 @@ private: template void drawCuboid(const aabb3f &box, const TileSpec *tiles, int tilecount, const f32 *txc, u8 mask, Fn &&face_lighter); - void generateCuboidTextureCoords(aabb3f const &box, f32 *coords); + static void generateCuboidTextureCoords(aabb3f const &box, f32 *coords); void drawAutoLightedCuboid(aabb3f box, const TileSpec &tile, f32 const *txc = nullptr, u8 mask = 0); void drawAutoLightedCuboid(aabb3f box, const TileSpec *tiles, int tile_count, f32 const *txc = nullptr, u8 mask = 0); u8 getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const; diff --git a/src/client/game.cpp b/src/client/game.cpp index f340f98e4..f9a52c43f 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -65,6 +65,8 @@ #include "client/sound/sound_openal.h" #endif +#include + class NodeDugEvent : public MtEvent { public: @@ -561,7 +563,7 @@ public: Game(); ~Game(); - bool startup(bool *kill, + bool startup(volatile std::sig_atomic_t *kill, InputHandler *input, RenderingEngine *rendering_engine, const GameStartData &game_params, @@ -793,14 +795,14 @@ private: This class does take ownership/responsibily for cleaning up etc of any of these items (e.g. device) */ - IrrlichtDevice *device; - RenderingEngine *m_rendering_engine; - video::IVideoDriver *driver; - scene::ISceneManager *smgr; - bool *kill; - std::string *error_message; - bool *reconnect_requested; - PausedNodesList paused_animated_nodes; + IrrlichtDevice *device; + RenderingEngine *m_rendering_engine; + video::IVideoDriver *driver; + scene::ISceneManager *smgr; + volatile std::sig_atomic_t *kill; + std::string *error_message; + bool *reconnect_requested; + PausedNodesList paused_animated_nodes; bool simple_singleplayer_mode; /* End 'cache' */ @@ -932,7 +934,7 @@ Game::~Game() m_rendering_engine->finalize(); } -bool Game::startup(bool *kill, +bool Game::startup(volatile std::sig_atomic_t *kill, InputHandler *input, RenderingEngine *rendering_engine, const GameStartData &start_data, @@ -1114,8 +1116,12 @@ void Game::run() void Game::shutdown() { - // Clear text when exiting. + // Delete text and menus first m_game_ui->clearText(); + m_game_formspec.reset(); + while (g_menumgr.menuCount() > 0) { + g_menumgr.deleteFront(); + } if (g_touchcontrols) g_touchcontrols->hide(); @@ -1126,11 +1132,6 @@ void Game::shutdown() sky.reset(); - /* cleanup menus */ - while (g_menumgr.menuCount() > 0) { - g_menumgr.deleteFront(); - } - // only if the shutdown progress bar isn't shown yet if (m_shutdown_progress == 0.0f) showOverlayMessage(N_("Shutting down..."), 0, 0); @@ -4236,7 +4237,7 @@ void Game::readSettings() ****************************************************************************/ /****************************************************************************/ -void the_game(bool *kill, +void the_game(volatile std::sig_atomic_t *kill, InputHandler *input, RenderingEngine *rendering_engine, const GameStartData &start_data, diff --git a/src/client/game.h b/src/client/game.h index 6b838e2bc..95976ac7b 100644 --- a/src/client/game.h +++ b/src/client/game.h @@ -6,6 +6,7 @@ #include "irrlichttypes.h" #include "config.h" +#include #include #if !IS_CLIENT_BUILD @@ -36,7 +37,7 @@ struct CameraOrientation { #define GAME_FALLBACK_TIMEOUT 1.8f #define GAME_CONNECTION_TIMEOUT 10.0f -void the_game(bool *kill, +void the_game(volatile std::sig_atomic_t *kill, InputHandler *input, RenderingEngine *rendering_engine, const GameStartData &start_data, diff --git a/src/client/game_formspec.cpp b/src/client/game_formspec.cpp index 3d46ddab5..3d8dc6fc8 100644 --- a/src/client/game_formspec.cpp +++ b/src/client/game_formspec.cpp @@ -31,16 +31,6 @@ struct TextDestNodeMetadata : public TextDest m_p = p; m_client = client; } - // This is deprecated I guess? -celeron55 - void gotText(const std::wstring &text) - { - std::string ntext = wide_to_utf8(text); - infostream << "Submitting 'text' field of node at (" << m_p.X << "," - << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl; - StringMap fields; - fields["text"] = ntext; - m_client->sendNodemetaFields(m_p, "", fields); - } void gotText(const StringMap &fields) { m_client->sendNodemetaFields(m_p, "", fields); @@ -217,10 +207,11 @@ void GameFormSpec::deleteFormspec() } } -GameFormSpec::~GameFormSpec() { +void GameFormSpec::reset() +{ if (m_formspec) m_formspec->quitMenu(); - this->deleteFormspec(); + deleteFormspec(); } bool GameFormSpec::handleEmptyFormspec(const std::string &formspec, const std::string &formname) diff --git a/src/client/game_formspec.h b/src/client/game_formspec.h index 6dff32e50..980dac47f 100644 --- a/src/client/game_formspec.h +++ b/src/client/game_formspec.h @@ -26,7 +26,7 @@ struct GameFormSpec { void init(Client *client, RenderingEngine *rendering_engine, InputHandler *input); - ~GameFormSpec(); + ~GameFormSpec() { reset(); } void showFormSpec(const std::string &formspec, const std::string &formname); void showCSMFormSpec(const std::string &formspec, const std::string &formname); @@ -43,6 +43,7 @@ struct GameFormSpec void disableDebugView(); bool handleCallbacks(); + void reset(); #ifdef __ANDROID__ // Returns false if no formspec open diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index a2c0ae327..45b665bba 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -3,15 +3,16 @@ // Copyright (C) 2010-2013 celeron55, Perttu Ahola #include "mesh.h" +#include "IMeshBuffer.h" +#include "SSkinMeshBuffer.h" #include "debug.h" #include "log.h" #include #include #include -#include #include #include "S3DVertex.h" -#include "SMesh.h" +#include #include "SMeshBuffer.h" inline static void applyShadeFactor(video::SColor& color, float factor) @@ -95,11 +96,23 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale) mesh->addMeshBuffer(buf); buf->drop(); } + scaleMesh(mesh, scale); // also recalculates bounding box + return mesh; +} - scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh); - mesh->drop(); - scaleMesh(anim_mesh, scale); // also recalculates bounding box - return anim_mesh; +template +inline static void transformMeshBuffer(scene::IMeshBuffer *buf, + const F &transform_vertex) +{ + const u32 stride = getVertexPitchFromType(buf->getVertexType()); + u32 vertex_count = buf->getVertexCount(); + u8 *vertices = (u8 *)buf->getVertices(); + for (u32 i = 0; i < vertex_count; i++) { + auto *vertex = (video::S3DVertex *)(vertices + i * stride); + transform_vertex(vertex); + } + buf->setDirty(scene::EBT_VERTEX); + buf->recalculateBoundingBox(); } void scaleMesh(scene::IMesh *mesh, v3f scale) @@ -112,14 +125,9 @@ void scaleMesh(scene::IMesh *mesh, v3f scale) u32 mc = mesh->getMeshBufferCount(); for (u32 j = 0; j < mc; j++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) - ((video::S3DVertex *)(vertices + i * stride))->Pos *= scale; - - buf->setDirty(scene::EBT_VERTEX); - buf->recalculateBoundingBox(); + transformMeshBuffer(buf, [scale](video::S3DVertex *vertex) { + vertex->Pos *= scale; + }); // calculate total bounding box if (j == 0) @@ -140,14 +148,9 @@ void translateMesh(scene::IMesh *mesh, v3f vec) u32 mc = mesh->getMeshBufferCount(); for (u32 j = 0; j < mc; j++) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - u32 vertex_count = buf->getVertexCount(); - u8 *vertices = (u8 *)buf->getVertices(); - for (u32 i = 0; i < vertex_count; i++) - ((video::S3DVertex *)(vertices + i * stride))->Pos += vec; - - buf->setDirty(scene::EBT_VERTEX); - buf->recalculateBoundingBox(); + transformMeshBuffer(buf, [vec](video::S3DVertex *vertex) { + vertex->Pos += vec; + }); // calculate total bounding box if (j == 0) @@ -330,44 +333,40 @@ bool checkMeshNormals(scene::IMesh *mesh) return true; } +template +static scene::IMeshBuffer *cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer) +{ + auto *v = static_cast(mesh_buffer->getVertices()); + u16 *indices = mesh_buffer->getIndices(); + auto *cloned_buffer = new SMeshBufferType(); + cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices, + mesh_buffer->getIndexCount()); + // Rigidly animated meshes may have transformation matrices that need to be applied + if (auto *sbuf = dynamic_cast(mesh_buffer)) { + transformMeshBuffer(cloned_buffer, [sbuf](video::S3DVertex *vertex) { + sbuf->Transformation.transformVect(vertex->Pos); + vertex->Normal = sbuf->Transformation.rotateAndScaleVect(vertex->Normal); + vertex->Normal.normalize(); + }); + } + return cloned_buffer; +} + scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer) { switch (mesh_buffer->getVertexType()) { - case video::EVT_STANDARD: { - video::S3DVertex *v = (video::S3DVertex *) mesh_buffer->getVertices(); - u16 *indices = mesh_buffer->getIndices(); - scene::SMeshBuffer *cloned_buffer = new scene::SMeshBuffer(); - cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices, - mesh_buffer->getIndexCount()); - return cloned_buffer; + case video::EVT_STANDARD: + return cloneMeshBuffer(mesh_buffer); + case video::EVT_2TCOORDS: + return cloneMeshBuffer(mesh_buffer); + case video::EVT_TANGENTS: + return cloneMeshBuffer(mesh_buffer); } - case video::EVT_2TCOORDS: { - video::S3DVertex2TCoords *v = - (video::S3DVertex2TCoords *) mesh_buffer->getVertices(); - u16 *indices = mesh_buffer->getIndices(); - scene::SMeshBufferLightMap *cloned_buffer = - new scene::SMeshBufferLightMap(); - cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices, - mesh_buffer->getIndexCount()); - return cloned_buffer; - } - case video::EVT_TANGENTS: { - video::S3DVertexTangents *v = - (video::S3DVertexTangents *) mesh_buffer->getVertices(); - u16 *indices = mesh_buffer->getIndices(); - scene::SMeshBufferTangents *cloned_buffer = - new scene::SMeshBufferTangents(); - cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices, - mesh_buffer->getIndexCount()); - return cloned_buffer; - } - } - // This should not happen. sanity_check(false); return NULL; } -scene::SMesh* cloneMesh(scene::IMesh *src_mesh) +scene::SMesh* cloneStaticMesh(scene::IMesh *src_mesh) { scene::SMesh* dst_mesh = new scene::SMesh(); for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) { diff --git a/src/client/mesh.h b/src/client/mesh.h index d8eb6080e..53c54fc51 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -93,10 +93,8 @@ void rotateMeshYZby (scene::IMesh *mesh, f64 degrees); */ scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer); -/* - Clone the mesh. -*/ -scene::SMesh* cloneMesh(scene::IMesh *src_mesh); +/// Clone a mesh. For an animated mesh, this will clone the static pose. +scene::SMesh* cloneStaticMesh(scene::IMesh *src_mesh); /* Convert nodeboxes to mesh. Each tile goes into a different buffer. diff --git a/src/client/meshgen/collector.cpp b/src/client/meshgen/collector.cpp index c8b726cde..6e3002624 100644 --- a/src/client/meshgen/collector.cpp +++ b/src/client/meshgen/collector.cpp @@ -14,25 +14,19 @@ void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertice const TileLayer *layer = &tile.layers[layernum]; if (layer->empty()) continue; - append(*layer, vertices, numVertices, indices, numIndices, layernum, - tile.world_aligned); + append(*layer, vertices, numVertices, indices, numIndices, layernum); } } void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *vertices, - u32 numVertices, const u16 *indices, u32 numIndices, u8 layernum, - bool use_scale) + u32 numVertices, const u16 *indices, u32 numIndices, u8 layernum) { PreMeshBuffer &p = findBuffer(layer, layernum, numVertices); - f32 scale = 1.0f; - if (use_scale) - scale = 1.0f / layer.scale; - u32 vertex_count = p.vertices.size(); for (u32 i = 0; i < numVertices; i++) { p.vertices.emplace_back(vertices[i].Pos + offset, vertices[i].Normal, - vertices[i].Color, scale * vertices[i].TCoords); + vertices[i].Color, vertices[i].TCoords); m_bounding_radius_sq = std::max(m_bounding_radius_sq, (vertices[i].Pos - m_center_pos).getLengthSQ()); } diff --git a/src/client/meshgen/collector.h b/src/client/meshgen/collector.h index f1f8a1481..876338baa 100644 --- a/src/client/meshgen/collector.h +++ b/src/client/meshgen/collector.h @@ -55,7 +55,7 @@ private: void append(const TileLayer &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, - u8 layernum, bool use_scale = false); + u8 layernum); PreMeshBuffer &findBuffer(const TileLayer &layer, u8 layernum, u32 numVertices); }; diff --git a/src/client/shadows/dynamicshadowsrender.cpp b/src/client/shadows/dynamicshadowsrender.cpp index 17260e21d..dbabf6dd7 100644 --- a/src/client/shadows/dynamicshadowsrender.cpp +++ b/src/client/shadows/dynamicshadowsrender.cpp @@ -410,7 +410,7 @@ video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name shadow_map_name.c_str(), texture_format); } - return m_driver->getTexture(shadow_map_name.c_str()); + return m_driver->findTexture(shadow_map_name.c_str()); } void ShadowRenderer::renderShadowMap(video::ITexture *target, diff --git a/src/client/tile.h b/src/client/tile.h index ffbe78bac..3293b4dd1 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -191,8 +191,6 @@ struct TileSpec bool world_aligned = false; //! Tile rotation. TileRotation rotation = TileRotation::None; - //! This much light does the tile emit. - u8 emissive_light = 0; //! The first is base texture, the second is overlay. TileLayer layers[MAX_TILE_LAYERS]; }; diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index baa72f1d9..bdd24a727 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -255,7 +255,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, dim = core::dimension2d(dim.Width, frame_height); } scene::IMesh *original = g_extrusion_mesh_cache->create(dim); - scene::SMesh *mesh = cloneMesh(original); + scene::SMesh *mesh = cloneStaticMesh(original); original->drop(); //set texture mesh->getMeshBuffer(0)->getMaterial().setTexture(0, @@ -639,7 +639,7 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, // get mesh core::dimension2d dim = texture->getSize(); scene::IMesh *original = g_extrusion_mesh_cache->create(dim); - scene::SMesh *mesh = cloneMesh(original); + scene::SMesh *mesh = cloneStaticMesh(original); original->drop(); //set texture diff --git a/src/dummymap.h b/src/dummymap.h index 2da371884..408f91341 100644 --- a/src/dummymap.h +++ b/src/dummymap.h @@ -27,12 +27,13 @@ public: void fill(v3s16 bpmin, v3s16 bpmax, MapNode n) { for (s16 z = bpmin.Z; z <= bpmax.Z; z++) - for (s16 y = bpmin.Y; y <= bpmax.Y; y++) - for (s16 x = bpmin.X; x <= bpmax.X; x++) { + for (s16 x = bpmin.X; x <= bpmax.X; x++) + for (s16 y = bpmin.Y; y <= bpmax.Y; y++) { MapBlock *block = getBlockNoCreateNoEx({x, y, z}); if (block) { + auto *data = block->getData(); for (size_t i = 0; i < MapBlock::nodecount; i++) - block->getData()[i] = n; + data[i] = n; block->expireIsAirCache(); } } diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index 9592ba922..9975549fe 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -718,19 +718,24 @@ void GUIButton::setFromStyle(const StyleSpec& style) setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); setOverrideFont(style.getFont()); + BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle); + if (style.isNotDefault(StyleSpec::BGIMG)) { video::ITexture *texture = style.getTexture(StyleSpec::BGIMG, getTextureSource()); - setImage(guiScalingImageButton( - Environment->getVideoDriver(), texture, - AbsoluteRect.getWidth(), AbsoluteRect.getHeight())); + if (BgMiddle.getArea() == 0) { + setImage(guiScalingImageButton( + Environment->getVideoDriver(), texture, + AbsoluteRect.getWidth(), AbsoluteRect.getHeight())); + } else { + // Scaling happens in `draw2DImage9Slice` + setImage(texture); + } setScaleImage(true); } else { setImage(nullptr); } - BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle); - // Child padding and offset Padding = style.getRect(StyleSpec::PADDING, core::rect()); Padding = core::rect( diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 8cc9954fc..e843b5b38 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -33,6 +33,8 @@ #include "client/sound/sound_openal.h" #endif +#include + /******************************************************************************/ void TextDestGuiEngine::gotText(const StringMap &fields) @@ -40,12 +42,6 @@ void TextDestGuiEngine::gotText(const StringMap &fields) m_engine->getScriptIface()->handleMainMenuButtons(fields); } -/******************************************************************************/ -void TextDestGuiEngine::gotText(const std::wstring &text) -{ - m_engine->getScriptIface()->handleMainMenuEvent(wide_to_utf8(text)); -} - /******************************************************************************/ MenuTextureSource::~MenuTextureSource() { @@ -74,6 +70,7 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id) if (retval) return retval; + verbosestream << "MenuTextureSource: loading " << name << std::endl; video::IImage *image = m_driver->createImageFromFile(name.c_str()); if (!image) return NULL; @@ -109,7 +106,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, RenderingEngine *rendering_engine, IMenuManager *menumgr, MainMenuData *data, - bool &kill) : + volatile std::sig_atomic_t &kill) : m_rendering_engine(rendering_engine), m_parent(parent), m_menumanager(menumgr), @@ -404,12 +401,6 @@ GUIEngine::~GUIEngine() m_sound_manager.reset(); m_irr_toplefttext->remove(); - - // delete textures - for (image_definition &texture : m_textures) { - if (texture.texture) - m_rendering_engine->get_video_driver()->removeTexture(texture.texture); - } } /******************************************************************************/ @@ -597,26 +588,16 @@ void GUIEngine::drawFooter(video::IVideoDriver *driver) bool GUIEngine::setTexture(texture_layer layer, const std::string &texturepath, bool tile_image, unsigned int minsize) { - video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); + m_textures[layer].texture = nullptr; - if (m_textures[layer].texture) { - driver->removeTexture(m_textures[layer].texture); - m_textures[layer].texture = NULL; - } - - if (texturepath.empty() || !fs::PathExists(texturepath)) { + if (texturepath.empty() || !fs::PathExists(texturepath)) return false; - } - m_textures[layer].texture = driver->getTexture(texturepath.c_str()); + m_textures[layer].texture = m_texture_source->getTexture(texturepath); m_textures[layer].tile = tile_image; m_textures[layer].minsize = minsize; - if (!m_textures[layer].texture) { - return false; - } - - return true; + return m_textures[layer].texture != nullptr; } /******************************************************************************/ diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h index 5c95e9a4c..4fde215fb 100644 --- a/src/gui/guiEngine.h +++ b/src/gui/guiEngine.h @@ -14,6 +14,8 @@ #include "util/enriched_string.h" #include "translation.h" +#include + /******************************************************************************/ /* Structs and macros */ /******************************************************************************/ @@ -61,12 +63,6 @@ public: */ void gotText(const StringMap &fields); - /** - * receive text/events transmitted by guiFormSpecMenu - * @param text textual representation of event - */ - void gotText(const std::wstring &text); - private: /** target to transmit data to */ GUIEngine *m_engine = nullptr; @@ -130,7 +126,7 @@ public: RenderingEngine *rendering_engine, IMenuManager *menumgr, MainMenuData *data, - bool &kill); + volatile std::sig_atomic_t &kill); /** default destructor */ virtual ~GUIEngine(); @@ -199,7 +195,7 @@ private: irr_ptr m_menu; /** reference to kill variable managed by SIGINT handler */ - bool &m_kill; + volatile std::sig_atomic_t &m_kill; /** variable used to abort menu and return back to main game handling */ bool m_startgame = false; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index cee066131..97d86b05f 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -77,6 +77,9 @@ return; \ } +// Element ID of the "Proceed" button shown for sizeless formspecs +constexpr s32 ID_PROCEED_BTN = 257; + /* GUIFormSpecMenu */ @@ -1235,10 +1238,14 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item)))); } - //now really show table GUITable *e = new GUITable(Environment, data->current_parent, spec.fid, rect, m_tsrc); + // Apply styling before calculating the cell sizes + auto style = getDefaultStyleForElement("table", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideFont(style.getFont()); + if (spec.fname == m_focused_element) { Environment->setFocus(e); } @@ -1252,10 +1259,6 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); - auto style = getDefaultStyleForElement("table", name); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); - e->setOverrideFont(style.getFont()); - m_tables.emplace_back(spec, e); m_fields.push_back(spec); } @@ -2998,7 +3001,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) gui::IGUIElement *focused_element = Environment->getFocus(); if (focused_element && focused_element->getParent() == this) { s32 focused_id = focused_element->getID(); - if (focused_id > 257) { + if (focused_id > ID_PROCEED_BTN) { for (const GUIFormSpecMenu::FieldSpec &field : m_fields) { if (field.fid == focused_id) { m_focused_element = field.fname; @@ -3308,7 +3311,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) size.X / 2 - 70, pos.Y, size.X / 2 - 70 + 140, pos.Y + m_btn_height * 2 ); - GUIButton::addButton(Environment, mydata.rect, m_tsrc, this, 257, + GUIButton::addButton(Environment, mydata.rect, m_tsrc, this, ID_PROCEED_BTN, wstrgettext("Proceed").c_str()); } } @@ -4031,12 +4034,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) if (m_joystick->wasKeyDown(KeyType::ESC)) { tryClose(); } else if (m_joystick->wasKeyDown(KeyType::JUMP)) { - if (m_allowclose) { - acceptInput(quit_mode_accept); - quitMenu(); - } else { - acceptInput(quit_mode_try); - } + trySubmitClose(); } } return handled; @@ -4053,7 +4051,16 @@ void GUIFormSpecMenu::tryClose() quitMenu(); } else { acceptInput(quit_mode_try); - m_text_dst->gotText(L"MenuQuit"); + } +} + +void GUIFormSpecMenu::trySubmitClose() +{ + if (m_allowclose) { + acceptInput(quit_mode_accept); + quitMenu(); + } else { + acceptInput(quit_mode_try); } } @@ -4100,12 +4107,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) break; } if (current_keys_pending.key_enter) { - if (m_allowclose) { - acceptInput(quit_mode_accept); - quitMenu(); - } else { - acceptInput(quit_mode_try); - } + trySubmitClose(); } else { acceptInput(); } @@ -4819,12 +4821,18 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } if (event.EventType == EET_GUI_EVENT) { - if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED - && isVisible()) { - // find the element that was clicked + const s32 caller_id = event.GUIEvent.Caller->getID(); + bool close_on_enter; + + switch (event.GUIEvent.EventType) { + case gui::EGET_TAB_CHANGED: + if (!isVisible()) + break; + + // find the element that was clicked for (GUIFormSpecMenu::FieldSpec &s : m_fields) { - if ((s.ftype == f_TabHeader) && - (s.fid == event.GUIEvent.Caller->getID())) { + if (s.ftype == f_TabHeader && + s.fid == caller_id) { if (!s.sound.empty() && m_sound_manager) m_sound_manager->playSound(0, SoundSpec(s.sound, 1.0f)); s.send = true; @@ -4833,26 +4841,26 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) return true; } } - } - if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST - && isVisible()) { + break; + + case gui::EGET_ELEMENT_FOCUS_LOST: + if (!isVisible()) + break; + if (!canTakeFocus(event.GUIEvent.Element)) { infostream<<"GUIFormSpecMenu: Not allowing focus change." <getID(); + break; - if (caller_id == 257) { - acceptInput(quit_mode_accept); - m_text_dst->gotText(L"ExitButton"); - quitMenu(); + case gui::EGET_BUTTON_CLICKED: + case gui::EGET_CHECKBOX_CHANGED: + case gui::EGET_COMBO_BOX_CHANGED: + case gui::EGET_SCROLL_BAR_CHANGED: + if (caller_id == ID_PROCEED_BTN) { + trySubmitClose(); return true; } @@ -4882,7 +4890,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if (s.is_exit) { acceptInput(quit_mode_accept); - m_text_dst->gotText(L"ExitButton"); quitMenu(); return true; } @@ -4923,60 +4930,57 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) s.send = false; } } - } - if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) { - // move scroll_containers - for (const std::pair &c : m_scroll_containers) - c.second->onScrollEvent(event.GUIEvent.Caller); - } + if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) { + // move scroll_containers + for (const std::pair &c : m_scroll_containers) + c.second->onScrollEvent(event.GUIEvent.Caller); + } + break; - if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) { - if (event.GUIEvent.Caller->getID() > 257) { - bool close_on_enter = true; - for (GUIFormSpecMenu::FieldSpec &s : m_fields) { - if (s.ftype == f_Unknown && - s.fid == event.GUIEvent.Caller->getID()) { - current_field_enter_pending = s.fname; - auto it = field_close_on_enter.find(s.fname); - if (it != field_close_on_enter.end()) - close_on_enter = (*it).second; + case gui::EGET_EDITBOX_ENTER: + if (caller_id <= ID_PROCEED_BTN) + break; - break; - } + close_on_enter = true; + for (GUIFormSpecMenu::FieldSpec &s : m_fields) { + if (s.ftype == f_Unknown && + s.fid == caller_id) { + current_field_enter_pending = s.fname; + auto it = field_close_on_enter.find(s.fname); + if (it != field_close_on_enter.end()) + close_on_enter = (*it).second; + + break; } + } - current_keys_pending.key_enter = true; + current_keys_pending.key_enter = true; - if (close_on_enter) { - if (m_allowclose) { - acceptInput(quit_mode_accept); - quitMenu(); - } else { - acceptInput(quit_mode_try); - } - } else { + if (close_on_enter) + trySubmitClose(); + else + acceptInput(); + return true; + + case gui::EGET_TABLE_CHANGED: + if (caller_id <= ID_PROCEED_BTN) + break; + + // find the element that was clicked + for (GUIFormSpecMenu::FieldSpec &s : m_fields) { + // if it's a table, set the send field + // so lua knows which table was changed + if (s.ftype == f_Table && s.fid == caller_id) { + s.send = true; acceptInput(); + s.send = false; } - return true; } - } + return true; - if (event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) { - int current_id = event.GUIEvent.Caller->getID(); - if (current_id > 257) { - // find the element that was clicked - for (GUIFormSpecMenu::FieldSpec &s : m_fields) { - // if it's a table, set the send field - // so lua knows which table was changed - if ((s.ftype == f_Table) && (s.fid == current_id)) { - s.send = true; - acceptInput(); - s.send=false; - } - } - return true; - } + default: + break; } } diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 8dbfcab93..f279bbb29 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -68,8 +68,6 @@ struct TextDest { virtual ~TextDest() = default; - // This is deprecated I guess? -celeron55 - virtual void gotText(const std::wstring &text) {} virtual void gotText(const StringMap &fields) = 0; std::string m_formname; @@ -493,6 +491,7 @@ private: bool parseMiddleRect(const std::string &value, core::rect *parsed_rect); void tryClose(); + void trySubmitClose(); void showTooltip(const std::wstring &text, const irr::video::SColor &color, const irr::video::SColor &bgcolor); diff --git a/src/gui/guiTable.h b/src/gui/guiTable.h index 7dbf17a51..2ade0ee3e 100644 --- a/src/gui/guiTable.h +++ b/src/gui/guiTable.h @@ -85,7 +85,7 @@ public: void setTextList(const std::vector &content, bool transparent); - /* Set generic table options, columns and content */ + /** Set generic table options, columns and content, calculate cell sizes */ // Adds empty strings to end of content if there is an incomplete row void setTable(const TableOptions &options, const TableColumns &columns, diff --git a/src/gui/mainmenumanager.h b/src/gui/mainmenumanager.h index 3bc8f7daa..553d6ffce 100644 --- a/src/gui/mainmenumanager.h +++ b/src/gui/mainmenumanager.h @@ -59,6 +59,8 @@ public: if(!m_stack.empty()) { m_stack.back()->setVisible(true); guienv->setFocus(m_stack.back()); + } else { + guienv->removeFocus(menu); } } diff --git a/src/main.cpp b/src/main.cpp index 95a2f7be4..51069cf19 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1138,7 +1138,7 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & return false; } ChatInterface iface; - bool &kill = *porting::signal_handler_killstatus(); + volatile auto &kill = *porting::signal_handler_killstatus(); try { // Create server @@ -1181,7 +1181,7 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings & server.start(); // Run server - bool &kill = *porting::signal_handler_killstatus(); + volatile auto &kill = *porting::signal_handler_killstatus(); dedicated_server_loop(server, kill); } catch (const ModError &e) { @@ -1226,7 +1226,7 @@ static bool migrate_map_database(const GameParams &game_params, const Settings & u32 count = 0; u64 last_update_time = 0; - bool &kill = *porting::signal_handler_killstatus(); + volatile auto &kill = *porting::signal_handler_killstatus(); std::vector blocks; old_db->listAllLoadableBlocks(blocks); @@ -1280,7 +1280,7 @@ static bool recompress_map_database(const GameParams &game_params, const Setting u32 count = 0; u64 last_update_time = 0; - bool &kill = *porting::signal_handler_killstatus(); + volatile auto &kill = *porting::signal_handler_killstatus(); const u8 serialize_as_ver = SER_FMT_VER_HIGHEST_WRITE; const s16 map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9); diff --git a/src/map.cpp b/src/map.cpp index d88200846..964d8b5af 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -772,52 +772,79 @@ void MMVManip::initialEmerge(v3s16 p_min, v3s16 p_max, bool load_if_inexistent) infostream< had_blocks; + // we can skip this calculation if the areas are disjoint + if (!m_area.intersect(block_area_nodes).hasEmptyExtent()) + had_blocks = getCoveredBlocks(); + + const bool all_new = m_area.hasEmptyExtent(); addArea(block_area_nodes); for(s32 z=p_min.Z; z<=p_max.Z; z++) for(s32 y=p_min.Y; y<=p_max.Y; y++) for(s32 x=p_min.X; x<=p_max.X; x++) { - u8 flags = 0; - MapBlock *block; v3s16 p(x,y,z); - if (m_loaded_blocks.count(p) > 0) + // if this block was already in the vmanip and it has data, skip + if (auto it = had_blocks.find(p); it != had_blocks.end() && it->second) continue; - bool block_data_inexistent = false; - { - TimeTaker timer2("emerge load", &emerge_load_time); - - block = m_map->getBlockNoCreateNoEx(p); - if (!block) - block_data_inexistent = true; - else - block->copyTo(*this); - } - - if(block_data_inexistent) - { - + MapBlock *block = m_map->getBlockNoCreateNoEx(p); + if (block) { + block->copyTo(*this); + } else { if (load_if_inexistent && !blockpos_over_max_limit(p)) { block = m_map->emergeBlock(p, true); + assert(block); block->copyTo(*this); } else { - flags |= VMANIP_BLOCK_DATA_INEXIST; - // Mark area inexistent VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1)); setFlags(a, VOXELFLAG_NO_DATA); } } - - m_loaded_blocks[p] = flags; } if (all_new) m_is_dirty = false; } +std::map MMVManip::getCoveredBlocks() const +{ + std::map ret; + if (m_area.hasEmptyExtent()) + return ret; + + // Figure out if *any* node in this block has data according to m_flags + const auto &check_block = [this] (v3s16 bp) -> bool { + v3s16 pmin = bp * MAP_BLOCKSIZE; + v3s16 pmax = pmin + v3s16(MAP_BLOCKSIZE-1); + for(s16 z=pmin.Z; z<=pmax.Z; z++) + for(s16 y=pmin.Y; y<=pmax.Y; y++) + for(s16 x=pmin.X; x<=pmax.X; x++) { + if (!(m_flags[m_area.index(x,y,z)] & VOXELFLAG_NO_DATA)) + return true; + } + return false; + }; + + v3s16 bpmin = getNodeBlockPos(m_area.MinEdge); + v3s16 bpmax = getNodeBlockPos(m_area.MaxEdge); + + if (bpmin * MAP_BLOCKSIZE != m_area.MinEdge) + throw BaseException("MMVManip not block-aligned"); + if ((bpmax+1) * MAP_BLOCKSIZE - v3s16(1) != m_area.MaxEdge) + throw BaseException("MMVManip not block-aligned"); + + for(s16 z=bpmin.Z; z<=bpmax.Z; z++) + for(s16 y=bpmin.Y; y<=bpmax.Y; y++) + for(s16 x=bpmin.X; x<=bpmax.X; x++) { + v3s16 bp(x,y,z); + ret[bp] = check_block(bp); + } + return ret; +} + void MMVManip::blitBackAll(std::map *modified_blocks, bool overwrite_generated) const { @@ -825,16 +852,27 @@ void MMVManip::blitBackAll(std::map *modified_blocks, return; assert(m_map); - /* - Copy data of all blocks - */ - assert(!m_loaded_blocks.empty()); - for (auto &loaded_block : m_loaded_blocks) { - v3s16 p = loaded_block.first; + size_t nload = 0; + + // Copy all the blocks with data back to the map + const auto loaded_blocks = getCoveredBlocks(); + for (auto &it : loaded_blocks) { + if (!it.second) + continue; + v3s16 p = it.first; MapBlock *block = m_map->getBlockNoCreateNoEx(p); - bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST); - if (!existed || (block == NULL) || - (!overwrite_generated && block->isGenerated())) + if (!block) { + if (!blockpos_over_max_limit(p)) { + block = m_map->emergeBlock(p, true); + nload++; + } + } + if (!block) { + warningstream << "blitBackAll: Couldn't load block " << p + << " to write data to map" << std::endl; + continue; + } + if (!overwrite_generated && block->isGenerated()) continue; block->copyFrom(*this); @@ -844,6 +882,10 @@ void MMVManip::blitBackAll(std::map *modified_blocks, if(modified_blocks) (*modified_blocks)[p] = block; } + + if (nload > 0) { + verbosestream << "blitBackAll: " << nload << " blocks had to be loaded for writing" << std::endl; + } } MMVManip *MMVManip::clone() const @@ -860,11 +902,7 @@ MMVManip *MMVManip::clone() const ret->m_flags = new u8[size]; memcpy(ret->m_flags, m_flags, size * sizeof(u8)); } - ret->m_is_dirty = m_is_dirty; - // Even if the copy is disconnected from a map object keep the information - // needed to write it back to one - ret->m_loaded_blocks = m_loaded_blocks; return ret; } diff --git a/src/map.h b/src/map.h index 9ec66cfb9..57ede8ee6 100644 --- a/src/map.h +++ b/src/map.h @@ -307,16 +307,29 @@ public: MMVManip(Map *map); virtual ~MMVManip() = default; - virtual void clear() - { - VoxelManipulator::clear(); - m_loaded_blocks.clear(); - } - + /* + Loads specified area from map and *adds* it to the area already + contained in the VManip. + */ void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, bool load_if_inexistent = true); - // This is much faster with big chunks of generated data + /** + Uses the flags array to determine which blocks the VManip covers, + and for which of them we have any data. + @warning requires VManip area to be block-aligned + @return map of blockpos -> any data? + */ + std::map getCoveredBlocks() const; + + /** + Writes data in VManip back to the map. Blocks without any data in the VManip + are skipped. + @note VOXELFLAG_NO_DATA is checked per-block, not per-node. So you need + to ensure that the relevant parts of m_data are initialized. + @param modified_blocks output array of touched blocks (optional) + @param overwrite_generated if false, blocks marked as generate in the map are not changed + */ void blitBackAll(std::map * modified_blocks, bool overwrite_generated = true) const; @@ -339,13 +352,4 @@ protected: // may be null Map *m_map = nullptr; - /* - key = blockpos - value = flags describing the block - */ - std::map m_loaded_blocks; - - enum : u8 { - VMANIP_BLOCK_DATA_INEXIST = 1 << 0, - }; }; diff --git a/src/network/mtp/impl.cpp b/src/network/mtp/impl.cpp index 07bdfc735..d54c116a2 100644 --- a/src/network/mtp/impl.cpp +++ b/src/network/mtp/impl.cpp @@ -1314,11 +1314,6 @@ Connection::~Connection() 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(); diff --git a/src/network/mtp/impl.h b/src/network/mtp/impl.h index 3f81fe27b..b8059f202 100644 --- a/src/network/mtp/impl.h +++ b/src/network/mtp/impl.h @@ -12,6 +12,7 @@ #include "util/numeric.h" #include "porting.h" #include "network/networkprotocol.h" +#include #include #include #include @@ -301,7 +302,7 @@ private: // Backwards compatibility PeerHandler *m_bc_peerhandler; - bool m_shutting_down = false; + std::atomic m_shutting_down = false; }; } // namespace diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index d9a38e878..e9012d351 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -40,10 +40,6 @@ void Server::handleCommand_Deprecated(NetworkPacket* pkt) void Server::handleCommand_Init(NetworkPacket* pkt) { - - if(pkt->getSize() < 1) - return; - session_t peer_id = pkt->getPeerId(); RemoteClient *client = getClient(peer_id, CS_Created); @@ -75,15 +71,6 @@ void Server::handleCommand_Init(NetworkPacket* pkt) verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id=" << peer_id << ")" << std::endl; - // Do not allow multiple players in simple singleplayer mode. - // This isn't a perfect way to do it, but will suffice for now - if (m_simple_singleplayer_mode && !m_clients.getClientIDs().empty()) { - infostream << "Server: Not allowing another client (" << addr_s << - ") to connect in simple singleplayer mode" << std::endl; - DenyAccess(peer_id, SERVER_ACCESSDENIED_SINGLEPLAYER); - return; - } - if (denyIfBanned(peer_id)) return; @@ -161,18 +148,14 @@ void Server::handleCommand_Init(NetworkPacket* pkt) return; } - RemotePlayer *player = m_env->getPlayer(playername, true); - - // If player is already connected, cancel - if (player && player->getPeerId() != PEER_ID_INEXISTENT) { - actionstream << "Server: Player with name \"" << playername << - "\" tried to connect, but player with same name is already connected" << std::endl; - DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED); + // Do not allow multiple players in simple singleplayer mode + if (isSingleplayer() && !m_clients.getClientIDs(CS_HelloSent).empty()) { + infostream << "Server: Not allowing another client (" << addr_s << + ") to connect in simple singleplayer mode" << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_SINGLEPLAYER); return; } - - m_clients.setPlayerName(peer_id, playername); - + // Or the "singleplayer" name to be used on regular servers if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) { actionstream << "Server: Player with the name \"singleplayer\" tried " "to connect from " << addr_s << std::endl; @@ -180,12 +163,25 @@ void Server::handleCommand_Init(NetworkPacket* pkt) return; } + { + RemotePlayer *player = m_env->getPlayer(playername, true); + // If player is already connected, cancel + if (player && player->getPeerId() != PEER_ID_INEXISTENT) { + actionstream << "Server: Player with name \"" << playername << + "\" tried to connect, but player with same name is already connected" << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED); + return; + } + } + + client->setName(playerName); + { std::string reason; if (m_script->on_prejoinplayer(playername, addr_s, &reason)) { actionstream << "Server: Player with the name \"" << playerName << "\" tried to connect from " << addr_s << - " but it was disallowed for the following reason: " << reason << + " but was disallowed for the following reason: " << reason << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING, reason); return; @@ -195,14 +191,11 @@ void Server::handleCommand_Init(NetworkPacket* pkt) infostream << "Server: New connection: \"" << playerName << "\" from " << addr_s << " (peer_id=" << peer_id << ")" << std::endl; - // Enforce user limit. - // Don't enforce for users that have some admin right or mod permits it. - if (m_clients.isUserLimitReached() && - playername != g_settings->get("name") && - !m_script->can_bypass_userlimit(playername, addr_s)) { + // Early check for user limit, so the client doesn't need to run + // through the join process only to be denied. + if (checkUserLimit(playerName, addr_s)) { actionstream << "Server: " << playername << " tried to join from " << - addr_s << ", but there are already max_users=" << - g_settings->getU16("max_users") << " players." << std::endl; + addr_s << ", but the user limit was reached." << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_TOO_MANY_USERS); return; } @@ -302,6 +295,7 @@ void Server::handleCommand_Init2(NetworkPacket* pkt) sendMediaAnnouncement(peer_id, lang); RemoteClient *client = getClient(peer_id, CS_InitDone); + assert(client); // Keep client language for server translations client->setLangCode(lang); @@ -354,6 +348,8 @@ void Server::handleCommand_RequestMedia(NetworkPacket* pkt) void Server::handleCommand_ClientReady(NetworkPacket* pkt) { session_t peer_id = pkt->getPeerId(); + RemoteClient *client = getClient(peer_id, CS_Created); + assert(client); // decode all information first u8 major_ver, minor_ver, patch_ver, reserved; @@ -364,8 +360,17 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt) if (pkt->getRemainingBytes() >= 2) *pkt >> formspec_ver; - m_clients.setClientVersion(peer_id, major_ver, minor_ver, patch_ver, - full_ver); + client->setVersionInfo(major_ver, minor_ver, patch_ver, full_ver); + + // Since only active clients count for the user limit, two could race the + // join process so we have to do a final check for the user limit here. + std::string addr_s = client->getAddress().serializeString(); + if (checkUserLimit(client->getName(), addr_s)) { + actionstream << "Server: " << client->getName() << " tried to join from " << + addr_s << ", but the user limit was reached (late)." << std::endl; + DenyAccess(peer_id, SERVER_ACCESSDENIED_TOO_MANY_USERS); + return; + } // Emerge player PlayerSAO* playersao = StageTwoClientInit(peer_id); @@ -1426,7 +1431,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt) std::string salt, verification_key; - std::string addr_s = getPeerAddress(peer_id).serializeString(); + std::string addr_s = client->getAddress().serializeString(); u8 is_empty; *pkt >> salt >> verification_key >> is_empty; @@ -1512,9 +1517,11 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) RemoteClient *client = getClient(peer_id, CS_Invalid); ClientState cstate = client->getState(); + std::string addr_s = client->getAddress().serializeString(); + if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { actionstream << "Server: got SRP _A packet in wrong state " << cstate << - " from " << getPeerAddress(peer_id).serializeString() << + " from " << addr_s << ". Ignoring." << std::endl; return; } @@ -1524,7 +1531,7 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) if (client->chosen_mech != AUTH_MECHANISM_NONE) { actionstream << "Server: got SRP _A packet, while auth is already " "going on with mech " << client->chosen_mech << " from " << - getPeerAddress(peer_id).serializeString() << + addr_s << " (wantSudo=" << wantSudo << "). Ignoring." << std::endl; if (wantSudo) { DenySudoAccess(peer_id); @@ -1541,7 +1548,7 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) infostream << "Server: TOSERVER_SRP_BYTES_A received with " << "based_on=" << int(based_on) << " and len_A=" - << bytes_A.length() << "." << std::endl; + << bytes_A.length() << std::endl; AuthMechanism chosen = (based_on == 0) ? AUTH_MECHANISM_LEGACY_PASSWORD : AUTH_MECHANISM_SRP; @@ -1550,17 +1557,17 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) // Right now, the auth mechs don't change between login and sudo mode. if (!client->isMechAllowed(chosen)) { actionstream << "Server: Player \"" << client->getName() << - "\" at " << getPeerAddress(peer_id).serializeString() << + "\" from " << addr_s << " tried to change password using unallowed mech " << chosen << - "." << std::endl; + std::endl; DenySudoAccess(peer_id); return; } } else { if (!client->isMechAllowed(chosen)) { actionstream << "Server: Client tried to authenticate from " << - getPeerAddress(peer_id).serializeString() << - " using unallowed mech " << chosen << "." << std::endl; + addr_s << + " using unallowed mech " << chosen << std::endl; DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA); return; } diff --git a/src/nodedef.cpp b/src/nodedef.cpp index d4dc16a61..9d54c3957 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -13,6 +13,7 @@ #include "client/texturesource.h" #include "client/tile.h" #include +#include #include #endif #include "log.h" @@ -959,23 +960,37 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc palette = tsrc->getPalette(palette_name); if (drawtype == NDT_MESH && !mesh.empty()) { - // Read the mesh and apply scale - mesh_ptr = client->getMesh(mesh); - if (mesh_ptr) { - v3f scale = v3f(BS) * visual_scale; - scaleMesh(mesh_ptr, scale); + // Note: By freshly reading, we get an unencumbered mesh. + if (scene::IMesh *src_mesh = client->getMesh(mesh)) { + bool apply_bs = false; + if (auto *skinned_mesh = dynamic_cast(src_mesh)) { + // Compatibility: Animated meshes, as well as static gltf meshes, are not scaled by BS. + // See https://github.com/luanti-org/luanti/pull/16112#issuecomment-2881860329 + bool is_gltf = skinned_mesh->getSourceFormat() == + scene::SkinnedMesh::SourceFormat::GLTF; + apply_bs = skinned_mesh->isStatic() && !is_gltf; + // Nodes do not support mesh animation, so we clone the static pose. + // This simplifies working with the mesh: We can just scale the vertices + // as transformations have already been applied. + mesh_ptr = cloneStaticMesh(src_mesh); + src_mesh->drop(); + } else { + auto *static_mesh = dynamic_cast(src_mesh); + assert(static_mesh); + mesh_ptr = static_mesh; + // Compatibility: Apply BS scaling to static meshes (.obj). See #15811. + apply_bs = true; + } + scaleMesh(mesh_ptr, v3f((apply_bs ? BS : 1.0f) * visual_scale)); recalculateBoundingBox(mesh_ptr); if (!checkMeshNormals(mesh_ptr)) { + // TODO this should be done consistently when the mesh is loaded infostream << "ContentFeatures: recalculating normals for mesh " << mesh << std::endl; meshmanip->recalculateNormals(mesh_ptr, true, false); - } else { - // Animation is not supported, but we need to reset it to - // default state if it is animated. - // Note: recalculateNormals() also does this hence the else-block - if (mesh_ptr->getMeshType() == scene::EAMT_SKINNED) - ((scene::SkinnedMesh*) mesh_ptr)->resetAnimation(); } + } else { + mesh_ptr = nullptr; } } } diff --git a/src/nodedef.h b/src/nodedef.h index 71a61896b..967e3fcd4 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -342,7 +342,7 @@ struct ContentFeatures enum NodeDrawType drawtype; std::string mesh; #if CHECK_CLIENT_BUILD() - scene::IMesh *mesh_ptr; // mesh in case of mesh node + scene::SMesh *mesh_ptr; // mesh in case of mesh node video::SColor minimap_color; #endif float visual_scale; // Misc. scale parameter diff --git a/src/porting.cpp b/src/porting.cpp index 11ff63a88..711b65db6 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -60,6 +60,7 @@ #include "util/string.h" #include "util/tracy_wrapper.h" #include +#include #include #include #include @@ -81,31 +82,28 @@ namespace porting Signal handler (grabs Ctrl-C on POSIX systems) */ -static bool g_killed = false; +volatile static std::sig_atomic_t g_killed = false; -bool *signal_handler_killstatus() +volatile std::sig_atomic_t *signal_handler_killstatus() { return &g_killed; } #if !defined(_WIN32) // POSIX +#define STDERR_FILENO 2 static void signal_handler(int sig) { if (!g_killed) { if (sig == SIGINT) { - dstream << "INFO: signal_handler(): " - << "Ctrl-C pressed, shutting down." << std::endl; + const char *dbg_text{"INFO: signal_handler(): " + "Ctrl-C pressed, shutting down.\n"}; + write(STDERR_FILENO, dbg_text, strlen(dbg_text)); } else if (sig == SIGTERM) { - dstream << "INFO: signal_handler(): " - << "got SIGTERM, shutting down." << std::endl; + const char *dbg_text{"INFO: signal_handler(): " + "got SIGTERM, shutting down.\n"}; + write(STDERR_FILENO, dbg_text, strlen(dbg_text)); } - - // Comment out for less clutter when testing scripts - /*dstream << "INFO: sigint_handler(): " - << "Printing debug stacks" << std::endl; - debug_stacks_print();*/ - g_killed = true; } else { (void)signal(sig, SIG_DFL); diff --git a/src/porting.h b/src/porting.h index f7d623d33..1a4bb9e7b 100644 --- a/src/porting.h +++ b/src/porting.h @@ -13,6 +13,7 @@ #endif // Be mindful of what you include here! +#include #include #include "config.h" #include "irrlichttypes.h" // u64 @@ -77,7 +78,7 @@ namespace porting void signal_handler_init(); // Returns a pointer to a bool. // When the bool is true, program should quit. -[[nodiscard]] bool *signal_handler_killstatus(); +[[nodiscard]] volatile std::sig_atomic_t *signal_handler_killstatus(); /* Path of static data directory. diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index e4d94b3fc..91aa5b405 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -56,9 +56,16 @@ void read_item_definition(lua_State* L, int index, if (index < 0) index = lua_gettop(L) + 1 + index; - def.type = (ItemType)getenumfield(L, index, "type", - es_ItemType, ITEM_NONE); + def.name.clear(); getstringfield(L, index, "name", def.name); + + { + auto str = getstringfield_default(L, index, "type", ""); + if (!string_to_enum(es_ItemType, def.type, str)) + warningstream << "Item " << def.name + << " has unknown type \"" << str << '"' << std::endl; + } + getstringfield(L, index, "description", def.description); getstringfield(L, index, "short_description", def.short_description); getstringfield(L, index, "inventory_image", def.inventory_image); @@ -605,9 +612,6 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype, bool special) case NDT_PLANTLIKE: case NDT_FIRELIKE: default_tiling = false; - // "break" is omitted here intentionaly, as PLANTLIKE - // FIRELIKE drawtype both should default to having - // backface_culling to false. [[fallthrough]]; case NDT_MESH: case NDT_LIQUID: @@ -621,7 +625,6 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype, bool special) break; } - // key at index -2 and value at index if(lua_isstring(L, index)){ // "default_lava.png" tiledef.name = lua_tostring(L, index); @@ -634,7 +637,10 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype, bool special) // name="default_lava.png" tiledef.name.clear(); getstringfield(L, index, "name", tiledef.name); - getstringfield(L, index, "image", tiledef.name); // MaterialSpec compat. + warn_if_field_exists(L, index, "image", "TileDef", + "Deprecated: new name is \"name\"."); + getstringfield(L, index, "image", tiledef.name); + tiledef.backface_culling = getboolfield_default( L, index, "backface_culling", default_culling); tiledef.tileable_horizontal = getboolfield_default( @@ -659,6 +665,9 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype, bool special) lua_getfield(L, index, "animation"); tiledef.animation = read_animation_definition(L, -1); lua_pop(L, 1); + } else if (!lua_isnil(L, index)) { + // TODO: should be an error + errorstream << "TileDef: Invalid type! (expected string or table)" << std::endl; } return tiledef; @@ -672,13 +681,13 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) /* Cache existence of some callbacks */ lua_getfield(L, index, "on_construct"); - if(!lua_isnil(L, -1)) f.has_on_construct = true; + f.has_on_construct = !lua_isnil(L, -1); lua_pop(L, 1); lua_getfield(L, index, "on_destruct"); - if(!lua_isnil(L, -1)) f.has_on_destruct = true; + f.has_on_destruct = !lua_isnil(L, -1); lua_pop(L, 1); lua_getfield(L, index, "after_destruct"); - if(!lua_isnil(L, -1)) f.has_after_destruct = true; + f.has_after_destruct = !lua_isnil(L, -1); lua_pop(L, 1); lua_getfield(L, index, "on_rightclick"); @@ -695,8 +704,13 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) /* Visual definition */ - f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", - ScriptApiNode::es_DrawType,NDT_NORMAL); + { + auto str = getstringfield_default(L, index, "drawtype", ""); + if (!string_to_enum(ScriptApiNode::es_DrawType, f.drawtype, str)) + warningstream << "Node " << f.name + << " has unknown drawtype \"" << str << '"' << std::endl; + } + getfloatfield(L, index, "visual_scale", f.visual_scale); /* Meshnode model filename */ @@ -796,10 +810,7 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) if (lua_toboolean(L, -1)) f.alpha = (f.drawtype == NDT_NORMAL) ? ALPHAMODE_CLIP : ALPHAMODE_BLEND; } else if (check_field_or_nil(L, -1, LUA_TSTRING, "use_texture_alpha")) { - int result = f.alpha; - string_to_enum(ScriptApiNode::es_TextureAlphaMode, result, - std::string(lua_tostring(L, -1))); - f.alpha = static_cast(result); + string_to_enum(ScriptApiNode::es_TextureAlphaMode, f.alpha, lua_tostring(L, -1)); } lua_pop(L, 1); @@ -817,10 +828,18 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) getboolfield(L, index, "post_effect_color_shaded", f.post_effect_color_shaded); - f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", - ScriptApiNode::es_ContentParamType, CPT_NONE); - f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2", - ScriptApiNode::es_ContentParamType2, CPT2_NONE); + { + auto str = getstringfield_default(L, index, "paramtype", ""); + if (!string_to_enum(ScriptApiNode::es_ContentParamType, f.param_type, str)) + warningstream << "Node " << f.name + << " has unknown paramtype \"" << str << '"' << std::endl; + } + { + auto str = getstringfield_default(L, index, "paramtype2", ""); + if (!string_to_enum(ScriptApiNode::es_ContentParamType2, f.param_type_2, str)) + warningstream << "Node " << f.name + << " has unknown paramtype2 \"" << str << '"' << std::endl; + } if (!f.palette_name.empty() && !(f.param_type_2 == CPT2_COLOR || @@ -855,8 +874,12 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) // Liquids flow into and replace node getboolfield(L, index, "floodable", f.floodable); // Whether the node is non-liquid, source liquid or flowing liquid - f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", - ScriptApiNode::es_LiquidType, LIQUID_NONE); + { + auto str = getstringfield_default(L, index, "liquidtype", ""); + if (!string_to_enum(ScriptApiNode::es_LiquidType, f.liquid_type, str)) + warningstream << "Node " << f.name + << " has unknown liquidtype \"" << str << '"' << std::endl; + } // If the content is liquid, this is the flowing version of the liquid. getstringfield(L, index, "liquid_alternative_flowing", f.liquid_alternative_flowing); @@ -915,7 +938,7 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) lua_pushnil(L); while (lua_next(L, table) != 0) { // Value at -1 - std::string side(lua_tostring(L, -1)); + std::string_view side(lua_tostring(L, -1)); // Note faces are flipped to make checking easier if (side == "top") f.connect_sides |= 2; @@ -986,6 +1009,7 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index) } else if(lua_isnil(L, -1)) { f.liquid_move_physics = f.liquid_type != LIQUID_NONE; } else { + // TODO: should be an error errorstream << "Field \"liquid_move_physics\": Invalid type!" << std::endl; } lua_pop(L, 1); @@ -1805,10 +1829,8 @@ WearBarParams read_wear_bar_params( auto blend = WearBarParams::BLEND_MODE_CONSTANT; lua_getfield(L, stack_idx, "blend"); if (check_field_or_nil(L, -1, LUA_TSTRING, "blend")) { - int blendInt; - if (!string_to_enum(WearBarParams::es_BlendMode, blendInt, std::string(lua_tostring(L, -1)))) + if (!string_to_enum(WearBarParams::es_BlendMode, blend, lua_tostring(L, -1))) throw LuaError("Invalid wear bar color blend mode"); - blend = static_cast(blendInt); } lua_pop(L, 1); @@ -2395,14 +2417,9 @@ void push_hud_element(lua_State *L, HudElement *elem) bool read_hud_change(lua_State *L, HudElementStat &stat, HudElement *elem, void **value) { std::string statstr = lua_tostring(L, 3); - { - int statint; - if (!string_to_enum(es_HudElementStat, statint, statstr)) { - script_log_unique(L, "Unknown HUD stat type: " + statstr, warningstream); - return false; - } - - stat = static_cast(statint); + if (!string_to_enum(es_HudElementStat, stat, statstr)) { + script_log_unique(L, "Unknown HUD stat type: " + statstr, warningstream); + return false; } switch (stat) { diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 138063d54..2b711bfda 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -18,6 +18,8 @@ extern "C" { #include #include "common/c_types.h" +static v3d read_v3d(lua_State *L, int index); +static v3d check_v3d(lua_State *L, int index); #define CHECK_TYPE(index, name, type) do { \ int t = lua_type(L, (index)); \ @@ -28,6 +30,16 @@ extern "C" { } \ } while(0) +// TODO: this should be turned into an error in 2026. +// Just revert the commit that added this line. +#define CHECK_NOT_NIL(index, name) do { \ + if (lua_isnoneornil(L, (index))) { \ + auto msg = std::string("Invalid ") + (name) + \ + " (value is nil)."; \ + log_deprecated(L, msg, 1, true); \ + } \ + } while(0) + #define CHECK_FLOAT(value, name) do {\ if (std::isnan(value) || std::isinf(value)) { \ throw LuaError("Invalid float value for '" name \ @@ -35,7 +47,13 @@ extern "C" { } \ } while (0) +// strictly check type of coordinate +// (this won't permit string-to-int conversion, so maybe not the best idea?) #define CHECK_POS_COORD(index, name) CHECK_TYPE(index, "vector coordinate " name, LUA_TNUMBER) +// loosely check type of coordinate +#define CHECK_POS_COORD2(index, name) CHECK_NOT_NIL(index, "vector coordinate " name) + +// Note: not needed when using read_v3_aux #define CHECK_POS_TAB(index) CHECK_TYPE(index, "vector", LUA_TTABLE) @@ -44,6 +62,7 @@ extern "C" { */ static void read_v3_aux(lua_State *L, int index) { + // TODO: someone find out if it's faster to have the type check in Lua too CHECK_POS_TAB(index); lua_pushvalue(L, index); lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_READ_VECTOR); @@ -87,24 +106,12 @@ void push_v2f(lua_State *L, v2f p) v2s16 read_v2s16(lua_State *L, int index) { - v2s16 p; - CHECK_POS_TAB(index); - lua_getfield(L, index, "x"); - p.X = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - p.Y = lua_tonumber(L, -1); - lua_pop(L, 1); - return p; + return v2s16::from(read_v2f(L, index)); } void push_v2s16(lua_State *L, v2s16 p) { - lua_createtable(L, 0, 2); - lua_pushinteger(L, p.X); - lua_setfield(L, -2, "x"); - lua_pushinteger(L, p.Y); - lua_setfield(L, -2, "y"); + push_v2s32(L, v2s32::from(p)); } void push_v2s32(lua_State *L, v2s32 p) @@ -127,15 +134,7 @@ void push_v2u32(lua_State *L, v2u32 p) v2s32 read_v2s32(lua_State *L, int index) { - v2s32 p; - CHECK_POS_TAB(index); - lua_getfield(L, index, "x"); - p.X = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - p.Y = lua_tonumber(L, -1); - lua_pop(L, 1); - return p; + return v2s32::from(read_v2f(L, index)); } v2f read_v2f(lua_State *L, int index) @@ -143,9 +142,11 @@ v2f read_v2f(lua_State *L, int index) v2f p; CHECK_POS_TAB(index); lua_getfield(L, index, "x"); + CHECK_POS_COORD2(-1, "x"); p.X = lua_tonumber(L, -1); lua_pop(L, 1); lua_getfield(L, index, "y"); + CHECK_POS_COORD2(-1, "y"); p.Y = lua_tonumber(L, -1); lua_pop(L, 1); return p; @@ -170,30 +171,20 @@ v2f check_v2f(lua_State *L, int index) v3f read_v3f(lua_State *L, int index) { - read_v3_aux(L, index); - float x = lua_tonumber(L, -3); - float y = lua_tonumber(L, -2); - float z = lua_tonumber(L, -1); - lua_pop(L, 3); - return v3f(x, y, z); + return v3f::from(read_v3d(L, index)); } v3f check_v3f(lua_State *L, int index) { - read_v3_aux(L, index); - CHECK_POS_COORD(-3, "x"); - CHECK_POS_COORD(-2, "y"); - CHECK_POS_COORD(-1, "z"); - float x = lua_tonumber(L, -3); - float y = lua_tonumber(L, -2); - float z = lua_tonumber(L, -1); - lua_pop(L, 3); - return v3f(x, y, z); + return v3f::from(check_v3d(L, index)); } v3d read_v3d(lua_State *L, int index) { read_v3_aux(L, index); + CHECK_POS_COORD2(-3, "x"); + CHECK_POS_COORD2(-2, "y"); + CHECK_POS_COORD2(-1, "z"); double x = lua_tonumber(L, -3); double y = lua_tonumber(L, -2); double z = lua_tonumber(L, -1); @@ -211,6 +202,9 @@ v3d check_v3d(lua_State *L, int index) double y = lua_tonumber(L, -2); double z = lua_tonumber(L, -1); lua_pop(L, 3); + CHECK_FLOAT(x, "x"); + CHECK_FLOAT(y, "y"); + CHECK_FLOAT(z, "z"); return v3d(x, y, z); } @@ -286,18 +280,23 @@ video::SColor read_ARGB8(lua_State *L, int index) return std::fmax(0.0, std::fmin(255.0, c)); }; + // FIXME: maybe we should have strict type checks here. compare to is_color_table() + video::SColor color(0); CHECK_TYPE(index, "ARGB color", LUA_TTABLE); lua_getfield(L, index, "a"); color.setAlpha(lua_isnumber(L, -1) ? clamp_col(lua_tonumber(L, -1)) : 0xFF); lua_pop(L, 1); lua_getfield(L, index, "r"); + CHECK_NOT_NIL(-1, "color component R"); color.setRed(clamp_col(lua_tonumber(L, -1))); lua_pop(L, 1); lua_getfield(L, index, "g"); + CHECK_NOT_NIL(-1, "color component G"); color.setGreen(clamp_col(lua_tonumber(L, -1))); lua_pop(L, 1); lua_getfield(L, index, "b"); + CHECK_NOT_NIL(-1, "color component B"); color.setBlue(clamp_col(lua_tonumber(L, -1))); lua_pop(L, 1); return color; diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h index b55f1c9c9..2744fa0b5 100644 --- a/src/script/common/c_converter.h +++ b/src/script/common/c_converter.h @@ -74,13 +74,23 @@ v2f check_v2f(lua_State *L, int index); v3f check_v3f(lua_State *L, int index); v3s16 check_v3s16(lua_State *L, int index); +// TODO: some day we should figure out the type-checking situation so it's done +// everywhere. (right now {x=true, y=false} as v2f is {0,0} with no warning) + +/// @warning relaxed type-checking, prefer `check_v3f`. v3f read_v3f(lua_State *L, int index); +/// @warning relaxed type-checking, prefer `check_v2f`. v2f read_v2f(lua_State *L, int index); +/// @warning relaxed type-checking v2s16 read_v2s16(lua_State *L, int index); +/// @warning relaxed type-checking v2s32 read_v2s32(lua_State *L, int index); +/// @warning relaxed type-checking, prefer `check_v3s16`. +v3s16 read_v3s16(lua_State *L, int index); + video::SColor read_ARGB8(lua_State *L, int index); bool read_color(lua_State *L, int index, video::SColor *color); -bool is_color_table (lua_State *L, int index); +bool is_color_table(lua_State *L, int index); /** * Read a floating-point axis-aligned box from Lua. @@ -95,7 +105,6 @@ bool is_color_table (lua_State *L, int index); */ aabb3f read_aabb3f(lua_State *L, int index, f32 scale); -v3s16 read_v3s16(lua_State *L, int index); std::vector read_aabb3f_vector (lua_State *L, int index, f32 scale); size_t read_stringlist(lua_State *L, int index, std::vector *result); diff --git a/src/script/cpp_api/s_node.h b/src/script/cpp_api/s_node.h index bf4dc6af5..2c7133df1 100644 --- a/src/script/cpp_api/s_node.h +++ b/src/script/cpp_api/s_node.h @@ -38,7 +38,6 @@ public: static struct EnumString es_ContentParamType[]; static struct EnumString es_ContentParamType2[]; static struct EnumString es_LiquidType[]; - static struct EnumString es_LiquidMoveType[]; static struct EnumString es_NodeBoxType[]; static struct EnumString es_TextureAlphaMode[]; }; diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index aafde7540..e21a954ac 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -266,15 +266,22 @@ int ModApiEnv::l_bulk_swap_node(lua_State *L) // get_node_raw(x, y, z) -> content, param1, param2, pos_ok int ModApiEnv::l_get_node_raw(lua_State *L) { - GET_ENV_PTR; + GET_PLAIN_ENV_PTR; - // pos - // mirrors implementation of read_v3s16 (with the exact same rounding) - double x = lua_tonumber(L, 1); - double y = lua_tonumber(L, 2); - double z = lua_tonumber(L, 3); - v3s16 pos = doubleToInt(v3d(x, y, z), 1.0); - // Do it + v3s16 pos; + // mirrors the implementation of read_v3s16 (with the exact same rounding) + { + if (lua_isnoneornil(L, 1)) + log_deprecated(L, "X position is nil", 1, true); + if (lua_isnoneornil(L, 2)) + log_deprecated(L, "Y position is nil", 1, true); + if (lua_isnoneornil(L, 3)) + log_deprecated(L, "Z position is nil", 1, true); + double x = lua_tonumber(L, 1); + double y = lua_tonumber(L, 2); + double z = lua_tonumber(L, 3); + pos = doubleToInt(v3d(x, y, z), 1.0); + } bool pos_ok; MapNode n = env->getMap().getNode(pos, &pos_ok); // Return node and pos_ok diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 74ee5e5c9..698b2dba6 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -261,7 +261,7 @@ int ModApiServer::l_get_player_information(lua_State *L) lua_settable(L, table); lua_pushstring(L,"state"); - lua_pushstring(L, ClientInterface::state2Name(info.state).c_str()); + lua_pushstring(L, ClientInterface::state2Name(info.state)); lua_settable(L, table); #endif diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index 5815d3bf3..af254b105 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -44,7 +44,40 @@ int LuaVoxelManip::l_read_from_map(lua_State *L) push_v3s16(L, vm->m_area.MinEdge); push_v3s16(L, vm->m_area.MaxEdge); + return 2; +} +int LuaVoxelManip::l_initialize(lua_State *L) +{ + MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkObject(L, 1); + MMVManip *vm = o->vm; + + if (o->is_mapgen_vm) + throw LuaError("Cannot modify mapgen VoxelManip object"); + + VoxelArea area; + { + v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 2)); + v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 3)); + sortBoxVerticies(bp1, bp2); + area = VoxelArea(bp1 * MAP_BLOCKSIZE, (bp2+1) * MAP_BLOCKSIZE - v3s16(1)); + } + assert(!area.hasEmptyExtent()); + + vm->clear(); + vm->addArea(area); + if (lua_istable(L, 4)) { + MapNode n = readnode(L, 4); + const u32 volume = vm->m_area.getVolume(); + for (u32 i = 0; i != volume; i++) + vm->m_data[i] = n; + vm->clearFlags(vm->m_area, VOXELFLAG_NO_DATA); + } + + push_v3s16(L, vm->m_area.MinEdge); + push_v3s16(L, vm->m_area.MaxEdge); return 2; } @@ -93,11 +126,12 @@ int LuaVoxelManip::l_set_data(lua_State *L) lua_pop(L, 1); } - // FIXME: in theory we should clear VOXELFLAG_NO_DATA here - // However there is no way to tell which values Lua code has intended to set - // (if they were VOXELFLAG_NO_DATA before), and which were just not touched. - // In practice this doesn't cause problems because read_from_map() will cause - // all covered blocks to be loaded anyway. + // Mark all data as present, since we just got it from Lua + // Note that we can't tell if the caller intended to put CONTENT_IGNORE or + // is just repeating the dummy values we push in l_get_data() in case + // VOXELFLAG_NO_DATA is set. In practice this doesn't matter since ignore + // isn't written back to the map anyway. + vm->clearFlags(vm->m_area, VOXELFLAG_NO_DATA); return 0; } @@ -344,6 +378,19 @@ int LuaVoxelManip::l_get_emerged_area(lua_State *L) return 2; } +int LuaVoxelManip::l_close(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaVoxelManip *o = checkObject(L, 1); + + if (o->is_mapgen_vm) + throw LuaError("Cannot dispose of mapgen VoxelManip object"); + o->vm->clear(); + + return 0; +} + LuaVoxelManip::LuaVoxelManip(MMVManip *mmvm, bool is_mg_vm) : is_mapgen_vm(is_mg_vm), vm(mmvm) @@ -436,6 +483,7 @@ void LuaVoxelManip::Register(lua_State *L) const char LuaVoxelManip::className[] = "VoxelManip"; const luaL_Reg LuaVoxelManip::methods[] = { luamethod(LuaVoxelManip, read_from_map), + luamethod(LuaVoxelManip, initialize), luamethod(LuaVoxelManip, get_data), luamethod(LuaVoxelManip, set_data), luamethod(LuaVoxelManip, get_node_at), @@ -451,5 +499,6 @@ const luaL_Reg LuaVoxelManip::methods[] = { luamethod(LuaVoxelManip, set_param2_data), luamethod(LuaVoxelManip, was_modified), luamethod(LuaVoxelManip, get_emerged_area), + luamethod(LuaVoxelManip, close), {0,0} }; diff --git a/src/script/lua_api/l_vmanip.h b/src/script/lua_api/l_vmanip.h index 5ba1caffa..95bb82ce2 100644 --- a/src/script/lua_api/l_vmanip.h +++ b/src/script/lua_api/l_vmanip.h @@ -24,6 +24,7 @@ private: static int gc_object(lua_State *L); static int l_read_from_map(lua_State *L); + static int l_initialize(lua_State *L); static int l_get_data(lua_State *L); static int l_set_data(lua_State *L); static int l_write_to_map(lua_State *L); @@ -45,6 +46,8 @@ private: static int l_was_modified(lua_State *L); static int l_get_emerged_area(lua_State *L); + static int l_close(lua_State *L); + public: MMVManip *vm = nullptr; diff --git a/src/server.cpp b/src/server.cpp index 11fe6fd3e..e3854802d 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -65,6 +65,8 @@ #include "gettext.h" #include "util/tracy_wrapper.h" +#include + class ClientNotFoundException : public BaseException { public: @@ -586,14 +588,16 @@ void Server::start() R"(| | |_| | (_| | | | | |_| |)", R"(|_|\__,_|\__,_|_| |_|\__|_|)", }; - if (!m_admin_chat) { // we're not printing to rawstream to avoid it showing up in the logs. // however it would then mess up the ncurses terminal (m_admin_chat), // so we skip it in that case. - for (auto line : art) - std::cerr << line << std::endl; + for (size_t i = 0; i < ARRLEN(art); ++i) + std::cerr << art[i] << (i == ARRLEN(art) - 1 ? "" : "\n"); + // add a "tail" with the engine version + std::cerr << " ___ " << g_version_hash << std::endl; } + actionstream << "World at [" << m_path_world << "]" << std::endl; actionstream << "Server for gameid=\"" << m_gamespec.id << "\" listening on "; @@ -1572,7 +1576,8 @@ void Server::SendChatMessage(session_t peer_id, const ChatMessage &message) if (peer_id != PEER_ID_INEXISTENT) { Send(&pkt); } else { - m_clients.sendToAll(&pkt); + // If a client has completed auth but is still joining, still send chat + m_clients.sendToAll(&pkt, CS_InitDone); } } @@ -3183,9 +3188,7 @@ std::wstring Server::handleChat(const std::string &name, ChatMessage chatmsg(line); - std::vector clients = m_clients.getClientIDs(); - for (u16 cid : clients) - SendChatMessage(cid, chatmsg); + SendChatMessage(PEER_ID_INEXISTENT, chatmsg); return L""; } @@ -3357,6 +3360,15 @@ bool Server::denyIfBanned(session_t peer_id) return false; } +bool Server::checkUserLimit(const std::string &player_name, const std::string &addr_s) +{ + if (!m_clients.isUserLimitReached()) + return false; + if (player_name == g_settings->get("name")) // admin can always join + return false; + return !m_script->can_bypass_userlimit(player_name, addr_s); +} + void Server::notifyPlayer(const char *name, const std::wstring &msg) { // m_env will be NULL if the server is initializing @@ -3490,7 +3502,6 @@ void Server::hudSetHotbarSelectedImage(RemotePlayer *player, const std::string & Address Server::getPeerAddress(session_t peer_id) { - // Note that this is only set after Init was received in Server::handleCommand_Init return getClient(peer_id, CS_Invalid)->getAddress(); } @@ -4105,7 +4116,7 @@ std::unique_ptr Server::emergePlayer(const char *name, session_t peer return playersao; } -void dedicated_server_loop(Server &server, bool &kill) +void dedicated_server_loop(Server &server, volatile std::sig_atomic_t &kill) { verbosestream<<"dedicated_server_loop()"< +#include #include #include #include @@ -368,6 +369,7 @@ public: void hudSetHotbarImage(RemotePlayer *player, const std::string &name); void hudSetHotbarSelectedImage(RemotePlayer *player, const std::string &name); + /// @note this is only available for client state >= CS_HelloSent Address getPeerAddress(session_t peer_id); void setLocalPlayerAnimations(RemotePlayer *player, v2f animation_frames[4], @@ -611,6 +613,10 @@ private: void handleChatInterfaceEvent(ChatEvent *evt); + /// @brief Checks if user limit allows a potential client to join + /// @return true if the client can NOT join + bool checkUserLimit(const std::string &player_name, const std::string &addr_s); + // This returns the answer to the sender of wmessage, or "" if there is none std::wstring handleChat(const std::string &name, std::wstring wmessage_input, bool check_shout_priv = false, RemotePlayer *player = nullptr); @@ -794,4 +800,4 @@ private: Shuts down when kill is set to true. */ -void dedicated_server_loop(Server &server, bool &kill); +void dedicated_server_loop(Server &server, volatile std::sig_atomic_t &kill); diff --git a/src/server/clientiface.cpp b/src/server/clientiface.cpp index 13cf14c07..bbcd4720d 100644 --- a/src/server/clientiface.cpp +++ b/src/server/clientiface.cpp @@ -47,7 +47,7 @@ const char *ClientInterface::statenames[] = { "SudoMode", }; -std::string ClientInterface::state2Name(ClientState state) +const char *ClientInterface::state2Name(ClientState state) { return statenames[state]; } @@ -659,7 +659,7 @@ std::vector ClientInterface::getClientIDs(ClientState min_state) { std::vector reply; RecursiveMutexAutoLock clientslock(m_clients_mutex); - + reply.reserve(m_clients.size()); for (const auto &m_client : m_clients) { if (m_client.second->getState() >= min_state) reply.push_back(m_client.second->peer_id); @@ -677,14 +677,10 @@ void ClientInterface::markBlocksNotSent(const std::vector &positions, boo } } -/** - * Verify if user limit was reached. - * User limit count all clients from HelloSent state (MT protocol user) to Active state - * @return true if user limit was reached - */ bool ClientInterface::isUserLimitReached() { - return getClientIDs(CS_HelloSent).size() >= g_settings->getU16("max_users"); + // Note that this only counts clients that have fully joined + return getClientIDs().size() >= g_settings->getU16("max_users"); } void ClientInterface::step(float dtime) @@ -703,16 +699,13 @@ void ClientInterface::step(float dtime) RecursiveMutexAutoLock clientslock(m_clients_mutex); for (const auto &it : m_clients) { auto state = it.second->getState(); - if (state >= CS_HelloSent) + if (state >= CS_InitDone) continue; if (it.second->uptime() <= LINGER_TIMEOUT) continue; - // CS_Created means nobody has even noticed the client is there - // (this is before on_prejoinplayer runs) - // CS_Invalid should not happen - // -> log those as warning, the rest as info - std::ostream &os = state == CS_Created || state == CS_Invalid ? - warningstream : infostream; + // Complain louder if this situation is unexpected + auto &os = state == CS_Disconnecting || state == CS_Denied ? + infostream : warningstream; try { Address addr = m_con->GetPeerAddress(it.second->peer_id); os << "Disconnecting lingering client from " @@ -770,33 +763,22 @@ void ClientInterface::sendCustom(session_t peer_id, u8 channel, NetworkPacket *p m_con->Send(peer_id, channel, pkt, reliable); } -void ClientInterface::sendToAll(NetworkPacket *pkt) +void ClientInterface::sendToAll(NetworkPacket *pkt, ClientState state_min) { + auto &ccf = clientCommandFactoryTable[pkt->getCommand()]; + FATAL_ERROR_IF(!ccf.name, "packet type missing in table"); RecursiveMutexAutoLock clientslock(m_clients_mutex); - for (auto &client_it : m_clients) { - RemoteClient *client = client_it.second; - - if (client->net_proto_version != 0) { - auto &ccf = clientCommandFactoryTable[pkt->getCommand()]; - FATAL_ERROR_IF(!ccf.name, "packet type missing in table"); - m_con->Send(client->peer_id, ccf.channel, pkt, ccf.reliable); - } + for (auto &[peer_id, client] : m_clients) { + if (client->getState() >= state_min) + m_con->Send(peer_id, ccf.channel, pkt, ccf.reliable); } } RemoteClient* ClientInterface::getClientNoEx(session_t peer_id, ClientState state_min) { RecursiveMutexAutoLock clientslock(m_clients_mutex); - RemoteClientMap::const_iterator n = m_clients.find(peer_id); - // The client may not exist; clients are immediately removed if their - // access is denied, and this event occurs later then. - if (n == m_clients.end()) - return NULL; - - if (n->second->getState() >= state_min) - return n->second; - - return NULL; + RemoteClient *client = lockedGetClientNoEx(peer_id, state_min); + return client; } RemoteClient* ClientInterface::lockedGetClientNoEx(session_t peer_id, ClientState state_min) @@ -805,12 +787,13 @@ RemoteClient* ClientInterface::lockedGetClientNoEx(session_t peer_id, ClientStat // The client may not exist; clients are immediately removed if their // access is denied, and this event occurs later then. if (n == m_clients.end()) - return NULL; + return nullptr; + assert(n->second->peer_id == peer_id); if (n->second->getState() >= state_min) return n->second; - return NULL; + return nullptr; } ClientState ClientInterface::getClientState(session_t peer_id) @@ -825,16 +808,6 @@ ClientState ClientInterface::getClientState(session_t peer_id) return n->second->getState(); } -void ClientInterface::setPlayerName(session_t peer_id, const std::string &name) -{ - RecursiveMutexAutoLock clientslock(m_clients_mutex); - RemoteClientMap::iterator n = m_clients.find(peer_id); - // The client may not exist; clients are immediately removed if their - // access is denied, and this event occurs later then. - if (n != m_clients.end()) - n->second->setName(name); -} - void ClientInterface::DeleteClient(session_t peer_id) { RecursiveMutexAutoLock conlock(m_clients_mutex); @@ -915,18 +888,3 @@ u16 ClientInterface::getProtocolVersion(session_t peer_id) return n->second->net_proto_version; } - -void ClientInterface::setClientVersion(session_t peer_id, u8 major, u8 minor, u8 patch, - const std::string &full) -{ - RecursiveMutexAutoLock conlock(m_clients_mutex); - - // Error check - RemoteClientMap::iterator n = m_clients.find(peer_id); - - // No client to set versions - if (n == m_clients.end()) - return; - - n->second->setVersionInfo(major, minor, patch, full); -} diff --git a/src/server/clientiface.h b/src/server/clientiface.h index 44b278e63..d0e91dcca 100644 --- a/src/server/clientiface.h +++ b/src/server/clientiface.h @@ -445,7 +445,7 @@ public: /* mark blocks as not sent on all active clients */ void markBlocksNotSent(const std::vector &positions, bool low_priority = false); - /* verify is server user limit was reached */ + /* verify if server user limit was reached */ bool isUserLimitReached(); /* get list of client player names */ @@ -458,7 +458,7 @@ public: void sendCustom(session_t peer_id, u8 channel, NetworkPacket *pkt, bool reliable); /* send to all clients */ - void sendToAll(NetworkPacket *pkt); + void sendToAll(NetworkPacket *pkt, ClientState state_min = CS_Active); /* delete a client */ void DeleteClient(session_t peer_id); @@ -475,16 +475,9 @@ public: /* get state of client by id*/ ClientState getClientState(session_t peer_id); - /* set client playername */ - void setPlayerName(session_t peer_id, const std::string &name); - /* get protocol version of client */ u16 getProtocolVersion(session_t peer_id); - /* set client version */ - void setClientVersion(session_t peer_id, u8 major, u8 minor, u8 patch, - const std::string &full); - /* event to update client state */ void event(session_t peer_id, ClientStateEvent event); @@ -495,7 +488,8 @@ public: m_env = env; } - static std::string state2Name(ClientState state); + static const char *state2Name(ClientState state); + protected: class AutoLock { public: @@ -514,9 +508,9 @@ private: // Connection std::shared_ptr m_con; std::recursive_mutex m_clients_mutex; - // Connected clients (behind the con mutex) + // Connected clients (behind the mutex) RemoteClientMap m_clients; - std::vector m_clients_names; //for announcing masterserver + std::vector m_clients_names; // for announcing to server list // Environment ServerEnvironment *m_env; @@ -526,5 +520,7 @@ private: static const char *statenames[]; - static constexpr int LINGER_TIMEOUT = 10; + // Note that this puts a fixed timeout on the init & auth phase for a client. + // (lingering is enforced until CS_InitDone) + static constexpr int LINGER_TIMEOUT = 12; }; diff --git a/src/terminal_chat_console.h b/src/terminal_chat_console.h index f49557d0c..3324656dc 100644 --- a/src/terminal_chat_console.h +++ b/src/terminal_chat_console.h @@ -9,6 +9,8 @@ #include "util/container.h" #include "log.h" #include "log_internal.h" + +#include #include #include @@ -45,7 +47,7 @@ public: void setup( ChatInterface *iface, - bool *kill_requested, + volatile std::sig_atomic_t *kill_requested, const std::string &nick) { m_nick = nick; @@ -96,9 +98,9 @@ private: int m_rows; bool m_can_draw_text; - bool *m_kill_requested = nullptr; - ChatBackend m_chat_backend; - ChatInterface *m_chat_interface; + volatile std::sig_atomic_t *m_kill_requested = nullptr; + ChatBackend m_chat_backend; + ChatInterface *m_chat_interface; TermLogOutput m_log_output; diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt index 9ac275d7f..159e816c7 100644 --- a/src/unittest/CMakeLists.txt +++ b/src/unittest/CMakeLists.txt @@ -36,6 +36,7 @@ set (UNITTEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_random.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_sao.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_schematic.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_scriptapi.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_serialization.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_serveractiveobjectmgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_server_shutdown_state.cpp @@ -58,6 +59,8 @@ set (UNITTEST_CLIENT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_eventmanager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_gameui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_irr_gltf_mesh_loader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_irr_x_mesh_loader.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_irr_matrix4.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mesh_compare.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_keycode.cpp PARENT_SCOPE) diff --git a/src/unittest/test.h b/src/unittest/test.h index dcecb9fb4..7e861a24a 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -41,7 +41,7 @@ public: #define UTEST(x, fmt, ...) \ if (!(x)) { \ char utest_buf[1024]; \ - snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \ + porting::mt_snprintf(utest_buf, sizeof(utest_buf), fmt, __VA_ARGS__); \ throw TestFailedException(utest_buf, __FILE__, __LINE__); \ } @@ -68,7 +68,7 @@ public: } catch (EType &e) { \ exception_thrown = true; \ } \ - UASSERT(exception_thrown); \ + UTEST(exception_thrown, "Exception %s not thrown", #EType); \ } class IGameDef; diff --git a/src/unittest/test_irr_gltf_mesh_loader.cpp b/src/unittest/test_irr_gltf_mesh_loader.cpp index f689838db..6cd6f0f1d 100644 --- a/src/unittest/test_irr_gltf_mesh_loader.cpp +++ b/src/unittest/test_irr_gltf_mesh_loader.cpp @@ -394,36 +394,40 @@ SECTION("simple skin") const auto joints = csm->getAllJoints(); REQUIRE(joints.size() == 3); - const auto findJoint = [&](const std::function &predicate) { - for (std::size_t i = 0; i < joints.size(); ++i) { - if (predicate(joints[i])) { - return joints[i]; + const auto findJoint = [&](const std::function &predicate) { + for (const auto *joint : joints) { + if (predicate(joint)) { + return joint; } } throw std::runtime_error("joint not found"); }; // Check the node hierarchy - const auto parent = findJoint([](auto joint) { - return !joint->Children.empty(); + const auto child = findJoint([&](auto *joint) { + return !!joint->ParentJointID; }); - REQUIRE(parent->Children.size() == 1); - const auto child = parent->Children[0]; - REQUIRE(child != parent); + const auto *parent = joints.at(*child->ParentJointID); SECTION("transformations are correct") { - CHECK(parent->Animatedposition == v3f(0, 0, 0)); - CHECK(parent->Animatedrotation == irr::core::quaternion()); - CHECK(parent->Animatedscale == v3f(1, 1, 1)); - CHECK(parent->GlobalInversedMatrix == irr::core::matrix4()); - const v3f childTranslation(0, 1, 0); - CHECK(child->Animatedposition == childTranslation); - CHECK(child->Animatedrotation == irr::core::quaternion()); - CHECK(child->Animatedscale == v3f(1, 1, 1)); - irr::core::matrix4 inverseBindMatrix; - inverseBindMatrix.setTranslation(-childTranslation); - CHECK(child->GlobalInversedMatrix == inverseBindMatrix); + { + const auto &transform = std::get(parent->transform); + CHECK(transform.translation == v3f(0, 0, 0)); + CHECK(transform.rotation == irr::core::quaternion()); + CHECK(transform.scale == v3f(1, 1, 1)); + CHECK(parent->GlobalInversedMatrix == irr::core::matrix4()); + } + { + const auto &transform = std::get(child->transform); + const v3f translation(0, 1, 0); + CHECK(transform.translation == translation); + CHECK(transform.rotation == irr::core::quaternion()); + CHECK(transform.scale == v3f(1, 1, 1)); + irr::core::matrix4 inverseBindMatrix; + inverseBindMatrix.setTranslation(-translation); + CHECK(child->GlobalInversedMatrix == inverseBindMatrix); + } } SECTION("weights are correct") diff --git a/src/unittest/test_irr_x_mesh_loader.cpp b/src/unittest/test_irr_x_mesh_loader.cpp new file mode 100644 index 000000000..cb61ce7da --- /dev/null +++ b/src/unittest/test_irr_x_mesh_loader.cpp @@ -0,0 +1,111 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "catch_amalgamated.hpp" +#include "content/subgames.h" +#include "filesys.h" + +#include "irrlichttypes.h" +#include "irr_ptr.h" + +#include "EDriverTypes.h" +#include "IFileSystem.h" +#include "IReadFile.h" +#include "ISceneManager.h" +#include "SkinnedMesh.h" +#include "irrlicht.h" + +#include "catch.h" +#include "matrix4.h" + +TEST_CASE("x") { + +const auto gamespec = findSubgame("devtest"); + +if (!gamespec.isValid()) + SKIP(); + +irr::SIrrlichtCreationParameters p; +p.DriverType = video::EDT_NULL; +auto *driver = irr::createDeviceEx(p); + +REQUIRE(driver); + +auto *smgr = driver->getSceneManager(); +const auto loadMesh = [&] (const io::path& filepath) { + irr_ptr file(driver->getFileSystem()->createAndOpenFile(filepath)); + REQUIRE(file); + return smgr->getMesh(file.get()); +}; + +const static auto model_stem = gamespec.gamemods_path + + DIR_DELIM + "testentities" + DIR_DELIM + "models" + DIR_DELIM + "testentities_"; + +SECTION("cool guy") { + const auto *mesh = dynamic_cast(loadMesh(model_stem + "cool_guy.x")); + REQUIRE(mesh); + REQUIRE(mesh->getMeshBufferCount() == 1); + + auto getJointId = [&](auto name) { + return mesh->getJointNumber(name).value(); + }; + + const auto root = getJointId("Root"); + const auto armature = getJointId("Armature"); + const auto armature_body = getJointId("Armature_body"); + const auto armature_arm_r = getJointId("Armature_arm_r"); + + std::vector matrices; + matrices.reserve(mesh->getJointCount()); + for (auto *joint : mesh->getAllJoints()) { + if (const auto *matrix = std::get_if(&joint->transform)) + matrices.push_back(*matrix); + else + matrices.push_back(std::get(joint->transform).buildMatrix()); + } + auto local_matrices = matrices; + mesh->calculateGlobalMatrices(matrices); + + SECTION("joints are topologically sorted") { + REQUIRE(root < armature); + REQUIRE(armature < armature_body); + REQUIRE(armature_body < armature_arm_r); + } + + SECTION("parents are correct") { + const auto get_parent = [&](auto id) { + return mesh->getAllJoints()[id]->ParentJointID; + }; + REQUIRE(!get_parent(root)); + REQUIRE(get_parent(armature).value() == root); + REQUIRE(get_parent(armature_body).value() == armature); + REQUIRE(get_parent(armature_arm_r).value() == armature_body); + } + + SECTION("local matrices are correct") { + REQUIRE(local_matrices[root].equals(core::IdentityMatrix)); + REQUIRE(local_matrices[armature].equals(core::IdentityMatrix)); + REQUIRE(local_matrices[armature_body] == core::matrix4( + -1,0,0,0, + 0,0,1,0, + 0,1,0,0, + 0,2.571201,0,1 + )); + REQUIRE(local_matrices[armature_arm_r] == core::matrix4( + -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 + )); + } + + SECTION("global matrices are correct") { + REQUIRE(matrices[armature_body] == local_matrices[armature_body]); + REQUIRE(matrices[armature_arm_r] == + matrices[armature_body] * local_matrices[armature_arm_r]); + } +} + +driver->closeDevice(); +driver->drop(); +} diff --git a/src/unittest/test_scriptapi.cpp b/src/unittest/test_scriptapi.cpp new file mode 100644 index 000000000..03c3713b2 --- /dev/null +++ b/src/unittest/test_scriptapi.cpp @@ -0,0 +1,196 @@ +// Luanti +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (C) 2022 Minetest core developers & community + +#include "test.h" + +#include +#include "script/cpp_api/s_base.h" +#include "script/lua_api/l_util.h" +#include "script/lua_api/l_settings.h" +#include "script/common/c_converter.h" +#include "irrlicht_changes/printing.h" +#include "server.h" + +namespace { + class MyScriptApi : virtual public ScriptApiBase { + public: + MyScriptApi() : ScriptApiBase(ScriptingType::Async) {}; + void init(); + using ScriptApiBase::getStack; + }; +} + +class TestScriptApi : public TestBase +{ +public: + TestScriptApi() { TestManager::registerTestModule(this); } + const char *getName() { return "TestScriptApi"; } + + void runTests(IGameDef *gamedef); + + void testVectorMetatable(MyScriptApi *script); + void testVectorRead(MyScriptApi *script); + void testVectorReadErr(MyScriptApi *script); + void testVectorReadMix(MyScriptApi *script); +}; + +static TestScriptApi g_test_instance; + +void MyScriptApi::init() +{ + lua_State *L = getStack(); + + lua_getglobal(L, "core"); + int top = lua_gettop(L); + + // By creating an environment of 'async' type we have the fewest amount + // of external classes needed. + lua_pushstring(L, "async"); + lua_setglobal(L, "INIT"); + + LuaSettings::Register(L); + ModApiUtil::InitializeAsync(L, top); + + lua_pop(L, 1); + + loadMod(Server::getBuiltinLuaPath() + DIR_DELIM + "init.lua", BUILTIN_MOD_NAME); + checkSetByBuiltin(); +} + +void TestScriptApi::runTests(IGameDef *gamedef) +{ + MyScriptApi script; + try { + script.init(); + } catch (ModError &e) { + rawstream << e.what() << std::endl; + num_tests_failed = 1; + return; + } + + TEST(testVectorMetatable, &script); + TEST(testVectorRead, &script); + TEST(testVectorReadErr, &script); + TEST(testVectorReadMix, &script); +} + +// Runs Lua code and leaves `nresults` return values on the stack +static void run(lua_State *L, const char *code, int nresults) +{ + if (luaL_loadstring(L, code) != 0) { + rawstream << lua_tostring(L, -1) << std::endl; + UASSERT(false); + } + if (lua_pcall(L, 0, nresults, 0) != 0) { + throw LuaError(lua_tostring(L, -1)); + } +} + +void TestScriptApi::testVectorMetatable(MyScriptApi *script) +{ + lua_State *L = script->getStack(); + StackUnroller unroller(L); + + const auto &call_vector_check = [&] () -> bool { + lua_setglobal(L, "tmp"); + run(L, "return vector.check(tmp)", 1); + return lua_toboolean(L, -1); + }; + + push_v3s16(L, {1, 2, 3}); + UASSERT(call_vector_check()); + + push_v3f(L, {1, 2, 3}); + UASSERT(call_vector_check()); + + // 2-component vectors must not have this metatable + push_v2s32(L, {0, 0}); + UASSERT(!call_vector_check()); + + push_v2f(L, {0, 0}); + UASSERT(!call_vector_check()); +} + +void TestScriptApi::testVectorRead(MyScriptApi *script) +{ + lua_State *L = script->getStack(); + StackUnroller unroller(L); + + // both methods should parse these + const std::pair pairs1[] = { + {"return {x=1, y=-2, z=3}", {1, -2, 3}}, + {"return {x=1.1, y=0, z=0}", {1, 0, 0}}, + {"return {x=1.5, y=0, z=0}", {2, 0, 0}}, + {"return {x=-1.1, y=0, z=0}", {-1, 0, 0}}, + {"return {x=-1.5, y=0, z=0}", {-2, 0, 0}}, + {"return vector.new(5, 6, 7)", {5, 6, 7}}, + {"return vector.new(32767, 0, -32768)", {S16_MAX, 0, S16_MIN}}, + }; + for (auto &it : pairs1) { + run(L, it.first, 1); + v3s16 v = read_v3s16(L, -1); + UASSERTEQ(auto, v, it.second); + v = check_v3s16(L, -1); + UASSERTEQ(auto, v, it.second); + lua_pop(L, 1); + } +} + +void TestScriptApi::testVectorReadErr(MyScriptApi *script) +{ + lua_State *L = script->getStack(); + StackUnroller unroller(L); + + // both methods should reject these + const char *errs1[] = { + "return 'bamboo'", + "return function() end", + "return nil", + }; + for (auto &it : errs1) { + infostream << it << std::endl; + run(L, it, 1); + EXCEPTION_CHECK(LuaError, read_v3s16(L, -1)); + EXCEPTION_CHECK(LuaError, check_v3s16(L, -1)); + } +} + +void TestScriptApi::testVectorReadMix(MyScriptApi *script) +{ + lua_State *L = script->getStack(); + StackUnroller unroller(L); + + // read_v3s16 should allow these, but check_v3s16 should not + const std::pair pairs2[] = { + {"return {}", {0, 0, 0}}, + {"return {y=1, z=3}", {0, 1, 3}}, + {"return {x=1, z=3}", {1, 0, 3}}, + {"return {x=1, y=3}", {1, 3, 0}}, + {"return {x='3', y='2.9', z=3}", {3, 3, 3}}, + {"return {x=false, y=0, z=0}", {0, 0, 0}}, + {"return {x='?', y=0, z=0}", {0, 0, 0}}, + {"return {x={'baguette'}, y=0, z=0}", {0, 0, 0}}, + }; + for (auto &it : pairs2) { + infostream << it.first << std::endl; + run(L, it.first, 1); + v3s16 v = read_v3s16(L, -1); + UASSERTEQ(auto, v, it.second); + EXCEPTION_CHECK(LuaError, check_v3s16(L, -1)); + lua_pop(L, 1); + } + + // same but even the result is undefined + const char *errs2[] = { + "return {x=0, y=0, z=0/0}", // nan + "return {x=0, y=0, z=math.huge}", // inf + }; + for (auto &it : errs2) { + infostream << it << std::endl; + run(L, it, 1); + (void)read_v3s16(L, -1); + EXCEPTION_CHECK(LuaError, check_v3s16(L, -1)); + lua_pop(L, 1); + } +} diff --git a/src/unittest/test_voxelarea.cpp b/src/unittest/test_voxelarea.cpp index d9380caf9..52bf1bfd8 100644 --- a/src/unittest/test_voxelarea.cpp +++ b/src/unittest/test_voxelarea.cpp @@ -264,6 +264,10 @@ void TestVoxelArea::test_intersect() UASSERT(v3.intersect(v1) == v1.intersect(v3)); UASSERT(v1.intersect(v4) == VoxelArea({-10, -2, -10}, {10, 2, 10})); + + // edge cases + UASSERT(VoxelArea().intersect(v1).hasEmptyExtent()); + UASSERT(v1.intersect(VoxelArea()).hasEmptyExtent()); } void TestVoxelArea::test_index_xyz_all_pos() diff --git a/src/unittest/test_voxelmanipulator.cpp b/src/unittest/test_voxelmanipulator.cpp index 09d4b5781..9c1f85d24 100644 --- a/src/unittest/test_voxelmanipulator.cpp +++ b/src/unittest/test_voxelmanipulator.cpp @@ -4,11 +4,13 @@ #include "test.h" -#include +#include #include "gamedef.h" #include "log.h" #include "voxel.h" +#include "dummymap.h" +#include "irrlicht_changes/printing.h" class TestVoxelManipulator : public TestBase { public: @@ -17,59 +19,32 @@ public: void runTests(IGameDef *gamedef); - void testVoxelArea(); - void testVoxelManipulator(const NodeDefManager *nodedef); + void testBasic(const NodeDefManager *nodedef); + void testEmerge(IGameDef *gamedef); + void testBlitBack(IGameDef *gamedef); + void testBlitBack2(IGameDef *gamedef); }; static TestVoxelManipulator g_test_instance; void TestVoxelManipulator::runTests(IGameDef *gamedef) { - TEST(testVoxelArea); - TEST(testVoxelManipulator, gamedef->getNodeDefManager()); + TEST(testBasic, gamedef->ndef()); + TEST(testEmerge, gamedef); + TEST(testBlitBack, gamedef); + TEST(testBlitBack2, gamedef); } //////////////////////////////////////////////////////////////////////////////// -void TestVoxelManipulator::testVoxelArea() -{ - VoxelArea a(v3s16(-1,-1,-1), v3s16(1,1,1)); - UASSERT(a.index(0,0,0) == 1*3*3 + 1*3 + 1); - UASSERT(a.index(-1,-1,-1) == 0); - - VoxelArea c(v3s16(-2,-2,-2), v3s16(2,2,2)); - // An area that is 1 bigger in x+ and z- - VoxelArea d(v3s16(-2,-2,-3), v3s16(3,2,2)); - - std::list aa; - d.diff(c, aa); - - // Correct results - std::vector results; - results.emplace_back(v3s16(-2,-2,-3), v3s16(3,2,-3)); - results.emplace_back(v3s16(3,-2,-2), v3s16(3,2,2)); - - UASSERT(aa.size() == results.size()); - - infostream<<"Result of diff:"<print(infostream); - infostream << std::endl; - - auto j = std::find(results.begin(), results.end(), *it); - UASSERT(j != results.end()); - results.erase(j); - } -} - - -void TestVoxelManipulator::testVoxelManipulator(const NodeDefManager *nodedef) +void TestVoxelManipulator::testBasic(const NodeDefManager *nodedef) { VoxelManipulator v; v.print(infostream, nodedef); + UASSERT(v.m_area.hasEmptyExtent()); - infostream << "*** Setting (-1,0,-1)=2 ***" << std::endl; + infostream << "*** Setting (-1,0,-1) ***" << std::endl; v.setNode(v3s16(-1,0,-1), MapNode(t_CONTENT_GRASS)); v.print(infostream, nodedef); @@ -89,3 +64,118 @@ void TestVoxelManipulator::testVoxelManipulator(const NodeDefManager *nodedef) UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == t_CONTENT_GRASS); EXCEPTION_CHECK(InvalidPositionException, v.getNode(v3s16(0,1,1))); } + +void TestVoxelManipulator::testEmerge(IGameDef *gamedef) +{ + constexpr int bs = MAP_BLOCKSIZE; + + DummyMap map(gamedef, {0,0,0}, {1,1,1}); + map.fill({0,0,0}, {1,1,1}, CONTENT_AIR); + + MMVManip vm(&map); + UASSERT(!vm.isOrphan()); + + // emerge something + vm.initialEmerge({0,0,0}, {0,0,0}); + UASSERTEQ(auto, vm.m_area.MinEdge, v3s16(0)); + UASSERTEQ(auto, vm.m_area.MaxEdge, v3s16(bs-1)); + UASSERTEQ(auto, vm.getNodeNoExNoEmerge({0,0,0}).getContent(), CONTENT_AIR); + + map.setNode({0, 1,0}, t_CONTENT_BRICK); + map.setNode({0,bs+1,0}, t_CONTENT_BRICK); + + // emerge top block: this should not re-read the first one + vm.initialEmerge({0,0,0}, {0,1,0}); + UASSERTEQ(auto, vm.m_area.getExtent(), v3s32(bs,2*bs,bs)); + + UASSERTEQ(auto, vm.getNodeNoExNoEmerge({0, 1,0}).getContent(), CONTENT_AIR); + UASSERTEQ(auto, vm.getNodeNoExNoEmerge({0,bs+1,0}).getContent(), t_CONTENT_BRICK); + + // emerge out of bounds: should produce empty data + vm.initialEmerge({0,2,0}, {0,2,0}, false); + UASSERTEQ(auto, vm.m_area.getExtent(), v3s32(bs,3*bs,bs)); + + UASSERTEQ(auto, vm.getNodeNoExNoEmerge({0,2*bs,0}).getContent(), CONTENT_IGNORE); + UASSERT(!vm.exists({0,2*bs,0})); + + // clear + vm.clear(); + UASSERT(vm.m_area.hasEmptyExtent()); +} + +void TestVoxelManipulator::testBlitBack(IGameDef *gamedef) +{ + DummyMap map(gamedef, {-1,-1,-1}, {1,1,1}); + map.fill({0,0,0}, {0,0,0}, CONTENT_AIR); + + std::unique_ptr vm2; + + { + MMVManip vm(&map); + vm.initialEmerge({0,0,0}, {0,0,0}); + UASSERT(vm.exists({0,0,0})); + vm.setNodeNoEmerge({0,0,0}, t_CONTENT_STONE); + vm.setNodeNoEmerge({1,1,1}, t_CONTENT_GRASS); + vm.setNodeNoEmerge({2,2,2}, CONTENT_IGNORE); + // test out clone and reparent too + vm2.reset(vm.clone()); + } + + UASSERT(vm2); + UASSERT(vm2->isOrphan()); + vm2->reparent(&map); + + std::map modified; + vm2->blitBackAll(&modified); + UASSERTEQ(size_t, modified.size(), 1); + UASSERTEQ(auto, modified.begin()->first, v3s16(0,0,0)); + + UASSERTEQ(auto, map.getNode({0,0,0}).getContent(), t_CONTENT_STONE); + UASSERTEQ(auto, map.getNode({1,1,1}).getContent(), t_CONTENT_GRASS); + // ignore nodes are not written (is this an intentional feature?) + UASSERTEQ(auto, map.getNode({2,2,2}).getContent(), CONTENT_AIR); +} + +void TestVoxelManipulator::testBlitBack2(IGameDef *gamedef) +{ + constexpr int bs = MAP_BLOCKSIZE; + + DummyMap map(gamedef, {0,0,0}, {1,1,1}); + map.fill({0,0,0}, {1,1,1}, CONTENT_AIR); + + // Create a vmanip "manually" without using initialEmerge + MMVManip vm(&map); + vm.addArea(VoxelArea({0,0,0}, v3s16(1,2,1) * bs - v3s16(1))); + + // Lower block is initialized with ignore, upper with lava + for(s16 z=0; z= bs ? t_CONTENT_LAVA : CONTENT_IGNORE; + vm.setNodeNoEmerge({x,y,z}, c); + } + // But pretend the upper block was not actually initialized + vm.setFlags(VoxelArea({0,bs,0}, v3s16(1,2,1) * bs - v3s16(1)), VOXELFLAG_NO_DATA); + // Add a node to the lower one + vm.setNodeNoEmerge({0,1,0}, t_CONTENT_TORCH); + + // Verify covered blocks + { + auto cov = vm.getCoveredBlocks(); + UASSERTEQ(size_t, cov.size(), 2); + auto it = cov.find({0,0,0}); + UASSERT(it != cov.end() && it->second); + it = cov.find({0,1,0}); + UASSERT(it != cov.end() && !it->second); + } + + // Now blit it back + std::map modified; + vm.blitBackAll(&modified); + // The lower block data should have been written + UASSERTEQ(size_t, modified.size(), 1); + UASSERTEQ(auto, modified.begin()->first, v3s16(0,0,0)); + UASSERTEQ(auto, map.getNode({0,1,0}).getContent(), t_CONTENT_TORCH); + // The upper one should not! + UASSERTEQ(auto, map.getNode({0,bs,0}).getContent(), CONTENT_AIR); +} diff --git a/src/util/auth.cpp b/src/util/auth.cpp index 040d15bf9..5116e4be7 100644 --- a/src/util/auth.cpp +++ b/src/util/auth.cpp @@ -67,9 +67,9 @@ void generate_srp_verifier_and_salt(const std::string &name, std::string *salt) { char *bytes_v = nullptr; - size_t verifier_len; + size_t verifier_len = 0; char *salt_ptr = nullptr; - size_t salt_len; + size_t salt_len = 0; gen_srp_v(name, password, &salt_ptr, &salt_len, &bytes_v, &verifier_len); *verifier = std::string(bytes_v, verifier_len); *salt = std::string(salt_ptr, salt_len); diff --git a/src/voxel.cpp b/src/voxel.cpp index 4d0ce84b7..5a69b0725 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -15,7 +15,6 @@ Debug stuff */ u64 emerge_time = 0; -u64 emerge_load_time = 0; VoxelManipulator::~VoxelManipulator() { diff --git a/src/voxel.h b/src/voxel.h index c47f97a30..7fbaadc28 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -34,7 +34,6 @@ class NodeDefManager; Debug stuff */ extern u64 emerge_time; -extern u64 emerge_load_time; /* This class resembles aabbox3d a lot, but has inclusive @@ -469,7 +468,7 @@ public: Control */ - virtual void clear(); + void clear(); void print(std::ostream &o, const NodeDefManager *nodemgr, VoxelPrintMode mode=VOXELPRINT_MATERIAL) const; diff --git a/util/bump_version.sh b/util/bump_version.sh index 77b4e603b..0fd4f3ae1 100755 --- a/util/bump_version.sh +++ b/util/bump_version.sh @@ -124,10 +124,10 @@ perform_release() { local release_version=$1 RELEASE_DATE=$(date +%Y-%m-%d) - sed -i '/\