mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
fix merge conflict
This commit is contained in:
commit
c94c466d3d
121 changed files with 2791 additions and 2948 deletions
14
.github/workflows/linux.yml
vendored
14
.github/workflows/linux.yml
vendored
|
@ -132,16 +132,16 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./util/test_multiplayer.sh
|
./util/test_multiplayer.sh
|
||||||
|
|
||||||
# Build with prometheus-cpp (server-only)
|
# Build with prometheus-cpp (server-only), also runs on ARM64
|
||||||
clang_11_prometheus:
|
clang_prometheus_arm:
|
||||||
name: "clang_11 (PROMETHEUS=1)"
|
name: "clang (with Prometheus, ARM64)"
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04-arm
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
source ./util/ci/common.sh
|
source ./util/ci/common.sh
|
||||||
install_linux_deps clang-11
|
install_linux_deps --headless clang libluajit-5.1-dev
|
||||||
|
|
||||||
- name: Build prometheus-cpp
|
- name: Build prometheus-cpp
|
||||||
run: ./util/ci/build_prometheus_cpp.sh
|
run: ./util/ci/build_prometheus_cpp.sh
|
||||||
|
@ -150,8 +150,8 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
./util/ci/build.sh
|
./util/ci/build.sh
|
||||||
env:
|
env:
|
||||||
CC: clang-11
|
CC: clang
|
||||||
CXX: clang++-11
|
CXX: clang++
|
||||||
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0 -DENABLE_CURSES=0"
|
CMAKE_FLAGS: "-DENABLE_PROMETHEUS=1 -DBUILD_CLIENT=0 -DENABLE_CURSES=0"
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
|
|
|
@ -14,7 +14,7 @@ set(CLANG_MINIMUM_VERSION "7.0.1")
|
||||||
|
|
||||||
# You should not need to edit these manually, use util/bump_version.sh
|
# You should not need to edit these manually, use util/bump_version.sh
|
||||||
set(VERSION_MAJOR 5)
|
set(VERSION_MAJOR 5)
|
||||||
set(VERSION_MINOR 12)
|
set(VERSION_MINOR 13)
|
||||||
set(VERSION_PATCH 0)
|
set(VERSION_PATCH 0)
|
||||||
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string")
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// 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("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
|
project.ext.set("versionPatch", 0) // Version Patch
|
||||||
// ^ keep in sync with cmake
|
// ^ keep in sync with cmake
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ end
|
||||||
local translation_file_header = [[
|
local translation_file_header = [[
|
||||||
// This file is automatically generated
|
// This file is automatically generated
|
||||||
// It contains a bunch of fake gettext calls, to tell xgettext about the strings in config files
|
// 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() {]]
|
fake_function() {]]
|
||||||
|
|
||||||
|
@ -110,15 +110,15 @@ local function create_translation_file(settings)
|
||||||
local result = { translation_file_header }
|
local result = { translation_file_header }
|
||||||
for _, entry in ipairs(settings) do
|
for _, entry in ipairs(settings) do
|
||||||
if entry.type == "category" then
|
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
|
else
|
||||||
if entry.readable_name then
|
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
|
end
|
||||||
if entry.comment ~= "" then
|
if entry.comment ~= "" then
|
||||||
local comment_escaped = entry.comment:gsub("\n", "\\n")
|
local comment_escaped = entry.comment:gsub("\n", "\\n")
|
||||||
comment_escaped = comment_escaped:gsub("\"", "\\\"")
|
comment_escaped = comment_escaped:gsub("\"", "\\\"")
|
||||||
insert(result, "\tgettext(\"" .. comment_escaped .. "\");")
|
insert(result, "\t/* xgettext:no-c-format */ gettext(\"" .. comment_escaped .. "\");")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,12 +19,14 @@ function meta:__newindex(name, value)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local info = getinfo(2, "Sl")
|
local info = getinfo(2, "Sl")
|
||||||
local desc = ("%s:%d"):format(info.short_src, info.currentline)
|
if info ~= nil then
|
||||||
local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name)
|
local desc = ("%s:%d"):format(info.short_src, info.currentline)
|
||||||
if not warned[warn_key] and info.what ~= "main" and info.what ~= "C" then
|
local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name)
|
||||||
core.log("warning", ("Assignment to undeclared global %q inside a function at %s.")
|
if not warned[warn_key] and info.what ~= "main" and info.what ~= "C" then
|
||||||
:format(name, desc))
|
core.log("warning", ("Assignment to undeclared global %q inside a function at %s.")
|
||||||
warned[warn_key] = true
|
:format(name, desc))
|
||||||
|
warned[warn_key] = true
|
||||||
|
end
|
||||||
end
|
end
|
||||||
declared[name] = true
|
declared[name] = true
|
||||||
end
|
end
|
||||||
|
@ -35,6 +37,9 @@ function meta:__index(name)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local info = getinfo(2, "Sl")
|
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)
|
local warn_key = ("%s\0%d\0%s"):format(info.source, info.currentline, name)
|
||||||
if not warned[warn_key] and info.what ~= "C" then
|
if not warned[warn_key] and info.what ~= "C" then
|
||||||
core.log("warning", ("Undeclared global variable %q accessed at %s:%s")
|
core.log("warning", ("Undeclared global variable %q accessed at %s:%s")
|
||||||
|
|
|
@ -118,7 +118,7 @@ function ui.update()
|
||||||
|
|
||||||
if (active_toplevel_ui_elements > 1) then
|
if (active_toplevel_ui_elements > 1) then
|
||||||
core.log("warning", "more than one active ui "..
|
core.log("warning", "more than one active ui "..
|
||||||
"element, self most likely isn't intended")
|
"element, this most likely isn't intended")
|
||||||
end
|
end
|
||||||
|
|
||||||
if (active_toplevel_ui_elements == 0) then
|
if (active_toplevel_ui_elements == 0) then
|
||||||
|
@ -166,6 +166,10 @@ end
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
--------------------------------------------------------------------------------
|
--------------------------------------------------------------------------------
|
||||||
core.button_handler = function(fields)
|
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
|
if fields["btn_reconnect_yes"] then
|
||||||
gamedata.reconnect_requested = false
|
gamedata.reconnect_requested = false
|
||||||
gamedata.errormessage = nil
|
gamedata.errormessage = nil
|
||||||
|
|
|
@ -170,14 +170,16 @@ function contentdb.get_package_by_id(id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function contentdb.calculate_package_id(type, author, name)
|
local function strip_game_suffix(type, name)
|
||||||
local id = author:lower() .. "/"
|
|
||||||
if (type == nil or type == "game") and #name > 5 and name:sub(#name - 4) == "_game" then
|
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
|
else
|
||||||
id = id .. name
|
return name
|
||||||
end
|
end
|
||||||
return id
|
end
|
||||||
|
|
||||||
|
function contentdb.calculate_package_id(type, author, name)
|
||||||
|
return author:lower() .. "/" .. strip_game_suffix(type, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@ -427,7 +429,7 @@ function contentdb.set_packages_from_api(packages)
|
||||||
-- We currently don't support name changing
|
-- We currently don't support name changing
|
||||||
local suffix = "/" .. package.name
|
local suffix = "/" .. package.name
|
||||||
if alias:sub(-#suffix) == suffix then
|
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
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -47,22 +47,20 @@
|
||||||
],
|
],
|
||||||
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
|
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
|
||||||
"contributors": [
|
"contributors": [
|
||||||
"JosiahWI",
|
|
||||||
"Erich Schubert",
|
"Erich Schubert",
|
||||||
"wrrrzr",
|
"wrrrzr",
|
||||||
"1F616EMO",
|
|
||||||
"red-001 <red-001@outlook.ie>",
|
|
||||||
"veprogames",
|
|
||||||
"paradust7",
|
|
||||||
"AFCMS",
|
|
||||||
"siliconsniffer",
|
"siliconsniffer",
|
||||||
"Wuzzy",
|
"JosiahWI",
|
||||||
"Zemtzov7"
|
"veprogames",
|
||||||
|
"Miguel P.L",
|
||||||
|
"AFCMS"
|
||||||
],
|
],
|
||||||
"previous_contributors": [
|
"previous_contributors": [
|
||||||
"Ælla Chiana Moskopp (erle) <erle@dieweltistgarnichtso.net> [Logo]",
|
"Ælla Chiana Moskopp (erle) <erle@dieweltistgarnichtso.net> [Logo]",
|
||||||
"numzero",
|
"numzero",
|
||||||
|
"red-001 <red-001@outlook.ie>",
|
||||||
"Giuseppe Bilotta",
|
"Giuseppe Bilotta",
|
||||||
|
"HybridDog",
|
||||||
"ClobberXD",
|
"ClobberXD",
|
||||||
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
|
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
|
||||||
"MirceaKitsune <mirceakitsune@gmail.com>",
|
"MirceaKitsune <mirceakitsune@gmail.com>",
|
||||||
|
@ -75,6 +73,7 @@
|
||||||
"stujones11",
|
"stujones11",
|
||||||
"Rogier <rogier777@gmail.com>",
|
"Rogier <rogier777@gmail.com>",
|
||||||
"Gregory Currie (gregorycu)",
|
"Gregory Currie (gregorycu)",
|
||||||
|
"paradust7",
|
||||||
"JacobF",
|
"JacobF",
|
||||||
"Jeija <jeija@mesecons.net>"
|
"Jeija <jeija@mesecons.net>"
|
||||||
]
|
]
|
||||||
|
|
|
@ -33,13 +33,13 @@ end
|
||||||
|
|
||||||
local function buttonhandler(this, fields)
|
local function buttonhandler(this, fields)
|
||||||
if fields.reconfigure then
|
if fields.reconfigure then
|
||||||
|
local parent = this.parent
|
||||||
|
|
||||||
close_dialog(this)
|
close_dialog(this)
|
||||||
|
|
||||||
local maintab = ui.find_by_name("maintab")
|
|
||||||
|
|
||||||
local dlg = create_settings_dlg("controls_keyboard_and_mouse")
|
local dlg = create_settings_dlg("controls_keyboard_and_mouse")
|
||||||
dlg:set_parent(maintab)
|
dlg:set_parent(parent)
|
||||||
maintab:hide()
|
parent:hide()
|
||||||
dlg:show()
|
dlg:show()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
@ -74,7 +74,7 @@ local function create_rebind_keys_dlg()
|
||||||
return dlg
|
return dlg
|
||||||
end
|
end
|
||||||
|
|
||||||
function migrate_keybindings()
|
function migrate_keybindings(parent)
|
||||||
-- Show migration dialog if the user upgraded from an earlier version
|
-- 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
|
-- and this has not yet been shown before, *or* if keys settings had to be changed
|
||||||
if core.is_first_run then
|
if core.is_first_run then
|
||||||
|
@ -95,14 +95,14 @@ function migrate_keybindings()
|
||||||
end
|
end
|
||||||
|
|
||||||
if not has_migration then
|
if not has_migration then
|
||||||
return
|
return parent
|
||||||
end
|
end
|
||||||
|
|
||||||
local maintab = ui.find_by_name("maintab")
|
|
||||||
|
|
||||||
local dlg = create_rebind_keys_dlg()
|
local dlg = create_rebind_keys_dlg()
|
||||||
dlg:set_parent(maintab)
|
dlg:set_parent(parent)
|
||||||
maintab:hide()
|
parent:hide()
|
||||||
dlg:show()
|
dlg:show()
|
||||||
ui.update()
|
ui.update()
|
||||||
|
|
||||||
|
return dlg
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
local SETTING_NAME = "no_mtg_notification"
|
local SETTING_NAME = "no_mtg_notification"
|
||||||
|
|
||||||
function check_reinstall_mtg()
|
function check_reinstall_mtg(parent)
|
||||||
-- used to be in minetest.conf
|
-- used to be in minetest.conf
|
||||||
if core.settings:get_bool(SETTING_NAME) then
|
if core.settings:get_bool(SETTING_NAME) then
|
||||||
cache_settings:set_bool(SETTING_NAME, true)
|
cache_settings:set_bool(SETTING_NAME, true)
|
||||||
|
@ -19,14 +19,14 @@ function check_reinstall_mtg()
|
||||||
end
|
end
|
||||||
|
|
||||||
if cache_settings:get_bool(SETTING_NAME) then
|
if cache_settings:get_bool(SETTING_NAME) then
|
||||||
return
|
return parent
|
||||||
end
|
end
|
||||||
|
|
||||||
local games = core.get_games()
|
local games = core.get_games()
|
||||||
for _, game in ipairs(games) do
|
for _, game in ipairs(games) do
|
||||||
if game.id == "minetest" then
|
if game.id == "minetest" then
|
||||||
cache_settings:set_bool(SETTING_NAME, true)
|
cache_settings:set_bool(SETTING_NAME, true)
|
||||||
return
|
return parent
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -40,16 +40,16 @@ function check_reinstall_mtg()
|
||||||
end
|
end
|
||||||
if not mtg_world_found then
|
if not mtg_world_found then
|
||||||
cache_settings:set_bool(SETTING_NAME, true)
|
cache_settings:set_bool(SETTING_NAME, true)
|
||||||
return
|
return parent
|
||||||
end
|
end
|
||||||
|
|
||||||
local maintab = ui.find_by_name("maintab")
|
|
||||||
|
|
||||||
local dlg = create_reinstall_mtg_dlg()
|
local dlg = create_reinstall_mtg_dlg()
|
||||||
dlg:set_parent(maintab)
|
dlg:set_parent(parent)
|
||||||
maintab:hide()
|
parent:hide()
|
||||||
dlg:show()
|
dlg:show()
|
||||||
ui.update()
|
ui.update()
|
||||||
|
|
||||||
|
return dlg
|
||||||
end
|
end
|
||||||
|
|
||||||
local function get_formspec(dialogdata)
|
local function get_formspec(dialogdata)
|
||||||
|
@ -74,22 +74,22 @@ end
|
||||||
|
|
||||||
local function buttonhandler(this, fields)
|
local function buttonhandler(this, fields)
|
||||||
if fields.reinstall then
|
if fields.reinstall then
|
||||||
|
local parent = this.parent
|
||||||
|
|
||||||
-- Don't set "no_mtg_notification" here so that the dialog will be shown
|
-- Don't set "no_mtg_notification" here so that the dialog will be shown
|
||||||
-- again if downloading MTG fails for whatever reason.
|
-- again if downloading MTG fails for whatever reason.
|
||||||
this:delete()
|
this:delete()
|
||||||
|
|
||||||
local maintab = ui.find_by_name("maintab")
|
|
||||||
|
|
||||||
local dlg = create_contentdb_dlg(nil, "minetest/minetest")
|
local dlg = create_contentdb_dlg(nil, "minetest/minetest")
|
||||||
dlg:set_parent(maintab)
|
dlg:set_parent(parent)
|
||||||
maintab:hide()
|
parent:hide()
|
||||||
dlg:show()
|
dlg:show()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
if fields.dismiss then
|
if fields.dismiss then
|
||||||
cache_settings:set_bool("no_mtg_notification", true)
|
cache_settings:set_bool(SETTING_NAME, true)
|
||||||
this:delete()
|
this:delete()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
|
@ -112,8 +112,12 @@ local function init_globals()
|
||||||
tv_main:show()
|
tv_main:show()
|
||||||
ui.update()
|
ui.update()
|
||||||
|
|
||||||
check_reinstall_mtg()
|
-- synchronous, chain parents to only show one at a time
|
||||||
migrate_keybindings()
|
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()
|
check_new_version()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -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 built-in knockback and related functions entirely
|
||||||
* remove `safe` parameter from `core.serialize`, always enforce `safe = true`.
|
* remove `safe` parameter from `core.serialize`, always enforce `safe = true`.
|
||||||
possibly error when `loadstring` calls are encountered in `core.deserialize`.
|
possibly error when `loadstring` calls are encountered in `core.deserialize`.
|
||||||
|
* introduce strict type checking for all instances of `v3s16` / `v3f` read from Lua
|
||||||
|
|
|
@ -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`),
|
**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).
|
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.
|
`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 <http://www.luanti.org/>
|
* More information at <http://www.luanti.org/>
|
||||||
* Luanti Documentation: <https://docs.luanti.org/>
|
* Additional documentation: <https://docs.luanti.org/>
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
228
doc/lua_api.md
228
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).
|
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.
|
`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 <http://www.luanti.org/>
|
* More information at <http://www.luanti.org/>
|
||||||
* Luanti Documentation: <https://docs.luanti.org/>
|
* Additional documentation: <https://docs.luanti.org/>
|
||||||
* (Unofficial) Minetest Modding Book by rubenwardy: <https://rubenwardy.com/minetest_modding_book/>
|
* (Unofficial) Luanti Modding Book by rubenwardy: <https://rubenwardy.com/minetest_modding_book/>
|
||||||
* Modding tools: <https://github.com/luanti-org/modtools>
|
* Modding tools: <https://github.com/luanti-org/modtools>
|
||||||
|
|
||||||
Introduction
|
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).
|
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)
|
You can use the [Khronos glTF validator](https://github.com/KhronosGroup/glTF-Validator)
|
||||||
to check whether a model is a valid glTF file.
|
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:
|
The final fractal value noise variation is created as follows:
|
||||||
|
|
||||||
|
```
|
||||||
noise = offset + scale * (octave1 +
|
noise = offset + scale * (octave1 +
|
||||||
octave2 * persistence +
|
octave2 * persistence +
|
||||||
octave3 * persistence ^ 2 +
|
octave3 * persistence ^ 2 +
|
||||||
octave4 * persistence ^ 3 +
|
octave4 * persistence ^ 3 +
|
||||||
...)
|
...)
|
||||||
|
```
|
||||||
|
|
||||||
Noise Parameters
|
Noise Parameters
|
||||||
----------------
|
----------------
|
||||||
|
@ -4699,11 +4707,13 @@ with restraint.
|
||||||
The absolute value of each octave's noise variation is used when combining the
|
The absolute value of each octave's noise variation is used when combining the
|
||||||
octaves. The final value noise variation is created as follows:
|
octaves. The final value noise variation is created as follows:
|
||||||
|
|
||||||
|
```
|
||||||
noise = offset + scale * (abs(octave1) +
|
noise = offset + scale * (abs(octave1) +
|
||||||
abs(octave2) * persistence +
|
abs(octave2) * persistence +
|
||||||
abs(octave3) * persistence ^ 2 +
|
abs(octave3) * persistence ^ 2 +
|
||||||
abs(octave4) * persistence ^ 3 +
|
abs(octave4) * persistence ^ 3 +
|
||||||
...)
|
...)
|
||||||
|
```
|
||||||
|
|
||||||
### Format example
|
### 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,
|
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.
|
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
|
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
|
Note that `VoxelManip:read_from_map()` returns two position vectors. The region
|
||||||
formed by these positions indicate the minimum and maximum (respectively)
|
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
|
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()`,
|
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
|
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
|
Nodes in a VoxelManip object may also be read in bulk to a flat array table
|
||||||
using:
|
using:
|
||||||
|
|
||||||
* `VoxelManip:get_data()` for node content (in Content ID form, see section
|
* `VoxelManip:get_data()` for node content (in Content ID form, see section
|
||||||
[Content IDs]),
|
[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.
|
* `VoxelManip:get_param2_data()` for the node type-dependent "param2" values.
|
||||||
|
|
||||||
See section [Flat array format] for more details.
|
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
|
Once the bulk data has been edited to your liking, the internal VoxelManip
|
||||||
state can be set using:
|
state can be set using:
|
||||||
|
|
||||||
* `VoxelManip:set_data()` for node content (in Content ID form, see section
|
* `VoxelManip:set_data()` or
|
||||||
[Content IDs]),
|
* `VoxelManip:set_light_data()` or
|
||||||
* `VoxelManip:set_light_data()` for node light levels, and
|
* `VoxelManip:set_param2_data()`
|
||||||
* `VoxelManip:set_param2_data()` for the node type-dependent `param2` values.
|
|
||||||
|
|
||||||
The parameter to each of the above three functions can use any table at all in
|
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
|
the same flat array format as produced by `get_data()` etc. and is not required
|
||||||
to be a table retrieved from `get_data()`.
|
to be a table retrieved from `get_data()`.
|
||||||
|
|
||||||
Once the internal VoxelManip state has been modified to your liking, the
|
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
|
### Flat array format
|
||||||
|
|
||||||
|
@ -5173,15 +5183,22 @@ inside the VoxelManip.
|
||||||
Methods
|
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`.
|
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
|
* Note that calling this multiple times will *add* to the area loaded in the
|
||||||
VoxelManip, and not reset it.
|
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
|
* `write_to_map([light])`: Writes the data loaded from the `VoxelManip` back to
|
||||||
the map.
|
the map.
|
||||||
* **important**: data must be set using `VoxelManip:set_data()` before
|
* **important**: you should call `set_data()` before this, or nothing will change.
|
||||||
calling this.
|
|
||||||
* if `light` is true, then lighting is automatically recalculated.
|
* if `light` is true, then lighting is automatically recalculated.
|
||||||
The default value is true.
|
The default value is true.
|
||||||
If `light` is false, no light calculations happen, and you should correct
|
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.
|
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!
|
* 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.
|
* `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`
|
`VoxelArea`
|
||||||
-----------
|
-----------
|
||||||
|
@ -6562,13 +6588,10 @@ Environment access
|
||||||
* The actual seed used is the noiseparams seed plus the world seed.
|
* The actual seed used is the noiseparams seed plus the world seed.
|
||||||
* `core.get_value_noise(seeddiff, octaves, persistence, spread)`
|
* `core.get_value_noise(seeddiff, octaves, persistence, spread)`
|
||||||
* Deprecated: use `core.get_value_noise(noiseparams)` instead.
|
* Deprecated: use `core.get_value_noise(noiseparams)` instead.
|
||||||
* Return world-specific value noise
|
|
||||||
* `core.get_perlin(noiseparams)`
|
* `core.get_perlin(noiseparams)`
|
||||||
* Deprecated: use `core.get_value_noise(noiseparams)` instead.
|
* Deprecated: renamed to `core.get_value_noise` in version 5.12.0.
|
||||||
* Return world-specific value noise (was not Perlin noise)
|
|
||||||
* `core.get_perlin(seeddiff, octaves, persistence, spread)`
|
* `core.get_perlin(seeddiff, octaves, persistence, spread)`
|
||||||
* Deprecated: use `core.get_value_noise(noiseparams)` instead.
|
* Deprecated: renamed to `core.get_value_noise` in version 5.12.0.
|
||||||
* Return world-specific value noise (was not Perlin noise)
|
|
||||||
* `core.get_voxel_manip([pos1, pos2])`
|
* `core.get_voxel_manip([pos1, pos2])`
|
||||||
* Return voxel manipulator object.
|
* Return voxel manipulator object.
|
||||||
* Loads the manipulator from the map if positions are passed.
|
* 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
|
* `get_state()`: return generator state encoded in string
|
||||||
* `set_state(state_string)`: restore generator state from encoded 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 `<size.x>` times `<size.y>` 2D array of 2D noise
|
|
||||||
with values starting at `pos={x=,y=}`
|
|
||||||
* `get_3d_map(pos)`: returns a `<size.x>` times `<size.y>` times `<size.z>`
|
|
||||||
3D array of 3D noise with values starting at `pos={x=,y=,z=}`.
|
|
||||||
* `get_2d_map_flat(pos, buffer)`: returns a flat `<size.x * size.y>` 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`
|
`PlayerMetaRef`
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
@ -9184,14 +9135,17 @@ end
|
||||||
The map is loaded as the ray advances. If the map is modified after the
|
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.
|
`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
|
It can be created via `Raycast(pos1, pos2, objects, liquids, pointabilities)`
|
||||||
`core.raycast(pos1, pos2, objects, liquids)` where:
|
or `core.raycast(pos1, pos2, objects, liquids, pointabilities)` where:
|
||||||
|
|
||||||
* `pos1`: start of the ray
|
* `pos1`: start of the ray
|
||||||
* `pos2`: end 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
|
* `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
|
### Limitations
|
||||||
|
|
||||||
|
@ -9307,6 +9261,81 @@ to restrictions of JSON.
|
||||||
|
|
||||||
* All methods in MetaDataRef
|
* 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 `<size.x>` times `<size.y>` 2D array of 2D noise
|
||||||
|
with values starting at `pos={x=,y=}`
|
||||||
|
* `get_3d_map(pos)`: returns a `<size.x>` times `<size.y>` times `<size.z>`
|
||||||
|
3D array of 3D noise with values starting at `pos={x=,y=,z=}`.
|
||||||
|
* `get_2d_map_flat(pos, buffer)`: returns a flat `<size.x * size.y>` 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 = "",
|
mesh = "",
|
||||||
-- File name of mesh when using "mesh" drawtype
|
-- File name of mesh when using "mesh" drawtype
|
||||||
-- The center of the node is the model origin.
|
-- The center of the node is the model origin.
|
||||||
-- For legacy reasons, models in OBJ format use a scale of 1 node = 1 unit;
|
-- For legacy reasons, this uses a different scale depending on the mesh:
|
||||||
-- all other model file formats use a scale of 1 node = 10 units,
|
-- 1. For glTF models: 10 units = 1 node (consistent with the scale for entities).
|
||||||
-- consistent with the scale used 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 = {
|
selection_box = {
|
||||||
-- see [Node boxes] for possibilities
|
-- see [Node boxes] for possibilities
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
Luanti Lua Mainmenu API Reference 5.12.0
|
Luanti Lua Mainmenu API Reference 5.13.0
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
|
@ -23,8 +23,8 @@ Callbacks
|
||||||
* `core.button_handler(fields)`: called when a button is pressed.
|
* `core.button_handler(fields)`: called when a button is pressed.
|
||||||
* `fields` = `{name1 = value1, name2 = value2, ...}`
|
* `fields` = `{name1 = value1, name2 = value2, ...}`
|
||||||
* `core.event_handler(event)`
|
* `core.event_handler(event)`
|
||||||
* `event`: `"MenuQuit"`, `"KeyEnter"`, `"ExitButton"`, `"EditBoxEnter"` or
|
* `event`: `"MenuQuit"` (derived from `quit`) or `"FullscreenChange"`
|
||||||
`"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
|
* `core.on_before_close()`: called before the menu is closed, either to exit or
|
||||||
to join a game
|
to join a game
|
||||||
|
|
||||||
|
|
|
@ -12,4 +12,10 @@ Jordach (CC BY-SA 3.0):
|
||||||
|
|
||||||
Zeg9 (CC BY-SA 3.0):
|
Zeg9 (CC BY-SA 3.0):
|
||||||
testentities_lava_flan.x
|
testentities_lava_flan.x
|
||||||
testentities_lava_flan.png
|
testentities_lava_flan.png
|
||||||
|
|
||||||
|
"Cool Guy":
|
||||||
|
|
||||||
|
hecks (refer to irr/LICENSE):
|
||||||
|
testentities_cool_guy.x
|
||||||
|
testentities_cool_guy.png
|
||||||
|
|
BIN
games/devtest/mods/testentities/models/testentities_cool_guy.png
Normal file
BIN
games/devtest/mods/testentities/models/testentities_cool_guy.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
2
games/devtest/mods/testentities/models/testentities_cool_guy.x
Executable file
2
games/devtest/mods/testentities/models/testentities_cool_guy.x
Executable file
File diff suppressed because one or more lines are too long
|
@ -102,6 +102,19 @@ core.register_entity("testentities:lava_flan", {
|
||||||
end,
|
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
|
-- Advanced visual tests
|
||||||
|
|
||||||
-- An entity for testing animated and yaw-modulated sprites
|
-- An entity for testing animated and yaw-modulated sprites
|
||||||
|
|
|
@ -67,18 +67,6 @@ local function test_dynamic_media(cb, player)
|
||||||
end
|
end
|
||||||
unittests.register("test_dynamic_media", test_dynamic_media, {async=true, player=true})
|
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 function test_clear_meta(_, pos)
|
||||||
local ref = core.get_meta(pos)
|
local ref = core.get_meta(pos)
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@ namespace scene
|
||||||
//! Interface for an animated mesh.
|
//! Interface for an animated mesh.
|
||||||
/** There are already simple implementations of this interface available so
|
/** 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 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,
|
You might want to use irr::scene::SMesh, irr::scene::SMeshBuffer etc.
|
||||||
irr::scene::SMeshBuffer etc. */
|
*/
|
||||||
class IAnimatedMesh : public IMesh
|
class IAnimatedMesh : public IMesh
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -34,22 +34,8 @@ public:
|
||||||
scene node the mesh is instantiated in.*/
|
scene node the mesh is instantiated in.*/
|
||||||
virtual void setAnimationSpeed(f32 fps) = 0;
|
virtual void setAnimationSpeed(f32 fps) = 0;
|
||||||
|
|
||||||
//! Returns the IMesh interface for a frame.
|
//! Returns the type of the animated mesh. Useful for safe downcasts.
|
||||||
/** \param frame: Frame number, >= 0, <= getMaxFrameNumber()
|
E_ANIMATED_MESH_TYPE getMeshType() const = 0;
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace scene
|
} // end namespace scene
|
||||||
|
|
|
@ -12,35 +12,8 @@ namespace irr
|
||||||
{
|
{
|
||||||
namespace scene
|
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;
|
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.
|
//! Scene node capable of displaying an animated mesh.
|
||||||
class IAnimatedMeshSceneNode : public ISceneNode
|
class IAnimatedMeshSceneNode : public ISceneNode
|
||||||
{
|
{
|
||||||
|
@ -120,11 +93,10 @@ public:
|
||||||
/** When true the animations are played looped */
|
/** When true the animations are played looped */
|
||||||
virtual bool getLoopMode() const = 0;
|
virtual bool getLoopMode() const = 0;
|
||||||
|
|
||||||
//! Sets a callback interface which will be called if an animation playback has ended.
|
//! Will be called right after the joints have been animated,
|
||||||
/** Set this to 0 to disable the callback again.
|
//! but before the transforms have been propagated recursively to children.
|
||||||
Please note that this will only be called when in non looped
|
virtual void setOnAnimateCallback(
|
||||||
mode, see IAnimatedMeshSceneNode::setLoopMode(). */
|
const std::function<void(f32 dtime)> &cb) = 0;
|
||||||
virtual void setAnimationEndCallback(IAnimationEndCallBack *callback = 0) = 0;
|
|
||||||
|
|
||||||
//! Sets if the scene node should not copy the materials of the mesh but use them in a read only style.
|
//! 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
|
/** In this way it is possible to change the materials a mesh
|
||||||
|
@ -139,20 +111,15 @@ public:
|
||||||
virtual void setMesh(IAnimatedMesh *mesh) = 0;
|
virtual void setMesh(IAnimatedMesh *mesh) = 0;
|
||||||
|
|
||||||
//! Returns the current mesh
|
//! Returns the current mesh
|
||||||
virtual IAnimatedMesh *getMesh(void) = 0;
|
virtual IAnimatedMesh *getMesh() = 0;
|
||||||
|
|
||||||
//! Set how the joints should be updated on render
|
|
||||||
virtual void setJointMode(E_JOINT_UPDATE_ON_RENDER mode) = 0;
|
|
||||||
|
|
||||||
//! Sets the transition time in seconds
|
//! Sets the transition time in seconds
|
||||||
/** Note: This needs to enable joints, and setJointmode set to
|
/** Note: You must call animateJoints(), or the mesh will not animate. */
|
||||||
EJUOR_CONTROL. You must call animateJoints(), or the mesh will
|
|
||||||
not animate. */
|
|
||||||
virtual void setTransitionTime(f32 Time) = 0;
|
virtual void setTransitionTime(f32 Time) = 0;
|
||||||
|
|
||||||
//! animates the joints in the mesh based on the current frame.
|
//! animates the joints in the mesh based on the current frame.
|
||||||
/** Also takes in to account transitions. */
|
/** Also takes in to account transitions. */
|
||||||
virtual void animateJoints(bool CalculateAbsolutePositions = true) = 0;
|
virtual void animateJoints() = 0;
|
||||||
|
|
||||||
//! render mesh ignoring its transformation.
|
//! render mesh ignoring its transformation.
|
||||||
/** Culling is unaffected. */
|
/** Culling is unaffected. */
|
||||||
|
|
|
@ -11,85 +11,41 @@ namespace irr
|
||||||
namespace scene
|
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.
|
//! Interface for bones used for skeletal animation.
|
||||||
/** Used with SkinnedMesh and IAnimatedMeshSceneNode. */
|
/** Used with SkinnedMesh and IAnimatedMeshSceneNode. */
|
||||||
class IBoneSceneNode : public ISceneNode
|
class IBoneSceneNode : public ISceneNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id = -1) :
|
IBoneSceneNode(ISceneNode *parent, ISceneManager *mgr,
|
||||||
ISceneNode(parent, mgr, id), positionHint(-1), scaleHint(-1), rotationHint(-1) {}
|
s32 id = -1, u32 boneIndex = 0,
|
||||||
|
const std::optional<std::string> &boneName = std::nullopt)
|
||||||
|
:
|
||||||
|
ISceneNode(parent, mgr, id),
|
||||||
|
BoneIndex(boneIndex)
|
||||||
|
{
|
||||||
|
setName(boneName);
|
||||||
|
}
|
||||||
|
|
||||||
//! Get the index of the bone
|
//! Returns the index of the bone
|
||||||
virtual u32 getBoneIndex() const = 0;
|
u32 getBoneIndex() const
|
||||||
|
{
|
||||||
|
return BoneIndex;
|
||||||
|
}
|
||||||
|
|
||||||
//! Sets the animation mode of the bone.
|
//! returns the axis aligned bounding box of this node
|
||||||
/** \return True if successful. (Unused) */
|
const core::aabbox3d<f32> &getBoundingBox() const override
|
||||||
virtual bool setAnimationMode(E_BONE_ANIMATION_MODE mode) = 0;
|
{
|
||||||
|
return Box;
|
||||||
|
}
|
||||||
|
|
||||||
//! Gets the current animation mode of the bone
|
const u32 BoneIndex;
|
||||||
virtual E_BONE_ANIMATION_MODE getAnimationMode() const = 0;
|
|
||||||
|
|
||||||
//! Get the axis aligned bounding box of this node
|
// Bogus box; bone scene nodes are not rendered anyways.
|
||||||
const core::aabbox3d<f32> &getBoundingBox() const override = 0;
|
static constexpr core::aabbox3d<f32> Box = {{0, 0, 0}};
|
||||||
|
|
||||||
//! Returns the relative transformation of the scene node.
|
|
||||||
// virtual core::matrix4 getRelativeTransformation() const = 0;
|
|
||||||
|
|
||||||
//! The animation method.
|
|
||||||
void OnAnimate(u32 timeMs) override = 0;
|
|
||||||
|
|
||||||
//! The render method.
|
//! The render method.
|
||||||
/** Does nothing as bones are not visible. */
|
/** Does nothing as bones are not visible. */
|
||||||
void render() override {}
|
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
|
} // end namespace scene
|
||||||
|
|
|
@ -20,38 +20,6 @@ enum E_ANIMATED_MESH_TYPE
|
||||||
//! Unknown animated mesh type.
|
//! Unknown animated mesh type.
|
||||||
EAMT_UNKNOWN = 0,
|
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
|
//! generic skinned mesh
|
||||||
EAMT_SKINNED,
|
EAMT_SKINNED,
|
||||||
|
|
||||||
|
@ -119,9 +87,7 @@ public:
|
||||||
virtual void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) = 0;
|
virtual void setDirty(E_BUFFER_TYPE buffer = EBT_VERTEX_AND_INDEX) = 0;
|
||||||
|
|
||||||
//! Returns the type of the meshes.
|
//! Returns the type of the meshes.
|
||||||
/** This is useful for making a safe downcast. For example,
|
/** This is useful for making a safe downcast.
|
||||||
if getMeshType() returns EAMT_MD2 it's safe to cast the
|
|
||||||
IMesh to IAnimatedMeshMD2.
|
|
||||||
Note: It's no longer just about animated meshes, that name has just historical reasons.
|
Note: It's no longer just about animated meshes, that name has just historical reasons.
|
||||||
\returns Type of the mesh */
|
\returns Type of the mesh */
|
||||||
virtual E_ANIMATED_MESH_TYPE getMeshType() const
|
virtual E_ANIMATED_MESH_TYPE getMeshType() const
|
||||||
|
|
|
@ -66,26 +66,6 @@ public:
|
||||||
IReferenceCounted::drop() for more information. */
|
IReferenceCounted::drop() for more information. */
|
||||||
virtual SMesh *createMeshCopy(IMesh *mesh) const = 0;
|
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
|
//! Apply a manipulator on the Meshbuffer
|
||||||
/** \param func A functor defining the mesh manipulation.
|
/** \param func A functor defining the mesh manipulation.
|
||||||
\param buffer The Meshbuffer to apply the manipulator to.
|
\param buffer The Meshbuffer to apply the manipulator to.
|
||||||
|
|
|
@ -32,7 +32,7 @@ public:
|
||||||
|
|
||||||
//! Get the currently defined mesh for display.
|
//! Get the currently defined mesh for display.
|
||||||
/** \return Pointer to mesh which is displayed by this node. */
|
/** \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.
|
//! 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
|
/** In this way it is possible to change the materials of a mesh
|
||||||
|
|
|
@ -94,16 +94,12 @@ public:
|
||||||
\param timeMs Current time in milliseconds. */
|
\param timeMs Current time in milliseconds. */
|
||||||
virtual void OnAnimate(u32 timeMs)
|
virtual void OnAnimate(u32 timeMs)
|
||||||
{
|
{
|
||||||
if (IsVisible) {
|
if (!IsVisible && Children.empty())
|
||||||
// update absolute position
|
return;
|
||||||
updateAbsolutePosition();
|
|
||||||
|
|
||||||
// perform the post render process on all children
|
updateAbsolutePosition();
|
||||||
|
for (auto *child : Children)
|
||||||
ISceneNodeList::iterator it = Children.begin();
|
child->OnAnimate(timeMs);
|
||||||
for (; it != Children.end(); ++it)
|
|
||||||
(*it)->OnAnimate(timeMs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Renders the node.
|
//! Renders the node.
|
||||||
|
|
|
@ -172,7 +172,7 @@ public:
|
||||||
\return Pointer to the texture, or 0 if the texture
|
\return Pointer to the texture, or 0 if the texture
|
||||||
could not be loaded. This pointer should not be dropped. See
|
could not be loaded. This pointer should not be dropped. See
|
||||||
IReferenceCounted::drop() for more information. */
|
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.
|
//! Get access to a named texture.
|
||||||
/** Loads the texture from disk if it is not
|
/** Loads the texture from disk if it is not
|
||||||
|
@ -184,7 +184,7 @@ public:
|
||||||
\return Pointer to the texture, or 0 if the texture
|
\return Pointer to the texture, or 0 if the texture
|
||||||
could not be loaded. This pointer should not be dropped. See
|
could not be loaded. This pointer should not be dropped. See
|
||||||
IReferenceCounted::drop() for more information. */
|
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
|
//! Returns amount of textures currently loaded
|
||||||
/** \return Amount of textures currently loaded */
|
/** \return Amount of textures currently loaded */
|
||||||
|
|
|
@ -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 <vector>
|
|
||||||
#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<f32>(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<s32>(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<f32> &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<IMesh *> Meshes;
|
|
||||||
|
|
||||||
//! The bounding box of this mesh
|
|
||||||
core::aabbox3d<f32> 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
|
|
|
@ -5,7 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "IMesh.h"
|
#include "IAnimatedMesh.h"
|
||||||
#include "IMeshBuffer.h"
|
#include "IMeshBuffer.h"
|
||||||
#include "aabbox3d.h"
|
#include "aabbox3d.h"
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace irr
|
||||||
namespace scene
|
namespace scene
|
||||||
{
|
{
|
||||||
//! Simple implementation of the IMesh interface.
|
//! Simple implementation of the IMesh interface.
|
||||||
struct SMesh final : public IMesh
|
struct SMesh final : public IAnimatedMesh
|
||||||
{
|
{
|
||||||
//! constructor
|
//! constructor
|
||||||
SMesh() {}
|
SMesh() {}
|
||||||
|
@ -134,6 +134,15 @@ struct SMesh final : public IMesh
|
||||||
|
|
||||||
//! The bounding box of this mesh
|
//! The bounding box of this mesh
|
||||||
core::aabbox3d<f32> BoundingBox{{0, 0, 0}};
|
core::aabbox3d<f32> 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
|
} // end namespace scene
|
||||||
|
|
|
@ -8,11 +8,18 @@
|
||||||
#include "ISceneManager.h"
|
#include "ISceneManager.h"
|
||||||
#include "SMeshBuffer.h"
|
#include "SMeshBuffer.h"
|
||||||
#include "SSkinMeshBuffer.h"
|
#include "SSkinMeshBuffer.h"
|
||||||
|
#include "aabbox3d.h"
|
||||||
|
#include "irrMath.h"
|
||||||
|
#include "irrTypes.h"
|
||||||
|
#include "matrix4.h"
|
||||||
#include "quaternion.h"
|
#include "quaternion.h"
|
||||||
#include "vector3d.h"
|
#include "vector3d.h"
|
||||||
|
#include "Transform.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace irr
|
namespace irr
|
||||||
{
|
{
|
||||||
|
@ -26,12 +33,20 @@ class ISceneManager;
|
||||||
class SkinnedMesh : public IAnimatedMesh
|
class SkinnedMesh : public IAnimatedMesh
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum class SourceFormat {
|
||||||
|
B3D,
|
||||||
|
X,
|
||||||
|
GLTF,
|
||||||
|
OTHER,
|
||||||
|
};
|
||||||
|
|
||||||
//! constructor
|
//! constructor
|
||||||
SkinnedMesh() :
|
SkinnedMesh(SourceFormat src_format) :
|
||||||
EndFrame(0.f), FramesPerSecond(25.f),
|
EndFrame(0.f), FramesPerSecond(25.f),
|
||||||
LastAnimatedFrame(-1), SkinnedLastFrame(false),
|
|
||||||
HasAnimation(false), PreparedForSkinning(false),
|
HasAnimation(false), PreparedForSkinning(false),
|
||||||
AnimateNormals(true), HardwareSkinning(false)
|
AnimateNormals(true),
|
||||||
|
SrcFormat(src_format)
|
||||||
{
|
{
|
||||||
SkinningBuffers = &LocalBuffers;
|
SkinningBuffers = &LocalBuffers;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +54,10 @@ public:
|
||||||
//! destructor
|
//! destructor
|
||||||
virtual ~SkinnedMesh();
|
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.
|
//! If the duration is 0, it is a static (=non animated) mesh.
|
||||||
f32 getMaxFrameNumber() const override;
|
f32 getMaxFrameNumber() const override;
|
||||||
|
|
||||||
|
@ -51,14 +70,12 @@ public:
|
||||||
The actual speed is set in the scene node the mesh is instantiated in.*/
|
The actual speed is set in the scene node the mesh is instantiated in.*/
|
||||||
void setAnimationSpeed(f32 fps) override;
|
void setAnimationSpeed(f32 fps) override;
|
||||||
|
|
||||||
//! returns the animated mesh for the given frame
|
//! Turns the given array of local matrices into an array of global matrices
|
||||||
IMesh *getMesh(f32) override;
|
//! by multiplying with respective parent matrices.
|
||||||
|
void calculateGlobalMatrices(std::vector<core::matrix4> &matrices) const;
|
||||||
|
|
||||||
//! Animates joints based on frame input
|
//! Performs a software skin on this mesh based on the given joint matrices
|
||||||
void animateMesh(f32 frame);
|
void skinMesh(const std::vector<core::matrix4> &animated_transforms);
|
||||||
|
|
||||||
//! Performs a software skin on this mesh based of joint positions
|
|
||||||
void skinMesh();
|
|
||||||
|
|
||||||
//! returns amount of mesh buffers.
|
//! returns amount of mesh buffers.
|
||||||
u32 getMeshBufferCount() const override;
|
u32 getMeshBufferCount() const override;
|
||||||
|
@ -76,14 +93,15 @@ public:
|
||||||
|
|
||||||
void setTextureSlot(u32 meshbufNr, u32 textureSlot);
|
void setTextureSlot(u32 meshbufNr, u32 textureSlot);
|
||||||
|
|
||||||
//! returns an axis aligned bounding box
|
//! Returns bounding box of the mesh *in static pose*.
|
||||||
const core::aabbox3d<f32> &getBoundingBox() const override {
|
const core::aabbox3d<f32> &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 {
|
void setBoundingBox(const core::aabbox3df &box) override {
|
||||||
BoundingBox = box;
|
StaticPoseBox = box;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! set the hardware mapping hint, for driver
|
//! set the hardware mapping hint, for driver
|
||||||
|
@ -127,28 +145,15 @@ public:
|
||||||
return !HasAnimation;
|
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
|
//! Refreshes vertex data cached in joints such as positions and normals
|
||||||
void refreshJointCache();
|
void refreshJointCache();
|
||||||
|
|
||||||
//! Moves the mesh into static position.
|
//! Moves the mesh into static position.
|
||||||
void resetAnimation();
|
void resetAnimation();
|
||||||
|
|
||||||
void updateBoundingBox();
|
|
||||||
|
|
||||||
//! Recovers the joints from the mesh
|
|
||||||
void recoverJointsFromMesh(std::vector<IBoneSceneNode *> &jointChildSceneNodes);
|
|
||||||
|
|
||||||
//! Transfers the joint data to the mesh
|
|
||||||
void transferJointsToMesh(const std::vector<IBoneSceneNode *> &jointChildSceneNodes);
|
|
||||||
|
|
||||||
//! Creates an array of joints from this mesh as children of node
|
//! Creates an array of joints from this mesh as children of node
|
||||||
void addJoints(std::vector<IBoneSceneNode *> &jointChildSceneNodes,
|
std::vector<IBoneSceneNode *> addJoints(
|
||||||
IAnimatedMeshSceneNode *node,
|
IAnimatedMeshSceneNode *node, ISceneManager *smgr);
|
||||||
ISceneManager *smgr);
|
|
||||||
|
|
||||||
//! A vertex weight
|
//! A vertex weight
|
||||||
struct SWeight
|
struct SWeight
|
||||||
|
@ -223,7 +228,7 @@ public:
|
||||||
|
|
||||||
static core::quaternion interpolateValue(core::quaternion from, core::quaternion to, f32 time) {
|
static core::quaternion interpolateValue(core::quaternion from, core::quaternion to, f32 time) {
|
||||||
core::quaternion result;
|
core::quaternion result;
|
||||||
result.slerp(from, to, time, 0.001f);
|
result.slerp(from, to, time);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,15 +280,14 @@ public:
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateTransform(f32 frame,
|
void updateTransform(f32 frame, core::Transform &transform) const
|
||||||
core::vector3df &t, core::quaternion &r, core::vector3df &s) const
|
|
||||||
{
|
{
|
||||||
if (auto pos = position.get(frame))
|
if (auto pos = position.get(frame))
|
||||||
t = *pos;
|
transform.translation = *pos;
|
||||||
if (auto rot = rotation.get(frame))
|
if (auto rot = rotation.get(frame))
|
||||||
r = *rot;
|
transform.rotation = *rot;
|
||||||
if (auto scl = scale.get(frame))
|
if (auto scl = scale.get(frame))
|
||||||
s = *scl;
|
transform.scale = *scl;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
|
@ -296,16 +300,34 @@ public:
|
||||||
//! Joints
|
//! Joints
|
||||||
struct SJoint
|
struct SJoint
|
||||||
{
|
{
|
||||||
SJoint() : GlobalSkinningSpace(false) {}
|
SJoint() {}
|
||||||
|
|
||||||
//! The name of this joint
|
//! The name of this joint
|
||||||
std::optional<std::string> Name;
|
std::optional<std::string> Name;
|
||||||
|
|
||||||
//! Local matrix of this joint
|
//! Local transformation to be set by loaders. Mutated by animation.
|
||||||
core::matrix4 LocalMatrix;
|
using VariantTransform = std::variant<core::Transform, core::matrix4>;
|
||||||
|
VariantTransform transform{core::Transform{}};
|
||||||
|
|
||||||
|
VariantTransform animate(f32 frame) const {
|
||||||
|
if (keys.empty())
|
||||||
|
return transform;
|
||||||
|
|
||||||
|
if (std::holds_alternative<core::matrix4>(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<core::Transform>(transform);
|
||||||
|
keys.updateTransform(frame, trs);
|
||||||
|
return {trs};
|
||||||
|
}
|
||||||
|
|
||||||
//! List of child joints
|
|
||||||
std::vector<SJoint *> Children;
|
|
||||||
|
|
||||||
//! List of attached meshes
|
//! List of attached meshes
|
||||||
std::vector<u32> AttachedMeshes;
|
std::vector<u32> AttachedMeshes;
|
||||||
|
@ -316,42 +338,49 @@ public:
|
||||||
//! Skin weights
|
//! Skin weights
|
||||||
std::vector<SWeight> Weights;
|
std::vector<SWeight> Weights;
|
||||||
|
|
||||||
|
//! Bounding box of all affected vertices, in local space
|
||||||
|
core::aabbox3df LocalBoundingBox{{0, 0, 0}};
|
||||||
|
|
||||||
//! Unnecessary for loaders, will be overwritten on finalize
|
//! 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 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
|
// The .x and .gltf formats pre-calculate this
|
||||||
std::optional<core::matrix4> GlobalInversedMatrix;
|
std::optional<core::matrix4> GlobalInversedMatrix;
|
||||||
private:
|
|
||||||
//! Internal members used by SkinnedMesh
|
|
||||||
friend class SkinnedMesh;
|
|
||||||
|
|
||||||
bool GlobalSkinningSpace;
|
void setParent(SJoint *parent) {
|
||||||
|
ParentJointID = parent ? parent->JointID : std::optional<u16>{};
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 JointID; // TODO refactor away: pointers -> IDs (problem: .x loader abuses SJoint)
|
||||||
|
std::optional<u16> ParentJointID;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//! Animates joints based on frame input
|
||||||
|
std::vector<SJoint::VariantTransform> animateMesh(f32 frame);
|
||||||
|
|
||||||
|
//! Calculates a bounding box given an animation in the form of global joint transforms.
|
||||||
|
core::aabbox3df calculateBoundingBox(
|
||||||
|
const std::vector<core::matrix4> &global_transforms);
|
||||||
|
|
||||||
|
void recalculateBaseBoundingBoxes();
|
||||||
|
|
||||||
const std::vector<SJoint *> &getAllJoints() const {
|
const std::vector<SJoint *> &getAllJoints() const {
|
||||||
return AllJoints;
|
return AllJoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void checkForAnimation();
|
bool checkForAnimation() const;
|
||||||
|
|
||||||
|
void topoSortJoints();
|
||||||
|
|
||||||
|
void prepareForSkinning();
|
||||||
|
|
||||||
|
void calculateStaticBoundingBox();
|
||||||
|
void calculateJointBoundingBoxes();
|
||||||
|
void calculateBufferBoundingBoxes();
|
||||||
|
|
||||||
void normalizeWeights();
|
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,
|
void calculateTangents(core::vector3df &normal,
|
||||||
core::vector3df &tangent, core::vector3df &binormal,
|
core::vector3df &tangent, core::vector3df &binormal,
|
||||||
const core::vector3df &vt1, const core::vector3df &vt2, const core::vector3df &vt3,
|
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
|
//! Mapping from meshbuffer number to bindable texture slot
|
||||||
std::vector<u32> TextureSlots;
|
std::vector<u32> TextureSlots;
|
||||||
|
|
||||||
|
//! Joints, topologically sorted (parents come before their children).
|
||||||
std::vector<SJoint *> AllJoints;
|
std::vector<SJoint *> AllJoints;
|
||||||
std::vector<SJoint *> RootJoints;
|
|
||||||
|
|
||||||
// bool can't be used here because std::vector<bool>
|
// bool can't be used here because std::vector<bool>
|
||||||
// doesn't allow taking a reference to individual elements.
|
// doesn't allow taking a reference to individual elements.
|
||||||
std::vector<std::vector<char>> Vertices_Moved;
|
std::vector<std::vector<char>> Vertices_Moved;
|
||||||
|
|
||||||
core::aabbox3d<f32> 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 EndFrame;
|
||||||
f32 FramesPerSecond;
|
f32 FramesPerSecond;
|
||||||
|
|
||||||
f32 LastAnimatedFrame;
|
|
||||||
bool SkinnedLastFrame;
|
|
||||||
|
|
||||||
bool HasAnimation;
|
bool HasAnimation;
|
||||||
bool PreparedForSkinning;
|
bool PreparedForSkinning;
|
||||||
bool AnimateNormals;
|
bool AnimateNormals;
|
||||||
bool HardwareSkinning;
|
|
||||||
|
SourceFormat SrcFormat;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Interface for mesh loaders
|
// Interface for mesh loaders
|
||||||
class SkinnedMeshBuilder : public SkinnedMesh {
|
class SkinnedMeshBuilder : public SkinnedMesh {
|
||||||
public:
|
public:
|
||||||
SkinnedMeshBuilder() : SkinnedMesh() {}
|
SkinnedMeshBuilder(SourceFormat src_format) : SkinnedMesh(src_format) {}
|
||||||
|
|
||||||
//! loaders should call this after populating the mesh
|
//! loaders should call this after populating the mesh
|
||||||
// returns *this, so do not try to drop the mesh builder instance
|
// returns *this, so do not try to drop the mesh builder instance
|
||||||
|
|
42
irr/include/Transform.h
Normal file
42
irr/include/Transform.h
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "irrMath.h"
|
||||||
|
#include <matrix4.h>
|
||||||
|
#include <vector3d.h>
|
||||||
|
#include <quaternion.h>
|
||||||
|
|
||||||
|
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
|
|
@ -180,7 +180,7 @@ public:
|
||||||
linear interpolation.
|
linear interpolation.
|
||||||
*/
|
*/
|
||||||
quaternion &slerp(quaternion q1, quaternion q2,
|
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.
|
//! Set this quaternion to represent a rotation from angle and axis.
|
||||||
/** Axis must be unit length.
|
/** Axis must be unit length.
|
||||||
|
|
|
@ -3,9 +3,13 @@
|
||||||
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
||||||
|
|
||||||
#include "CAnimatedMeshSceneNode.h"
|
#include "CAnimatedMeshSceneNode.h"
|
||||||
|
#include "CBoneSceneNode.h"
|
||||||
#include "IVideoDriver.h"
|
#include "IVideoDriver.h"
|
||||||
#include "ISceneManager.h"
|
#include "ISceneManager.h"
|
||||||
#include "S3DVertex.h"
|
#include "S3DVertex.h"
|
||||||
|
#include "Transform.h"
|
||||||
|
#include "irrTypes.h"
|
||||||
|
#include "matrix4.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
#include "SkinnedMesh.h"
|
#include "SkinnedMesh.h"
|
||||||
#include "IDummyTransformationSceneNode.h"
|
#include "IDummyTransformationSceneNode.h"
|
||||||
|
@ -17,6 +21,9 @@
|
||||||
#include "IFileSystem.h"
|
#include "IFileSystem.h"
|
||||||
#include "quaternion.h"
|
#include "quaternion.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <optional>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
namespace irr
|
namespace irr
|
||||||
{
|
{
|
||||||
|
@ -30,13 +37,13 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh *mesh,
|
||||||
const core::vector3df &rotation,
|
const core::vector3df &rotation,
|
||||||
const core::vector3df &scale) :
|
const core::vector3df &scale) :
|
||||||
IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale),
|
IAnimatedMeshSceneNode(parent, mgr, id, position, rotation, scale),
|
||||||
Mesh(0),
|
Mesh(nullptr),
|
||||||
StartFrame(0), EndFrame(0), FramesPerSecond(0.025f),
|
StartFrame(0), EndFrame(0), FramesPerSecond(0.025f),
|
||||||
CurrentFrameNr(0.f), LastTimeMs(0),
|
CurrentFrameNr(0.f), LastTimeMs(0),
|
||||||
TransitionTime(0), Transiting(0.f), TransitingBlend(0.f),
|
TransitionTime(0), Transiting(0.f), TransitingBlend(0.f),
|
||||||
JointMode(EJUOR_NONE), JointsUsed(false),
|
JointsUsed(false),
|
||||||
Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false),
|
Looping(true), ReadOnlyMaterials(false), RenderFromIdentity(false),
|
||||||
LoopCallBack(0), PassCount(0)
|
PassCount(0)
|
||||||
{
|
{
|
||||||
setMesh(mesh);
|
setMesh(mesh);
|
||||||
}
|
}
|
||||||
|
@ -44,8 +51,6 @@ CAnimatedMeshSceneNode::CAnimatedMeshSceneNode(IAnimatedMesh *mesh,
|
||||||
//! destructor
|
//! destructor
|
||||||
CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode()
|
CAnimatedMeshSceneNode::~CAnimatedMeshSceneNode()
|
||||||
{
|
{
|
||||||
if (LoopCallBack)
|
|
||||||
LoopCallBack->drop();
|
|
||||||
if (Mesh)
|
if (Mesh)
|
||||||
Mesh->drop();
|
Mesh->drop();
|
||||||
}
|
}
|
||||||
|
@ -87,8 +92,7 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
|
||||||
if (FramesPerSecond > 0.f) { // forwards...
|
if (FramesPerSecond > 0.f) { // forwards...
|
||||||
if (CurrentFrameNr > EndFrame)
|
if (CurrentFrameNr > EndFrame)
|
||||||
CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, EndFrame - StartFrame);
|
CurrentFrameNr = StartFrame + fmodf(CurrentFrameNr - StartFrame, EndFrame - StartFrame);
|
||||||
} else // backwards...
|
} else { // backwards...
|
||||||
{
|
|
||||||
if (CurrentFrameNr < StartFrame)
|
if (CurrentFrameNr < StartFrame)
|
||||||
CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, EndFrame - StartFrame);
|
CurrentFrameNr = EndFrame - fmodf(EndFrame - CurrentFrameNr, EndFrame - StartFrame);
|
||||||
}
|
}
|
||||||
|
@ -97,18 +101,9 @@ void CAnimatedMeshSceneNode::buildFrameNr(u32 timeMs)
|
||||||
|
|
||||||
CurrentFrameNr += timeMs * FramesPerSecond;
|
CurrentFrameNr += timeMs * FramesPerSecond;
|
||||||
if (FramesPerSecond > 0.f) { // forwards...
|
if (FramesPerSecond > 0.f) { // forwards...
|
||||||
if (CurrentFrameNr > EndFrame) {
|
CurrentFrameNr = std::min(CurrentFrameNr, EndFrame);
|
||||||
CurrentFrameNr = EndFrame;
|
} else { // backwards...
|
||||||
if (LoopCallBack)
|
CurrentFrameNr = std::max(CurrentFrameNr, StartFrame);
|
||||||
LoopCallBack->OnAnimationEnd(this);
|
|
||||||
}
|
|
||||||
} else // backwards...
|
|
||||||
{
|
|
||||||
if (CurrentFrameNr < StartFrame) {
|
|
||||||
CurrentFrameNr = StartFrame;
|
|
||||||
if (LoopCallBack)
|
|
||||||
LoopCallBack->OnAnimationEnd(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,38 +151,18 @@ void CAnimatedMeshSceneNode::OnRegisterSceneNode()
|
||||||
IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame()
|
IMesh *CAnimatedMeshSceneNode::getMeshForCurrentFrame()
|
||||||
{
|
{
|
||||||
if (Mesh->getMeshType() != EAMT_SKINNED) {
|
if (Mesh->getMeshType() != EAMT_SKINNED) {
|
||||||
return Mesh->getMesh(getFrameNr());
|
return Mesh;
|
||||||
} 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<SkinnedMesh *>(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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<SkinnedMesh *>(Mesh);
|
||||||
|
|
||||||
|
// Matrices have already been calculated in OnAnimate
|
||||||
|
skinnedMesh->skinMesh(PerJoint.GlobalMatrices);
|
||||||
|
|
||||||
|
return skinnedMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! OnAnimate() is called just before rendering the whole scene.
|
//! OnAnimate() is called just before rendering the whole scene.
|
||||||
|
@ -201,7 +176,28 @@ void CAnimatedMeshSceneNode::OnAnimate(u32 timeMs)
|
||||||
buildFrameNr(timeMs - LastTimeMs);
|
buildFrameNr(timeMs - LastTimeMs);
|
||||||
LastTimeMs = timeMs;
|
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);
|
IAnimatedMeshSceneNode::OnAnimate(timeMs);
|
||||||
|
|
||||||
|
if (auto *skinnedMesh = dynamic_cast<SkinnedMesh*>(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.
|
//! renders the node.
|
||||||
|
@ -218,15 +214,7 @@ void CAnimatedMeshSceneNode::render()
|
||||||
++PassCount;
|
++PassCount;
|
||||||
|
|
||||||
scene::IMesh *m = getMeshForCurrentFrame();
|
scene::IMesh *m = getMeshForCurrentFrame();
|
||||||
|
assert(m);
|
||||||
if (m) {
|
|
||||||
Box = m->getBoundingBox();
|
|
||||||
} else {
|
|
||||||
#ifdef _DEBUG
|
|
||||||
os::Printer::log("Animated Mesh returned no mesh to render.", ELL_WARNING);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
|
||||||
|
|
||||||
|
@ -294,11 +282,12 @@ void CAnimatedMeshSceneNode::render()
|
||||||
if (DebugDataVisible & scene::EDS_SKELETON) {
|
if (DebugDataVisible & scene::EDS_SKELETON) {
|
||||||
if (Mesh->getMeshType() == EAMT_SKINNED) {
|
if (Mesh->getMeshType() == EAMT_SKINNED) {
|
||||||
// draw skeleton
|
// draw skeleton
|
||||||
|
const auto &joints = (static_cast<SkinnedMesh *>(Mesh))->getAllJoints();
|
||||||
for (auto *joint : ((SkinnedMesh *)Mesh)->getAllJoints()) {
|
for (u16 i = 0; i < PerJoint.GlobalMatrices.size(); ++i) {
|
||||||
for (const auto *childJoint : joint->Children) {
|
const auto translation = PerJoint.GlobalMatrices[i].getTranslation();
|
||||||
driver->draw3DLine(joint->GlobalAnimatedMatrix.getTranslation(),
|
if (auto pjid = joints[i]->ParentJointID) {
|
||||||
childJoint->GlobalAnimatedMatrix.getTranslation(),
|
const auto parent_translation = PerJoint.GlobalMatrices[*pjid].getTranslation();
|
||||||
|
driver->draw3DLine(parent_translation, translation,
|
||||||
video::SColor(255, 51, 66, 255));
|
video::SColor(255, 51, 66, 255));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -407,12 +396,12 @@ IBoneSceneNode *CAnimatedMeshSceneNode::getJointNode(const c8 *jointName)
|
||||||
return 0;
|
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);
|
os::Printer::log("Joint was found in mesh, but is not loaded into node", jointName, ELL_WARNING);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JointChildSceneNodes[*number];
|
return PerJoint.SceneNodes[*number];
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Returns a pointer to a child node, which has the same transformation as
|
//! Returns a pointer to a child node, which has the same transformation as
|
||||||
|
@ -426,12 +415,12 @@ IBoneSceneNode *CAnimatedMeshSceneNode::getJointNode(u32 jointID)
|
||||||
|
|
||||||
checkJoints();
|
checkJoints();
|
||||||
|
|
||||||
if (JointChildSceneNodes.size() <= jointID) {
|
if (PerJoint.SceneNodes.size() <= jointID) {
|
||||||
os::Printer::log("Joint not loaded into node", ELL_WARNING);
|
os::Printer::log("Joint not loaded into node", ELL_WARNING);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return JointChildSceneNodes[jointID];
|
return PerJoint.SceneNodes[jointID];
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Gets joint count.
|
//! Gets joint count.
|
||||||
|
@ -452,9 +441,9 @@ bool CAnimatedMeshSceneNode::removeChild(ISceneNode *child)
|
||||||
{
|
{
|
||||||
if (ISceneNode::removeChild(child)) {
|
if (ISceneNode::removeChild(child)) {
|
||||||
if (JointsUsed) { // stop weird bugs caused while changing parents as the joints are being created
|
if (JointsUsed) { // stop weird bugs caused while changing parents as the joints are being created
|
||||||
for (u32 i = 0; i < JointChildSceneNodes.size(); ++i) {
|
for (u32 i = 0; i < PerJoint.SceneNodes.size(); ++i) {
|
||||||
if (JointChildSceneNodes[i] == child) {
|
if (PerJoint.SceneNodes[i] == child) {
|
||||||
JointChildSceneNodes[i] = 0; // remove link to child
|
PerJoint.SceneNodes[i] = 0; // remove link to child
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -478,22 +467,6 @@ bool CAnimatedMeshSceneNode::getLoopMode() const
|
||||||
return Looping;
|
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.
|
//! 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)
|
void CAnimatedMeshSceneNode::setReadOnlyMaterials(bool readonly)
|
||||||
{
|
{
|
||||||
|
@ -525,18 +498,15 @@ void CAnimatedMeshSceneNode::setMesh(IAnimatedMesh *mesh)
|
||||||
// get materials and bounding box
|
// get materials and bounding box
|
||||||
Box = Mesh->getBoundingBox();
|
Box = Mesh->getBoundingBox();
|
||||||
|
|
||||||
IMesh *m = Mesh->getMesh(0);
|
Materials.clear();
|
||||||
if (m) {
|
Materials.reallocate(Mesh->getMeshBufferCount());
|
||||||
Materials.clear();
|
|
||||||
Materials.reallocate(m->getMeshBufferCount());
|
|
||||||
|
|
||||||
for (u32 i = 0; i < m->getMeshBufferCount(); ++i) {
|
for (u32 i = 0; i < Mesh->getMeshBufferCount(); ++i) {
|
||||||
IMeshBuffer *mb = m->getMeshBuffer(i);
|
IMeshBuffer *mb = Mesh->getMeshBuffer(i);
|
||||||
if (mb)
|
if (mb)
|
||||||
Materials.push_back(mb->getMaterial());
|
Materials.push_back(mb->getMaterial());
|
||||||
else
|
else
|
||||||
Materials.push_back(video::SMaterial());
|
Materials.push_back(video::SMaterial());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up joint nodes
|
// clean up joint nodes
|
||||||
|
@ -556,14 +526,7 @@ void CAnimatedMeshSceneNode::updateAbsolutePosition()
|
||||||
IAnimatedMeshSceneNode::updateAbsolutePosition();
|
IAnimatedMeshSceneNode::updateAbsolutePosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set)
|
//! Sets the transition time in seconds (note: This needs to enable joints)
|
||||||
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)
|
|
||||||
//! you must call animateJoints(), or the mesh will not animate
|
//! you must call animateJoints(), or the mesh will not animate
|
||||||
void CAnimatedMeshSceneNode::setTransitionTime(f32 time)
|
void CAnimatedMeshSceneNode::setTransitionTime(f32 time)
|
||||||
{
|
{
|
||||||
|
@ -571,10 +534,6 @@ void CAnimatedMeshSceneNode::setTransitionTime(f32 time)
|
||||||
if (TransitionTime == ttime)
|
if (TransitionTime == ttime)
|
||||||
return;
|
return;
|
||||||
TransitionTime = ttime;
|
TransitionTime = ttime;
|
||||||
if (ttime != 0)
|
|
||||||
setJointMode(EJUOR_CONTROL);
|
|
||||||
else
|
|
||||||
setJointMode(EJUOR_NONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
|
//! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
|
||||||
|
@ -583,120 +542,104 @@ void CAnimatedMeshSceneNode::setRenderFromIdentity(bool enable)
|
||||||
RenderFromIdentity = enable;
|
RenderFromIdentity = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! updates the joint positions of this mesh
|
void CAnimatedMeshSceneNode::addJoints()
|
||||||
void CAnimatedMeshSceneNode::animateJoints(bool CalculateAbsolutePositions)
|
|
||||||
{
|
{
|
||||||
if (Mesh && Mesh->getMeshType() == EAMT_SKINNED) {
|
const auto &joints = static_cast<SkinnedMesh*>(Mesh)->getAllJoints();
|
||||||
checkJoints();
|
PerJoint.setN(joints.size());
|
||||||
const f32 frame = getFrameNr(); // old?
|
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<core::matrix4>(&joint->transform);
|
||||||
|
PerJoint.SceneNodes.push_back(new CBoneSceneNode(
|
||||||
|
parent, SceneManager, 0, i, joint->Name,
|
||||||
|
matrix ? core::Transform{} : std::get<core::Transform>(joint->transform),
|
||||||
|
matrix ? *matrix : std::optional<core::matrix4>{}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SkinnedMesh *skinnedMesh = static_cast<SkinnedMesh *>(Mesh);
|
void CAnimatedMeshSceneNode::updateJointSceneNodes(
|
||||||
|
const std::vector<SkinnedMesh::SJoint::VariantTransform> &transforms)
|
||||||
skinnedMesh->animateMesh(frame);
|
{
|
||||||
skinnedMesh->recoverJointsFromMesh(JointChildSceneNodes);
|
for (size_t i = 0; i < transforms.size(); ++i) {
|
||||||
|
const auto &transform = transforms[i];
|
||||||
//-----------------------------------------
|
auto *node = static_cast<CBoneSceneNode*>(PerJoint.SceneNodes[i]);
|
||||||
// Transition
|
if (const auto *trs = std::get_if<core::Transform>(&transform)) {
|
||||||
//-----------------------------------------
|
node->setTransform(*trs);
|
||||||
|
// .x lets animations override matrix transforms entirely.
|
||||||
if (Transiting != 0.f) {
|
node->Matrix = std::nullopt;
|
||||||
// Init additional matrices
|
} else {
|
||||||
if (PretransitingSave.size() < JointChildSceneNodes.size()) {
|
node->Matrix = std::get<core::matrix4>(transform);
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (CalculateAbsolutePositions) {
|
//! updates the joint positions of this mesh
|
||||||
//---slow---
|
void CAnimatedMeshSceneNode::animateJoints()
|
||||||
for (u32 n = 0; n < JointChildSceneNodes.size(); ++n) {
|
{
|
||||||
if (JointChildSceneNodes[n]->getParent() == this) {
|
if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
|
||||||
JointChildSceneNodes[n]->updateAbsolutePositionOfAllChildren(); // temp, should be an option
|
return;
|
||||||
}
|
|
||||||
|
checkJoints();
|
||||||
|
|
||||||
|
SkinnedMesh *skinnedMesh = static_cast<SkinnedMesh *>(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()
|
void CAnimatedMeshSceneNode::checkJoints()
|
||||||
{
|
{
|
||||||
if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
|
if (!Mesh || Mesh->getMeshType() != EAMT_SKINNED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!JointsUsed) {
|
if (!JointsUsed) {
|
||||||
for (u32 i = 0; i < JointChildSceneNodes.size(); ++i)
|
for (u32 i = 0; i < PerJoint.SceneNodes.size(); ++i)
|
||||||
removeChild(JointChildSceneNodes[i]);
|
removeChild(PerJoint.SceneNodes[i]);
|
||||||
JointChildSceneNodes.clear();
|
addJoints();
|
||||||
|
|
||||||
// Create joints for SkinnedMesh
|
|
||||||
((SkinnedMesh *)Mesh)->addJoints(JointChildSceneNodes, this, SceneManager);
|
|
||||||
((SkinnedMesh *)Mesh)->recoverJointsFromMesh(JointChildSceneNodes);
|
|
||||||
|
|
||||||
JointsUsed = true;
|
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()
|
void CAnimatedMeshSceneNode::beginTransition()
|
||||||
{
|
{
|
||||||
if (!JointsUsed)
|
if (!JointsUsed)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (TransitionTime != 0) {
|
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);
|
Transiting = core::reciprocal((f32)TransitionTime);
|
||||||
}
|
}
|
||||||
TransitingBlend = 0.f;
|
TransitingBlend = 0.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
|
||||||
*/
|
|
||||||
ISceneNode *CAnimatedMeshSceneNode::clone(ISceneNode *newParent, ISceneManager *newManager)
|
ISceneNode *CAnimatedMeshSceneNode::clone(ISceneNode *newParent, ISceneManager *newManager)
|
||||||
{
|
{
|
||||||
if (!newParent)
|
if (!newParent)
|
||||||
|
@ -722,19 +665,15 @@ ISceneNode *CAnimatedMeshSceneNode::clone(ISceneNode *newParent, ISceneManager *
|
||||||
newNode->EndFrame = EndFrame;
|
newNode->EndFrame = EndFrame;
|
||||||
newNode->FramesPerSecond = FramesPerSecond;
|
newNode->FramesPerSecond = FramesPerSecond;
|
||||||
newNode->CurrentFrameNr = CurrentFrameNr;
|
newNode->CurrentFrameNr = CurrentFrameNr;
|
||||||
newNode->JointMode = JointMode;
|
|
||||||
newNode->JointsUsed = JointsUsed;
|
newNode->JointsUsed = JointsUsed;
|
||||||
newNode->TransitionTime = TransitionTime;
|
newNode->TransitionTime = TransitionTime;
|
||||||
newNode->Transiting = Transiting;
|
newNode->Transiting = Transiting;
|
||||||
newNode->TransitingBlend = TransitingBlend;
|
newNode->TransitingBlend = TransitingBlend;
|
||||||
newNode->Looping = Looping;
|
newNode->Looping = Looping;
|
||||||
newNode->ReadOnlyMaterials = ReadOnlyMaterials;
|
newNode->ReadOnlyMaterials = ReadOnlyMaterials;
|
||||||
newNode->LoopCallBack = LoopCallBack;
|
|
||||||
if (newNode->LoopCallBack)
|
|
||||||
newNode->LoopCallBack->grab();
|
|
||||||
newNode->PassCount = PassCount;
|
newNode->PassCount = PassCount;
|
||||||
newNode->JointChildSceneNodes = JointChildSceneNodes;
|
newNode->PerJoint.SceneNodes = PerJoint.SceneNodes;
|
||||||
newNode->PretransitingSave = PretransitingSave;
|
newNode->PerJoint.PreTransSaves = PerJoint.PreTransSaves;
|
||||||
newNode->RenderFromIdentity = RenderFromIdentity;
|
newNode->RenderFromIdentity = RenderFromIdentity;
|
||||||
|
|
||||||
return newNode;
|
return newNode;
|
||||||
|
|
|
@ -4,9 +4,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "CBoneSceneNode.h"
|
||||||
#include "IAnimatedMeshSceneNode.h"
|
#include "IAnimatedMeshSceneNode.h"
|
||||||
#include "IAnimatedMesh.h"
|
#include "IAnimatedMesh.h"
|
||||||
|
|
||||||
|
#include "SkinnedMesh.h"
|
||||||
|
#include "Transform.h"
|
||||||
#include "matrix4.h"
|
#include "matrix4.h"
|
||||||
|
|
||||||
namespace irr
|
namespace irr
|
||||||
|
@ -54,9 +57,11 @@ public:
|
||||||
//! returns the current loop mode
|
//! returns the current loop mode
|
||||||
bool getLoopMode() const override;
|
bool getLoopMode() const override;
|
||||||
|
|
||||||
//! Sets a callback interface which will be called if an animation
|
void setOnAnimateCallback(
|
||||||
//! playback has ended. Set this to 0 to disable the callback again.
|
const std::function<void(f32 dtime)> &cb) override
|
||||||
void setAnimationEndCallback(IAnimationEndCallBack *callback = 0) override;
|
{
|
||||||
|
OnAnimateCallback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
//! sets the speed with which the animation is played
|
//! 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
|
//! 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
|
//! updates the absolute position based on the relative and the parents position
|
||||||
void updateAbsolutePosition() override;
|
void updateAbsolutePosition() override;
|
||||||
|
|
||||||
//! Set the joint update mode (0-unused, 1-get joints only, 2-set joints only, 3-move and set)
|
//! Sets the transition time in seconds (note: This needs to enable joints)
|
||||||
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)
|
|
||||||
//! you must call animateJoints(), or the mesh will not animate
|
//! you must call animateJoints(), or the mesh will not animate
|
||||||
void setTransitionTime(f32 Time) override;
|
void setTransitionTime(f32 Time) override;
|
||||||
|
|
||||||
|
void updateJointSceneNodes(const std::vector<SkinnedMesh::SJoint::VariantTransform> &transforms);
|
||||||
|
|
||||||
//! updates the joint positions of this mesh
|
//! 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)
|
//! render mesh ignoring its transformation. Used with ragdolls. (culling is unaffected)
|
||||||
void setRenderFromIdentity(bool On) override;
|
void setRenderFromIdentity(bool On) override;
|
||||||
|
@ -142,6 +148,7 @@ private:
|
||||||
|
|
||||||
void buildFrameNr(u32 timeMs);
|
void buildFrameNr(u32 timeMs);
|
||||||
void checkJoints();
|
void checkJoints();
|
||||||
|
void copyOldTransforms();
|
||||||
void beginTransition();
|
void beginTransition();
|
||||||
|
|
||||||
core::array<video::SMaterial> Materials;
|
core::array<video::SMaterial> Materials;
|
||||||
|
@ -158,19 +165,30 @@ private:
|
||||||
f32 Transiting; // is mesh transiting (plus cache of TransitionTime)
|
f32 Transiting; // is mesh transiting (plus cache of TransitionTime)
|
||||||
f32 TransitingBlend; // 0-1, calculated on buildFrameNr
|
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 JointsUsed;
|
||||||
|
|
||||||
bool Looping;
|
bool Looping;
|
||||||
bool ReadOnlyMaterials;
|
bool ReadOnlyMaterials;
|
||||||
bool RenderFromIdentity;
|
bool RenderFromIdentity;
|
||||||
|
|
||||||
IAnimationEndCallBack *LoopCallBack;
|
|
||||||
s32 PassCount;
|
s32 PassCount;
|
||||||
|
std::function<void(f32)> OnAnimateCallback;
|
||||||
|
|
||||||
std::vector<IBoneSceneNode *> JointChildSceneNodes;
|
struct PerJointData {
|
||||||
core::array<core::matrix4> PretransitingSave;
|
std::vector<CBoneSceneNode *> SceneNodes;
|
||||||
|
std::vector<core::matrix4> GlobalMatrices;
|
||||||
|
std::vector<std::optional<core::Transform>> 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
|
} // end namespace scene
|
||||||
|
|
|
@ -48,7 +48,7 @@ IAnimatedMesh *CB3DMeshFileLoader::createMesh(io::IReadFile *file)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
B3DFile = file;
|
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
|
ShowWarning = true; // If true a warning is issued if too many textures are used
|
||||||
VerticesStart = 0;
|
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);
|
os::Printer::log(logStr.c_str(), joint->Name.value_or("").c_str(), ELL_DEBUG);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
f32 position[3], scale[3], rotation[4];
|
core::Transform transform;
|
||||||
|
{
|
||||||
|
f32 t[3], s[3], r[4];
|
||||||
|
|
||||||
readFloats(position, 3);
|
readFloats(t, 3);
|
||||||
readFloats(scale, 3);
|
readFloats(s, 3);
|
||||||
readFloats(rotation, 4);
|
readFloats(r, 4);
|
||||||
|
|
||||||
joint->Animatedposition = core::vector3df(position[0], position[1], position[2]);
|
joint->transform = transform = {
|
||||||
joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]);
|
{t[0], t[1], t[2]},
|
||||||
joint->Animatedrotation = core::quaternion(rotation[1], rotation[2], rotation[3], rotation[0]);
|
{r[1], r[2], r[3], r[0]},
|
||||||
|
{s[0], s[1], s[2]},
|
||||||
// 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;
|
|
||||||
|
|
||||||
if (inJoint)
|
if (inJoint)
|
||||||
joint->GlobalMatrix = inJoint->GlobalMatrix * joint->LocalMatrix;
|
joint->GlobalMatrix = inJoint->GlobalMatrix * transform.buildMatrix();
|
||||||
else
|
else
|
||||||
joint->GlobalMatrix = joint->LocalMatrix;
|
joint->GlobalMatrix = transform.buildMatrix();
|
||||||
|
|
||||||
while (B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats
|
while (B3dStack.getLast().startposition + B3dStack.getLast().length > B3DFile->getPos()) // this chunk repeats
|
||||||
{
|
{
|
||||||
|
|
|
@ -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 <optional>
|
|
||||||
|
|
||||||
namespace irr
|
|
||||||
{
|
|
||||||
namespace scene
|
|
||||||
{
|
|
||||||
|
|
||||||
//! constructor
|
|
||||||
CBoneSceneNode::CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr, s32 id,
|
|
||||||
u32 boneIndex, const std::optional<std::string> &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<f32> &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
|
|
|
@ -7,6 +7,8 @@
|
||||||
// Used with SkinnedMesh and IAnimatedMeshSceneNode, for boned meshes
|
// Used with SkinnedMesh and IAnimatedMeshSceneNode, for boned meshes
|
||||||
|
|
||||||
#include "IBoneSceneNode.h"
|
#include "IBoneSceneNode.h"
|
||||||
|
#include "Transform.h"
|
||||||
|
#include "matrix4.h"
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
|
@ -21,49 +23,48 @@ public:
|
||||||
//! constructor
|
//! constructor
|
||||||
CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr,
|
CBoneSceneNode(ISceneNode *parent, ISceneManager *mgr,
|
||||||
s32 id = -1, u32 boneIndex = 0,
|
s32 id = -1, u32 boneIndex = 0,
|
||||||
const std::optional<std::string> &boneName = std::nullopt);
|
const std::optional<std::string> &boneName = std::nullopt,
|
||||||
|
const core::Transform &transform = {},
|
||||||
//! Returns the index of the bone
|
const std::optional<core::matrix4> &matrix = std::nullopt) :
|
||||||
u32 getBoneIndex() const override;
|
IBoneSceneNode(parent, mgr, id, boneIndex, boneName),
|
||||||
|
Matrix(matrix)
|
||||||
//! 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<f32> &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
|
|
||||||
{
|
{
|
||||||
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:
|
core::Transform getTransform() const
|
||||||
void helper_updateAbsolutePositionOfAllChildren(ISceneNode *Node);
|
{
|
||||||
|
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<f32> Box{-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
|
//! Some file formats alternatively let bones specify a transformation matrix.
|
||||||
|
//! If this is set, it overrides the TRS properties.
|
||||||
E_BONE_ANIMATION_MODE AnimationMode;
|
std::optional<core::matrix4> Matrix;
|
||||||
E_BONE_SKINNING_SPACE SkinningSpace;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace scene
|
} // end namespace scene
|
||||||
|
|
|
@ -347,7 +347,8 @@ IAnimatedMesh* SelfType::createMesh(io::IReadFile* file)
|
||||||
const char *filename = file->getFileName().c_str();
|
const char *filename = file->getFileName().c_str();
|
||||||
try {
|
try {
|
||||||
tiniergltf::GlTF model = parseGLTF(file);
|
tiniergltf::GlTF model = parseGLTF(file);
|
||||||
irr_ptr<SkinnedMeshBuilder> mesh(new SkinnedMeshBuilder());
|
irr_ptr<SkinnedMeshBuilder> mesh(new SkinnedMeshBuilder(
|
||||||
|
SkinnedMesh::SourceFormat::GLTF));
|
||||||
MeshExtractor extractor(std::move(model), mesh.get());
|
MeshExtractor extractor(std::move(model), mesh.get());
|
||||||
try {
|
try {
|
||||||
extractor.load();
|
extractor.load();
|
||||||
|
@ -538,34 +539,25 @@ static core::matrix4 loadTransform(const tiniergltf::Node::Matrix &m, SkinnedMes
|
||||||
mat[i] = static_cast<f32>(m[i]);
|
mat[i] = static_cast<f32>(m[i]);
|
||||||
mat = convertHandedness(mat);
|
mat = convertHandedness(mat);
|
||||||
|
|
||||||
// Decompose the matrix into translation, scale, and rotation.
|
// Note: "When a node is targeted for animation [...],
|
||||||
joint->Animatedposition = mat.getTranslation();
|
// only TRS properties MAY be present; matrix MUST NOT be present."
|
||||||
|
// Thus we MUST NOT do any decomposition, which in general need not exist.
|
||||||
auto scale = mat.getScale();
|
joint->transform = mat;
|
||||||
joint->Animatedscale = scale;
|
|
||||||
joint->Animatedrotation = mat.getRotationRadians(scale);
|
|
||||||
// Invert the rotation because it is applied using `getMatrix_transposed`,
|
|
||||||
// which again inverts.
|
|
||||||
joint->Animatedrotation.makeInverse();
|
|
||||||
|
|
||||||
return mat;
|
return mat;
|
||||||
}
|
}
|
||||||
|
|
||||||
static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, SkinnedMesh::SJoint *joint)
|
static core::matrix4 loadTransform(const tiniergltf::Node::TRS &trs, SkinnedMesh::SJoint *joint)
|
||||||
{
|
{
|
||||||
const auto &trans = trs.translation;
|
const auto &t = trs.translation;
|
||||||
const auto &rot = trs.rotation;
|
const auto &r = trs.rotation;
|
||||||
const auto &scale = trs.scale;
|
const auto &s = trs.scale;
|
||||||
core::matrix4 transMat;
|
core::Transform transform{
|
||||||
joint->Animatedposition = convertHandedness(core::vector3df(trans[0], trans[1], trans[2]));
|
convertHandedness(core::vector3df(t[0], t[1], t[2])),
|
||||||
transMat.setTranslation(joint->Animatedposition);
|
convertHandedness(core::quaternion(r[0], r[1], r[2], r[3])),
|
||||||
core::matrix4 rotMat;
|
core::vector3df(s[0], s[1], s[2]),
|
||||||
joint->Animatedrotation = convertHandedness(core::quaternion(rot[0], rot[1], rot[2], rot[3]));
|
};
|
||||||
core::quaternion(joint->Animatedrotation).getMatrix_transposed(rotMat);
|
joint->transform = transform;
|
||||||
joint->Animatedscale = core::vector3df(scale[0], scale[1], scale[2]);
|
return transform.buildMatrix();
|
||||||
core::matrix4 scaleMat;
|
|
||||||
scaleMat.setScale(joint->Animatedscale);
|
|
||||||
return transMat * rotMat * scaleMat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static core::matrix4 loadTransform(std::optional<std::variant<tiniergltf::Node::Matrix, tiniergltf::Node::TRS>> transform,
|
static core::matrix4 loadTransform(std::optional<std::variant<tiniergltf::Node::Matrix, tiniergltf::Node::TRS>> transform,
|
||||||
|
@ -583,8 +575,7 @@ void SelfType::MeshExtractor::loadNode(
|
||||||
const auto &node = m_gltf_model.nodes->at(nodeIdx);
|
const auto &node = m_gltf_model.nodes->at(nodeIdx);
|
||||||
auto *joint = m_irr_model->addJoint(parent);
|
auto *joint = m_irr_model->addJoint(parent);
|
||||||
const core::matrix4 transform = loadTransform(node.transform, joint);
|
const core::matrix4 transform = loadTransform(node.transform, joint);
|
||||||
joint->LocalMatrix = transform;
|
joint->GlobalMatrix = parent ? parent->GlobalMatrix * transform : transform;
|
||||||
joint->GlobalMatrix = parent ? parent->GlobalMatrix * joint->LocalMatrix : joint->LocalMatrix;
|
|
||||||
if (node.name.has_value()) {
|
if (node.name.has_value()) {
|
||||||
joint->Name = node.name->c_str();
|
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);
|
const auto &anim = m_gltf_model.animations->at(animIdx);
|
||||||
for (const auto &channel : anim.channels) {
|
for (const auto &channel : anim.channels) {
|
||||||
|
|
||||||
const auto &sampler = anim.samplers.at(channel.sampler);
|
const auto &sampler = anim.samplers.at(channel.sampler);
|
||||||
|
|
||||||
bool interpolate = ([&]() {
|
bool interpolate = ([&]() {
|
||||||
|
@ -662,6 +652,11 @@ void SelfType::MeshExtractor::loadAnimation(const std::size_t animIdx)
|
||||||
throw std::runtime_error("no animated node");
|
throw std::runtime_error("no animated node");
|
||||||
|
|
||||||
auto *joint = m_loaded_nodes.at(*channel.target.node);
|
auto *joint = m_loaded_nodes.at(*channel.target.node);
|
||||||
|
if (std::holds_alternative<core::matrix4>(joint->transform)) {
|
||||||
|
warn("nodes using matrix transforms must not be animated");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
switch (channel.target.path) {
|
switch (channel.target.path) {
|
||||||
case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: {
|
case tiniergltf::AnimationChannelTarget::Path::TRANSLATION: {
|
||||||
const auto outputAccessor = Accessor<core::vector3df>::make(m_gltf_model, sampler.output);
|
const auto outputAccessor = Accessor<core::vector3df>::make(m_gltf_model, sampler.output);
|
||||||
|
|
|
@ -320,7 +320,6 @@ set(IRRMESHLOADER
|
||||||
|
|
||||||
add_library(IRRMESHOBJ OBJECT
|
add_library(IRRMESHOBJ OBJECT
|
||||||
SkinnedMesh.cpp
|
SkinnedMesh.cpp
|
||||||
CBoneSceneNode.cpp
|
|
||||||
CMeshSceneNode.cpp
|
CMeshSceneNode.cpp
|
||||||
CAnimatedMeshSceneNode.cpp
|
CAnimatedMeshSceneNode.cpp
|
||||||
${IRRMESHLOADER}
|
${IRRMESHLOADER}
|
||||||
|
|
|
@ -35,7 +35,7 @@ void CMeshCache::removeMesh(const IMesh *const mesh)
|
||||||
if (!mesh)
|
if (!mesh)
|
||||||
return;
|
return;
|
||||||
for (u32 i = 0; i < Meshes.size(); ++i) {
|
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[i].Mesh->drop();
|
||||||
Meshes.erase(i);
|
Meshes.erase(i);
|
||||||
return;
|
return;
|
||||||
|
@ -53,7 +53,7 @@ u32 CMeshCache::getMeshCount() const
|
||||||
s32 CMeshCache::getMeshIndex(const IMesh *const mesh) const
|
s32 CMeshCache::getMeshIndex(const IMesh *const mesh) const
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < Meshes.size(); ++i) {
|
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;
|
return (s32)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ const io::SNamedPath &CMeshCache::getMeshName(const IMesh *const mesh) const
|
||||||
return emptyNamedPath;
|
return emptyNamedPath;
|
||||||
|
|
||||||
for (u32 i = 0; i < Meshes.size(); ++i) {
|
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;
|
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)
|
bool CMeshCache::renameMesh(const IMesh *const mesh, const io::path &name)
|
||||||
{
|
{
|
||||||
for (u32 i = 0; i < Meshes.size(); ++i) {
|
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[i].NamedPath.setPath(name);
|
||||||
Meshes.sort();
|
Meshes.sort();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include "SkinnedMesh.h"
|
#include "SkinnedMesh.h"
|
||||||
#include "SMesh.h"
|
#include "SMesh.h"
|
||||||
#include "CMeshBuffer.h"
|
#include "CMeshBuffer.h"
|
||||||
#include "SAnimatedMesh.h"
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
@ -178,34 +177,5 @@ SMesh *CMeshManipulator::createMeshCopy(scene::IMesh *mesh) const
|
||||||
return clone;
|
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 scene
|
||||||
} // end namespace irr
|
} // end namespace irr
|
||||||
|
|
|
@ -31,15 +31,6 @@ public:
|
||||||
|
|
||||||
//! Clones a static IMesh into a modifiable SMesh.
|
//! Clones a static IMesh into a modifiable SMesh.
|
||||||
SMesh *createMeshCopy(scene::IMesh *mesh) const override;
|
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
|
} // end namespace scene
|
||||||
|
|
|
@ -1238,7 +1238,7 @@ void CNullDriver::addOcclusionQuery(scene::ISceneNode *node, const scene::IMesh
|
||||||
else if (node->getType() == scene::ESNT_MESH)
|
else if (node->getType() == scene::ESNT_MESH)
|
||||||
mesh = static_cast<scene::IMeshSceneNode *>(node)->getMesh();
|
mesh = static_cast<scene::IMeshSceneNode *>(node)->getMesh();
|
||||||
else
|
else
|
||||||
mesh = static_cast<scene::IAnimatedMeshSceneNode *>(node)->getMesh()->getMesh(0);
|
mesh = static_cast<scene::IAnimatedMeshSceneNode *>(node)->getMesh();
|
||||||
if (!mesh)
|
if (!mesh)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include "IVideoDriver.h"
|
#include "IVideoDriver.h"
|
||||||
#include "SMesh.h"
|
#include "SMesh.h"
|
||||||
#include "SMeshBuffer.h"
|
#include "SMeshBuffer.h"
|
||||||
#include "SAnimatedMesh.h"
|
|
||||||
#include "IReadFile.h"
|
#include "IReadFile.h"
|
||||||
#include "fast_atof.h"
|
#include "fast_atof.h"
|
||||||
#include "coreutil.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
|
// Clean up the allocate obj file contents
|
||||||
delete[] buf;
|
delete[] buf;
|
||||||
// more cleaning up
|
// more cleaning up
|
||||||
cleanUp();
|
cleanUp();
|
||||||
mesh->drop();
|
|
||||||
|
|
||||||
return animMesh;
|
// Nothing in the mesh
|
||||||
|
if (mesh->getMeshBufferCount() == 0) {
|
||||||
|
mesh->drop();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
mesh->recalculateBoundingBox();
|
||||||
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Read RGB color
|
//! Read RGB color
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include "CSceneManager.h"
|
#include "CSceneManager.h"
|
||||||
#include "IVideoDriver.h"
|
#include "IVideoDriver.h"
|
||||||
#include "IFileSystem.h"
|
#include "IFileSystem.h"
|
||||||
#include "SAnimatedMesh.h"
|
|
||||||
#include "CMeshCache.h"
|
#include "CMeshCache.h"
|
||||||
#include "IGUIEnvironment.h"
|
#include "IGUIEnvironment.h"
|
||||||
#include "IMaterialRenderer.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
|
//! Get a skinned mesh, which is not available as header-only code
|
||||||
SkinnedMesh *CSceneManager::createSkinnedMesh()
|
SkinnedMesh *CSceneManager::createSkinnedMesh()
|
||||||
{
|
{
|
||||||
return new SkinnedMesh();
|
return new SkinnedMesh(SkinnedMesh::SourceFormat::OTHER);
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a scenemanager
|
// creates a scenemanager
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "CXMeshFileLoader.h"
|
#include "CXMeshFileLoader.h"
|
||||||
#include "SkinnedMesh.h"
|
#include "SkinnedMesh.h"
|
||||||
|
#include "Transform.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
#include "fast_atof.h"
|
#include "fast_atof.h"
|
||||||
|
@ -54,7 +55,7 @@ IAnimatedMesh *CXMeshFileLoader::createMesh(io::IReadFile *file)
|
||||||
u32 time = os::Timer::getRealTime();
|
u32 time = os::Timer::getRealTime();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AnimatedMesh = new SkinnedMeshBuilder();
|
AnimatedMesh = new SkinnedMeshBuilder(SkinnedMesh::SourceFormat::X);
|
||||||
|
|
||||||
SkinnedMesh *res = nullptr;
|
SkinnedMesh *res = nullptr;
|
||||||
if (load(file)) {
|
if (load(file)) {
|
||||||
|
@ -513,6 +514,7 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent)
|
||||||
if (n.has_value()) {
|
if (n.has_value()) {
|
||||||
JointID = *n;
|
JointID = *n;
|
||||||
joint = AnimatedMesh->getAllJoints()[JointID];
|
joint = AnimatedMesh->getAllJoints()[JointID];
|
||||||
|
joint->setParent(Parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -527,8 +529,6 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent)
|
||||||
#ifdef _XREADER_DEBUG
|
#ifdef _XREADER_DEBUG
|
||||||
os::Printer::log("using joint ", name.c_str(), ELL_DEBUG);
|
os::Printer::log("using joint ", name.c_str(), ELL_DEBUG);
|
||||||
#endif
|
#endif
|
||||||
if (Parent)
|
|
||||||
Parent->Children.push_back(joint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now inside a frame.
|
// Now inside a frame.
|
||||||
|
@ -552,12 +552,10 @@ bool CXMeshFileLoader::parseDataObjectFrame(SkinnedMesh::SJoint *Parent)
|
||||||
if (!parseDataObjectFrame(joint))
|
if (!parseDataObjectFrame(joint))
|
||||||
return false;
|
return false;
|
||||||
} else if (objectName == "FrameTransformMatrix") {
|
} else if (objectName == "FrameTransformMatrix") {
|
||||||
if (!parseDataObjectTransformationMatrix(joint->LocalMatrix))
|
core::matrix4 matrix;
|
||||||
|
if (!parseDataObjectTransformationMatrix(matrix))
|
||||||
return false;
|
return false;
|
||||||
|
joint->transform = matrix;
|
||||||
// joint->LocalAnimatedMatrix
|
|
||||||
// joint->LocalAnimatedMatrix.makeInverse();
|
|
||||||
// joint->LocalMatrix=tmp*joint->LocalAnimatedMatrix;
|
|
||||||
} else if (objectName == "Mesh") {
|
} else if (objectName == "Mesh") {
|
||||||
/*
|
/*
|
||||||
frame.Meshes.push_back(SXMesh());
|
frame.Meshes.push_back(SXMesh());
|
||||||
|
|
|
@ -7,7 +7,15 @@
|
||||||
#include "CBoneSceneNode.h"
|
#include "CBoneSceneNode.h"
|
||||||
#include "IAnimatedMeshSceneNode.h"
|
#include "IAnimatedMeshSceneNode.h"
|
||||||
#include "SSkinMeshBuffer.h"
|
#include "SSkinMeshBuffer.h"
|
||||||
|
#include "Transform.h"
|
||||||
|
#include "aabbox3d.h"
|
||||||
|
#include "irrMath.h"
|
||||||
|
#include "matrix4.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
#include "vector3d.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <variant>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
@ -48,183 +56,77 @@ void SkinnedMesh::setAnimationSpeed(f32 fps)
|
||||||
FramesPerSecond = fps;
|
FramesPerSecond = fps;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! returns the animated mesh based
|
// Keyframe Animation
|
||||||
IMesh *SkinnedMesh::getMesh(f32 frame)
|
|
||||||
{
|
|
||||||
// animate(frame,startFrameLoop, endFrameLoop);
|
|
||||||
if (frame == -1)
|
|
||||||
return this;
|
|
||||||
|
|
||||||
animateMesh(frame);
|
|
||||||
skinMesh();
|
using VariantTransform = SkinnedMesh::SJoint::VariantTransform;
|
||||||
return this;
|
std::vector<VariantTransform> SkinnedMesh::animateMesh(f32 frame)
|
||||||
|
{
|
||||||
|
assert(HasAnimation);
|
||||||
|
std::vector<VariantTransform> result;
|
||||||
|
result.reserve(AllJoints.size());
|
||||||
|
for (auto *joint : AllJoints)
|
||||||
|
result.push_back(joint->animate(frame));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
core::aabbox3df SkinnedMesh::calculateBoundingBox(
|
||||||
// Keyframe Animation
|
const std::vector<core::matrix4> &global_transforms)
|
||||||
//--------------------------------------------------------------------------
|
|
||||||
|
|
||||||
//! Animates joints based on frame input
|
|
||||||
void SkinnedMesh::animateMesh(f32 frame)
|
|
||||||
{
|
{
|
||||||
if (!HasAnimation || LastAnimatedFrame == frame)
|
assert(global_transforms.size() == AllJoints.size());
|
||||||
return;
|
core::aabbox3df result = StaticPartsBox;
|
||||||
|
// skeletal animation
|
||||||
LastAnimatedFrame = frame;
|
for (u16 i = 0; i < AllJoints.size(); ++i) {
|
||||||
SkinnedLastFrame = false;
|
auto box = AllJoints[i]->LocalBoundingBox;
|
||||||
|
global_transforms[i].transformBoxEx(box);
|
||||||
for (auto *joint : AllJoints) {
|
result.addInternalBox(box);
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
// rigid animation
|
||||||
// Note:
|
for (u16 i = 0; i < AllJoints.size(); ++i) {
|
||||||
// LocalAnimatedMatrix needs to be built at some point, but this function may be called lots of times for
|
for (u32 j : AllJoints[i]->AttachedMeshes) {
|
||||||
// one render (to play two animations at the same time) LocalAnimatedMatrix only needs to be built once.
|
auto box = (*SkinningBuffers)[j]->BoundingBox;
|
||||||
// a call to buildAllLocalAnimatedMatrices is needed before skinning the mesh, and before the user gets the joints to move
|
global_transforms[i].transformBoxEx(box);
|
||||||
|
result.addInternalBox(box);
|
||||||
//----------------
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SkinnedLastFrame = false;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinnedMesh::buildAllGlobalAnimatedMatrices(SJoint *joint, SJoint *parentJoint)
|
// Software Skinning
|
||||||
|
|
||||||
|
void SkinnedMesh::skinMesh(const std::vector<core::matrix4> &global_matrices)
|
||||||
{
|
{
|
||||||
if (!joint) {
|
if (!HasAnimation)
|
||||||
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)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//----------------
|
// rigid animation
|
||||||
// This is marked as "Temp!". A shiny dubloon to whomever can tell me why.
|
for (size_t i = 0; i < AllJoints.size(); ++i) {
|
||||||
buildAllGlobalAnimatedMatrices();
|
auto *joint = AllJoints[i];
|
||||||
//-----------------
|
for (u32 attachedMeshIdx : joint->AttachedMeshes) {
|
||||||
|
SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx];
|
||||||
SkinnedLastFrame = true;
|
Buffer->Transformation = global_matrices[i];
|
||||||
if (!HardwareSkinning) {
|
|
||||||
// rigid animation
|
|
||||||
for (auto *joint : AllJoints) {
|
|
||||||
for (u32 attachedMeshIdx : joint->AttachedMeshes) {
|
|
||||||
SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx];
|
|
||||||
Buffer->Transformation = joint->GlobalAnimatedMatrix;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear skinning helper array
|
|
||||||
for (std::vector<char> &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)
|
// clear skinning helper array
|
||||||
{
|
for (std::vector<char> &buf : Vertices_Moved)
|
||||||
if (joint->Weights.size()) {
|
std::fill(buf.begin(), buf.end(), false);
|
||||||
// Find this joints pull on vertices...
|
|
||||||
|
// 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.
|
// 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;
|
core::vector3df thisVertexMove, thisNormalMove;
|
||||||
|
|
||||||
auto &buffersUsed = *SkinningBuffers;
|
auto &buffersUsed = *SkinningBuffers;
|
||||||
|
|
||||||
// Skin Vertices Positions and Normals...
|
// Skin Vertices, Positions and Normals
|
||||||
for (const auto &weight : joint->Weights) {
|
for (const auto &weight : joint->Weights) {
|
||||||
// Pull this vertex...
|
// Pull this vertex...
|
||||||
jointVertexPull.transformVect(thisVertexMove, weight.StaticPos);
|
jointVertexPull.transformVect(thisVertexMove, weight.StaticPos);
|
||||||
|
@ -251,14 +153,11 @@ void SkinnedMesh::skinJoint(SJoint *joint, SJoint *parentJoint)
|
||||||
|
|
||||||
//*(weight._Pos) += thisVertexMove * weight.strength;
|
//*(weight._Pos) += thisVertexMove * weight.strength;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffersUsed[weight.buffer_id]->boundingBoxNeedsRecalculated();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skin all children
|
for (auto *buffer : *SkinningBuffers)
|
||||||
for (auto *childJoint : joint->Children)
|
buffer->setDirty(EBT_VERTEX);
|
||||||
skinJoint(childJoint, joint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Gets joint count.
|
//! Gets joint count.
|
||||||
|
@ -310,7 +209,7 @@ IMeshBuffer *SkinnedMesh::getMeshBuffer(const video::SMaterial &material) const
|
||||||
if (LocalBuffers[i]->getMaterial() == material)
|
if (LocalBuffers[i]->getMaterial() == material)
|
||||||
return LocalBuffers[i];
|
return LocalBuffers[i];
|
||||||
}
|
}
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 SkinnedMesh::getTextureSlot(u32 meshbufNr) const
|
u32 SkinnedMesh::getTextureSlot(u32 meshbufNr) const
|
||||||
|
@ -337,29 +236,6 @@ void SkinnedMesh::setDirty(E_BUFFER_TYPE buffer)
|
||||||
LocalBuffers[i]->setDirty(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()
|
void SkinnedMesh::refreshJointCache()
|
||||||
{
|
{
|
||||||
// copy cache from the mesh...
|
// copy cache from the mesh...
|
||||||
|
@ -384,113 +260,192 @@ void SkinnedMesh::resetAnimation()
|
||||||
LocalBuffers[buffer_id]->getVertex(vertex_id)->Normal = weight.StaticNormal;
|
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<core::matrix4> &matrices) const
|
||||||
{
|
{
|
||||||
if (!joint && parentJoint) // bit of protection from endless loops
|
// Note that the joints are topologically sorted.
|
||||||
return;
|
for (u16 i = 0; i < AllJoints.size(); ++i) {
|
||||||
|
if (auto parent_id = AllJoints[i]->ParentJointID) {
|
||||||
// Go through the root bones
|
matrices[i] = matrices[*parent_id] * matrices[i];
|
||||||
if (!joint) {
|
}
|
||||||
for (auto *rootJoint : RootJoints)
|
|
||||||
calculateGlobalMatrices(rootJoint, nullptr);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
for (auto *joint : AllJoints) {
|
||||||
if (!joint->keys.empty()) {
|
if (!joint->keys.empty()) {
|
||||||
HasAnimation = true;
|
return true;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// meshes with weights, are still counted as animated for ragdolls, etc
|
// meshes with weights are animatable
|
||||||
if (!HasAnimation) {
|
for (auto *joint : AllJoints) {
|
||||||
for (auto *joint : AllJoints) {
|
if (!joint->Weights.empty()) {
|
||||||
if (joint->Weights.size()) {
|
return true;
|
||||||
HasAnimation = true;
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
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) {
|
for (u32 i = 0; i < Vertices_Moved.size(); ++i)
|
||||||
EndFrame = 0.0f;
|
for (u32 j = 0; j < Vertices_Moved[i].size(); ++j)
|
||||||
for (const auto *joint : AllJoints) {
|
Vertices_Moved[i][j] = false;
|
||||||
EndFrame = std::max(EndFrame, joint->keys.getEndFrame());
|
|
||||||
|
// 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) {
|
normalizeWeights();
|
||||||
PreparedForSkinning = true;
|
|
||||||
|
|
||||||
// check for bugs:
|
for (auto *joint : AllJoints) {
|
||||||
for (auto *joint : AllJoints) {
|
joint->keys.cleanup();
|
||||||
for (auto &weight : joint->Weights) {
|
}
|
||||||
const u16 buffer_id = weight.buffer_id;
|
}
|
||||||
const u32 vertex_id = weight.vertex_id;
|
|
||||||
|
|
||||||
// check for invalid ids
|
void SkinnedMesh::calculateStaticBoundingBox()
|
||||||
if (buffer_id >= LocalBuffers.size()) {
|
{
|
||||||
os::Printer::log("Skinned Mesh: Weight buffer id too large", ELL_WARNING);
|
std::vector<std::vector<bool>> animated(getMeshBufferCount());
|
||||||
weight.buffer_id = weight.vertex_id = 0;
|
for (u32 mb = 0; mb < getMeshBufferCount(); mb++)
|
||||||
} else if (vertex_id >= LocalBuffers[buffer_id]->getVertexCount()) {
|
animated[mb] = std::vector<bool>(getMeshBuffer(mb)->getVertexCount());
|
||||||
os::Printer::log("Skinned Mesh: Weight vertex id too large", ELL_WARNING);
|
|
||||||
weight.buffer_id = weight.vertex_id = 0;
|
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
|
void SkinnedMesh::calculateJointBoundingBoxes()
|
||||||
|
{
|
||||||
for (u32 i = 0; i < Vertices_Moved.size(); ++i)
|
for (auto *joint : AllJoints) {
|
||||||
for (u32 j = 0; j < Vertices_Moved[i].size(); ++j)
|
bool first = true;
|
||||||
Vertices_Moved[i][j] = false;
|
for (auto &weight : joint->Weights) {
|
||||||
|
if (weight.strength < 1e-6)
|
||||||
// For skinning: cache weight values for speed
|
continue;
|
||||||
|
auto pos = weight.StaticPos;
|
||||||
for (auto *joint : AllJoints) {
|
joint->GlobalInversedMatrix.value().transformVect(pos);
|
||||||
for (auto &weight : joint->Weights) {
|
if (!first) {
|
||||||
const u16 buffer_id = weight.buffer_id;
|
joint->LocalBoundingBox.addInternalPoint(pos);
|
||||||
const u32 vertex_id = weight.vertex_id;
|
} else {
|
||||||
|
joint->LocalBoundingBox.reset(pos);
|
||||||
weight.Moved = &Vertices_Moved[buffer_id][vertex_id];
|
first = false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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<u16> new_to_old_id;
|
||||||
|
|
||||||
|
std::vector<std::vector<u16>> 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<u16> old_to_new_id(n);
|
||||||
|
for (u16 i = 0; i < n; ++i)
|
||||||
|
old_to_new_id[new_to_old_id[i]] = i;
|
||||||
|
|
||||||
|
std::vector<SJoint *> 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
|
//! 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);
|
os::Printer::log("Skinned Mesh - finalize", ELL_DEBUG);
|
||||||
|
|
||||||
// Make sure we recalc the next frame
|
topoSortJoints();
|
||||||
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...
|
|
||||||
|
|
||||||
|
// Set array sizes
|
||||||
for (u32 i = 0; i < LocalBuffers.size(); ++i) {
|
for (u32 i = 0; i < LocalBuffers.size(); ++i) {
|
||||||
Vertices_Moved.emplace_back(LocalBuffers[i]->getVertexCount());
|
Vertices_Moved.emplace_back(LocalBuffers[i]->getVertexCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
checkForAnimation();
|
prepareForSkinning();
|
||||||
|
|
||||||
if (HasAnimation) {
|
std::vector<core::matrix4> matrices;
|
||||||
for (auto *joint : AllJoints) {
|
matrices.reserve(AllJoints.size());
|
||||||
joint->keys.cleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Needed for animation and skinning...
|
|
||||||
|
|
||||||
calculateGlobalMatrices(0, 0);
|
|
||||||
|
|
||||||
// rigid animation for non animated meshes
|
|
||||||
for (auto *joint : AllJoints) {
|
for (auto *joint : AllJoints) {
|
||||||
|
if (const auto *matrix = std::get_if<core::matrix4>(&joint->transform))
|
||||||
|
matrices.push_back(*matrix);
|
||||||
|
else
|
||||||
|
matrices.push_back(std::get<core::Transform>(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) {
|
for (u32 attachedMeshIdx : joint->AttachedMeshes) {
|
||||||
SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx];
|
SSkinMeshBuffer *Buffer = (*SkinningBuffers)[attachedMeshIdx];
|
||||||
Buffer->Transformation = joint->GlobalAnimatedMatrix;
|
Buffer->Transformation = matrices[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate bounding box
|
recalculateBaseBoundingBoxes();
|
||||||
if (LocalBuffers.empty())
|
StaticPoseBox = calculateBoundingBox(matrices);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
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 *SkinnedMeshBuilder::addMeshBuffer()
|
||||||
{
|
{
|
||||||
scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer();
|
scene::SSkinMeshBuffer *buffer = new scene::SSkinMeshBuffer();
|
||||||
|
@ -607,14 +508,10 @@ void SkinnedMeshBuilder::addMeshBuffer(SSkinMeshBuffer *meshbuf)
|
||||||
SkinnedMesh::SJoint *SkinnedMeshBuilder::addJoint(SJoint *parent)
|
SkinnedMesh::SJoint *SkinnedMeshBuilder::addJoint(SJoint *parent)
|
||||||
{
|
{
|
||||||
SJoint *joint = new SJoint;
|
SJoint *joint = new SJoint;
|
||||||
|
joint->setParent(parent);
|
||||||
|
|
||||||
|
joint->JointID = AllJoints.size();
|
||||||
AllJoints.push_back(joint);
|
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;
|
return joint;
|
||||||
}
|
}
|
||||||
|
@ -684,73 +581,6 @@ void SkinnedMesh::normalizeWeights()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SkinnedMesh::recoverJointsFromMesh(std::vector<IBoneSceneNode *> &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<IBoneSceneNode *> &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<IBoneSceneNode *> &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()
|
void SkinnedMesh::convertMeshToTangents()
|
||||||
{
|
{
|
||||||
// now calculate tangents
|
// now calculate tangents
|
||||||
|
|
|
@ -916,12 +916,7 @@ struct Node {
|
||||||
std::optional<std::size_t> skin;
|
std::optional<std::size_t> skin;
|
||||||
std::optional<std::vector<double>> weights;
|
std::optional<std::vector<double>> weights;
|
||||||
Node(const Json::Value &o)
|
Node(const Json::Value &o)
|
||||||
: transform(Matrix {
|
: transform(TRS{})
|
||||||
1, 0, 0, 0,
|
|
||||||
0, 1, 0, 0,
|
|
||||||
0, 0, 1, 0,
|
|
||||||
0, 0, 0, 1
|
|
||||||
})
|
|
||||||
{
|
{
|
||||||
check(o.isObject());
|
check(o.isObject());
|
||||||
if (o.isMember("camera")) {
|
if (o.isMember("camera")) {
|
||||||
|
|
|
@ -95,366 +95,366 @@
|
||||||
### Keybindings
|
### Keybindings
|
||||||
|
|
||||||
# Key for moving the player forward.
|
# 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
|
# type: key
|
||||||
# keymap_forward = KEY_KEY_W
|
# keymap_forward = SYSTEM_SCANCODE_26
|
||||||
|
|
||||||
# Key for moving the player backward.
|
# Key for moving the player backward.
|
||||||
# Will also disable autoforward, when active.
|
# 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
|
# type: key
|
||||||
# keymap_backward = KEY_KEY_S
|
# keymap_backward = SYSTEM_SCANCODE_22
|
||||||
|
|
||||||
# Key for moving the player left.
|
# 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
|
# type: key
|
||||||
# keymap_left = KEY_KEY_A
|
# keymap_left = SYSTEM_SCANCODE_4
|
||||||
|
|
||||||
# Key for moving the player right.
|
# 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
|
# type: key
|
||||||
# keymap_right = KEY_KEY_D
|
# keymap_right = SYSTEM_SCANCODE_7
|
||||||
|
|
||||||
# Key for jumping.
|
# 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
|
# type: key
|
||||||
# keymap_jump = KEY_SPACE
|
# keymap_jump = SYSTEM_SCANCODE_44
|
||||||
|
|
||||||
# Key for sneaking.
|
# Key for sneaking.
|
||||||
# Also used for climbing down and descending in water if aux1_descends is disabled.
|
# 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
|
# type: key
|
||||||
# keymap_sneak = KEY_LSHIFT
|
# keymap_sneak = SYSTEM_SCANCODE_225
|
||||||
|
|
||||||
# Key for digging, punching or using something.
|
# Key for digging, punching or using something.
|
||||||
# (Note: The actual meaning might vary on a per-game basis.)
|
# (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
|
# type: key
|
||||||
# keymap_dig = KEY_LBUTTON
|
# keymap_dig = KEY_LBUTTON
|
||||||
|
|
||||||
# Key for placing an item/block or for using something.
|
# Key for placing an item/block or for using something.
|
||||||
# (Note: The actual meaning might vary on a per-game basis.)
|
# (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
|
# type: key
|
||||||
# keymap_place = KEY_RBUTTON
|
# keymap_place = KEY_RBUTTON
|
||||||
|
|
||||||
# Key for opening the inventory.
|
# 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
|
# type: key
|
||||||
# keymap_inventory = KEY_KEY_I
|
# keymap_inventory = SYSTEM_SCANCODE_12
|
||||||
|
|
||||||
# Key for moving fast in fast mode.
|
# 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
|
# type: key
|
||||||
# keymap_aux1 = KEY_KEY_E
|
# keymap_aux1 = SYSTEM_SCANCODE_8
|
||||||
|
|
||||||
# Key for opening the chat window.
|
# 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
|
# type: key
|
||||||
# keymap_chat = KEY_KEY_T
|
# keymap_chat = SYSTEM_SCANCODE_23
|
||||||
|
|
||||||
# Key for opening the chat window to type commands.
|
# 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
|
# type: key
|
||||||
# keymap_cmd = /
|
# keymap_cmd = SYSTEM_SCANCODE_56
|
||||||
|
|
||||||
# Key for opening the chat window to type local commands.
|
# 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
|
# type: key
|
||||||
# keymap_cmd_local = .
|
# keymap_cmd_local = SYSTEM_SCANCODE_55
|
||||||
|
|
||||||
# Key for toggling unlimited view range.
|
# 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
|
# type: key
|
||||||
# keymap_rangeselect =
|
# keymap_rangeselect =
|
||||||
|
|
||||||
# Key for toggling flying.
|
# 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
|
# type: key
|
||||||
# keymap_freemove = KEY_KEY_K
|
# keymap_freemove = SYSTEM_SCANCODE_14
|
||||||
|
|
||||||
# Key for toggling pitch move mode.
|
# 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
|
# type: key
|
||||||
# keymap_pitchmove =
|
# keymap_pitchmove =
|
||||||
|
|
||||||
# Key for toggling fast mode.
|
# 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
|
# type: key
|
||||||
# keymap_fastmove = KEY_KEY_J
|
# keymap_fastmove = SYSTEM_SCANCODE_13
|
||||||
|
|
||||||
# Key for toggling noclip mode.
|
# 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
|
# type: key
|
||||||
# keymap_noclip = KEY_KEY_H
|
# keymap_noclip = SYSTEM_SCANCODE_11
|
||||||
|
|
||||||
# Key for selecting the next item in the hotbar.
|
# 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
|
# type: key
|
||||||
# keymap_hotbar_next = KEY_KEY_N
|
# keymap_hotbar_next = SYSTEM_SCANCODE_17
|
||||||
|
|
||||||
# Key for selecting the previous item in the hotbar.
|
# 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
|
# type: key
|
||||||
# keymap_hotbar_previous = KEY_KEY_B
|
# keymap_hotbar_previous = SYSTEM_SCANCODE_5
|
||||||
|
|
||||||
# Key for muting the game.
|
# 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
|
# type: key
|
||||||
# keymap_mute = KEY_KEY_M
|
# keymap_mute = SYSTEM_SCANCODE_16
|
||||||
|
|
||||||
# Key for increasing the volume.
|
# 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
|
# type: key
|
||||||
# keymap_increase_volume =
|
# keymap_increase_volume =
|
||||||
|
|
||||||
# Key for decreasing the 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
|
# type: key
|
||||||
# keymap_decrease_volume =
|
# keymap_decrease_volume =
|
||||||
|
|
||||||
# Key for toggling autoforward.
|
# 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
|
# type: key
|
||||||
# keymap_autoforward =
|
# keymap_autoforward =
|
||||||
|
|
||||||
# Key for toggling cinematic mode.
|
# 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
|
# type: key
|
||||||
# keymap_cinematic =
|
# keymap_cinematic =
|
||||||
|
|
||||||
# Key for toggling display of minimap.
|
# 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
|
# type: key
|
||||||
# keymap_minimap = KEY_KEY_V
|
# keymap_minimap = SYSTEM_SCANCODE_25
|
||||||
|
|
||||||
# Key for taking screenshots.
|
# 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
|
# type: key
|
||||||
# keymap_screenshot = KEY_F12
|
# keymap_screenshot = SYSTEM_SCANCODE_69
|
||||||
|
|
||||||
# Key for toggling fullscreen mode.
|
# 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
|
# type: key
|
||||||
# keymap_fullscreen = KEY_F11
|
# keymap_fullscreen = SYSTEM_SCANCODE_68
|
||||||
|
|
||||||
# Key for dropping the currently selected item.
|
# 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
|
# type: key
|
||||||
# keymap_drop = KEY_KEY_Q
|
# keymap_drop = SYSTEM_SCANCODE_20
|
||||||
|
|
||||||
# Key to use view zoom when possible.
|
# 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
|
# type: key
|
||||||
# keymap_zoom = KEY_KEY_Z
|
# keymap_zoom = SYSTEM_SCANCODE_29
|
||||||
|
|
||||||
# Key for toggling the display of the HUD.
|
# 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
|
# type: key
|
||||||
# keymap_toggle_hud = KEY_F1
|
# keymap_toggle_hud = SYSTEM_SCANCODE_58
|
||||||
|
|
||||||
# Key for toggling the display of chat.
|
# 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
|
# type: key
|
||||||
# keymap_toggle_chat = KEY_F2
|
# keymap_toggle_chat = SYSTEM_SCANCODE_59
|
||||||
|
|
||||||
# Key for toggling the display of the large chat console.
|
# 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
|
# type: key
|
||||||
# keymap_console = KEY_F10
|
# keymap_console = SYSTEM_SCANCODE_67
|
||||||
|
|
||||||
# Key for toggling the display of fog.
|
# 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
|
# type: key
|
||||||
# keymap_toggle_fog = KEY_F3
|
# keymap_toggle_fog = SYSTEM_SCANCODE_60
|
||||||
|
|
||||||
# Key for toggling the display of debug info.
|
# 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
|
# type: key
|
||||||
# keymap_toggle_debug = KEY_F5
|
# keymap_toggle_debug = SYSTEM_SCANCODE_62
|
||||||
|
|
||||||
# Key for toggling the display of the profiler. Used for development.
|
# 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
|
# type: key
|
||||||
# keymap_toggle_profiler = KEY_F6
|
# keymap_toggle_profiler = SYSTEM_SCANCODE_63
|
||||||
|
|
||||||
# Key for toggling the display of mapblock boundaries.
|
# 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
|
# type: key
|
||||||
# keymap_toggle_block_bounds =
|
# keymap_toggle_block_bounds =
|
||||||
|
|
||||||
# Key for switching between first- and third-person camera.
|
# 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
|
# type: key
|
||||||
# keymap_camera_mode = KEY_KEY_C
|
# keymap_camera_mode = SYSTEM_SCANCODE_6
|
||||||
|
|
||||||
# Key for increasing the viewing range.
|
# 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
|
# type: key
|
||||||
# keymap_increase_viewing_range_min = +
|
# keymap_increase_viewing_range_min = SYSTEM_SCANCODE_46
|
||||||
|
|
||||||
# Key for decreasing the viewing range.
|
# 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
|
# type: key
|
||||||
# keymap_decrease_viewing_range_min = -
|
# keymap_decrease_viewing_range_min = SYSTEM_SCANCODE_45
|
||||||
|
|
||||||
# Key for selecting the first hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot1 = KEY_KEY_1
|
# keymap_slot1 = SYSTEM_SCANCODE_30
|
||||||
|
|
||||||
# Key for selecting the second hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot2 = KEY_KEY_2
|
# keymap_slot2 = SYSTEM_SCANCODE_31
|
||||||
|
|
||||||
# Key for selecting the third hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot3 = KEY_KEY_3
|
# keymap_slot3 = SYSTEM_SCANCODE_32
|
||||||
|
|
||||||
# Key for selecting the fourth hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot4 = KEY_KEY_4
|
# keymap_slot4 = SYSTEM_SCANCODE_33
|
||||||
|
|
||||||
# Key for selecting the fifth hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot5 = KEY_KEY_5
|
# keymap_slot5 = SYSTEM_SCANCODE_34
|
||||||
|
|
||||||
# Key for selecting the sixth hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot6 = KEY_KEY_6
|
# keymap_slot6 = SYSTEM_SCANCODE_35
|
||||||
|
|
||||||
# Key for selecting the seventh hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot7 = KEY_KEY_7
|
# keymap_slot7 = SYSTEM_SCANCODE_36
|
||||||
|
|
||||||
# Key for selecting the eighth hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot8 = KEY_KEY_8
|
# keymap_slot8 = SYSTEM_SCANCODE_37
|
||||||
|
|
||||||
# Key for selecting the ninth hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot9 = KEY_KEY_9
|
# keymap_slot9 = SYSTEM_SCANCODE_38
|
||||||
|
|
||||||
# Key for selecting the tenth hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot10 = KEY_KEY_0
|
# keymap_slot10 = SYSTEM_SCANCODE_39
|
||||||
|
|
||||||
# Key for selecting the 11th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot11 =
|
# keymap_slot11 =
|
||||||
|
|
||||||
# Key for selecting the 12th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot12 =
|
# keymap_slot12 =
|
||||||
|
|
||||||
# Key for selecting the 13th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot13 =
|
# keymap_slot13 =
|
||||||
|
|
||||||
# Key for selecting the 14th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot14 =
|
# keymap_slot14 =
|
||||||
|
|
||||||
# Key for selecting the 15th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot15 =
|
# keymap_slot15 =
|
||||||
|
|
||||||
# Key for selecting the 16th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot16 =
|
# keymap_slot16 =
|
||||||
|
|
||||||
# Key for selecting the 17th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot17 =
|
# keymap_slot17 =
|
||||||
|
|
||||||
# Key for selecting the 18th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot18 =
|
# keymap_slot18 =
|
||||||
|
|
||||||
# Key for selecting the 19th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot19 =
|
# keymap_slot19 =
|
||||||
|
|
||||||
# Key for selecting the 20th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot20 =
|
# keymap_slot20 =
|
||||||
|
|
||||||
# Key for selecting the 21st hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot21 =
|
# keymap_slot21 =
|
||||||
|
|
||||||
# Key for selecting the 22nd hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot22 =
|
# keymap_slot22 =
|
||||||
|
|
||||||
# Key for selecting the 23rd hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot23 =
|
# keymap_slot23 =
|
||||||
|
|
||||||
# Key for selecting the 24th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot24 =
|
# keymap_slot24 =
|
||||||
|
|
||||||
# Key for selecting the 25th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot25 =
|
# keymap_slot25 =
|
||||||
|
|
||||||
# Key for selecting the 26th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot26 =
|
# keymap_slot26 =
|
||||||
|
|
||||||
# Key for selecting the 27th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot27 =
|
# keymap_slot27 =
|
||||||
|
|
||||||
# Key for selecting the 28th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot28 =
|
# keymap_slot28 =
|
||||||
|
|
||||||
# Key for selecting the 29th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot29 =
|
# keymap_slot29 =
|
||||||
|
|
||||||
# Key for selecting the 30th hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot30 =
|
# keymap_slot30 =
|
||||||
|
|
||||||
# Key for selecting the 31st hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot31 =
|
# keymap_slot31 =
|
||||||
|
|
||||||
# Key for selecting the 32nd hotbar slot.
|
# 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
|
# type: key
|
||||||
# keymap_slot32 =
|
# keymap_slot32 =
|
||||||
|
|
||||||
|
@ -925,10 +925,6 @@
|
||||||
# type: bool
|
# type: bool
|
||||||
# enable_translucent_foliage = false
|
# enable_translucent_foliage = false
|
||||||
|
|
||||||
# Apply specular shading to nodes.
|
|
||||||
# type: bool
|
|
||||||
# enable_node_specular = false
|
|
||||||
|
|
||||||
# When enabled, liquid reflections are simulated.
|
# When enabled, liquid reflections are simulated.
|
||||||
# type: bool
|
# type: bool
|
||||||
# enable_water_reflections = false
|
# enable_water_reflections = false
|
||||||
|
@ -3554,27 +3550,27 @@
|
||||||
### Client Debugging
|
### Client Debugging
|
||||||
|
|
||||||
# Key for toggling the camera update. Only usable with 'debug' privilege.
|
# 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
|
# type: key
|
||||||
# keymap_toggle_update_camera =
|
# keymap_toggle_update_camera =
|
||||||
|
|
||||||
# Key for switching to the previous entry in Quicktune.
|
# 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
|
# type: key
|
||||||
# keymap_quicktune_prev =
|
# keymap_quicktune_prev =
|
||||||
|
|
||||||
# Key for switching to the next entry in Quicktune.
|
# 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
|
# type: key
|
||||||
# keymap_quicktune_next =
|
# keymap_quicktune_next =
|
||||||
|
|
||||||
# Key for decrementing the selected value in Quicktune.
|
# 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
|
# type: key
|
||||||
# keymap_quicktune_dec =
|
# keymap_quicktune_dec =
|
||||||
|
|
||||||
# Key for incrementing the selected value in Quicktune.
|
# 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
|
# type: key
|
||||||
# keymap_quicktune_inc =
|
# keymap_quicktune_inc =
|
||||||
|
|
||||||
|
|
|
@ -56,5 +56,3 @@ script: |
|
||||||
# Is a backup icon location in case
|
# Is a backup icon location in case
|
||||||
mkdir -p AppDir/usr/share/luanti/misc
|
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
|
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
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ Comment[fr]=Plate-forme de jeu multijoueurs à base de blocs
|
||||||
Exec=luanti
|
Exec=luanti
|
||||||
Icon=luanti
|
Icon=luanti
|
||||||
Terminal=false
|
Terminal=false
|
||||||
PrefersNonDefaultGPU=true
|
# Note: don't add PrefersNonDefaultGPU here, see #16095
|
||||||
Type=Application
|
Type=Application
|
||||||
Categories=Game;Simulation;
|
Categories=Game;Simulation;
|
||||||
StartupNotify=false
|
StartupNotify=false
|
||||||
|
|
|
@ -174,6 +174,6 @@
|
||||||
<update_contact>celeron55@gmail.com</update_contact>
|
<update_contact>celeron55@gmail.com</update_contact>
|
||||||
|
|
||||||
<releases>
|
<releases>
|
||||||
<release date="2025-02-14" version="5.11.0"/>
|
<release date="2025-05-23" version="5.12.0"/>
|
||||||
</releases>
|
</releases>
|
||||||
</component>
|
</component>
|
||||||
|
|
|
@ -3,8 +3,8 @@ msgstr ""
|
||||||
"Project-Id-Version: German (Minetest)\n"
|
"Project-Id-Version: German (Minetest)\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
||||||
"PO-Revision-Date: 2025-04-25 19:50+0000\n"
|
"PO-Revision-Date: 2025-05-16 10:31+0000\n"
|
||||||
"Last-Translator: Wuzzy <Wuzzy@disroot.org>\n"
|
"Last-Translator: sfan5 <sfan5@live.de>\n"
|
||||||
"Language-Team: German <https://hosted.weblate.org/projects/minetest/minetest/"
|
"Language-Team: German <https://hosted.weblate.org/projects/minetest/minetest/"
|
||||||
"de/>\n"
|
"de/>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
|
@ -937,29 +937,27 @@ msgstr "Die Welt „$1“ löschen?"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "As a result, your keybindings may have been changed."
|
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
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Check out the key settings or refer to the documentation:"
|
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
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr "Schließen"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Keybindings changed"
|
msgid "Keybindings changed"
|
||||||
msgstr "Tastenbelegung"
|
msgstr "Geänderte Tastenbelegung"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Open settings"
|
msgid "Open settings"
|
||||||
msgstr "Einstellungen"
|
msgstr "Einstellungen öffnen"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "The input handling system was reworked in Luanti 5.12.0."
|
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
|
#: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp
|
||||||
msgid "Confirm Password"
|
msgid "Confirm Password"
|
||||||
|
@ -4660,7 +4658,7 @@ msgid ""
|
||||||
"Instrument global callback functions on registration.\n"
|
"Instrument global callback functions on registration.\n"
|
||||||
"(anything you pass to a core.register_*() function)"
|
"(anything you pass to a core.register_*() function)"
|
||||||
msgstr ""
|
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)."
|
"(alles, was man einer Funktion wie core.register_*() übergibt)."
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
|
|
|
@ -3,8 +3,8 @@ msgstr ""
|
||||||
"Project-Id-Version: minetest\n"
|
"Project-Id-Version: minetest\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
||||||
"PO-Revision-Date: 2022-04-29 20:12+0000\n"
|
"PO-Revision-Date: 2025-05-21 10:48+0000\n"
|
||||||
"Last-Translator: JonAnder Oier <jonanderetaoier@gmail.com>\n"
|
"Last-Translator: Josu Igoa <josuigoa@ni.eus>\n"
|
||||||
"Language-Team: Basque <https://hosted.weblate.org/projects/minetest/minetest/"
|
"Language-Team: Basque <https://hosted.weblate.org/projects/minetest/minetest/"
|
||||||
"eu/>\n"
|
"eu/>\n"
|
||||||
"Language: eu\n"
|
"Language: eu\n"
|
||||||
|
@ -12,7 +12,7 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=n != 1;\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
|
#: builtin/client/chatcommands.lua
|
||||||
msgid "Clear the out chat queue"
|
msgid "Clear the out chat queue"
|
||||||
|
@ -23,9 +23,8 @@ msgid "Empty command."
|
||||||
msgstr "Agindu hutsa."
|
msgstr "Agindu hutsa."
|
||||||
|
|
||||||
#: builtin/client/chatcommands.lua
|
#: builtin/client/chatcommands.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Exit to main menu"
|
msgid "Exit to main menu"
|
||||||
msgstr "Itzuli menu nagusira"
|
msgstr "Irten menu nagusira"
|
||||||
|
|
||||||
#: builtin/client/chatcommands.lua
|
#: builtin/client/chatcommands.lua
|
||||||
msgid "Invalid command: "
|
msgid "Invalid command: "
|
||||||
|
@ -64,9 +63,8 @@ msgid "Command not available: "
|
||||||
msgstr "Komandoa ez dago eskuragarri: "
|
msgstr "Komandoa ez dago eskuragarri: "
|
||||||
|
|
||||||
#: builtin/common/chatcommands.lua
|
#: builtin/common/chatcommands.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Get help for commands (-t: output in chat)"
|
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
|
#: builtin/common/chatcommands.lua
|
||||||
msgid ""
|
msgid ""
|
||||||
|
@ -76,9 +74,8 @@ msgstr ""
|
||||||
"zerrendatzeko."
|
"zerrendatzeko."
|
||||||
|
|
||||||
#: builtin/common/chatcommands.lua
|
#: builtin/common/chatcommands.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "[all | <cmd>] [-t]"
|
msgid "[all | <cmd>] [-t]"
|
||||||
msgstr "[guztia | <cmd>]"
|
msgstr "[all | <cmd>] [-t]"
|
||||||
|
|
||||||
#: builtin/common/settings/components.lua
|
#: builtin/common/settings/components.lua
|
||||||
msgid "Browse"
|
msgid "Browse"
|
||||||
|
|
|
@ -3,7 +3,7 @@ msgstr ""
|
||||||
"Project-Id-Version: French (Minetest)\n"
|
"Project-Id-Version: French (Minetest)\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-14 23:02+0200\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 <piero@live.ie>\n"
|
"Last-Translator: waxtatect <piero@live.ie>\n"
|
||||||
"Language-Team: French <https://hosted.weblate.org/projects/minetest/minetest/"
|
"Language-Team: French <https://hosted.weblate.org/projects/minetest/minetest/"
|
||||||
"fr/>\n"
|
"fr/>\n"
|
||||||
|
@ -938,29 +938,27 @@ msgstr "Supprimer le monde « $1 » ?"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "As a result, your keybindings may have been changed."
|
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
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Check out the key settings or refer to the documentation:"
|
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
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr "Fermer"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Keybindings changed"
|
msgid "Keybindings changed"
|
||||||
msgstr "Raccourcis clavier"
|
msgstr "Modification des raccourcis clavier"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Open settings"
|
msgid "Open settings"
|
||||||
msgstr "Paramètres"
|
msgstr "Ouvrir les paramètres"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "The input handling system was reworked in Luanti 5.12.0."
|
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
|
#: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp
|
||||||
msgid "Confirm Password"
|
msgid "Confirm Password"
|
||||||
|
|
|
@ -3,7 +3,7 @@ msgstr ""
|
||||||
"Project-Id-Version: Indonesian (Minetest)\n"
|
"Project-Id-Version: Indonesian (Minetest)\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-14 23:02+0200\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 <linerly@proton.me>\n"
|
"Last-Translator: Linerly <linerly@proton.me>\n"
|
||||||
"Language-Team: Indonesian <https://hosted.weblate.org/projects/minetest/"
|
"Language-Team: Indonesian <https://hosted.weblate.org/projects/minetest/"
|
||||||
"minetest/id/>\n"
|
"minetest/id/>\n"
|
||||||
|
@ -12,7 +12,7 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\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
|
#: builtin/client/chatcommands.lua
|
||||||
msgid "Clear the out chat queue"
|
msgid "Clear the out chat queue"
|
||||||
|
@ -930,29 +930,27 @@ msgstr "Hapus dunia \"$1\"?"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "As a result, your keybindings may have been changed."
|
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
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Check out the key settings or refer to the documentation:"
|
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
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr "Tutup"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Keybindings changed"
|
msgid "Keybindings changed"
|
||||||
msgstr "Pengikatan tombol"
|
msgstr "Pengikatan tombol berubah"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Open settings"
|
msgid "Open settings"
|
||||||
msgstr "Pengaturan"
|
msgstr "Buka pengaturan"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "The input handling system was reworked in Luanti 5.12.0."
|
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
|
#: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp
|
||||||
msgid "Confirm Password"
|
msgid "Confirm Password"
|
||||||
|
|
783
po/it/luanti.po
783
po/it/luanti.po
File diff suppressed because it is too large
Load diff
|
@ -3,8 +3,8 @@ msgstr ""
|
||||||
"Project-Id-Version: Portuguese (Minetest)\n"
|
"Project-Id-Version: Portuguese (Minetest)\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
||||||
"PO-Revision-Date: 2025-05-12 16:41+0000\n"
|
"PO-Revision-Date: 2025-05-18 15:01+0000\n"
|
||||||
"Last-Translator: Felipe Amaral <contato.feamaral@gmail.com>\n"
|
"Last-Translator: Ian Pedras <ian@pedras.org>\n"
|
||||||
"Language-Team: Portuguese <https://hosted.weblate.org/projects/minetest/"
|
"Language-Team: Portuguese <https://hosted.weblate.org/projects/minetest/"
|
||||||
"minetest/pt/>\n"
|
"minetest/pt/>\n"
|
||||||
"Language: pt\n"
|
"Language: pt\n"
|
||||||
|
@ -934,30 +934,29 @@ msgid "Delete World \"$1\"?"
|
||||||
msgstr "Eliminar mundo \"$1\"?"
|
msgstr "Eliminar mundo \"$1\"?"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
|
#, fuzzy
|
||||||
msgid "As a result, your keybindings may have been changed."
|
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
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Check out the key settings or refer to the documentation:"
|
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
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr "Fechar"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Keybindings changed"
|
msgid "Keybindings changed"
|
||||||
msgstr "Combinações de teclas."
|
msgstr "Combinações de teclas mudadas"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Open settings"
|
msgid "Open settings"
|
||||||
msgstr "Definições"
|
msgstr "Abrir Definições"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "The input handling system was reworked in Luanti 5.12.0."
|
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
|
#: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp
|
||||||
msgid "Confirm Password"
|
msgid "Confirm Password"
|
||||||
|
@ -2058,7 +2057,7 @@ msgstr "Scroll Lock"
|
||||||
#. ~ Key name
|
#. ~ Key name
|
||||||
#: src/client/keycode.cpp
|
#: src/client/keycode.cpp
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr "Seleccionar"
|
msgstr "Selecionar"
|
||||||
|
|
||||||
#: src/client/keycode.cpp
|
#: src/client/keycode.cpp
|
||||||
msgid "Shift Key"
|
msgid "Shift Key"
|
||||||
|
@ -2115,14 +2114,14 @@ msgid "Minimap in texture mode"
|
||||||
msgstr "Minimapa em modo de textura"
|
msgstr "Minimapa em modo de textura"
|
||||||
|
|
||||||
#: src/client/shader.cpp
|
#: src/client/shader.cpp
|
||||||
#, fuzzy, c-format
|
#, c-format
|
||||||
msgid "Failed to compile the \"%s\" shader."
|
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
|
#: src/client/shader.cpp
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "GLSL is not supported by the driver"
|
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"
|
#. ~ Error when a mod is missing dependencies. Ex: "Mod Title is missing: mod1, mod2, mod3"
|
||||||
#: src/content/mod_configuration.cpp
|
#: src/content/mod_configuration.cpp
|
||||||
|
@ -2168,11 +2167,11 @@ msgstr "Continuar"
|
||||||
|
|
||||||
#: src/gui/guiOpenURL.cpp
|
#: src/gui/guiOpenURL.cpp
|
||||||
msgid "Open"
|
msgid "Open"
|
||||||
msgstr ""
|
msgstr "Abrir"
|
||||||
|
|
||||||
#: src/gui/guiOpenURL.cpp
|
#: src/gui/guiOpenURL.cpp
|
||||||
msgid "Open URL?"
|
msgid "Open URL?"
|
||||||
msgstr ""
|
msgstr "Abrir URL?"
|
||||||
|
|
||||||
#: src/gui/guiOpenURL.cpp
|
#: src/gui/guiOpenURL.cpp
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
|
@ -2207,33 +2206,34 @@ msgstr "Volume do som: %d%%"
|
||||||
#: src/gui/touchscreeneditor.cpp
|
#: src/gui/touchscreeneditor.cpp
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Add button"
|
msgid "Add button"
|
||||||
msgstr "Roda do Rato"
|
msgstr "Adicionar botão"
|
||||||
|
|
||||||
#: src/gui/touchscreeneditor.cpp
|
#: src/gui/touchscreeneditor.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Done"
|
msgid "Done"
|
||||||
msgstr "Feito!"
|
msgstr "Feito"
|
||||||
|
|
||||||
#: src/gui/touchscreeneditor.cpp
|
#: src/gui/touchscreeneditor.cpp
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr "Servidor remoto"
|
msgstr "Remover"
|
||||||
|
|
||||||
#: src/gui/touchscreeneditor.cpp
|
#: src/gui/touchscreeneditor.cpp
|
||||||
|
#, fuzzy
|
||||||
msgid "Reset"
|
msgid "Reset"
|
||||||
msgstr ""
|
msgstr "Refazer"
|
||||||
|
|
||||||
#: src/gui/touchscreeneditor.cpp
|
#: src/gui/touchscreeneditor.cpp
|
||||||
|
#, fuzzy
|
||||||
msgid "Start dragging a button to add. Tap outside to cancel."
|
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
|
#: src/gui/touchscreeneditor.cpp
|
||||||
msgid "Tap a button to select it. Drag a button to move it."
|
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
|
#: src/gui/touchscreeneditor.cpp
|
||||||
msgid "Tap outside to deselect."
|
msgid "Tap outside to deselect."
|
||||||
msgstr ""
|
msgstr "Click fora para desselecionar."
|
||||||
|
|
||||||
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
||||||
msgid "Aux1"
|
msgid "Aux1"
|
||||||
|
@ -2245,7 +2245,7 @@ msgstr "Mudar camera"
|
||||||
|
|
||||||
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
||||||
msgid "Dig/punch/use"
|
msgid "Dig/punch/use"
|
||||||
msgstr ""
|
msgstr "Escavar/Attacar/Usar"
|
||||||
|
|
||||||
#: src/gui/touchscreenlayout.cpp
|
#: src/gui/touchscreenlayout.cpp
|
||||||
msgid "Drop"
|
msgid "Drop"
|
||||||
|
@ -2370,27 +2370,32 @@ msgstr ""
|
||||||
|
|
||||||
#: src/network/clientpackethandler.cpp
|
#: src/network/clientpackethandler.cpp
|
||||||
msgid "The server is running in singleplayer mode. You cannot connect."
|
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
|
#: src/network/clientpackethandler.cpp
|
||||||
msgid "Too many users"
|
msgid "Too many users"
|
||||||
msgstr ""
|
msgstr "Demasiados utilizadores"
|
||||||
|
|
||||||
#: src/network/clientpackethandler.cpp
|
#: src/network/clientpackethandler.cpp
|
||||||
msgid "Unknown disconnect reason."
|
msgid "Unknown disconnect reason."
|
||||||
msgstr ""
|
msgstr "Razão de desconecto desconhecido."
|
||||||
|
|
||||||
#: src/network/clientpackethandler.cpp
|
#: src/network/clientpackethandler.cpp
|
||||||
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your client sent something the server didn't expect. Try reconnecting or "
|
"Your client sent something the server didn't expect. Try reconnecting or "
|
||||||
"updating your client."
|
"updating your client."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"O seu cliente mandou algo que o servidor não esperava. Tente reconectar ou "
|
||||||
|
"atualizar o seu cliente."
|
||||||
|
|
||||||
#: src/network/clientpackethandler.cpp
|
#: src/network/clientpackethandler.cpp
|
||||||
msgid ""
|
msgid ""
|
||||||
"Your client's version is not supported.\n"
|
"Your client's version is not supported.\n"
|
||||||
"Please contact the server administrator."
|
"Please contact the server administrator."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
"A versão do seu cliente não é supportado.\n"
|
||||||
|
"Por favor contacte o administrador do servidor."
|
||||||
|
|
||||||
#: src/server.cpp
|
#: src/server.cpp
|
||||||
#, c-format
|
#, c-format
|
||||||
|
@ -2465,7 +2470,7 @@ msgstr "Ruído 2D que localiza os vales e canais dos rios."
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "3D"
|
msgid "3D"
|
||||||
msgstr ""
|
msgstr "3D"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "3D clouds"
|
msgid "3D clouds"
|
||||||
|
@ -2638,13 +2643,13 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Allow clouds to look 3D instead of flat."
|
msgid "Allow clouds to look 3D instead of flat."
|
||||||
msgstr "Usar nuvens 3D em vez de planas."
|
msgstr "Usar nuvens 3D em vez de planas."
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
|
#, fuzzy
|
||||||
msgid "Allows liquids to be translucent."
|
msgid "Allows liquids to be translucent."
|
||||||
msgstr ""
|
msgstr "Permitir liquidos semi transparentes."
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid ""
|
msgid ""
|
||||||
|
@ -2697,8 +2702,9 @@ msgid "Anticheat flags"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
|
#, fuzzy
|
||||||
msgid "Anticheat movement tolerance"
|
msgid "Anticheat movement tolerance"
|
||||||
msgstr ""
|
msgstr "Tolerância de movimentos anti-batota"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Append item name"
|
msgid "Append item name"
|
||||||
|
@ -2724,8 +2730,9 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
|
#, fuzzy
|
||||||
msgid "Apply specular shading to nodes."
|
msgid "Apply specular shading to nodes."
|
||||||
msgstr ""
|
msgstr "Aplicar shading especular aos nodes."
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Arm inertia"
|
msgid "Arm inertia"
|
||||||
|
@ -2824,9 +2831,8 @@ msgid "Base terrain height."
|
||||||
msgstr "Altura base do terreno."
|
msgstr "Altura base do terreno."
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Base texture size"
|
msgid "Base texture size"
|
||||||
msgstr "Tamanho mínimo da textura"
|
msgstr "Tamanho base da textura"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Basic privileges"
|
msgid "Basic privileges"
|
||||||
|
@ -2849,9 +2855,8 @@ msgid "Bind address"
|
||||||
msgstr "Endereço de bind"
|
msgstr "Endereço de bind"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Biome API"
|
msgid "Biome API"
|
||||||
msgstr "Biomas"
|
msgstr "API de Biomas"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Biome noise"
|
msgid "Biome noise"
|
||||||
|
@ -3016,13 +3021,12 @@ msgid "Client"
|
||||||
msgstr "Cliente"
|
msgstr "Cliente"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Client Debugging"
|
msgid "Client Debugging"
|
||||||
msgstr "Debugging"
|
msgstr "Debugging de cliente"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Client Mesh Chunksize"
|
msgid "Client Mesh Chunksize"
|
||||||
msgstr ""
|
msgstr "Tamhãnho do Client Mesh Chunksize"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Client and Server"
|
msgid "Client and Server"
|
||||||
|
@ -3063,7 +3067,7 @@ msgstr "Nuvens no menu"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Color depth for post-processing texture"
|
msgid "Color depth for post-processing texture"
|
||||||
msgstr ""
|
msgstr "Profundidade da cor para a textura post-processing"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Colored fog"
|
msgid "Colored fog"
|
||||||
|
|
|
@ -3,8 +3,8 @@ msgstr ""
|
||||||
"Project-Id-Version: Russian (Minetest)\n"
|
"Project-Id-Version: Russian (Minetest)\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
||||||
"PO-Revision-Date: 2025-05-03 05:01+0000\n"
|
"PO-Revision-Date: 2025-05-16 16:16+0000\n"
|
||||||
"Last-Translator: Nana_M <sab.pyrope@gmail.com>\n"
|
"Last-Translator: BlackImpostor <SkyBuilderOFFICAL@yandex.ru>\n"
|
||||||
"Language-Team: Russian <https://hosted.weblate.org/projects/minetest/"
|
"Language-Team: Russian <https://hosted.weblate.org/projects/minetest/"
|
||||||
"minetest/ru/>\n"
|
"minetest/ru/>\n"
|
||||||
"Language: ru\n"
|
"Language: ru\n"
|
||||||
|
@ -934,29 +934,27 @@ msgstr "Удалить мир «$1»?"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "As a result, your keybindings may have been changed."
|
msgid "As a result, your keybindings may have been changed."
|
||||||
msgstr ""
|
msgstr "В результате ваши привязки клавиш, возможно, были изменены."
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Check out the key settings or refer to the documentation:"
|
msgid "Check out the key settings or refer to the documentation:"
|
||||||
msgstr ""
|
msgstr "Ознакомьтесь с настройками клавиш или обратитесь к документации:"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr "Закрыть"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Keybindings changed"
|
msgid "Keybindings changed"
|
||||||
msgstr "Привязки клавиш"
|
msgstr "Изменены привязки клавиш"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Open settings"
|
msgid "Open settings"
|
||||||
msgstr "Настройки"
|
msgstr "Открыть настройки"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "The input handling system was reworked in Luanti 5.12.0."
|
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
|
#: builtin/mainmenu/dlg_register.lua src/gui/guiPasswordChange.cpp
|
||||||
msgid "Confirm Password"
|
msgid "Confirm Password"
|
||||||
|
@ -2250,7 +2248,7 @@ msgstr "Бросить"
|
||||||
|
|
||||||
#: src/gui/touchscreenlayout.cpp
|
#: src/gui/touchscreenlayout.cpp
|
||||||
msgid "Exit"
|
msgid "Exit"
|
||||||
msgstr "Закрыть"
|
msgstr "Выйти"
|
||||||
|
|
||||||
#: src/gui/touchscreenlayout.cpp
|
#: src/gui/touchscreenlayout.cpp
|
||||||
msgid "Inventory"
|
msgid "Inventory"
|
||||||
|
|
|
@ -3,8 +3,8 @@ msgstr ""
|
||||||
"Project-Id-Version: Chinese (Simplified) (Minetest)\n"
|
"Project-Id-Version: Chinese (Simplified) (Minetest)\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
"POT-Creation-Date: 2025-05-14 23:02+0200\n"
|
||||||
"PO-Revision-Date: 2025-02-24 16:36+0000\n"
|
"PO-Revision-Date: 2025-05-15 23:02+0000\n"
|
||||||
"Last-Translator: BX Zhang <zbx1425@outlook.com>\n"
|
"Last-Translator: y5nw <y5nw@users.noreply.hosted.weblate.org>\n"
|
||||||
"Language-Team: Chinese (Simplified Han script) <https://hosted.weblate.org/"
|
"Language-Team: Chinese (Simplified Han script) <https://hosted.weblate.org/"
|
||||||
"projects/minetest/minetest/zh_Hans/>\n"
|
"projects/minetest/minetest/zh_Hans/>\n"
|
||||||
"Language: zh_CN\n"
|
"Language: zh_CN\n"
|
||||||
|
@ -12,7 +12,7 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=1; plural=0;\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
|
#: builtin/client/chatcommands.lua
|
||||||
msgid "Clear the out chat queue"
|
msgid "Clear the out chat queue"
|
||||||
|
@ -82,16 +82,15 @@ msgstr "浏览"
|
||||||
|
|
||||||
#: builtin/common/settings/components.lua
|
#: builtin/common/settings/components.lua
|
||||||
msgid "Conflicts with \"$1\""
|
msgid "Conflicts with \"$1\""
|
||||||
msgstr ""
|
msgstr "与“$1”冲突"
|
||||||
|
|
||||||
#: builtin/common/settings/components.lua
|
#: builtin/common/settings/components.lua
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr "编辑"
|
msgstr "编辑"
|
||||||
|
|
||||||
#: builtin/common/settings/components.lua
|
#: builtin/common/settings/components.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Remove keybinding"
|
msgid "Remove keybinding"
|
||||||
msgstr "按键绑定。"
|
msgstr "移除按键绑定"
|
||||||
|
|
||||||
#: builtin/common/settings/components.lua
|
#: builtin/common/settings/components.lua
|
||||||
msgid "Select directory"
|
msgid "Select directory"
|
||||||
|
@ -253,7 +252,7 @@ msgstr "通用"
|
||||||
|
|
||||||
#: builtin/common/settings/dlg_settings.lua
|
#: builtin/common/settings/dlg_settings.lua
|
||||||
msgid "Long tap"
|
msgid "Long tap"
|
||||||
msgstr ""
|
msgstr "长按"
|
||||||
|
|
||||||
#: builtin/common/settings/dlg_settings.lua
|
#: builtin/common/settings/dlg_settings.lua
|
||||||
msgid "Movement"
|
msgid "Movement"
|
||||||
|
@ -558,12 +557,11 @@ msgstr "捐赠"
|
||||||
|
|
||||||
#: builtin/mainmenu/content/dlg_package.lua
|
#: builtin/mainmenu/content/dlg_package.lua
|
||||||
msgid "Error loading package information"
|
msgid "Error loading package information"
|
||||||
msgstr ""
|
msgstr "无法获取软件包信息"
|
||||||
|
|
||||||
#: builtin/mainmenu/content/dlg_package.lua
|
#: builtin/mainmenu/content/dlg_package.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Error loading reviews"
|
msgid "Error loading reviews"
|
||||||
msgstr "创建客户端出错:%s"
|
msgstr "无法加载评价"
|
||||||
|
|
||||||
#: builtin/mainmenu/content/dlg_package.lua
|
#: builtin/mainmenu/content/dlg_package.lua
|
||||||
msgid "Forum Topic"
|
msgid "Forum Topic"
|
||||||
|
@ -583,7 +581,7 @@ msgstr "问题跟踪器"
|
||||||
|
|
||||||
#: builtin/mainmenu/content/dlg_package.lua
|
#: builtin/mainmenu/content/dlg_package.lua
|
||||||
msgid "Reviews"
|
msgid "Reviews"
|
||||||
msgstr ""
|
msgstr "评价"
|
||||||
|
|
||||||
#: builtin/mainmenu/content/dlg_package.lua
|
#: builtin/mainmenu/content/dlg_package.lua
|
||||||
msgid "Source"
|
msgid "Source"
|
||||||
|
@ -638,10 +636,11 @@ msgid "Unable to install a $1 as a texture pack"
|
||||||
msgstr "无法将$1安装为材质包"
|
msgstr "无法将$1安装为材质包"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_clients_list.lua
|
#: builtin/mainmenu/dlg_clients_list.lua
|
||||||
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
"Players connected to\n"
|
"Players connected to\n"
|
||||||
"$1"
|
"$1"
|
||||||
msgstr ""
|
msgstr "连接到$1的玩家"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_config_world.lua
|
#: builtin/mainmenu/dlg_config_world.lua
|
||||||
msgid "(Enabled, has error)"
|
msgid "(Enabled, has error)"
|
||||||
|
@ -924,25 +923,23 @@ msgstr "删除世界“$1”?"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "As a result, your keybindings may have been changed."
|
msgid "As a result, your keybindings may have been changed."
|
||||||
msgstr ""
|
msgstr "您的一些按键绑定有可能已被更改。"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Check out the key settings or refer to the documentation:"
|
msgid "Check out the key settings or refer to the documentation:"
|
||||||
msgstr ""
|
msgstr "请查看按键绑定设置或参考文档:"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "Close"
|
msgid "Close"
|
||||||
msgstr ""
|
msgstr "关闭"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Keybindings changed"
|
msgid "Keybindings changed"
|
||||||
msgstr "按键绑定。"
|
msgstr "按键绑定已被更改"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
#, fuzzy
|
|
||||||
msgid "Open settings"
|
msgid "Open settings"
|
||||||
msgstr "设置"
|
msgstr "打开设置"
|
||||||
|
|
||||||
#: builtin/mainmenu/dlg_rebind_keys.lua
|
#: builtin/mainmenu/dlg_rebind_keys.lua
|
||||||
msgid "The input handling system was reworked in Luanti 5.12.0."
|
msgid "The input handling system was reworked in Luanti 5.12.0."
|
||||||
|
@ -1292,12 +1289,11 @@ msgid "Ping"
|
||||||
msgstr "ping值"
|
msgstr "ping值"
|
||||||
|
|
||||||
#: builtin/mainmenu/tab_online.lua
|
#: builtin/mainmenu/tab_online.lua
|
||||||
#, fuzzy
|
|
||||||
msgid ""
|
msgid ""
|
||||||
"Players:\n"
|
"Players:\n"
|
||||||
"$1"
|
"$1"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"客户端:\n"
|
"玩家::\n"
|
||||||
"$1"
|
"$1"
|
||||||
|
|
||||||
#: builtin/mainmenu/tab_online.lua
|
#: builtin/mainmenu/tab_online.lua
|
||||||
|
@ -2247,7 +2243,7 @@ msgstr "溢出菜单"
|
||||||
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid "Place/use"
|
msgid "Place/use"
|
||||||
msgstr "放置键"
|
msgstr "放置或使用"
|
||||||
|
|
||||||
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
||||||
msgid "Range select"
|
msgid "Range select"
|
||||||
|
@ -2262,9 +2258,8 @@ msgid "Toggle chat log"
|
||||||
msgstr "启用/禁用聊天记录"
|
msgstr "启用/禁用聊天记录"
|
||||||
|
|
||||||
#: src/gui/touchscreenlayout.cpp
|
#: src/gui/touchscreenlayout.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Toggle debug"
|
msgid "Toggle debug"
|
||||||
msgstr "启用/禁用雾"
|
msgstr "显示/隐藏调试信息"
|
||||||
|
|
||||||
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
#: src/gui/touchscreenlayout.cpp src/settings_translation_file.cpp
|
||||||
msgid "Toggle fast"
|
msgid "Toggle fast"
|
||||||
|
@ -2301,9 +2296,8 @@ msgid "Internal server error"
|
||||||
msgstr "内部服务器错误"
|
msgstr "内部服务器错误"
|
||||||
|
|
||||||
#: src/network/clientpackethandler.cpp
|
#: src/network/clientpackethandler.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Invalid password"
|
msgid "Invalid password"
|
||||||
msgstr "旧密码"
|
msgstr "密码错误"
|
||||||
|
|
||||||
#. ~ DO NOT TRANSLATE THIS LITERALLY!
|
#. ~ DO NOT TRANSLATE THIS LITERALLY!
|
||||||
#. This is a special string which needs to contain the translation's
|
#. This is a special string which needs to contain the translation's
|
||||||
|
@ -2977,9 +2971,8 @@ msgid "Client"
|
||||||
msgstr "客户端"
|
msgstr "客户端"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Client Debugging"
|
msgid "Client Debugging"
|
||||||
msgstr "调试"
|
msgstr "客户端调试"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Client Mesh Chunksize"
|
msgid "Client Mesh Chunksize"
|
||||||
|
@ -3233,7 +3226,6 @@ msgid "Decrease view range"
|
||||||
msgstr "减少可视范围"
|
msgstr "减少可视范围"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Decrease volume"
|
msgid "Decrease volume"
|
||||||
msgstr "减小音量"
|
msgstr "减小音量"
|
||||||
|
|
||||||
|
@ -3446,9 +3438,8 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Drop item"
|
msgid "Drop item"
|
||||||
msgstr "丢弃物品键"
|
msgstr "丢弃物品"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Dump the mapgen debug information."
|
msgid "Dump the mapgen debug information."
|
||||||
|
@ -4133,164 +4124,132 @@ msgstr ""
|
||||||
"单位为方块每二次方秒。"
|
"单位为方块每二次方秒。"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 1"
|
msgid "Hotbar slot 1"
|
||||||
msgstr "快捷栏1键"
|
msgstr "快捷栏第1项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 10"
|
msgid "Hotbar slot 10"
|
||||||
msgstr "快捷栏10键"
|
msgstr "快捷栏第10项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 11"
|
msgid "Hotbar slot 11"
|
||||||
msgstr "快捷栏11键"
|
msgstr "快捷栏第11项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 12"
|
msgid "Hotbar slot 12"
|
||||||
msgstr "快捷栏12键"
|
msgstr "快捷栏第12项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 13"
|
msgid "Hotbar slot 13"
|
||||||
msgstr "快捷栏13键"
|
msgstr "快捷栏第13项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 14"
|
msgid "Hotbar slot 14"
|
||||||
msgstr "快捷栏14键"
|
msgstr "快捷栏第14项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 15"
|
msgid "Hotbar slot 15"
|
||||||
msgstr "快捷栏15键"
|
msgstr "快捷栏第15项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 16"
|
msgid "Hotbar slot 16"
|
||||||
msgstr "快捷栏16键"
|
msgstr "快捷栏第16项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 17"
|
msgid "Hotbar slot 17"
|
||||||
msgstr "快捷栏17键"
|
msgstr "快捷栏第17项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 18"
|
msgid "Hotbar slot 18"
|
||||||
msgstr "快捷栏18键"
|
msgstr "快捷栏第18项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 19"
|
msgid "Hotbar slot 19"
|
||||||
msgstr "快捷栏19键"
|
msgstr "快捷栏第19项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 2"
|
msgid "Hotbar slot 2"
|
||||||
msgstr "快捷栏2键"
|
msgstr "快捷栏第2项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 20"
|
msgid "Hotbar slot 20"
|
||||||
msgstr "快捷栏20键"
|
msgstr "快捷栏第20项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 21"
|
msgid "Hotbar slot 21"
|
||||||
msgstr "快捷栏21键"
|
msgstr "快捷栏第21项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 22"
|
msgid "Hotbar slot 22"
|
||||||
msgstr "快捷栏22键"
|
msgstr "快捷栏第22项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 23"
|
msgid "Hotbar slot 23"
|
||||||
msgstr "快捷栏23键"
|
msgstr "快捷栏第23项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 24"
|
msgid "Hotbar slot 24"
|
||||||
msgstr "快捷栏24键"
|
msgstr "快捷栏第24项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 25"
|
msgid "Hotbar slot 25"
|
||||||
msgstr "快捷栏25键"
|
msgstr "快捷栏第25项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 26"
|
msgid "Hotbar slot 26"
|
||||||
msgstr "快捷栏26键"
|
msgstr "快捷栏第26项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 27"
|
msgid "Hotbar slot 27"
|
||||||
msgstr "快捷栏27键"
|
msgstr "快捷栏第27项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 28"
|
msgid "Hotbar slot 28"
|
||||||
msgstr "快捷栏28键"
|
msgstr "快捷栏第28项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 29"
|
msgid "Hotbar slot 29"
|
||||||
msgstr "快捷栏29键"
|
msgstr "快捷栏第29项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 3"
|
msgid "Hotbar slot 3"
|
||||||
msgstr "快捷栏3键"
|
msgstr "快捷栏第3项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 30"
|
msgid "Hotbar slot 30"
|
||||||
msgstr "快捷栏30键"
|
msgstr "快捷栏第30项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 31"
|
msgid "Hotbar slot 31"
|
||||||
msgstr "快捷栏31键"
|
msgstr "快捷栏第31项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 32"
|
msgid "Hotbar slot 32"
|
||||||
msgstr "快捷栏32键"
|
msgstr "快捷栏第32项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 4"
|
msgid "Hotbar slot 4"
|
||||||
msgstr "快捷栏4键"
|
msgstr "快捷栏第4项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 5"
|
msgid "Hotbar slot 5"
|
||||||
msgstr "快捷栏5键"
|
msgstr "快捷栏第5项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 6"
|
msgid "Hotbar slot 6"
|
||||||
msgstr "快捷栏6键"
|
msgstr "快捷栏第6项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 7"
|
msgid "Hotbar slot 7"
|
||||||
msgstr "快捷栏7键"
|
msgstr "快捷栏第7项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 8"
|
msgid "Hotbar slot 8"
|
||||||
msgstr "快捷栏8键"
|
msgstr "快捷栏第8项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar slot 9"
|
msgid "Hotbar slot 9"
|
||||||
msgstr "快捷栏9键"
|
msgstr "快捷栏第9项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Hotbar: Enable mouse wheel for selection"
|
msgid "Hotbar: Enable mouse wheel for selection"
|
||||||
|
@ -4301,14 +4260,12 @@ msgid "Hotbar: Invert mouse wheel direction"
|
||||||
msgstr "快捷栏:反转鼠标滚轮方向"
|
msgstr "快捷栏:反转鼠标滚轮方向"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar: select next item"
|
msgid "Hotbar: select next item"
|
||||||
msgstr "快捷栏下一个键"
|
msgstr "选择快捷栏下一项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Hotbar: select previous item"
|
msgid "Hotbar: select previous item"
|
||||||
msgstr "快捷栏上一个键"
|
msgstr "选择快捷栏上一项"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "How deep to make rivers."
|
msgid "How deep to make rivers."
|
||||||
|
@ -4513,7 +4470,6 @@ msgid "Increase view range"
|
||||||
msgstr "增加可视范围"
|
msgstr "增加可视范围"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Increase volume"
|
msgid "Increase volume"
|
||||||
msgstr "增大音量"
|
msgstr "增大音量"
|
||||||
|
|
||||||
|
@ -5031,9 +4987,8 @@ msgid "Key to use view zoom when possible."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Keybindings"
|
msgid "Keybindings"
|
||||||
msgstr "按键绑定。"
|
msgstr "按键绑定"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Keyboard and Mouse"
|
msgid "Keyboard and Mouse"
|
||||||
|
@ -5072,9 +5027,8 @@ msgid "Large cave proportion flooded"
|
||||||
msgstr "大型洞穴淹没比"
|
msgstr "大型洞穴淹没比"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Large chat console"
|
msgid "Large chat console"
|
||||||
msgstr "大型聊天控制台键"
|
msgstr "大型聊天控制台"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Leaves style"
|
msgid "Leaves style"
|
||||||
|
@ -5714,23 +5668,20 @@ msgid "Mouse sensitivity multiplier."
|
||||||
msgstr "鼠标灵敏度倍数。"
|
msgstr "鼠标灵敏度倍数。"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Move backward"
|
msgid "Move backward"
|
||||||
msgstr "向后"
|
msgstr "后退"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Move forward"
|
msgid "Move forward"
|
||||||
msgstr "自动向前"
|
msgstr "前进"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Move left"
|
msgid "Move left"
|
||||||
msgstr "移动"
|
msgstr "往左移动"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Move right"
|
msgid "Move right"
|
||||||
msgstr ""
|
msgstr "往右移动"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
|
@ -5879,14 +5830,12 @@ msgid ""
|
||||||
msgstr "默认字体后阴影的透明度(alpha),取值范围0~255。"
|
msgstr "默认字体后阴影的透明度(alpha),取值范围0~255。"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Open chat"
|
msgid "Open chat"
|
||||||
msgstr "打开"
|
msgstr "打开聊天室"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Open inventory"
|
msgid "Open inventory"
|
||||||
msgstr "物品栏"
|
msgstr "打开物品栏"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid ""
|
msgid ""
|
||||||
|
@ -7174,14 +7123,12 @@ msgid "Toggle Sneak key"
|
||||||
msgstr "启用/禁用拍照模式键"
|
msgstr "启用/禁用拍照模式键"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Toggle automatic forward"
|
msgid "Toggle automatic forward"
|
||||||
msgstr "自动前进键"
|
msgstr "启用/禁用自动前进"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Toggle block bounds"
|
msgid "Toggle block bounds"
|
||||||
msgstr "地图块边界"
|
msgstr "显示/隐藏地图块边界"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
|
@ -7199,18 +7146,16 @@ msgid "Toggle cinematic mode"
|
||||||
msgstr "切换电影模式"
|
msgstr "切换电影模式"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Toggle debug info"
|
msgid "Toggle debug info"
|
||||||
msgstr "启用/禁用雾"
|
msgstr "显示/隐藏调试信息"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Toggle fog"
|
msgid "Toggle fog"
|
||||||
msgstr "启用/禁用雾"
|
msgstr "启用/禁用雾"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Toggle fullscreen"
|
msgid "Toggle fullscreen"
|
||||||
msgstr "启用/禁用飞行模式"
|
msgstr "切换全屏模式"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Toggle pitchmove"
|
msgid "Toggle pitchmove"
|
||||||
|
@ -7238,9 +7183,8 @@ msgid "Touchscreen"
|
||||||
msgstr "触摸屏"
|
msgstr "触摸屏"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
#, fuzzy
|
|
||||||
msgid "Touchscreen controls"
|
msgid "Touchscreen controls"
|
||||||
msgstr "触屏阈值"
|
msgstr "触屏控制"
|
||||||
|
|
||||||
#: src/settings_translation_file.cpp
|
#: src/settings_translation_file.cpp
|
||||||
msgid "Touchscreen sensitivity"
|
msgid "Touchscreen sensitivity"
|
||||||
|
|
|
@ -147,8 +147,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||||
/*
|
/*
|
||||||
Menu-game loop
|
Menu-game loop
|
||||||
*/
|
*/
|
||||||
bool retval = true;
|
bool retval = true;
|
||||||
bool *kill = porting::signal_handler_killstatus();
|
volatile auto *kill = porting::signal_handler_killstatus();
|
||||||
|
|
||||||
while (m_rendering_engine->run() && !*kill &&
|
while (m_rendering_engine->run() && !*kill &&
|
||||||
!g_gamecallback->shutdown_requested) {
|
!g_gamecallback->shutdown_requested) {
|
||||||
|
@ -326,6 +326,18 @@ void ClientLauncher::setting_changed_callback(const std::string &name, void *dat
|
||||||
static_cast<ClientLauncher*>(data)->config_guienv();
|
static_cast<ClientLauncher*>(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()
|
void ClientLauncher::config_guienv()
|
||||||
{
|
{
|
||||||
gui::IGUISkin *skin = guienv->getSkin();
|
gui::IGUISkin *skin = guienv->getSkin();
|
||||||
|
@ -364,10 +376,9 @@ void ClientLauncher::config_guienv()
|
||||||
if (cached_id != sprite_ids.end()) {
|
if (cached_id != sprite_ids.end()) {
|
||||||
skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, cached_id->second);
|
skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, cached_id->second);
|
||||||
} else {
|
} else {
|
||||||
gui::IGUISpriteBank *sprites = skin->getSpriteBank();
|
auto *driver = m_rendering_engine->get_video_driver();
|
||||||
video::IVideoDriver *driver = m_rendering_engine->get_video_driver();
|
auto *texture = loadTexture(driver, path.c_str());
|
||||||
video::ITexture *texture = driver->getTexture(path.c_str());
|
s32 id = skin->getSpriteBank()->addTextureAsSprite(texture);
|
||||||
s32 id = sprites->addTextureAsSprite(texture);
|
|
||||||
if (id != -1) {
|
if (id != -1) {
|
||||||
skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, id);
|
skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, id);
|
||||||
sprite_ids.emplace(path, id);
|
sprite_ids.emplace(path, id);
|
||||||
|
@ -529,9 +540,9 @@ bool ClientLauncher::launch_game(std::string &error_message,
|
||||||
|
|
||||||
void ClientLauncher::main_menu(MainMenuData *menudata)
|
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();
|
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
|
// Wait until app is in foreground because of #15883
|
||||||
infostream << "Waiting for app to be in foreground" << std::endl;
|
infostream << "Waiting for app to be in foreground" << std::endl;
|
||||||
|
|
|
@ -178,15 +178,6 @@ static void setBillboardTextureMatrix(scene::IBillboardSceneNode *bill,
|
||||||
matrix.setTextureScale(txs, tys);
|
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)
|
static bool logOnce(const std::ostringstream &from, std::ostream &log_to)
|
||||||
{
|
{
|
||||||
thread_local std::vector<u64> logged;
|
thread_local std::vector<u64> 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 = m_smgr->addAnimatedMeshSceneNode(mesh, m_matrixnode);
|
||||||
m_animated_meshnode->grab();
|
m_animated_meshnode->grab();
|
||||||
mesh->drop(); // The scene node took hold of it
|
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);
|
m_animated_meshnode->setScale(m_prop.visual_size);
|
||||||
|
|
||||||
// set vertex colors to ensure alpha is set
|
// 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) {
|
m_animated_meshnode->forEachMaterial([this] (auto &mat) {
|
||||||
mat.BackfaceCulling = m_prop.backface_culling;
|
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
|
} else
|
||||||
errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
|
errorstream<<"GenericCAO::addToScene(): Could not load mesh "<<m_prop.mesh<<std::endl;
|
||||||
break;
|
break;
|
||||||
|
@ -783,7 +788,6 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr)
|
||||||
updateMarker();
|
updateMarker();
|
||||||
updateNodePos();
|
updateNodePos();
|
||||||
updateAnimation();
|
updateAnimation();
|
||||||
updateBones(.0f);
|
|
||||||
updateAttachments();
|
updateAttachments();
|
||||||
setNodeLight(m_last_light);
|
setNodeLight(m_last_light);
|
||||||
updateMeshCulling();
|
updateMeshCulling();
|
||||||
|
@ -1174,18 +1178,6 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
|
||||||
rot_translator.val_current = m_rotation;
|
rot_translator.val_current = m_rotation;
|
||||||
updateNodePos();
|
updateNodePos();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_animated_meshnode) {
|
|
||||||
// Everything must be updated; the whole transform
|
|
||||||
// chain as well as the animated mesh node.
|
|
||||||
// Otherwise, bone attachments would be relative to
|
|
||||||
// a position that's one frame old.
|
|
||||||
if (m_matrixnode)
|
|
||||||
updatePositionRecursive(m_matrixnode);
|
|
||||||
m_animated_meshnode->updateAbsolutePosition();
|
|
||||||
m_animated_meshnode->animateJoints();
|
|
||||||
updateBones(dtime);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count)
|
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);
|
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()
|
void GenericCAO::updateAttachments()
|
||||||
{
|
{
|
||||||
ClientActiveObject *parent = getParent();
|
ClientActiveObject *parent = getParent();
|
||||||
|
@ -1747,7 +1701,6 @@ void GenericCAO::processMessage(const std::string &data)
|
||||||
} else {
|
} else {
|
||||||
m_bone_override[bone] = props;
|
m_bone_override[bone] = props;
|
||||||
}
|
}
|
||||||
// updateBones(); now called every step
|
|
||||||
} else if (cmd == AO_CMD_ATTACH_TO) {
|
} else if (cmd == AO_CMD_ATTACH_TO) {
|
||||||
u16 parent_id = readS16(is);
|
u16 parent_id = readS16(is);
|
||||||
std::string bone = deSerializeString16(is);
|
std::string bone = deSerializeString16(is);
|
||||||
|
|
|
@ -286,8 +286,6 @@ public:
|
||||||
|
|
||||||
void updateAnimationSpeed();
|
void updateAnimationSpeed();
|
||||||
|
|
||||||
void updateBones(f32 dtime);
|
|
||||||
|
|
||||||
void processMessage(const std::string &data) override;
|
void processMessage(const std::string &data) override;
|
||||||
|
|
||||||
bool directReportPunch(v3f dir, const ItemStack *punchitem,
|
bool directReportPunch(v3f dir, const ItemStack *punchitem,
|
||||||
|
|
|
@ -137,11 +137,20 @@ void MapblockMeshGenerator::drawQuad(const TileSpec &tile, v3f *coords, const v3
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::array<video::S3DVertex, 24> setupCuboidVertices(const aabb3f &box,
|
static std::array<video::S3DVertex, 24> 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 min = box.MinEdge;
|
||||||
v3f max = box.MaxEdge;
|
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<video::S3DVertex, 24> vertices = {{
|
std::array<video::S3DVertex, 24> vertices = {{
|
||||||
// top
|
// top
|
||||||
video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, {}, txc[0], txc[1]),
|
video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, {}, txc[0], txc[1]),
|
||||||
|
@ -185,15 +194,47 @@ static std::array<video::S3DVertex, 24> setupCuboidVertices(const aabb3f &box,
|
||||||
case TileRotation::None:
|
case TileRotation::None:
|
||||||
break;
|
break;
|
||||||
case TileRotation::R90:
|
case TileRotation::R90:
|
||||||
tcoords.set(-tcoords.Y, tcoords.X);
|
tcoords.set(1 - tcoords.Y, tcoords.X);
|
||||||
break;
|
break;
|
||||||
case TileRotation::R180:
|
case TileRotation::R180:
|
||||||
tcoords.set(-tcoords.X, -tcoords.Y);
|
tcoords.set(1 - tcoords.X, 1 - tcoords.Y);
|
||||||
break;
|
break;
|
||||||
case TileRotation::R270:
|
case TileRotation::R270:
|
||||||
tcoords.set(tcoords.Y, -tcoords.X);
|
tcoords.set(tcoords.Y, 1 - tcoords.X);
|
||||||
break;
|
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
|
// for the opposite corners of each face - therefore, there
|
||||||
// should be (2+2)*6=24 values in the list. The order of
|
// 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
|
// the faces in the list is up-down-right-left-back-front
|
||||||
|
// if nullptr use standard [0,1] coords
|
||||||
// (compatible with ContentFeatures).
|
// (compatible with ContentFeatures).
|
||||||
// mask - a bit mask that suppresses drawing of tiles.
|
// mask - a bit mask that suppresses drawing of tiles.
|
||||||
// tile i will not be drawn if mask & (1 << i) is 1
|
// 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
|
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) {
|
for (int k = 0; k < 6; ++k) {
|
||||||
if (mask & (1 << 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)
|
void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
|
||||||
{
|
{
|
||||||
f32 tx1 = (box.MinEdge.X / BS) + 0.5;
|
// Generate texture coords which are aligned to coords of a solid nodes
|
||||||
f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
|
f32 tx1 = (box.MinEdge.X / BS) + 0.5f;
|
||||||
f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
|
f32 ty1 = (box.MinEdge.Y / BS) + 0.5f;
|
||||||
f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
|
f32 tz1 = (box.MinEdge.Z / BS) + 0.5f;
|
||||||
f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
|
f32 tx2 = (box.MaxEdge.X / BS) + 0.5f;
|
||||||
f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
|
f32 ty2 = (box.MaxEdge.Y / BS) + 0.5f;
|
||||||
|
f32 tz2 = (box.MaxEdge.Z / BS) + 0.5f;
|
||||||
f32 txc[24] = {
|
f32 txc[24] = {
|
||||||
tx1, 1 - tz2, tx2, 1 - tz1, // up
|
tx1, 1 - tz2, tx2, 1 - tz1, // up
|
||||||
tx1, tz1, tx2, tz2, // down
|
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)
|
const TileSpec *tiles, int tile_count, const f32 *txc, u8 mask)
|
||||||
{
|
{
|
||||||
bool scale = std::fabs(cur_node.f->visual_scale - 1.0f) > 1e-3f;
|
bool scale = std::fabs(cur_node.f->visual_scale - 1.0f) > 1e-3f;
|
||||||
f32 texture_coord_buf[24];
|
|
||||||
f32 dx1 = box.MinEdge.X;
|
f32 dx1 = box.MinEdge.X;
|
||||||
f32 dy1 = box.MinEdge.Y;
|
f32 dy1 = box.MinEdge.Y;
|
||||||
f32 dz1 = box.MinEdge.Z;
|
f32 dz1 = box.MinEdge.Z;
|
||||||
|
@ -342,19 +384,11 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box,
|
||||||
f32 dy2 = box.MaxEdge.Y;
|
f32 dy2 = box.MaxEdge.Y;
|
||||||
f32 dz2 = box.MaxEdge.Z;
|
f32 dz2 = box.MaxEdge.Z;
|
||||||
if (scale) {
|
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.MinEdge *= cur_node.f->visual_scale;
|
||||||
box.MaxEdge *= cur_node.f->visual_scale;
|
box.MaxEdge *= cur_node.f->visual_scale;
|
||||||
}
|
}
|
||||||
box.MinEdge += cur_node.origin;
|
box.MinEdge += cur_node.origin;
|
||||||
box.MaxEdge += cur_node.origin;
|
box.MaxEdge += cur_node.origin;
|
||||||
if (!txc) {
|
|
||||||
generateCuboidTextureCoords(box, texture_coord_buf);
|
|
||||||
txc = texture_coord_buf;
|
|
||||||
}
|
|
||||||
if (data->m_smooth_lighting) {
|
if (data->m_smooth_lighting) {
|
||||||
LightInfo lights[8];
|
LightInfo lights[8];
|
||||||
for (int j = 0; j < 8; ++j) {
|
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.
|
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);
|
cur_node.origin = intToFloat(cur_node.p, BS);
|
||||||
auto box = aabb3f(v3f(-0.5 * BS), v3f(0.5 * BS));
|
auto box = aabb3f(v3f(-0.5 * BS), v3f(0.5 * BS));
|
||||||
f32 texture_coord_buf[24];
|
|
||||||
box.MinEdge += cur_node.origin;
|
box.MinEdge += cur_node.origin;
|
||||||
box.MaxEdge += cur_node.origin;
|
box.MaxEdge += cur_node.origin;
|
||||||
generateCuboidTextureCoords(box, texture_coord_buf);
|
|
||||||
if (data->m_smooth_lighting) {
|
if (data->m_smooth_lighting) {
|
||||||
LightPair lights[6][4];
|
LightPair lights[6][4];
|
||||||
for (int face = 0; face < 6; ++face) {
|
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];
|
auto final_lights = lights[face];
|
||||||
for (int j = 0; j < 4; j++) {
|
for (int j = 0; j < 4; j++) {
|
||||||
video::S3DVertex &vertex = vertices[j];
|
video::S3DVertex &vertex = vertices[j];
|
||||||
|
@ -471,7 +503,7 @@ void MapblockMeshGenerator::drawSolidNode()
|
||||||
return QuadDiagonal::Diag02;
|
return QuadDiagonal::Diag02;
|
||||||
});
|
});
|
||||||
} else {
|
} 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);
|
video::SColor color = encode_light(lights[face], cur_node.f->light_source);
|
||||||
if (!cur_node.f->light_source)
|
if (!cur_node.f->light_source)
|
||||||
applyFacesShading(color, vertices[0].Normal);
|
applyFacesShading(color, vertices[0].Normal);
|
||||||
|
@ -952,7 +984,10 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
|
||||||
edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
|
edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
|
||||||
if (edge_invisible)
|
if (edge_invisible)
|
||||||
continue;
|
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++) {
|
for (int face = 0; face < 6; face++) {
|
||||||
|
@ -996,16 +1031,17 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode()
|
||||||
float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
|
float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
|
||||||
TileSpec tile;
|
TileSpec tile;
|
||||||
getSpecialTile(0, &tile);
|
getSpecialTile(0, &tile);
|
||||||
drawAutoLightedCuboid(
|
aabb3f box(
|
||||||
aabb3f(
|
-(nb[5] ? g : b),
|
||||||
-(nb[5] ? g : b),
|
-(nb[4] ? g : b),
|
||||||
-(nb[4] ? g : b),
|
-(nb[3] ? g : b),
|
||||||
-(nb[3] ? g : b),
|
(nb[2] ? g : b),
|
||||||
(nb[2] ? g : b),
|
(nb[1] ? g : b) * vlev,
|
||||||
(nb[1] ? g : b) * vlev,
|
(nb[0] ? g : b)
|
||||||
(nb[0] ? g : b)
|
);
|
||||||
),
|
f32 txc[24];
|
||||||
tile);
|
generateCuboidTextureCoords(box, txc);
|
||||||
|
drawAutoLightedCuboid(box, tile, txc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1649,7 +1685,10 @@ void MapblockMeshGenerator::drawNodeboxNode()
|
||||||
|
|
||||||
for (auto &box : boxes) {
|
for (auto &box : boxes) {
|
||||||
u8 mask = getNodeBoxMask(box, solid_neighbors, sametype_neighbors);
|
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) {
|
if (cur_node.f->mesh_ptr) {
|
||||||
// clone and rotate mesh
|
// clone and rotate mesh
|
||||||
mesh = cloneMesh(cur_node.f->mesh_ptr);
|
mesh = cloneStaticMesh(cur_node.f->mesh_ptr);
|
||||||
bool modified = true;
|
bool modified = true;
|
||||||
if (facedir)
|
if (facedir)
|
||||||
rotateMeshBy6dFacedir(mesh, facedir);
|
rotateMeshBy6dFacedir(mesh, facedir);
|
||||||
|
|
|
@ -86,7 +86,7 @@ private:
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
void drawCuboid(const aabb3f &box, const TileSpec *tiles, int tilecount,
|
void drawCuboid(const aabb3f &box, const TileSpec *tiles, int tilecount,
|
||||||
const f32 *txc, u8 mask, Fn &&face_lighter);
|
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 &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);
|
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;
|
u8 getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const;
|
||||||
|
|
|
@ -65,6 +65,8 @@
|
||||||
#include "client/sound/sound_openal.h"
|
#include "client/sound/sound_openal.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
class NodeDugEvent : public MtEvent
|
class NodeDugEvent : public MtEvent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -561,7 +563,7 @@ public:
|
||||||
Game();
|
Game();
|
||||||
~Game();
|
~Game();
|
||||||
|
|
||||||
bool startup(bool *kill,
|
bool startup(volatile std::sig_atomic_t *kill,
|
||||||
InputHandler *input,
|
InputHandler *input,
|
||||||
RenderingEngine *rendering_engine,
|
RenderingEngine *rendering_engine,
|
||||||
const GameStartData &game_params,
|
const GameStartData &game_params,
|
||||||
|
@ -793,14 +795,14 @@ private:
|
||||||
This class does take ownership/responsibily for cleaning up etc of any of
|
This class does take ownership/responsibily for cleaning up etc of any of
|
||||||
these items (e.g. device)
|
these items (e.g. device)
|
||||||
*/
|
*/
|
||||||
IrrlichtDevice *device;
|
IrrlichtDevice *device;
|
||||||
RenderingEngine *m_rendering_engine;
|
RenderingEngine *m_rendering_engine;
|
||||||
video::IVideoDriver *driver;
|
video::IVideoDriver *driver;
|
||||||
scene::ISceneManager *smgr;
|
scene::ISceneManager *smgr;
|
||||||
bool *kill;
|
volatile std::sig_atomic_t *kill;
|
||||||
std::string *error_message;
|
std::string *error_message;
|
||||||
bool *reconnect_requested;
|
bool *reconnect_requested;
|
||||||
PausedNodesList paused_animated_nodes;
|
PausedNodesList paused_animated_nodes;
|
||||||
|
|
||||||
bool simple_singleplayer_mode;
|
bool simple_singleplayer_mode;
|
||||||
/* End 'cache' */
|
/* End 'cache' */
|
||||||
|
@ -932,7 +934,7 @@ Game::~Game()
|
||||||
m_rendering_engine->finalize();
|
m_rendering_engine->finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Game::startup(bool *kill,
|
bool Game::startup(volatile std::sig_atomic_t *kill,
|
||||||
InputHandler *input,
|
InputHandler *input,
|
||||||
RenderingEngine *rendering_engine,
|
RenderingEngine *rendering_engine,
|
||||||
const GameStartData &start_data,
|
const GameStartData &start_data,
|
||||||
|
@ -1114,8 +1116,12 @@ void Game::run()
|
||||||
|
|
||||||
void Game::shutdown()
|
void Game::shutdown()
|
||||||
{
|
{
|
||||||
// Clear text when exiting.
|
// Delete text and menus first
|
||||||
m_game_ui->clearText();
|
m_game_ui->clearText();
|
||||||
|
m_game_formspec.reset();
|
||||||
|
while (g_menumgr.menuCount() > 0) {
|
||||||
|
g_menumgr.deleteFront();
|
||||||
|
}
|
||||||
|
|
||||||
if (g_touchcontrols)
|
if (g_touchcontrols)
|
||||||
g_touchcontrols->hide();
|
g_touchcontrols->hide();
|
||||||
|
@ -1126,11 +1132,6 @@ void Game::shutdown()
|
||||||
|
|
||||||
sky.reset();
|
sky.reset();
|
||||||
|
|
||||||
/* cleanup menus */
|
|
||||||
while (g_menumgr.menuCount() > 0) {
|
|
||||||
g_menumgr.deleteFront();
|
|
||||||
}
|
|
||||||
|
|
||||||
// only if the shutdown progress bar isn't shown yet
|
// only if the shutdown progress bar isn't shown yet
|
||||||
if (m_shutdown_progress == 0.0f)
|
if (m_shutdown_progress == 0.0f)
|
||||||
showOverlayMessage(N_("Shutting down..."), 0, 0);
|
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,
|
InputHandler *input,
|
||||||
RenderingEngine *rendering_engine,
|
RenderingEngine *rendering_engine,
|
||||||
const GameStartData &start_data,
|
const GameStartData &start_data,
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "irrlichttypes.h"
|
#include "irrlichttypes.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include <csignal>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#if !IS_CLIENT_BUILD
|
#if !IS_CLIENT_BUILD
|
||||||
|
@ -36,7 +37,7 @@ struct CameraOrientation {
|
||||||
#define GAME_FALLBACK_TIMEOUT 1.8f
|
#define GAME_FALLBACK_TIMEOUT 1.8f
|
||||||
#define GAME_CONNECTION_TIMEOUT 10.0f
|
#define GAME_CONNECTION_TIMEOUT 10.0f
|
||||||
|
|
||||||
void the_game(bool *kill,
|
void the_game(volatile std::sig_atomic_t *kill,
|
||||||
InputHandler *input,
|
InputHandler *input,
|
||||||
RenderingEngine *rendering_engine,
|
RenderingEngine *rendering_engine,
|
||||||
const GameStartData &start_data,
|
const GameStartData &start_data,
|
||||||
|
|
|
@ -31,16 +31,6 @@ struct TextDestNodeMetadata : public TextDest
|
||||||
m_p = p;
|
m_p = p;
|
||||||
m_client = client;
|
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)
|
void gotText(const StringMap &fields)
|
||||||
{
|
{
|
||||||
m_client->sendNodemetaFields(m_p, "", fields);
|
m_client->sendNodemetaFields(m_p, "", fields);
|
||||||
|
@ -217,10 +207,11 @@ void GameFormSpec::deleteFormspec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameFormSpec::~GameFormSpec() {
|
void GameFormSpec::reset()
|
||||||
|
{
|
||||||
if (m_formspec)
|
if (m_formspec)
|
||||||
m_formspec->quitMenu();
|
m_formspec->quitMenu();
|
||||||
this->deleteFormspec();
|
deleteFormspec();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameFormSpec::handleEmptyFormspec(const std::string &formspec, const std::string &formname)
|
bool GameFormSpec::handleEmptyFormspec(const std::string &formspec, const std::string &formname)
|
||||||
|
|
|
@ -26,7 +26,7 @@ struct GameFormSpec
|
||||||
{
|
{
|
||||||
void init(Client *client, RenderingEngine *rendering_engine, InputHandler *input);
|
void init(Client *client, RenderingEngine *rendering_engine, InputHandler *input);
|
||||||
|
|
||||||
~GameFormSpec();
|
~GameFormSpec() { reset(); }
|
||||||
|
|
||||||
void showFormSpec(const std::string &formspec, const std::string &formname);
|
void showFormSpec(const std::string &formspec, const std::string &formname);
|
||||||
void showCSMFormSpec(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();
|
void disableDebugView();
|
||||||
|
|
||||||
bool handleCallbacks();
|
bool handleCallbacks();
|
||||||
|
void reset();
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
// Returns false if no formspec open
|
// Returns false if no formspec open
|
||||||
|
|
|
@ -3,15 +3,16 @@
|
||||||
// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||||
|
|
||||||
#include "mesh.h"
|
#include "mesh.h"
|
||||||
|
#include "IMeshBuffer.h"
|
||||||
|
#include "SSkinMeshBuffer.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <IAnimatedMesh.h>
|
#include <IAnimatedMesh.h>
|
||||||
#include <SAnimatedMesh.h>
|
|
||||||
#include <IAnimatedMeshSceneNode.h>
|
#include <IAnimatedMeshSceneNode.h>
|
||||||
#include "S3DVertex.h"
|
#include "S3DVertex.h"
|
||||||
#include "SMesh.h"
|
#include <SMesh.h>
|
||||||
#include "SMeshBuffer.h"
|
#include "SMeshBuffer.h"
|
||||||
|
|
||||||
inline static void applyShadeFactor(video::SColor& color, float factor)
|
inline static void applyShadeFactor(video::SColor& color, float factor)
|
||||||
|
@ -95,11 +96,23 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale)
|
||||||
mesh->addMeshBuffer(buf);
|
mesh->addMeshBuffer(buf);
|
||||||
buf->drop();
|
buf->drop();
|
||||||
}
|
}
|
||||||
|
scaleMesh(mesh, scale); // also recalculates bounding box
|
||||||
|
return mesh;
|
||||||
|
}
|
||||||
|
|
||||||
scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh);
|
template<typename F>
|
||||||
mesh->drop();
|
inline static void transformMeshBuffer(scene::IMeshBuffer *buf,
|
||||||
scaleMesh(anim_mesh, scale); // also recalculates bounding box
|
const F &transform_vertex)
|
||||||
return anim_mesh;
|
{
|
||||||
|
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)
|
void scaleMesh(scene::IMesh *mesh, v3f scale)
|
||||||
|
@ -112,14 +125,9 @@ void scaleMesh(scene::IMesh *mesh, v3f scale)
|
||||||
u32 mc = mesh->getMeshBufferCount();
|
u32 mc = mesh->getMeshBufferCount();
|
||||||
for (u32 j = 0; j < mc; j++) {
|
for (u32 j = 0; j < mc; j++) {
|
||||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
|
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
|
||||||
const u32 stride = getVertexPitchFromType(buf->getVertexType());
|
transformMeshBuffer(buf, [scale](video::S3DVertex *vertex) {
|
||||||
u32 vertex_count = buf->getVertexCount();
|
vertex->Pos *= scale;
|
||||||
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();
|
|
||||||
|
|
||||||
// calculate total bounding box
|
// calculate total bounding box
|
||||||
if (j == 0)
|
if (j == 0)
|
||||||
|
@ -140,14 +148,9 @@ void translateMesh(scene::IMesh *mesh, v3f vec)
|
||||||
u32 mc = mesh->getMeshBufferCount();
|
u32 mc = mesh->getMeshBufferCount();
|
||||||
for (u32 j = 0; j < mc; j++) {
|
for (u32 j = 0; j < mc; j++) {
|
||||||
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
|
scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
|
||||||
const u32 stride = getVertexPitchFromType(buf->getVertexType());
|
transformMeshBuffer(buf, [vec](video::S3DVertex *vertex) {
|
||||||
u32 vertex_count = buf->getVertexCount();
|
vertex->Pos += vec;
|
||||||
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();
|
|
||||||
|
|
||||||
// calculate total bounding box
|
// calculate total bounding box
|
||||||
if (j == 0)
|
if (j == 0)
|
||||||
|
@ -330,44 +333,40 @@ bool checkMeshNormals(scene::IMesh *mesh)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class VertexType, class SMeshBufferType>
|
||||||
|
static scene::IMeshBuffer *cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
|
||||||
|
{
|
||||||
|
auto *v = static_cast<VertexType *>(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<scene::SSkinMeshBuffer *>(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)
|
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer)
|
||||||
{
|
{
|
||||||
switch (mesh_buffer->getVertexType()) {
|
switch (mesh_buffer->getVertexType()) {
|
||||||
case video::EVT_STANDARD: {
|
case video::EVT_STANDARD:
|
||||||
video::S3DVertex *v = (video::S3DVertex *) mesh_buffer->getVertices();
|
return cloneMeshBuffer<video::S3DVertex, scene::SMeshBuffer>(mesh_buffer);
|
||||||
u16 *indices = mesh_buffer->getIndices();
|
case video::EVT_2TCOORDS:
|
||||||
scene::SMeshBuffer *cloned_buffer = new scene::SMeshBuffer();
|
return cloneMeshBuffer<video::S3DVertex2TCoords, scene::SMeshBufferLightMap>(mesh_buffer);
|
||||||
cloned_buffer->append(v, mesh_buffer->getVertexCount(), indices,
|
case video::EVT_TANGENTS:
|
||||||
mesh_buffer->getIndexCount());
|
return cloneMeshBuffer<video::S3DVertexTangents, scene::SMeshBufferTangents>(mesh_buffer);
|
||||||
return cloned_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);
|
sanity_check(false);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
scene::SMesh* cloneMesh(scene::IMesh *src_mesh)
|
scene::SMesh* cloneStaticMesh(scene::IMesh *src_mesh)
|
||||||
{
|
{
|
||||||
scene::SMesh* dst_mesh = new scene::SMesh();
|
scene::SMesh* dst_mesh = new scene::SMesh();
|
||||||
for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
|
for (u16 j = 0; j < src_mesh->getMeshBufferCount(); j++) {
|
||||||
|
|
|
@ -93,10 +93,8 @@ void rotateMeshYZby (scene::IMesh *mesh, f64 degrees);
|
||||||
*/
|
*/
|
||||||
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer);
|
scene::IMeshBuffer* cloneMeshBuffer(scene::IMeshBuffer *mesh_buffer);
|
||||||
|
|
||||||
/*
|
/// Clone a mesh. For an animated mesh, this will clone the static pose.
|
||||||
Clone the mesh.
|
scene::SMesh* cloneStaticMesh(scene::IMesh *src_mesh);
|
||||||
*/
|
|
||||||
scene::SMesh* cloneMesh(scene::IMesh *src_mesh);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Convert nodeboxes to mesh. Each tile goes into a different buffer.
|
Convert nodeboxes to mesh. Each tile goes into a different buffer.
|
||||||
|
|
|
@ -14,25 +14,19 @@ void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertice
|
||||||
const TileLayer *layer = &tile.layers[layernum];
|
const TileLayer *layer = &tile.layers[layernum];
|
||||||
if (layer->empty())
|
if (layer->empty())
|
||||||
continue;
|
continue;
|
||||||
append(*layer, vertices, numVertices, indices, numIndices, layernum,
|
append(*layer, vertices, numVertices, indices, numIndices, layernum);
|
||||||
tile.world_aligned);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *vertices,
|
void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *vertices,
|
||||||
u32 numVertices, const u16 *indices, u32 numIndices, u8 layernum,
|
u32 numVertices, const u16 *indices, u32 numIndices, u8 layernum)
|
||||||
bool use_scale)
|
|
||||||
{
|
{
|
||||||
PreMeshBuffer &p = findBuffer(layer, layernum, numVertices);
|
PreMeshBuffer &p = findBuffer(layer, layernum, numVertices);
|
||||||
|
|
||||||
f32 scale = 1.0f;
|
|
||||||
if (use_scale)
|
|
||||||
scale = 1.0f / layer.scale;
|
|
||||||
|
|
||||||
u32 vertex_count = p.vertices.size();
|
u32 vertex_count = p.vertices.size();
|
||||||
for (u32 i = 0; i < numVertices; i++) {
|
for (u32 i = 0; i < numVertices; i++) {
|
||||||
p.vertices.emplace_back(vertices[i].Pos + offset, vertices[i].Normal,
|
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,
|
m_bounding_radius_sq = std::max(m_bounding_radius_sq,
|
||||||
(vertices[i].Pos - m_center_pos).getLengthSQ());
|
(vertices[i].Pos - m_center_pos).getLengthSQ());
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ private:
|
||||||
void append(const TileLayer &material,
|
void append(const TileLayer &material,
|
||||||
const video::S3DVertex *vertices, u32 numVertices,
|
const video::S3DVertex *vertices, u32 numVertices,
|
||||||
const u16 *indices, u32 numIndices,
|
const u16 *indices, u32 numIndices,
|
||||||
u8 layernum, bool use_scale = false);
|
u8 layernum);
|
||||||
|
|
||||||
PreMeshBuffer &findBuffer(const TileLayer &layer, u8 layernum, u32 numVertices);
|
PreMeshBuffer &findBuffer(const TileLayer &layer, u8 layernum, u32 numVertices);
|
||||||
};
|
};
|
||||||
|
|
|
@ -410,7 +410,7 @@ video::ITexture *ShadowRenderer::getSMTexture(const std::string &shadow_map_name
|
||||||
shadow_map_name.c_str(), texture_format);
|
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,
|
void ShadowRenderer::renderShadowMap(video::ITexture *target,
|
||||||
|
|
|
@ -191,8 +191,6 @@ struct TileSpec
|
||||||
bool world_aligned = false;
|
bool world_aligned = false;
|
||||||
//! Tile rotation.
|
//! Tile rotation.
|
||||||
TileRotation rotation = TileRotation::None;
|
TileRotation rotation = TileRotation::None;
|
||||||
//! This much light does the tile emit.
|
|
||||||
u8 emissive_light = 0;
|
|
||||||
//! The first is base texture, the second is overlay.
|
//! The first is base texture, the second is overlay.
|
||||||
TileLayer layers[MAX_TILE_LAYERS];
|
TileLayer layers[MAX_TILE_LAYERS];
|
||||||
};
|
};
|
||||||
|
|
|
@ -255,7 +255,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
|
||||||
dim = core::dimension2d<u32>(dim.Width, frame_height);
|
dim = core::dimension2d<u32>(dim.Width, frame_height);
|
||||||
}
|
}
|
||||||
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
||||||
scene::SMesh *mesh = cloneMesh(original);
|
scene::SMesh *mesh = cloneStaticMesh(original);
|
||||||
original->drop();
|
original->drop();
|
||||||
//set texture
|
//set texture
|
||||||
mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
|
mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
|
||||||
|
@ -639,7 +639,7 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc,
|
||||||
// get mesh
|
// get mesh
|
||||||
core::dimension2d<u32> dim = texture->getSize();
|
core::dimension2d<u32> dim = texture->getSize();
|
||||||
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
|
||||||
scene::SMesh *mesh = cloneMesh(original);
|
scene::SMesh *mesh = cloneStaticMesh(original);
|
||||||
original->drop();
|
original->drop();
|
||||||
|
|
||||||
//set texture
|
//set texture
|
||||||
|
|
|
@ -27,12 +27,13 @@ public:
|
||||||
void fill(v3s16 bpmin, v3s16 bpmax, MapNode n)
|
void fill(v3s16 bpmin, v3s16 bpmax, MapNode n)
|
||||||
{
|
{
|
||||||
for (s16 z = bpmin.Z; z <= bpmax.Z; z++)
|
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});
|
MapBlock *block = getBlockNoCreateNoEx({x, y, z});
|
||||||
if (block) {
|
if (block) {
|
||||||
|
auto *data = block->getData();
|
||||||
for (size_t i = 0; i < MapBlock::nodecount; i++)
|
for (size_t i = 0; i < MapBlock::nodecount; i++)
|
||||||
block->getData()[i] = n;
|
data[i] = n;
|
||||||
block->expireIsAirCache();
|
block->expireIsAirCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -718,19 +718,24 @@ void GUIButton::setFromStyle(const StyleSpec& style)
|
||||||
setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
|
setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
|
||||||
setOverrideFont(style.getFont());
|
setOverrideFont(style.getFont());
|
||||||
|
|
||||||
|
BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle);
|
||||||
|
|
||||||
if (style.isNotDefault(StyleSpec::BGIMG)) {
|
if (style.isNotDefault(StyleSpec::BGIMG)) {
|
||||||
video::ITexture *texture = style.getTexture(StyleSpec::BGIMG,
|
video::ITexture *texture = style.getTexture(StyleSpec::BGIMG,
|
||||||
getTextureSource());
|
getTextureSource());
|
||||||
setImage(guiScalingImageButton(
|
if (BgMiddle.getArea() == 0) {
|
||||||
Environment->getVideoDriver(), texture,
|
setImage(guiScalingImageButton(
|
||||||
AbsoluteRect.getWidth(), AbsoluteRect.getHeight()));
|
Environment->getVideoDriver(), texture,
|
||||||
|
AbsoluteRect.getWidth(), AbsoluteRect.getHeight()));
|
||||||
|
} else {
|
||||||
|
// Scaling happens in `draw2DImage9Slice`
|
||||||
|
setImage(texture);
|
||||||
|
}
|
||||||
setScaleImage(true);
|
setScaleImage(true);
|
||||||
} else {
|
} else {
|
||||||
setImage(nullptr);
|
setImage(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
BgMiddle = style.getRect(StyleSpec::BGIMG_MIDDLE, BgMiddle);
|
|
||||||
|
|
||||||
// Child padding and offset
|
// Child padding and offset
|
||||||
Padding = style.getRect(StyleSpec::PADDING, core::rect<s32>());
|
Padding = style.getRect(StyleSpec::PADDING, core::rect<s32>());
|
||||||
Padding = core::rect<s32>(
|
Padding = core::rect<s32>(
|
||||||
|
|
|
@ -33,6 +33,8 @@
|
||||||
#include "client/sound/sound_openal.h"
|
#include "client/sound/sound_openal.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
void TextDestGuiEngine::gotText(const StringMap &fields)
|
void TextDestGuiEngine::gotText(const StringMap &fields)
|
||||||
|
@ -40,12 +42,6 @@ void TextDestGuiEngine::gotText(const StringMap &fields)
|
||||||
m_engine->getScriptIface()->handleMainMenuButtons(fields);
|
m_engine->getScriptIface()->handleMainMenuButtons(fields);
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
void TextDestGuiEngine::gotText(const std::wstring &text)
|
|
||||||
{
|
|
||||||
m_engine->getScriptIface()->handleMainMenuEvent(wide_to_utf8(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
MenuTextureSource::~MenuTextureSource()
|
MenuTextureSource::~MenuTextureSource()
|
||||||
{
|
{
|
||||||
|
@ -74,6 +70,7 @@ video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
verbosestream << "MenuTextureSource: loading " << name << std::endl;
|
||||||
video::IImage *image = m_driver->createImageFromFile(name.c_str());
|
video::IImage *image = m_driver->createImageFromFile(name.c_str());
|
||||||
if (!image)
|
if (!image)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -109,7 +106,7 @@ GUIEngine::GUIEngine(JoystickController *joystick,
|
||||||
RenderingEngine *rendering_engine,
|
RenderingEngine *rendering_engine,
|
||||||
IMenuManager *menumgr,
|
IMenuManager *menumgr,
|
||||||
MainMenuData *data,
|
MainMenuData *data,
|
||||||
bool &kill) :
|
volatile std::sig_atomic_t &kill) :
|
||||||
m_rendering_engine(rendering_engine),
|
m_rendering_engine(rendering_engine),
|
||||||
m_parent(parent),
|
m_parent(parent),
|
||||||
m_menumanager(menumgr),
|
m_menumanager(menumgr),
|
||||||
|
@ -404,12 +401,6 @@ GUIEngine::~GUIEngine()
|
||||||
m_sound_manager.reset();
|
m_sound_manager.reset();
|
||||||
|
|
||||||
m_irr_toplefttext->remove();
|
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 GUIEngine::setTexture(texture_layer layer, const std::string &texturepath,
|
||||||
bool tile_image, unsigned int minsize)
|
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) {
|
if (texturepath.empty() || !fs::PathExists(texturepath))
|
||||||
driver->removeTexture(m_textures[layer].texture);
|
|
||||||
m_textures[layer].texture = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texturepath.empty() || !fs::PathExists(texturepath)) {
|
|
||||||
return false;
|
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].tile = tile_image;
|
||||||
m_textures[layer].minsize = minsize;
|
m_textures[layer].minsize = minsize;
|
||||||
|
|
||||||
if (!m_textures[layer].texture) {
|
return m_textures[layer].texture != nullptr;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include "util/enriched_string.h"
|
#include "util/enriched_string.h"
|
||||||
#include "translation.h"
|
#include "translation.h"
|
||||||
|
|
||||||
|
#include <csignal>
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* Structs and macros */
|
/* Structs and macros */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -61,12 +63,6 @@ public:
|
||||||
*/
|
*/
|
||||||
void gotText(const StringMap &fields);
|
void gotText(const StringMap &fields);
|
||||||
|
|
||||||
/**
|
|
||||||
* receive text/events transmitted by guiFormSpecMenu
|
|
||||||
* @param text textual representation of event
|
|
||||||
*/
|
|
||||||
void gotText(const std::wstring &text);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** target to transmit data to */
|
/** target to transmit data to */
|
||||||
GUIEngine *m_engine = nullptr;
|
GUIEngine *m_engine = nullptr;
|
||||||
|
@ -130,7 +126,7 @@ public:
|
||||||
RenderingEngine *rendering_engine,
|
RenderingEngine *rendering_engine,
|
||||||
IMenuManager *menumgr,
|
IMenuManager *menumgr,
|
||||||
MainMenuData *data,
|
MainMenuData *data,
|
||||||
bool &kill);
|
volatile std::sig_atomic_t &kill);
|
||||||
|
|
||||||
/** default destructor */
|
/** default destructor */
|
||||||
virtual ~GUIEngine();
|
virtual ~GUIEngine();
|
||||||
|
@ -199,7 +195,7 @@ private:
|
||||||
irr_ptr<GUIFormSpecMenu> m_menu;
|
irr_ptr<GUIFormSpecMenu> m_menu;
|
||||||
|
|
||||||
/** reference to kill variable managed by SIGINT handler */
|
/** 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 */
|
/** variable used to abort menu and return back to main game handling */
|
||||||
bool m_startgame = false;
|
bool m_startgame = false;
|
||||||
|
|
|
@ -77,6 +77,9 @@
|
||||||
return; \
|
return; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Element ID of the "Proceed" button shown for sizeless formspecs
|
||||||
|
constexpr s32 ID_PROCEED_BTN = 257;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GUIFormSpecMenu
|
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))));
|
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,
|
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
|
||||||
rect, m_tsrc);
|
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) {
|
if (spec.fname == m_focused_element) {
|
||||||
Environment->setFocus(e);
|
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")
|
if (!str_initial_selection.empty() && str_initial_selection != "0")
|
||||||
e->setSelected(stoi(str_initial_selection));
|
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_tables.emplace_back(spec, e);
|
||||||
m_fields.push_back(spec);
|
m_fields.push_back(spec);
|
||||||
}
|
}
|
||||||
|
@ -2998,7 +3001,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
||||||
gui::IGUIElement *focused_element = Environment->getFocus();
|
gui::IGUIElement *focused_element = Environment->getFocus();
|
||||||
if (focused_element && focused_element->getParent() == this) {
|
if (focused_element && focused_element->getParent() == this) {
|
||||||
s32 focused_id = focused_element->getID();
|
s32 focused_id = focused_element->getID();
|
||||||
if (focused_id > 257) {
|
if (focused_id > ID_PROCEED_BTN) {
|
||||||
for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
|
for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
|
||||||
if (field.fid == focused_id) {
|
if (field.fid == focused_id) {
|
||||||
m_focused_element = field.fname;
|
m_focused_element = field.fname;
|
||||||
|
@ -3308,7 +3311,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
||||||
size.X / 2 - 70, pos.Y,
|
size.X / 2 - 70, pos.Y,
|
||||||
size.X / 2 - 70 + 140, pos.Y + m_btn_height * 2
|
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());
|
wstrgettext("Proceed").c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4031,12 +4034,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
|
||||||
if (m_joystick->wasKeyDown(KeyType::ESC)) {
|
if (m_joystick->wasKeyDown(KeyType::ESC)) {
|
||||||
tryClose();
|
tryClose();
|
||||||
} else if (m_joystick->wasKeyDown(KeyType::JUMP)) {
|
} else if (m_joystick->wasKeyDown(KeyType::JUMP)) {
|
||||||
if (m_allowclose) {
|
trySubmitClose();
|
||||||
acceptInput(quit_mode_accept);
|
|
||||||
quitMenu();
|
|
||||||
} else {
|
|
||||||
acceptInput(quit_mode_try);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return handled;
|
return handled;
|
||||||
|
@ -4053,7 +4051,16 @@ void GUIFormSpecMenu::tryClose()
|
||||||
quitMenu();
|
quitMenu();
|
||||||
} else {
|
} else {
|
||||||
acceptInput(quit_mode_try);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
if (current_keys_pending.key_enter) {
|
if (current_keys_pending.key_enter) {
|
||||||
if (m_allowclose) {
|
trySubmitClose();
|
||||||
acceptInput(quit_mode_accept);
|
|
||||||
quitMenu();
|
|
||||||
} else {
|
|
||||||
acceptInput(quit_mode_try);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
acceptInput();
|
acceptInput();
|
||||||
}
|
}
|
||||||
|
@ -4819,12 +4821,18 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.EventType == EET_GUI_EVENT) {
|
if (event.EventType == EET_GUI_EVENT) {
|
||||||
if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED
|
const s32 caller_id = event.GUIEvent.Caller->getID();
|
||||||
&& isVisible()) {
|
bool close_on_enter;
|
||||||
// find the element that was clicked
|
|
||||||
|
switch (event.GUIEvent.EventType) {
|
||||||
|
case gui::EGET_TAB_CHANGED:
|
||||||
|
if (!isVisible())
|
||||||
|
break;
|
||||||
|
|
||||||
|
// find the element that was clicked
|
||||||
for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
|
for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
|
||||||
if ((s.ftype == f_TabHeader) &&
|
if (s.ftype == f_TabHeader &&
|
||||||
(s.fid == event.GUIEvent.Caller->getID())) {
|
s.fid == caller_id) {
|
||||||
if (!s.sound.empty() && m_sound_manager)
|
if (!s.sound.empty() && m_sound_manager)
|
||||||
m_sound_manager->playSound(0, SoundSpec(s.sound, 1.0f));
|
m_sound_manager->playSound(0, SoundSpec(s.sound, 1.0f));
|
||||||
s.send = true;
|
s.send = true;
|
||||||
|
@ -4833,26 +4841,26 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
|
|
||||||
&& isVisible()) {
|
case gui::EGET_ELEMENT_FOCUS_LOST:
|
||||||
|
if (!isVisible())
|
||||||
|
break;
|
||||||
|
|
||||||
if (!canTakeFocus(event.GUIEvent.Element)) {
|
if (!canTakeFocus(event.GUIEvent.Element)) {
|
||||||
infostream<<"GUIFormSpecMenu: Not allowing focus change."
|
infostream<<"GUIFormSpecMenu: Not allowing focus change."
|
||||||
<<std::endl;
|
<<std::endl;
|
||||||
// Returning true disables focus change
|
// Returning true disables focus change
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
if ((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
|
|
||||||
(event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) ||
|
|
||||||
(event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED) ||
|
|
||||||
(event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED)) {
|
|
||||||
s32 caller_id = event.GUIEvent.Caller->getID();
|
|
||||||
|
|
||||||
if (caller_id == 257) {
|
case gui::EGET_BUTTON_CLICKED:
|
||||||
acceptInput(quit_mode_accept);
|
case gui::EGET_CHECKBOX_CHANGED:
|
||||||
m_text_dst->gotText(L"ExitButton");
|
case gui::EGET_COMBO_BOX_CHANGED:
|
||||||
quitMenu();
|
case gui::EGET_SCROLL_BAR_CHANGED:
|
||||||
|
if (caller_id == ID_PROCEED_BTN) {
|
||||||
|
trySubmitClose();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4882,7 +4890,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||||
|
|
||||||
if (s.is_exit) {
|
if (s.is_exit) {
|
||||||
acceptInput(quit_mode_accept);
|
acceptInput(quit_mode_accept);
|
||||||
m_text_dst->gotText(L"ExitButton");
|
|
||||||
quitMenu();
|
quitMenu();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -4923,60 +4930,57 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
||||||
s.send = false;
|
s.send = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) {
|
if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) {
|
||||||
// move scroll_containers
|
// move scroll_containers
|
||||||
for (const std::pair<std::string, GUIScrollContainer *> &c : m_scroll_containers)
|
for (const std::pair<std::string, GUIScrollContainer *> &c : m_scroll_containers)
|
||||||
c.second->onScrollEvent(event.GUIEvent.Caller);
|
c.second->onScrollEvent(event.GUIEvent.Caller);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
|
case gui::EGET_EDITBOX_ENTER:
|
||||||
if (event.GUIEvent.Caller->getID() > 257) {
|
if (caller_id <= ID_PROCEED_BTN)
|
||||||
bool close_on_enter = true;
|
break;
|
||||||
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;
|
|
||||||
|
|
||||||
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 (close_on_enter)
|
||||||
if (m_allowclose) {
|
trySubmitClose();
|
||||||
acceptInput(quit_mode_accept);
|
else
|
||||||
quitMenu();
|
acceptInput();
|
||||||
} else {
|
return true;
|
||||||
acceptInput(quit_mode_try);
|
|
||||||
}
|
case gui::EGET_TABLE_CHANGED:
|
||||||
} else {
|
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();
|
acceptInput();
|
||||||
|
s.send = false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
return true;
|
||||||
|
|
||||||
if (event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) {
|
default:
|
||||||
int current_id = event.GUIEvent.Caller->getID();
|
break;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,8 +68,6 @@ struct TextDest
|
||||||
{
|
{
|
||||||
virtual ~TextDest() = default;
|
virtual ~TextDest() = default;
|
||||||
|
|
||||||
// This is deprecated I guess? -celeron55
|
|
||||||
virtual void gotText(const std::wstring &text) {}
|
|
||||||
virtual void gotText(const StringMap &fields) = 0;
|
virtual void gotText(const StringMap &fields) = 0;
|
||||||
|
|
||||||
std::string m_formname;
|
std::string m_formname;
|
||||||
|
@ -493,6 +491,7 @@ private:
|
||||||
bool parseMiddleRect(const std::string &value, core::rect<s32> *parsed_rect);
|
bool parseMiddleRect(const std::string &value, core::rect<s32> *parsed_rect);
|
||||||
|
|
||||||
void tryClose();
|
void tryClose();
|
||||||
|
void trySubmitClose();
|
||||||
|
|
||||||
void showTooltip(const std::wstring &text, const irr::video::SColor &color,
|
void showTooltip(const std::wstring &text, const irr::video::SColor &color,
|
||||||
const irr::video::SColor &bgcolor);
|
const irr::video::SColor &bgcolor);
|
||||||
|
|
|
@ -85,7 +85,7 @@ public:
|
||||||
void setTextList(const std::vector<std::string> &content,
|
void setTextList(const std::vector<std::string> &content,
|
||||||
bool transparent);
|
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
|
// Adds empty strings to end of content if there is an incomplete row
|
||||||
void setTable(const TableOptions &options,
|
void setTable(const TableOptions &options,
|
||||||
const TableColumns &columns,
|
const TableColumns &columns,
|
||||||
|
|
|
@ -59,6 +59,8 @@ public:
|
||||||
if(!m_stack.empty()) {
|
if(!m_stack.empty()) {
|
||||||
m_stack.back()->setVisible(true);
|
m_stack.back()->setVisible(true);
|
||||||
guienv->setFocus(m_stack.back());
|
guienv->setFocus(m_stack.back());
|
||||||
|
} else {
|
||||||
|
guienv->removeFocus(menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1138,7 +1138,7 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ChatInterface iface;
|
ChatInterface iface;
|
||||||
bool &kill = *porting::signal_handler_killstatus();
|
volatile auto &kill = *porting::signal_handler_killstatus();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create server
|
// Create server
|
||||||
|
@ -1181,7 +1181,7 @@ static bool run_dedicated_server(const GameParams &game_params, const Settings &
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
// Run server
|
// Run server
|
||||||
bool &kill = *porting::signal_handler_killstatus();
|
volatile auto &kill = *porting::signal_handler_killstatus();
|
||||||
dedicated_server_loop(server, kill);
|
dedicated_server_loop(server, kill);
|
||||||
|
|
||||||
} catch (const ModError &e) {
|
} catch (const ModError &e) {
|
||||||
|
@ -1226,7 +1226,7 @@ static bool migrate_map_database(const GameParams &game_params, const Settings &
|
||||||
|
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
u64 last_update_time = 0;
|
u64 last_update_time = 0;
|
||||||
bool &kill = *porting::signal_handler_killstatus();
|
volatile auto &kill = *porting::signal_handler_killstatus();
|
||||||
|
|
||||||
std::vector<v3s16> blocks;
|
std::vector<v3s16> blocks;
|
||||||
old_db->listAllLoadableBlocks(blocks);
|
old_db->listAllLoadableBlocks(blocks);
|
||||||
|
@ -1280,7 +1280,7 @@ static bool recompress_map_database(const GameParams &game_params, const Setting
|
||||||
|
|
||||||
u32 count = 0;
|
u32 count = 0;
|
||||||
u64 last_update_time = 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 u8 serialize_as_ver = SER_FMT_VER_HIGHEST_WRITE;
|
||||||
const s16 map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
|
const s16 map_compression_level = rangelim(g_settings->getS16("map_compression_level_disk"), -1, 9);
|
||||||
|
|
||||||
|
|
108
src/map.cpp
108
src/map.cpp
|
@ -772,52 +772,79 @@ void MMVManip::initialEmerge(v3s16 p_min, v3s16 p_max, bool load_if_inexistent)
|
||||||
infostream<<std::endl;
|
infostream<<std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool all_new = m_area.hasEmptyExtent() || block_area_nodes.contains(m_area);
|
std::map<v3s16, bool> 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);
|
addArea(block_area_nodes);
|
||||||
|
|
||||||
for(s32 z=p_min.Z; z<=p_max.Z; z++)
|
for(s32 z=p_min.Z; z<=p_max.Z; z++)
|
||||||
for(s32 y=p_min.Y; y<=p_max.Y; y++)
|
for(s32 y=p_min.Y; y<=p_max.Y; y++)
|
||||||
for(s32 x=p_min.X; x<=p_max.X; x++)
|
for(s32 x=p_min.X; x<=p_max.X; x++)
|
||||||
{
|
{
|
||||||
u8 flags = 0;
|
|
||||||
MapBlock *block;
|
|
||||||
v3s16 p(x,y,z);
|
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;
|
continue;
|
||||||
|
|
||||||
bool block_data_inexistent = false;
|
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
||||||
{
|
if (block) {
|
||||||
TimeTaker timer2("emerge load", &emerge_load_time);
|
block->copyTo(*this);
|
||||||
|
} else {
|
||||||
block = m_map->getBlockNoCreateNoEx(p);
|
|
||||||
if (!block)
|
|
||||||
block_data_inexistent = true;
|
|
||||||
else
|
|
||||||
block->copyTo(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(block_data_inexistent)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (load_if_inexistent && !blockpos_over_max_limit(p)) {
|
if (load_if_inexistent && !blockpos_over_max_limit(p)) {
|
||||||
block = m_map->emergeBlock(p, true);
|
block = m_map->emergeBlock(p, true);
|
||||||
|
assert(block);
|
||||||
block->copyTo(*this);
|
block->copyTo(*this);
|
||||||
} else {
|
} else {
|
||||||
flags |= VMANIP_BLOCK_DATA_INEXIST;
|
|
||||||
|
|
||||||
// Mark area inexistent
|
// Mark area inexistent
|
||||||
VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
|
VoxelArea a(p*MAP_BLOCKSIZE, (p+1)*MAP_BLOCKSIZE-v3s16(1,1,1));
|
||||||
setFlags(a, VOXELFLAG_NO_DATA);
|
setFlags(a, VOXELFLAG_NO_DATA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_loaded_blocks[p] = flags;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (all_new)
|
if (all_new)
|
||||||
m_is_dirty = false;
|
m_is_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::map<v3s16, bool> MMVManip::getCoveredBlocks() const
|
||||||
|
{
|
||||||
|
std::map<v3s16, bool> 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<v3s16, MapBlock*> *modified_blocks,
|
void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
|
||||||
bool overwrite_generated) const
|
bool overwrite_generated) const
|
||||||
{
|
{
|
||||||
|
@ -825,16 +852,27 @@ void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
|
||||||
return;
|
return;
|
||||||
assert(m_map);
|
assert(m_map);
|
||||||
|
|
||||||
/*
|
size_t nload = 0;
|
||||||
Copy data of all blocks
|
|
||||||
*/
|
// Copy all the blocks with data back to the map
|
||||||
assert(!m_loaded_blocks.empty());
|
const auto loaded_blocks = getCoveredBlocks();
|
||||||
for (auto &loaded_block : m_loaded_blocks) {
|
for (auto &it : loaded_blocks) {
|
||||||
v3s16 p = loaded_block.first;
|
if (!it.second)
|
||||||
|
continue;
|
||||||
|
v3s16 p = it.first;
|
||||||
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
MapBlock *block = m_map->getBlockNoCreateNoEx(p);
|
||||||
bool existed = !(loaded_block.second & VMANIP_BLOCK_DATA_INEXIST);
|
if (!block) {
|
||||||
if (!existed || (block == NULL) ||
|
if (!blockpos_over_max_limit(p)) {
|
||||||
(!overwrite_generated && block->isGenerated()))
|
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;
|
continue;
|
||||||
|
|
||||||
block->copyFrom(*this);
|
block->copyFrom(*this);
|
||||||
|
@ -844,6 +882,10 @@ void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
|
||||||
if(modified_blocks)
|
if(modified_blocks)
|
||||||
(*modified_blocks)[p] = block;
|
(*modified_blocks)[p] = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nload > 0) {
|
||||||
|
verbosestream << "blitBackAll: " << nload << " blocks had to be loaded for writing" << std::endl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MMVManip *MMVManip::clone() const
|
MMVManip *MMVManip::clone() const
|
||||||
|
@ -860,11 +902,7 @@ MMVManip *MMVManip::clone() const
|
||||||
ret->m_flags = new u8[size];
|
ret->m_flags = new u8[size];
|
||||||
memcpy(ret->m_flags, m_flags, size * sizeof(u8));
|
memcpy(ret->m_flags, m_flags, size * sizeof(u8));
|
||||||
}
|
}
|
||||||
|
|
||||||
ret->m_is_dirty = m_is_dirty;
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
36
src/map.h
36
src/map.h
|
@ -307,16 +307,29 @@ public:
|
||||||
MMVManip(Map *map);
|
MMVManip(Map *map);
|
||||||
virtual ~MMVManip() = default;
|
virtual ~MMVManip() = default;
|
||||||
|
|
||||||
virtual void clear()
|
/*
|
||||||
{
|
Loads specified area from map and *adds* it to the area already
|
||||||
VoxelManipulator::clear();
|
contained in the VManip.
|
||||||
m_loaded_blocks.clear();
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
|
void initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max,
|
||||||
bool load_if_inexistent = true);
|
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<v3s16, bool> 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<v3s16, MapBlock*> * modified_blocks,
|
void blitBackAll(std::map<v3s16, MapBlock*> * modified_blocks,
|
||||||
bool overwrite_generated = true) const;
|
bool overwrite_generated = true) const;
|
||||||
|
|
||||||
|
@ -339,13 +352,4 @@ protected:
|
||||||
|
|
||||||
// may be null
|
// may be null
|
||||||
Map *m_map = nullptr;
|
Map *m_map = nullptr;
|
||||||
/*
|
|
||||||
key = blockpos
|
|
||||||
value = flags describing the block
|
|
||||||
*/
|
|
||||||
std::map<v3s16, u8> m_loaded_blocks;
|
|
||||||
|
|
||||||
enum : u8 {
|
|
||||||
VMANIP_BLOCK_DATA_INEXIST = 1 << 0,
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1314,11 +1314,6 @@ Connection::~Connection()
|
||||||
m_sendThread->stop();
|
m_sendThread->stop();
|
||||||
m_receiveThread->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
|
// wait for threads to finish
|
||||||
m_sendThread->wait();
|
m_sendThread->wait();
|
||||||
m_receiveThread->wait();
|
m_receiveThread->wait();
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "util/numeric.h"
|
#include "util/numeric.h"
|
||||||
#include "porting.h"
|
#include "porting.h"
|
||||||
#include "network/networkprotocol.h"
|
#include "network/networkprotocol.h"
|
||||||
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -301,7 +302,7 @@ private:
|
||||||
// Backwards compatibility
|
// Backwards compatibility
|
||||||
PeerHandler *m_bc_peerhandler;
|
PeerHandler *m_bc_peerhandler;
|
||||||
|
|
||||||
bool m_shutting_down = false;
|
std::atomic<bool> m_shutting_down = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -40,10 +40,6 @@ void Server::handleCommand_Deprecated(NetworkPacket* pkt)
|
||||||
|
|
||||||
void Server::handleCommand_Init(NetworkPacket* pkt)
|
void Server::handleCommand_Init(NetworkPacket* pkt)
|
||||||
{
|
{
|
||||||
|
|
||||||
if(pkt->getSize() < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
session_t peer_id = pkt->getPeerId();
|
session_t peer_id = pkt->getPeerId();
|
||||||
RemoteClient *client = getClient(peer_id, CS_Created);
|
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 <<
|
verbosestream << "Server: Got TOSERVER_INIT from " << addr_s <<
|
||||||
" (peer_id=" << peer_id << ")" << std::endl;
|
" (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))
|
if (denyIfBanned(peer_id))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -161,18 +148,14 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
RemotePlayer *player = m_env->getPlayer(playername, true);
|
// Do not allow multiple players in simple singleplayer mode
|
||||||
|
if (isSingleplayer() && !m_clients.getClientIDs(CS_HelloSent).empty()) {
|
||||||
// If player is already connected, cancel
|
infostream << "Server: Not allowing another client (" << addr_s <<
|
||||||
if (player && player->getPeerId() != PEER_ID_INEXISTENT) {
|
") to connect in simple singleplayer mode" << std::endl;
|
||||||
actionstream << "Server: Player with name \"" << playername <<
|
DenyAccess(peer_id, SERVER_ACCESSDENIED_SINGLEPLAYER);
|
||||||
"\" tried to connect, but player with same name is already connected" << std::endl;
|
|
||||||
DenyAccess(peer_id, SERVER_ACCESSDENIED_ALREADY_CONNECTED);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Or the "singleplayer" name to be used on regular servers
|
||||||
m_clients.setPlayerName(peer_id, playername);
|
|
||||||
|
|
||||||
if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
|
if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
|
||||||
actionstream << "Server: Player with the name \"singleplayer\" tried "
|
actionstream << "Server: Player with the name \"singleplayer\" tried "
|
||||||
"to connect from " << addr_s << std::endl;
|
"to connect from " << addr_s << std::endl;
|
||||||
|
@ -180,12 +163,25 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
|
||||||
return;
|
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;
|
std::string reason;
|
||||||
if (m_script->on_prejoinplayer(playername, addr_s, &reason)) {
|
if (m_script->on_prejoinplayer(playername, addr_s, &reason)) {
|
||||||
actionstream << "Server: Player with the name \"" << playerName <<
|
actionstream << "Server: Player with the name \"" << playerName <<
|
||||||
"\" tried to connect from " << addr_s <<
|
"\" 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;
|
std::endl;
|
||||||
DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING, reason);
|
DenyAccess(peer_id, SERVER_ACCESSDENIED_CUSTOM_STRING, reason);
|
||||||
return;
|
return;
|
||||||
|
@ -195,14 +191,11 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
|
||||||
infostream << "Server: New connection: \"" << playerName << "\" from " <<
|
infostream << "Server: New connection: \"" << playerName << "\" from " <<
|
||||||
addr_s << " (peer_id=" << peer_id << ")" << std::endl;
|
addr_s << " (peer_id=" << peer_id << ")" << std::endl;
|
||||||
|
|
||||||
// Enforce user limit.
|
// Early check for user limit, so the client doesn't need to run
|
||||||
// Don't enforce for users that have some admin right or mod permits it.
|
// through the join process only to be denied.
|
||||||
if (m_clients.isUserLimitReached() &&
|
if (checkUserLimit(playerName, addr_s)) {
|
||||||
playername != g_settings->get("name") &&
|
|
||||||
!m_script->can_bypass_userlimit(playername, addr_s)) {
|
|
||||||
actionstream << "Server: " << playername << " tried to join from " <<
|
actionstream << "Server: " << playername << " tried to join from " <<
|
||||||
addr_s << ", but there are already max_users=" <<
|
addr_s << ", but the user limit was reached." << std::endl;
|
||||||
g_settings->getU16("max_users") << " players." << std::endl;
|
|
||||||
DenyAccess(peer_id, SERVER_ACCESSDENIED_TOO_MANY_USERS);
|
DenyAccess(peer_id, SERVER_ACCESSDENIED_TOO_MANY_USERS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -302,6 +295,7 @@ void Server::handleCommand_Init2(NetworkPacket* pkt)
|
||||||
sendMediaAnnouncement(peer_id, lang);
|
sendMediaAnnouncement(peer_id, lang);
|
||||||
|
|
||||||
RemoteClient *client = getClient(peer_id, CS_InitDone);
|
RemoteClient *client = getClient(peer_id, CS_InitDone);
|
||||||
|
assert(client);
|
||||||
|
|
||||||
// Keep client language for server translations
|
// Keep client language for server translations
|
||||||
client->setLangCode(lang);
|
client->setLangCode(lang);
|
||||||
|
@ -354,6 +348,8 @@ void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
|
||||||
void Server::handleCommand_ClientReady(NetworkPacket* pkt)
|
void Server::handleCommand_ClientReady(NetworkPacket* pkt)
|
||||||
{
|
{
|
||||||
session_t peer_id = pkt->getPeerId();
|
session_t peer_id = pkt->getPeerId();
|
||||||
|
RemoteClient *client = getClient(peer_id, CS_Created);
|
||||||
|
assert(client);
|
||||||
|
|
||||||
// decode all information first
|
// decode all information first
|
||||||
u8 major_ver, minor_ver, patch_ver, reserved;
|
u8 major_ver, minor_ver, patch_ver, reserved;
|
||||||
|
@ -364,8 +360,17 @@ void Server::handleCommand_ClientReady(NetworkPacket* pkt)
|
||||||
if (pkt->getRemainingBytes() >= 2)
|
if (pkt->getRemainingBytes() >= 2)
|
||||||
*pkt >> formspec_ver;
|
*pkt >> formspec_ver;
|
||||||
|
|
||||||
m_clients.setClientVersion(peer_id, major_ver, minor_ver, patch_ver,
|
client->setVersionInfo(major_ver, minor_ver, patch_ver, full_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
|
// Emerge player
|
||||||
PlayerSAO* playersao = StageTwoClientInit(peer_id);
|
PlayerSAO* playersao = StageTwoClientInit(peer_id);
|
||||||
|
@ -1426,7 +1431,7 @@ void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
|
||||||
|
|
||||||
std::string salt, verification_key;
|
std::string salt, verification_key;
|
||||||
|
|
||||||
std::string addr_s = getPeerAddress(peer_id).serializeString();
|
std::string addr_s = client->getAddress().serializeString();
|
||||||
u8 is_empty;
|
u8 is_empty;
|
||||||
|
|
||||||
*pkt >> salt >> verification_key >> 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);
|
RemoteClient *client = getClient(peer_id, CS_Invalid);
|
||||||
ClientState cstate = client->getState();
|
ClientState cstate = client->getState();
|
||||||
|
|
||||||
|
std::string addr_s = client->getAddress().serializeString();
|
||||||
|
|
||||||
if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
|
if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
|
||||||
actionstream << "Server: got SRP _A packet in wrong state " << cstate <<
|
actionstream << "Server: got SRP _A packet in wrong state " << cstate <<
|
||||||
" from " << getPeerAddress(peer_id).serializeString() <<
|
" from " << addr_s <<
|
||||||
". Ignoring." << std::endl;
|
". Ignoring." << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1524,7 +1531,7 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt)
|
||||||
if (client->chosen_mech != AUTH_MECHANISM_NONE) {
|
if (client->chosen_mech != AUTH_MECHANISM_NONE) {
|
||||||
actionstream << "Server: got SRP _A packet, while auth is already "
|
actionstream << "Server: got SRP _A packet, while auth is already "
|
||||||
"going on with mech " << client->chosen_mech << " from " <<
|
"going on with mech " << client->chosen_mech << " from " <<
|
||||||
getPeerAddress(peer_id).serializeString() <<
|
addr_s <<
|
||||||
" (wantSudo=" << wantSudo << "). Ignoring." << std::endl;
|
" (wantSudo=" << wantSudo << "). Ignoring." << std::endl;
|
||||||
if (wantSudo) {
|
if (wantSudo) {
|
||||||
DenySudoAccess(peer_id);
|
DenySudoAccess(peer_id);
|
||||||
|
@ -1541,7 +1548,7 @@ void Server::handleCommand_SrpBytesA(NetworkPacket* pkt)
|
||||||
|
|
||||||
infostream << "Server: TOSERVER_SRP_BYTES_A received with "
|
infostream << "Server: TOSERVER_SRP_BYTES_A received with "
|
||||||
<< "based_on=" << int(based_on) << " and len_A="
|
<< "based_on=" << int(based_on) << " and len_A="
|
||||||
<< bytes_A.length() << "." << std::endl;
|
<< bytes_A.length() << std::endl;
|
||||||
|
|
||||||
AuthMechanism chosen = (based_on == 0) ?
|
AuthMechanism chosen = (based_on == 0) ?
|
||||||
AUTH_MECHANISM_LEGACY_PASSWORD : AUTH_MECHANISM_SRP;
|
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.
|
// Right now, the auth mechs don't change between login and sudo mode.
|
||||||
if (!client->isMechAllowed(chosen)) {
|
if (!client->isMechAllowed(chosen)) {
|
||||||
actionstream << "Server: Player \"" << client->getName() <<
|
actionstream << "Server: Player \"" << client->getName() <<
|
||||||
"\" at " << getPeerAddress(peer_id).serializeString() <<
|
"\" from " << addr_s <<
|
||||||
" tried to change password using unallowed mech " << chosen <<
|
" tried to change password using unallowed mech " << chosen <<
|
||||||
"." << std::endl;
|
std::endl;
|
||||||
DenySudoAccess(peer_id);
|
DenySudoAccess(peer_id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!client->isMechAllowed(chosen)) {
|
if (!client->isMechAllowed(chosen)) {
|
||||||
actionstream << "Server: Client tried to authenticate from " <<
|
actionstream << "Server: Client tried to authenticate from " <<
|
||||||
getPeerAddress(peer_id).serializeString() <<
|
addr_s <<
|
||||||
" using unallowed mech " << chosen << "." << std::endl;
|
" using unallowed mech " << chosen << std::endl;
|
||||||
DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
|
DenyAccess(peer_id, SERVER_ACCESSDENIED_UNEXPECTED_DATA);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include "client/texturesource.h"
|
#include "client/texturesource.h"
|
||||||
#include "client/tile.h"
|
#include "client/tile.h"
|
||||||
#include <IMeshManipulator.h>
|
#include <IMeshManipulator.h>
|
||||||
|
#include <SMesh.h>
|
||||||
#include <SkinnedMesh.h>
|
#include <SkinnedMesh.h>
|
||||||
#endif
|
#endif
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
@ -959,23 +960,37 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
||||||
palette = tsrc->getPalette(palette_name);
|
palette = tsrc->getPalette(palette_name);
|
||||||
|
|
||||||
if (drawtype == NDT_MESH && !mesh.empty()) {
|
if (drawtype == NDT_MESH && !mesh.empty()) {
|
||||||
// Read the mesh and apply scale
|
// Note: By freshly reading, we get an unencumbered mesh.
|
||||||
mesh_ptr = client->getMesh(mesh);
|
if (scene::IMesh *src_mesh = client->getMesh(mesh)) {
|
||||||
if (mesh_ptr) {
|
bool apply_bs = false;
|
||||||
v3f scale = v3f(BS) * visual_scale;
|
if (auto *skinned_mesh = dynamic_cast<scene::SkinnedMesh *>(src_mesh)) {
|
||||||
scaleMesh(mesh_ptr, scale);
|
// 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<scene::SMesh *>(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);
|
recalculateBoundingBox(mesh_ptr);
|
||||||
if (!checkMeshNormals(mesh_ptr)) {
|
if (!checkMeshNormals(mesh_ptr)) {
|
||||||
|
// TODO this should be done consistently when the mesh is loaded
|
||||||
infostream << "ContentFeatures: recalculating normals for mesh "
|
infostream << "ContentFeatures: recalculating normals for mesh "
|
||||||
<< mesh << std::endl;
|
<< mesh << std::endl;
|
||||||
meshmanip->recalculateNormals(mesh_ptr, true, false);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -342,7 +342,7 @@ struct ContentFeatures
|
||||||
enum NodeDrawType drawtype;
|
enum NodeDrawType drawtype;
|
||||||
std::string mesh;
|
std::string mesh;
|
||||||
#if CHECK_CLIENT_BUILD()
|
#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;
|
video::SColor minimap_color;
|
||||||
#endif
|
#endif
|
||||||
float visual_scale; // Misc. scale parameter
|
float visual_scale; // Misc. scale parameter
|
||||||
|
|
|
@ -60,6 +60,7 @@
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
#include "util/tracy_wrapper.h"
|
#include "util/tracy_wrapper.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <csignal>
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
@ -81,31 +82,28 @@ namespace porting
|
||||||
Signal handler (grabs Ctrl-C on POSIX systems)
|
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;
|
return &g_killed;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(_WIN32) // POSIX
|
#if !defined(_WIN32) // POSIX
|
||||||
|
#define STDERR_FILENO 2
|
||||||
|
|
||||||
static void signal_handler(int sig)
|
static void signal_handler(int sig)
|
||||||
{
|
{
|
||||||
if (!g_killed) {
|
if (!g_killed) {
|
||||||
if (sig == SIGINT) {
|
if (sig == SIGINT) {
|
||||||
dstream << "INFO: signal_handler(): "
|
const char *dbg_text{"INFO: signal_handler(): "
|
||||||
<< "Ctrl-C pressed, shutting down." << std::endl;
|
"Ctrl-C pressed, shutting down.\n"};
|
||||||
|
write(STDERR_FILENO, dbg_text, strlen(dbg_text));
|
||||||
} else if (sig == SIGTERM) {
|
} else if (sig == SIGTERM) {
|
||||||
dstream << "INFO: signal_handler(): "
|
const char *dbg_text{"INFO: signal_handler(): "
|
||||||
<< "got SIGTERM, shutting down." << std::endl;
|
"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;
|
g_killed = true;
|
||||||
} else {
|
} else {
|
||||||
(void)signal(sig, SIG_DFL);
|
(void)signal(sig, SIG_DFL);
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Be mindful of what you include here!
|
// Be mindful of what you include here!
|
||||||
|
#include <csignal>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "irrlichttypes.h" // u64
|
#include "irrlichttypes.h" // u64
|
||||||
|
@ -77,7 +78,7 @@ namespace porting
|
||||||
void signal_handler_init();
|
void signal_handler_init();
|
||||||
// Returns a pointer to a bool.
|
// Returns a pointer to a bool.
|
||||||
// When the bool is true, program should quit.
|
// 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.
|
Path of static data directory.
|
||||||
|
|
|
@ -56,9 +56,16 @@ void read_item_definition(lua_State* L, int index,
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
index = lua_gettop(L) + 1 + index;
|
index = lua_gettop(L) + 1 + index;
|
||||||
|
|
||||||
def.type = (ItemType)getenumfield(L, index, "type",
|
def.name.clear();
|
||||||
es_ItemType, ITEM_NONE);
|
|
||||||
getstringfield(L, index, "name", def.name);
|
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, "description", def.description);
|
||||||
getstringfield(L, index, "short_description", def.short_description);
|
getstringfield(L, index, "short_description", def.short_description);
|
||||||
getstringfield(L, index, "inventory_image", def.inventory_image);
|
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_PLANTLIKE:
|
||||||
case NDT_FIRELIKE:
|
case NDT_FIRELIKE:
|
||||||
default_tiling = false;
|
default_tiling = false;
|
||||||
// "break" is omitted here intentionaly, as PLANTLIKE
|
|
||||||
// FIRELIKE drawtype both should default to having
|
|
||||||
// backface_culling to false.
|
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case NDT_MESH:
|
case NDT_MESH:
|
||||||
case NDT_LIQUID:
|
case NDT_LIQUID:
|
||||||
|
@ -621,7 +625,6 @@ TileDef read_tiledef(lua_State *L, int index, u8 drawtype, bool special)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// key at index -2 and value at index
|
|
||||||
if(lua_isstring(L, index)){
|
if(lua_isstring(L, index)){
|
||||||
// "default_lava.png"
|
// "default_lava.png"
|
||||||
tiledef.name = lua_tostring(L, index);
|
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"
|
// name="default_lava.png"
|
||||||
tiledef.name.clear();
|
tiledef.name.clear();
|
||||||
getstringfield(L, index, "name", tiledef.name);
|
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(
|
tiledef.backface_culling = getboolfield_default(
|
||||||
L, index, "backface_culling", default_culling);
|
L, index, "backface_culling", default_culling);
|
||||||
tiledef.tileable_horizontal = getboolfield_default(
|
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");
|
lua_getfield(L, index, "animation");
|
||||||
tiledef.animation = read_animation_definition(L, -1);
|
tiledef.animation = read_animation_definition(L, -1);
|
||||||
lua_pop(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;
|
return tiledef;
|
||||||
|
@ -672,13 +681,13 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
|
||||||
|
|
||||||
/* Cache existence of some callbacks */
|
/* Cache existence of some callbacks */
|
||||||
lua_getfield(L, index, "on_construct");
|
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_pop(L, 1);
|
||||||
lua_getfield(L, index, "on_destruct");
|
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_pop(L, 1);
|
||||||
lua_getfield(L, index, "after_destruct");
|
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_pop(L, 1);
|
||||||
|
|
||||||
lua_getfield(L, index, "on_rightclick");
|
lua_getfield(L, index, "on_rightclick");
|
||||||
|
@ -695,8 +704,13 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
|
||||||
|
|
||||||
/* Visual definition */
|
/* 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);
|
getfloatfield(L, index, "visual_scale", f.visual_scale);
|
||||||
|
|
||||||
/* Meshnode model filename */
|
/* Meshnode model filename */
|
||||||
|
@ -796,10 +810,7 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
|
||||||
if (lua_toboolean(L, -1))
|
if (lua_toboolean(L, -1))
|
||||||
f.alpha = (f.drawtype == NDT_NORMAL) ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
|
f.alpha = (f.drawtype == NDT_NORMAL) ? ALPHAMODE_CLIP : ALPHAMODE_BLEND;
|
||||||
} else if (check_field_or_nil(L, -1, LUA_TSTRING, "use_texture_alpha")) {
|
} else if (check_field_or_nil(L, -1, LUA_TSTRING, "use_texture_alpha")) {
|
||||||
int result = f.alpha;
|
string_to_enum(ScriptApiNode::es_TextureAlphaMode, f.alpha, lua_tostring(L, -1));
|
||||||
string_to_enum(ScriptApiNode::es_TextureAlphaMode, result,
|
|
||||||
std::string(lua_tostring(L, -1)));
|
|
||||||
f.alpha = static_cast<enum AlphaMode>(result);
|
|
||||||
}
|
}
|
||||||
lua_pop(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);
|
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);
|
auto str = getstringfield_default(L, index, "paramtype", "");
|
||||||
f.param_type_2 = (ContentParamType2)getenumfield(L, index, "paramtype2",
|
if (!string_to_enum(ScriptApiNode::es_ContentParamType, f.param_type, str))
|
||||||
ScriptApiNode::es_ContentParamType2, CPT2_NONE);
|
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() &&
|
if (!f.palette_name.empty() &&
|
||||||
!(f.param_type_2 == CPT2_COLOR ||
|
!(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
|
// Liquids flow into and replace node
|
||||||
getboolfield(L, index, "floodable", f.floodable);
|
getboolfield(L, index, "floodable", f.floodable);
|
||||||
// Whether the node is non-liquid, source liquid or flowing liquid
|
// 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.
|
// If the content is liquid, this is the flowing version of the liquid.
|
||||||
getstringfield(L, index, "liquid_alternative_flowing",
|
getstringfield(L, index, "liquid_alternative_flowing",
|
||||||
f.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);
|
lua_pushnil(L);
|
||||||
while (lua_next(L, table) != 0) {
|
while (lua_next(L, table) != 0) {
|
||||||
// Value at -1
|
// 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
|
// Note faces are flipped to make checking easier
|
||||||
if (side == "top")
|
if (side == "top")
|
||||||
f.connect_sides |= 2;
|
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)) {
|
} else if(lua_isnil(L, -1)) {
|
||||||
f.liquid_move_physics = f.liquid_type != LIQUID_NONE;
|
f.liquid_move_physics = f.liquid_type != LIQUID_NONE;
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: should be an error
|
||||||
errorstream << "Field \"liquid_move_physics\": Invalid type!" << std::endl;
|
errorstream << "Field \"liquid_move_physics\": Invalid type!" << std::endl;
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
|
@ -1805,10 +1829,8 @@ WearBarParams read_wear_bar_params(
|
||||||
auto blend = WearBarParams::BLEND_MODE_CONSTANT;
|
auto blend = WearBarParams::BLEND_MODE_CONSTANT;
|
||||||
lua_getfield(L, stack_idx, "blend");
|
lua_getfield(L, stack_idx, "blend");
|
||||||
if (check_field_or_nil(L, -1, LUA_TSTRING, "blend")) {
|
if (check_field_or_nil(L, -1, LUA_TSTRING, "blend")) {
|
||||||
int blendInt;
|
if (!string_to_enum(WearBarParams::es_BlendMode, blend, lua_tostring(L, -1)))
|
||||||
if (!string_to_enum(WearBarParams::es_BlendMode, blendInt, std::string(lua_tostring(L, -1))))
|
|
||||||
throw LuaError("Invalid wear bar color blend mode");
|
throw LuaError("Invalid wear bar color blend mode");
|
||||||
blend = static_cast<WearBarParams::BlendMode>(blendInt);
|
|
||||||
}
|
}
|
||||||
lua_pop(L, 1);
|
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)
|
bool read_hud_change(lua_State *L, HudElementStat &stat, HudElement *elem, void **value)
|
||||||
{
|
{
|
||||||
std::string statstr = lua_tostring(L, 3);
|
std::string statstr = lua_tostring(L, 3);
|
||||||
{
|
if (!string_to_enum(es_HudElementStat, stat, statstr)) {
|
||||||
int statint;
|
script_log_unique(L, "Unknown HUD stat type: " + statstr, warningstream);
|
||||||
if (!string_to_enum(es_HudElementStat, statint, statstr)) {
|
return false;
|
||||||
script_log_unique(L, "Unknown HUD stat type: " + statstr, warningstream);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
stat = static_cast<HudElementStat>(statint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (stat) {
|
switch (stat) {
|
||||||
|
|
|
@ -18,6 +18,8 @@ extern "C" {
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include "common/c_types.h"
|
#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 { \
|
#define CHECK_TYPE(index, name, type) do { \
|
||||||
int t = lua_type(L, (index)); \
|
int t = lua_type(L, (index)); \
|
||||||
|
@ -28,6 +30,16 @@ extern "C" {
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} 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 {\
|
#define CHECK_FLOAT(value, name) do {\
|
||||||
if (std::isnan(value) || std::isinf(value)) { \
|
if (std::isnan(value) || std::isinf(value)) { \
|
||||||
throw LuaError("Invalid float value for '" name \
|
throw LuaError("Invalid float value for '" name \
|
||||||
|
@ -35,7 +47,13 @@ extern "C" {
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} 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)
|
#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)
|
#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)
|
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);
|
CHECK_POS_TAB(index);
|
||||||
lua_pushvalue(L, index);
|
lua_pushvalue(L, index);
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_READ_VECTOR);
|
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 read_v2s16(lua_State *L, int index)
|
||||||
{
|
{
|
||||||
v2s16 p;
|
return v2s16::from(read_v2f(L, index));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_v2s16(lua_State *L, v2s16 p)
|
void push_v2s16(lua_State *L, v2s16 p)
|
||||||
{
|
{
|
||||||
lua_createtable(L, 0, 2);
|
push_v2s32(L, v2s32::from(p));
|
||||||
lua_pushinteger(L, p.X);
|
|
||||||
lua_setfield(L, -2, "x");
|
|
||||||
lua_pushinteger(L, p.Y);
|
|
||||||
lua_setfield(L, -2, "y");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_v2s32(lua_State *L, v2s32 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 read_v2s32(lua_State *L, int index)
|
||||||
{
|
{
|
||||||
v2s32 p;
|
return v2s32::from(read_v2f(L, index));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v2f read_v2f(lua_State *L, int index)
|
v2f read_v2f(lua_State *L, int index)
|
||||||
|
@ -143,9 +142,11 @@ v2f read_v2f(lua_State *L, int index)
|
||||||
v2f p;
|
v2f p;
|
||||||
CHECK_POS_TAB(index);
|
CHECK_POS_TAB(index);
|
||||||
lua_getfield(L, index, "x");
|
lua_getfield(L, index, "x");
|
||||||
|
CHECK_POS_COORD2(-1, "x");
|
||||||
p.X = lua_tonumber(L, -1);
|
p.X = lua_tonumber(L, -1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
lua_getfield(L, index, "y");
|
lua_getfield(L, index, "y");
|
||||||
|
CHECK_POS_COORD2(-1, "y");
|
||||||
p.Y = lua_tonumber(L, -1);
|
p.Y = lua_tonumber(L, -1);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
return p;
|
return p;
|
||||||
|
@ -170,30 +171,20 @@ v2f check_v2f(lua_State *L, int index)
|
||||||
|
|
||||||
v3f read_v3f(lua_State *L, int index)
|
v3f read_v3f(lua_State *L, int index)
|
||||||
{
|
{
|
||||||
read_v3_aux(L, index);
|
return v3f::from(read_v3d(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v3f check_v3f(lua_State *L, int index)
|
v3f check_v3f(lua_State *L, int index)
|
||||||
{
|
{
|
||||||
read_v3_aux(L, index);
|
return v3f::from(check_v3d(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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
v3d read_v3d(lua_State *L, int index)
|
v3d read_v3d(lua_State *L, int index)
|
||||||
{
|
{
|
||||||
read_v3_aux(L, 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 x = lua_tonumber(L, -3);
|
||||||
double y = lua_tonumber(L, -2);
|
double y = lua_tonumber(L, -2);
|
||||||
double z = lua_tonumber(L, -1);
|
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 y = lua_tonumber(L, -2);
|
||||||
double z = lua_tonumber(L, -1);
|
double z = lua_tonumber(L, -1);
|
||||||
lua_pop(L, 3);
|
lua_pop(L, 3);
|
||||||
|
CHECK_FLOAT(x, "x");
|
||||||
|
CHECK_FLOAT(y, "y");
|
||||||
|
CHECK_FLOAT(z, "z");
|
||||||
return v3d(x, y, 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));
|
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);
|
video::SColor color(0);
|
||||||
CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
|
CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
|
||||||
lua_getfield(L, index, "a");
|
lua_getfield(L, index, "a");
|
||||||
color.setAlpha(lua_isnumber(L, -1) ? clamp_col(lua_tonumber(L, -1)) : 0xFF);
|
color.setAlpha(lua_isnumber(L, -1) ? clamp_col(lua_tonumber(L, -1)) : 0xFF);
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
lua_getfield(L, index, "r");
|
lua_getfield(L, index, "r");
|
||||||
|
CHECK_NOT_NIL(-1, "color component R");
|
||||||
color.setRed(clamp_col(lua_tonumber(L, -1)));
|
color.setRed(clamp_col(lua_tonumber(L, -1)));
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
lua_getfield(L, index, "g");
|
lua_getfield(L, index, "g");
|
||||||
|
CHECK_NOT_NIL(-1, "color component G");
|
||||||
color.setGreen(clamp_col(lua_tonumber(L, -1)));
|
color.setGreen(clamp_col(lua_tonumber(L, -1)));
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
lua_getfield(L, index, "b");
|
lua_getfield(L, index, "b");
|
||||||
|
CHECK_NOT_NIL(-1, "color component B");
|
||||||
color.setBlue(clamp_col(lua_tonumber(L, -1)));
|
color.setBlue(clamp_col(lua_tonumber(L, -1)));
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
return color;
|
return color;
|
||||||
|
|
|
@ -74,13 +74,23 @@ v2f check_v2f(lua_State *L, int index);
|
||||||
v3f check_v3f(lua_State *L, int index);
|
v3f check_v3f(lua_State *L, int index);
|
||||||
v3s16 check_v3s16(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);
|
v3f read_v3f(lua_State *L, int index);
|
||||||
|
/// @warning relaxed type-checking, prefer `check_v2f`.
|
||||||
v2f read_v2f(lua_State *L, int index);
|
v2f read_v2f(lua_State *L, int index);
|
||||||
|
/// @warning relaxed type-checking
|
||||||
v2s16 read_v2s16(lua_State *L, int index);
|
v2s16 read_v2s16(lua_State *L, int index);
|
||||||
|
/// @warning relaxed type-checking
|
||||||
v2s32 read_v2s32(lua_State *L, int index);
|
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);
|
video::SColor read_ARGB8(lua_State *L, int index);
|
||||||
bool read_color(lua_State *L, int index, video::SColor *color);
|
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.
|
* 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);
|
aabb3f read_aabb3f(lua_State *L, int index, f32 scale);
|
||||||
|
|
||||||
v3s16 read_v3s16(lua_State *L, int index);
|
|
||||||
std::vector<aabb3f> read_aabb3f_vector (lua_State *L, int index, f32 scale);
|
std::vector<aabb3f> read_aabb3f_vector (lua_State *L, int index, f32 scale);
|
||||||
size_t read_stringlist(lua_State *L, int index,
|
size_t read_stringlist(lua_State *L, int index,
|
||||||
std::vector<std::string> *result);
|
std::vector<std::string> *result);
|
||||||
|
|
|
@ -38,7 +38,6 @@ public:
|
||||||
static struct EnumString es_ContentParamType[];
|
static struct EnumString es_ContentParamType[];
|
||||||
static struct EnumString es_ContentParamType2[];
|
static struct EnumString es_ContentParamType2[];
|
||||||
static struct EnumString es_LiquidType[];
|
static struct EnumString es_LiquidType[];
|
||||||
static struct EnumString es_LiquidMoveType[];
|
|
||||||
static struct EnumString es_NodeBoxType[];
|
static struct EnumString es_NodeBoxType[];
|
||||||
static struct EnumString es_TextureAlphaMode[];
|
static struct EnumString es_TextureAlphaMode[];
|
||||||
};
|
};
|
||||||
|
|
|
@ -266,15 +266,22 @@ int ModApiEnv::l_bulk_swap_node(lua_State *L)
|
||||||
// get_node_raw(x, y, z) -> content, param1, param2, pos_ok
|
// get_node_raw(x, y, z) -> content, param1, param2, pos_ok
|
||||||
int ModApiEnv::l_get_node_raw(lua_State *L)
|
int ModApiEnv::l_get_node_raw(lua_State *L)
|
||||||
{
|
{
|
||||||
GET_ENV_PTR;
|
GET_PLAIN_ENV_PTR;
|
||||||
|
|
||||||
// pos
|
v3s16 pos;
|
||||||
// mirrors implementation of read_v3s16 (with the exact same rounding)
|
// mirrors the implementation of read_v3s16 (with the exact same rounding)
|
||||||
double x = lua_tonumber(L, 1);
|
{
|
||||||
double y = lua_tonumber(L, 2);
|
if (lua_isnoneornil(L, 1))
|
||||||
double z = lua_tonumber(L, 3);
|
log_deprecated(L, "X position is nil", 1, true);
|
||||||
v3s16 pos = doubleToInt(v3d(x, y, z), 1.0);
|
if (lua_isnoneornil(L, 2))
|
||||||
// Do it
|
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;
|
bool pos_ok;
|
||||||
MapNode n = env->getMap().getNode(pos, &pos_ok);
|
MapNode n = env->getMap().getNode(pos, &pos_ok);
|
||||||
// Return node and pos_ok
|
// Return node and pos_ok
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue