Compare commits

...

23 Commits

Author SHA1 Message Date
Lars Müller
bb360d6ec1
Merge 7bf61ed9c73681b90f5ea2e46ec8da99038ccefe into 9a1501ae89ffe79c38dbd6756c9e7ed647dd7dc1 2024-06-28 11:51:49 +02:00
grorp
9a1501ae89
CIrrDeviceSDL: Fix numpad key events not having correct KeyInput.Char (#14780)
Allows you to change viewing range using numpad +/- again. This fix also works with the current unreleased version of SDL 3.

The keycodes for numpad keys (both SDL keycodes and Irrlicht keycodes) are not the same as the keycodes for the equivalent non-numpad keys and don't correspond to chars, so I mapped them to chars manually.

Since I think the resolution of https://github.com/minetest/minetest/issues/13770 was "just disable numlock", I made sure to only do this for the numpad number keys if numlock is enabled.
2024-06-27 14:44:44 +02:00
Erich Schubert
514e106414
Fix missing newline before Markdown list (#14783)
Renders incorrectly e.g. on https://api.minetest.net/spatial-vectors/
2024-06-26 22:21:18 +02:00
grorp
4c001bd248
Make button sprites (scrollbar arrows) DPI-aware (#14772) 2024-06-26 15:25:27 +02:00
1F616EMO~nya
fb6ceb2664
Properly escape Markdown markups at minetest.string_to_area (#14774)
Co-authored-by: DS <ds.desour@proton.me>
2024-06-24 22:14:16 +02:00
aminothere
50da26da91
Document alternative 'persist' key in noise parameter table (#14762) 2024-06-24 22:12:42 +02:00
kromka-chleba
28857841aa
Fix math.round floating point bug (#14757) 2024-06-24 20:57:06 +01:00
rubenwardy
157d129e30
Fix unnecessary content refreshing (#14705) 2024-06-24 20:56:37 +01:00
Lars Müller
7bf61ed9c7
Explain type vs ID 2024-06-02 20:54:13 +02:00
Lars Mueller
1588eab2f7 More refactoring 2024-06-02 18:31:32 +02:00
Lars Mueller
34bd7dcde1 Remove unnecessary assignments 2024-06-02 18:16:56 +02:00
Lars Mueller
8b11c73994 Fix items glowing with shaders enabled 2024-06-02 17:41:49 +02:00
Lars Mueller
7f1105cd47 More refactoring 2024-06-02 17:16:04 +02:00
Lars Mueller
b74c36961b Remove now unused includes 2024-06-02 17:01:08 +02:00
Lars Mueller
b3d957127c Remove always false use_shaders param 2024-06-02 16:38:42 +02:00
Lars Mueller
c1a76bc7db Make functions static as appropriate 2024-06-02 16:31:38 +02:00
Lars Mueller
ed11b33430 Refactor 2024-06-01 21:18:01 +02:00
Lars Mueller
a509aad6b2 Add translucent test item 2024-06-01 17:49:03 +02:00
Lars Mueller
f06364e6a5 Fix flowing water def in devtest 2024-06-01 17:34:49 +02:00
Lars Mueller
8c11c12c69 Set alpha mode of air to clip 2024-06-01 17:29:34 +02:00
Lars Mueller
67810800ad Register opaque variant of marble 2024-05-25 17:23:52 +02:00
Lars Mueller
9375f66c48 Fix rendering of translucent wield meshes 2024-05-25 17:23:52 +02:00
Lars Mueller
a73b4f27cb Make item alpha rendering consistent with their definition 2024-05-25 17:23:33 +02:00
35 changed files with 617 additions and 486 deletions

@ -240,12 +240,15 @@ function math.factorial(x)
return v
end
function math.round(x)
if x >= 0 then
return math.floor(x + 0.5)
if x < 0 then
local int = math.ceil(x)
local frac = x - int
return int - ((frac <= -0.5) and 1 or 0)
end
return math.ceil(x - 0.5)
local int = math.floor(x)
local frac = x - int
return int + ((frac >= 0.5) and 1 or 0)
end
local formspec_escapes = {

@ -176,3 +176,17 @@ describe("formspec_escape", function()
assert.equal("\\[Hello\\\\\\[", core.formspec_escape("[Hello\\["))
end)
end)
describe("math", function()
it("round()", function()
assert.equal(0, math.round(0))
assert.equal(10, math.round(10.3))
assert.equal(11, math.round(10.5))
assert.equal(11, math.round(10.7))
assert.equal(-10, math.round(-10.3))
assert.equal(-11, math.round(-10.5))
assert.equal(-11, math.round(-10.7))
assert.equal(0, math.round(0.49999999999999994))
assert.equal(0, math.round(-0.49999999999999994))
end)
end)

@ -133,6 +133,8 @@ local function start_install(package, reason)
conf:set("release", package.release)
conf:write()
end
pkgmgr.reload_by_type(package.type)
end
end
@ -146,7 +148,6 @@ local function start_install(package, reason)
start_install(next.package, next.reason)
end
ui.update()
end
@ -427,8 +428,9 @@ end
function contentdb.update_paths()
pkgmgr.load_all()
local mod_hash = {}
pkgmgr.refresh_globals()
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
local cdb_id = pkgmgr.get_contentdb_id(mod)
if cdb_id then
@ -437,7 +439,6 @@ function contentdb.update_paths()
end
local game_hash = {}
pkgmgr.update_gamelist()
for _, game in pairs(pkgmgr.games) do
local cdb_id = pkgmgr.get_contentdb_id(game)
if cdb_id then
@ -446,7 +447,7 @@ function contentdb.update_paths()
end
local txp_hash = {}
for _, txp in pairs(pkgmgr.get_texture_packs()) do
for _, txp in pairs(pkgmgr.texture_packs) do
local cdb_id = pkgmgr.get_contentdb_id(txp)
if cdb_id then
txp_hash[contentdb.aliases[cdb_id] or cdb_id] = txp

@ -110,7 +110,7 @@ pkgmgr = {}
-- @param modpack Currently processing modpack or nil/"" if none (recursion)
function pkgmgr.get_mods(path, virtual_path, listing, modpack)
local mods = core.get_dir_list(path, true)
local added = {}
for _, name in ipairs(mods) do
if name:sub(1, 1) ~= "." then
local mod_path = path .. DIR_DELIM .. name
@ -120,6 +120,7 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
parent_dir = path,
}
listing[#listing + 1] = toadd
added[#added + 1] = toadd
-- Get config file
local mod_conf
@ -150,8 +151,6 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
toadd.virtual_path = mod_virtual_path
toadd.type = "mod"
pkgmgr.update_translations({ toadd })
-- Check modpack.txt
-- Note: modpack.conf is already checked above
local modpackfile = io.open(mod_path .. DIR_DELIM .. "modpack.txt")
@ -171,6 +170,8 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
end
end
pkgmgr.update_translations(added)
if not modpack then
-- Sort all when the recursion is done
table.sort(listing, function(a, b)
@ -180,12 +181,13 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
end
--------------------------------------------------------------------------------
function pkgmgr.get_texture_packs()
function pkgmgr.reload_texture_packs()
local txtpath = core.get_texturepath()
local txtpath_system = core.get_texturepath_share()
local retval = {}
load_texture_packs(txtpath, retval)
-- on portable versions these two paths coincide. It avoids loading the path twice
if txtpath ~= txtpath_system then
load_texture_packs(txtpath_system, retval)
@ -197,11 +199,13 @@ function pkgmgr.get_texture_packs()
return a.title:lower() < b.title:lower()
end)
return retval
pkgmgr.texture_packs = retval
end
--------------------------------------------------------------------------------
function pkgmgr.get_all()
pkgmgr.load_all()
local result = {}
for _, mod in pairs(pkgmgr.global_mods:get_list()) do
@ -210,7 +214,7 @@ function pkgmgr.get_all()
for _, game in pairs(pkgmgr.games) do
result[#result + 1] = game
end
for _, txp in pairs(pkgmgr.get_texture_packs()) do
for _, txp in pairs(pkgmgr.texture_packs) do
result[#result + 1] = txp
end
@ -288,7 +292,7 @@ end
function pkgmgr.render_packagelist(render_list, use_technical_names, with_icon)
if not render_list then
if not pkgmgr.global_mods then
pkgmgr.refresh_globals()
pkgmgr.reload_global_mods()
end
render_list = pkgmgr.global_mods
end
@ -549,6 +553,7 @@ function pkgmgr.get_worldconfig(worldpath)
end
--------------------------------------------------------------------------------
-- Caller is responsible for reloading content types (see reload_by_type)
function pkgmgr.install_dir(expected_type, path, basename, targetpath)
assert(type(expected_type) == "string")
assert(type(path) == "string")
@ -615,12 +620,6 @@ function pkgmgr.install_dir(expected_type, path, basename, targetpath)
fgettext_ne("Failed to install $1 to $2", basename, targetpath)
end
if basefolder.type == "game" then
pkgmgr.update_gamelist()
else
pkgmgr.refresh_globals()
end
return targetpath, nil
end
@ -742,7 +741,7 @@ function pkgmgr.comparemod(elem1,elem2)
end
--------------------------------------------------------------------------------
function pkgmgr.refresh_globals()
function pkgmgr.reload_global_mods()
local function is_equal(element,uid) --uid match
if element.name == uid then
return true
@ -774,7 +773,7 @@ function pkgmgr.get_game_mods(gamespec, retval)
end
--------------------------------------------------------------------------------
function pkgmgr.update_gamelist()
function pkgmgr.reload_games()
pkgmgr.games = core.get_games()
table.sort(pkgmgr.games, function(a, b)
return a.title:lower() < b.title:lower()
@ -782,6 +781,32 @@ function pkgmgr.update_gamelist()
pkgmgr.update_translations(pkgmgr.games)
end
--------------------------------------------------------------------------------
function pkgmgr.reload_by_type(type)
if type == "game" then
pkgmgr.reload_games()
elseif type == "txp" then
pkgmgr.reload_texture_packs()
elseif type == "mod" or type == "modpack" then
pkgmgr.reload_global_mods()
else
error("Unknown package type: " .. type)
end
end
--------------------------------------------------------------------------------
function pkgmgr.load_all()
if not pkgmgr.global_mods then
pkgmgr.reload_global_mods()
end
if not pkgmgr.games then
pkgmgr.reload_games()
end
if not pkgmgr.texture_packs then
pkgmgr.reload_texture_packs()
end
end
--------------------------------------------------------------------------------
function pkgmgr.update_translations(list)
for _, item in ipairs(list) do
@ -831,4 +856,4 @@ end
--------------------------------------------------------------------------------
-- read initial data
--------------------------------------------------------------------------------
pkgmgr.update_gamelist()
pkgmgr.reload_games()

@ -46,6 +46,9 @@ local function reset()
function core.get_texturepath()
return txp_dir
end
function core.get_texturepath_share()
return txp_dir
end
function core.get_modpath()
return mods_dir
end
@ -59,13 +62,6 @@ local function reset()
setfenv(loadfile("builtin/common/misc_helpers.lua"), env)()
setfenv(loadfile("builtin/mainmenu/content/pkgmgr.lua"), env)()
function env.pkgmgr.update_gamelist()
table.insert(calls, { "update_gamelist" })
end
function env.pkgmgr.refresh_globals()
table.insert(calls, { "refresh_globals" })
end
function env.assert_calls(list)
assert.are.same(list, calls)
end
@ -113,7 +109,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", mods_dir .. "/mymod" },
{ "copy_dir", "/tmp/123", mods_dir .. "/mymod", false },
{ "refresh_globals" },
})
end)
@ -129,7 +124,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", mods_dir .. "/mymod" },
{ "copy_dir", "/tmp/123", mods_dir .. "/mymod", false },
{ "refresh_globals" },
})
end)
@ -145,7 +139,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", games_dir .. "/mygame" },
{ "copy_dir", "/tmp/123", games_dir .. "/mygame", false },
{ "update_gamelist" },
})
end)
@ -161,7 +154,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", mods_dir .. "/123" },
{ "copy_dir", "/tmp/123", mods_dir .. "/123", false },
{ "refresh_globals" },
})
end)
@ -188,7 +180,6 @@ describe("install_dir", function()
env.assert_calls({
{ "delete_dir", "/tmp/alt-target" },
{ "copy_dir", "/tmp/123", "/tmp/alt-target", false },
{ "refresh_globals" },
})
end)
@ -238,6 +229,5 @@ describe("install_dir", function()
path, message = env.pkgmgr.install_dir("txp", "/tmp/123", "name", nil)
assert.is._not._nil(path)
assert.is._nil(message)
end)
end)

@ -72,9 +72,6 @@ end
local function has_packages_from_cdb()
pkgmgr.refresh_globals()
pkgmgr.update_gamelist()
for _, content in pairs(pkgmgr.get_all()) do
if pkgmgr.get_contentdb_id(content) then
return true
@ -127,9 +124,6 @@ function update_detector.get_all()
return {}
end
pkgmgr.refresh_globals()
pkgmgr.update_gamelist()
local ret = {}
local all_content = pkgmgr.get_all()
for _, content in ipairs(all_content) do

@ -37,11 +37,7 @@ local function delete_content_buttonhandler(this, fields)
gamedata.errormessage = fgettext_ne("pkgmgr: failed to delete \"$1\"", this.data.content.path)
end
if this.data.content.type == "game" then
pkgmgr.update_gamelist()
else
pkgmgr.refresh_globals()
end
pkgmgr.reload_by_type(this.data.content.type)
else
gamedata.errormessage = fgettext_ne("pkgmgr: invalid path \"$1\"", this.data.content.path)
end

@ -45,7 +45,7 @@ local function rename_modpack_buttonhandler(this, fields)
local oldpath = this.data.mod.path
local targetpath = this.data.mod.parent_dir .. DIR_DELIM .. fields["te_modpack_name"]
os.rename(oldpath, targetpath)
pkgmgr.refresh_globals()
pkgmgr.reload_global_mods()
pkgmgr.selected_mod = pkgmgr.global_mods:get_current_index(
pkgmgr.global_mods:raw_index_by_uid(fields["te_modpack_name"]))

@ -22,16 +22,27 @@ local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
"settings" .. DIR_DELIM .. "shadows_component.lua")
local full_settings = settingtypes.parse_config_file(false, true)
local loaded = false
local full_settings
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
local all_pages = {}
local page_by_id = {}
local filtered_pages = all_pages
local filtered_page_by_id = page_by_id
local function get_setting_info(name)
for _, entry in ipairs(full_settings) do
if entry.type ~= "category" and entry.name == name then
return entry
end
end
return nil
end
local function add_page(page)
assert(type(page.id) == "string")
assert(type(page.title) == "string")
@ -46,49 +57,6 @@ local function add_page(page)
end
local change_keys = {
query_text = "Controls",
requires = {
keyboard_mouse = true,
},
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 3)
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
end,
on_submit = function(self, fields)
if fields.btn_change_keys then
core.show_keys_menu()
end
end,
}
add_page({
id = "accessibility",
title = fgettext_ne("Accessibility"),
content = {
"language",
{ heading = fgettext_ne("General") },
"font_size",
"chat_font_size",
"gui_scaling",
"hud_scaling",
"show_nametag_backgrounds",
{ heading = fgettext_ne("Chat") },
"console_height",
"console_alpha",
"console_color",
{ heading = fgettext_ne("Controls") },
"autojump",
"safe_dig_and_place",
{ heading = fgettext_ne("Movement") },
"arm_inertia",
"view_bobbing_amount",
"fall_bobbing_amount",
},
})
local function load_settingtypes()
local page = nil
local section = nil
@ -129,94 +97,134 @@ local function load_settingtypes()
end
end
end
load_settingtypes()
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
do
local content = page_by_id.graphics_and_audio_shaders.content
local idx = table.indexof(content, "enable_dynamic_shadows")
table.insert(content, idx, shadows_component)
end
local function get_setting_info(name)
for _, entry in ipairs(full_settings) do
if entry.type ~= "category" and entry.name == name then
return entry
end
local function load()
if loaded then
return
end
loaded = true
full_settings = settingtypes.parse_config_file(false, true)
local change_keys = {
query_text = "Controls",
requires = {
keyboard_mouse = true,
},
get_formspec = function(self, avail_w)
local btn_w = math.min(avail_w, 3)
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
end,
on_submit = function(self, fields)
if fields.btn_change_keys then
core.show_keys_menu()
end
end,
}
add_page({
id = "accessibility",
title = fgettext_ne("Accessibility"),
content = {
"language",
{ heading = fgettext_ne("General") },
"font_size",
"chat_font_size",
"gui_scaling",
"hud_scaling",
"show_nametag_backgrounds",
{ heading = fgettext_ne("Chat") },
"console_height",
"console_alpha",
"console_color",
{ heading = fgettext_ne("Controls") },
"autojump",
"safe_dig_and_place",
{ heading = fgettext_ne("Movement") },
"arm_inertia",
"view_bobbing_amount",
"fall_bobbing_amount",
},
})
load_settingtypes()
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
do
local content = page_by_id.graphics_and_audio_shaders.content
local idx = table.indexof(content, "enable_dynamic_shadows")
table.insert(content, idx, shadows_component)
end
return nil
-- These must not be translated, as they need to show in the local
-- language no matter the user's current language.
-- This list must be kept in sync with src/unsupported_language_list.txt.
get_setting_info("language").option_labels = {
[""] = fgettext_ne("(Use system language)"),
--ar = " [ar]", blacklisted
be = "Беларуская [be]",
bg = "Български [bg]",
ca = "Català [ca]",
cs = "Česky [cs]",
cy = "Cymraeg [cy]",
da = "Dansk [da]",
de = "Deutsch [de]",
--dv = " [dv]", blacklisted
el = "Ελληνικά [el]",
en = "English [en]",
eo = "Esperanto [eo]",
es = "Español [es]",
et = "Eesti [et]",
eu = "Euskara [eu]",
fi = "Suomi [fi]",
fil = "Wikang Filipino [fil]",
fr = "Français [fr]",
gd = "Gàidhlig [gd]",
gl = "Galego [gl]",
--he = " [he]", blacklisted
--hi = " [hi]", blacklisted
hu = "Magyar [hu]",
id = "Bahasa Indonesia [id]",
it = "Italiano [it]",
ja = "日本語 [ja]",
jbo = "Lojban [jbo]",
kk = "Қазақша [kk]",
--kn = " [kn]", blacklisted
ko = "한국어 [ko]",
ky = "Kırgızca / Кыргызча [ky]",
lt = "Lietuvių [lt]",
lv = "Latviešu [lv]",
mn = "Монгол [mn]",
mr = "मराठी [mr]",
ms = "Bahasa Melayu [ms]",
--ms_Arab = " [ms_Arab]", blacklisted
nb = "Norsk Bokmål [nb]",
nl = "Nederlands [nl]",
nn = "Norsk Nynorsk [nn]",
oc = "Occitan [oc]",
pl = "Polski [pl]",
pt = "Português [pt]",
pt_BR = "Português do Brasil [pt_BR]",
ro = "Română [ro]",
ru = "Русский [ru]",
sk = "Slovenčina [sk]",
sl = "Slovenščina [sl]",
sr_Cyrl = "Српски [sr_Cyrl]",
sr_Latn = "Srpski (Latinica) [sr_Latn]",
sv = "Svenska [sv]",
sw = "Kiswahili [sw]",
--th = " [th]", blacklisted
tr = "Türkçe [tr]",
tt = "Tatarça [tt]",
uk = "Українська [uk]",
vi = "Tiếng Việt [vi]",
zh_CN = "中文 (简体) [zh_CN]",
zh_TW = "正體中文 (繁體) [zh_TW]",
}
end
-- These must not be translated, as they need to show in the local
-- language no matter the user's current language.
-- This list must be kept in sync with src/unsupported_language_list.txt.
get_setting_info("language").option_labels = {
[""] = fgettext_ne("(Use system language)"),
--ar = " [ar]", blacklisted
be = "Беларуская [be]",
bg = "Български [bg]",
ca = "Català [ca]",
cs = "Česky [cs]",
cy = "Cymraeg [cy]",
da = "Dansk [da]",
de = "Deutsch [de]",
--dv = " [dv]", blacklisted
el = "Ελληνικά [el]",
en = "English [en]",
eo = "Esperanto [eo]",
es = "Español [es]",
et = "Eesti [et]",
eu = "Euskara [eu]",
fi = "Suomi [fi]",
fil = "Wikang Filipino [fil]",
fr = "Français [fr]",
gd = "Gàidhlig [gd]",
gl = "Galego [gl]",
--he = " [he]", blacklisted
--hi = " [hi]", blacklisted
hu = "Magyar [hu]",
id = "Bahasa Indonesia [id]",
it = "Italiano [it]",
ja = "日本語 [ja]",
jbo = "Lojban [jbo]",
kk = "Қазақша [kk]",
--kn = " [kn]", blacklisted
ko = "한국어 [ko]",
ky = "Kırgızca / Кыргызча [ky]",
lt = "Lietuvių [lt]",
lv = "Latviešu [lv]",
mn = "Монгол [mn]",
mr = "मराठी [mr]",
ms = "Bahasa Melayu [ms]",
--ms_Arab = " [ms_Arab]", blacklisted
nb = "Norsk Bokmål [nb]",
nl = "Nederlands [nl]",
nn = "Norsk Nynorsk [nn]",
oc = "Occitan [oc]",
pl = "Polski [pl]",
pt = "Português [pt]",
pt_BR = "Português do Brasil [pt_BR]",
ro = "Română [ro]",
ru = "Русский [ru]",
sk = "Slovenčina [sk]",
sl = "Slovenščina [sl]",
sr_Cyrl = "Српски [sr_Cyrl]",
sr_Latn = "Srpski (Latinica) [sr_Latn]",
sv = "Svenska [sv]",
sw = "Kiswahili [sw]",
--th = " [th]", blacklisted
tr = "Türkçe [tr]",
tt = "Tatarça [tt]",
uk = "Українська [uk]",
vi = "Tiếng Việt [vi]",
zh_CN = "中文 (简体) [zh_CN]",
zh_TW = "正體中文 (繁體) [zh_TW]",
}
-- See if setting matches keywords
local function get_setting_match_weight(entry, query_keywords)
local setting_score = 0
@ -734,6 +742,7 @@ end
function create_settings_dlg()
load()
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
dlg.data.page_id = update_filtered_pages("")

@ -439,9 +439,9 @@ function settingtypes.parse_config_file(read_all, parse_mods)
end
-- Parse mods
pkgmgr.load_all()
local mods_category_initialized = false
local mods = {}
pkgmgr.get_mods(core.get_modpath(), "mods", mods)
local mods = pkgmgr.global_mods:get_list()
table.sort(mods, function(a, b) return a.name < b.name end)
for _, mod in ipairs(mods) do

@ -29,16 +29,11 @@ end
local packages_raw, packages
local function update_packages()
if not pkgmgr.global_mods then
pkgmgr.refresh_globals()
end
if not pkgmgr.games then
pkgmgr.update_gamelist()
end
pkgmgr.load_all()
packages_raw = {}
table.insert_all(packages_raw, pkgmgr.games)
table.insert_all(packages_raw, pkgmgr.get_texture_packs())
table.insert_all(packages_raw, pkgmgr.texture_packs)
table.insert_all(packages_raw, pkgmgr.global_mods:get_list())
local function get_data()
@ -207,6 +202,7 @@ local function handle_doubleclick(pkg)
core.settings:set("texture_path", pkg.path)
end
packages = nil
pkgmgr.reload_texture_packs()
mm_game_theme.init()
mm_game_theme.set_engine()
@ -271,6 +267,7 @@ local function handle_buttons(tabview, fields, tabname, tabdata)
core.settings:set("texture_path", txp_path)
packages = nil
pkgmgr.reload_texture_packs()
mm_game_theme.init()
mm_game_theme.set_engine()

@ -3902,6 +3902,7 @@ Operators
---------
Operators can be used if all of the involved vectors have metatables:
* `v1 == v2`:
* Returns whether `v1` and `v2` are identical.
* `-v`:
@ -4007,8 +4008,9 @@ Helper functions
* X1, Y1, ... Z2 are coordinates
* `relative_to`: Optional. If set to a position, each coordinate
can use the tilde notation for relative positions
* Tilde notation: "~": Relative coordinate
"~<number>": Relative coordinate plus <number>
* Tilde notation
* `"~"`: Relative coordinate
* `"~<number>"`: Relative coordinate plus `<number>`
* Example: `minetest.string_to_area("(1,2,3) (~5,~-5,~)", {x=10,y=10,z=10})`
returns `{x=1,y=2,z=3}, {x=15,y=5,z=10}`
* `minetest.formspec_escape(string)`: returns a string
@ -4346,6 +4348,8 @@ the previous octave.
This may need to be tuned when altering `lacunarity`; when doing so consider
that a common medium value is 1 / lacunarity.
Instead of `persistence`, the key `persist` may be used to the same effect.
### `lacunarity`
Each additional octave has a 'wavelength' that is the 'wavelength' of the

@ -157,7 +157,7 @@ minetest.register_node("basenodes:water_flowing", {
"Drowning damage: 1",
drawtype = "flowingliquid",
waving = 3,
tiles = {"default_water_flowing.png"},
tiles = {"default_water_flowing.png"..WATER_ALPHA},
special_tiles = {
{name = "default_water_flowing.png"..WATER_ALPHA,
backface_culling = false},

@ -105,3 +105,9 @@ minetest.register_craftitem("testitems:telescope_stick", {
return itemstack
end,
})
minetest.register_craftitem("testitems:translucent_item", {
description = S("Translucent item (translucent inventory & wield image)"),
inventory_image = "testitems_translucent.png",
wield_image = "testitems_translucent.png",
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

@ -2,29 +2,22 @@
local S = minetest.get_translator("testnodes")
-- Complex mesh
minetest.register_node("testnodes:performance_mesh_clip", {
description = S("Performance Test Node") .. "\n" .. S("Marble with 'clip' transparency"),
drawtype = "mesh",
mesh = "testnodes_marble_glass.obj",
tiles = {"testnodes_marble_glass.png"},
paramtype = "light",
use_texture_alpha = "clip",
for use_texture_alpha, description in pairs({
opaque = S("Marble with 'opaque' transparency"),
clip = S("Marble with 'clip' transparency"),
blend = S("Marble with 'blend' transparency"),
}) do
minetest.register_node("testnodes:performance_mesh_" .. use_texture_alpha, {
description = S("Performance Test Node") .. "\n" .. description,
drawtype = "mesh",
mesh = "testnodes_marble_glass.obj",
tiles = {"testnodes_marble_glass.png"},
paramtype = "light",
use_texture_alpha = use_texture_alpha,
groups = {dig_immediate=3},
})
-- Complex mesh, alpha blending
minetest.register_node("testnodes:performance_mesh_blend", {
description = S("Performance Test Node") .. "\n" .. S("Marble with 'blend' transparency"),
drawtype = "mesh",
mesh = "testnodes_marble_glass.obj",
tiles = {"testnodes_marble_glass.png"},
paramtype = "light",
use_texture_alpha = "blend",
groups = {dig_immediate=3},
})
groups = {dig_immediate=3},
})
end
-- Overlay
minetest.register_node("testnodes:performance_overlay_clip", {

@ -200,7 +200,7 @@ class IGUIButton : public IGUIElement
\param loop: True if the animation should loop, false if not
\param scale: True if the sprite should scale to button size, false if not */
virtual void setSprite(EGUI_BUTTON_STATE state, s32 index,
video::SColor color = video::SColor(255, 255, 255, 255), bool loop = false, bool scale = false) = 0;
video::SColor color = video::SColor(255, 255, 255, 255), bool loop = false) = 0;
//! Get the sprite-index for the given state or -1 when no sprite is set
virtual s32 getSpriteIndex(EGUI_BUTTON_STATE state) const = 0;
@ -211,9 +211,6 @@ class IGUIButton : public IGUIElement
//! Returns if the sprite in the given state does loop
virtual bool getSpriteLoop(EGUI_BUTTON_STATE state) const = 0;
//! Returns if the sprite in the given state is scaled
virtual bool getSpriteScale(EGUI_BUTTON_STATE state) const = 0;
//! Sets if the button should behave like a push button.
/** Which means it can be in two states: Normal or Pressed. With a click on the button,
the user can change the state of the button. */

@ -374,6 +374,12 @@ const c8 *const GUISkinFontNames[EGDF_COUNT + 1] = {
class IGUISkin : virtual public IReferenceCounted
{
public:
//! returns display density scaling factor
virtual float getScale() const = 0;
//! sets display density scaling factor
virtual void setScale(float scale) = 0;
//! returns default color
virtual video::SColor getColor(EGUI_DEFAULT_COLOR color) const = 0;

@ -75,12 +75,11 @@ void CGUIButton::setSpriteBank(IGUISpriteBank *sprites)
SpriteBank = sprites;
}
void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale)
void CGUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop)
{
ButtonSprites[(u32)state].Index = index;
ButtonSprites[(u32)state].Color = color;
ButtonSprites[(u32)state].Loop = loop;
ButtonSprites[(u32)state].Scale = scale;
}
//! Get the sprite-index for the given state or -1 when no sprite is set
@ -101,12 +100,6 @@ bool CGUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const
return ButtonSprites[(u32)state].Loop;
}
//! Returns if the sprite in the given state is scaled
bool CGUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const
{
return ButtonSprites[(u32)state].Scale;
}
//! called if an event happened.
bool CGUIButton::OnEvent(const SEvent &event)
{
@ -294,19 +287,26 @@ void CGUIButton::draw()
void CGUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di &center)
{
u32 stateIdx = (u32)state;
s32 spriteIdx = ButtonSprites[stateIdx].Index;
if (spriteIdx == -1)
return;
if (ButtonSprites[stateIdx].Index != -1) {
if (ButtonSprites[stateIdx].Scale) {
const video::SColor colors[] = {ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color, ButtonSprites[stateIdx].Color};
SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, AbsoluteRect,
&AbsoluteClippingRect, colors,
os::Timer::getTime() - startTime, ButtonSprites[stateIdx].Loop);
} else {
SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, center,
&AbsoluteClippingRect, ButtonSprites[stateIdx].Color, startTime, os::Timer::getTime(),
ButtonSprites[stateIdx].Loop, true);
}
}
u32 rectIdx = SpriteBank->getSprites()[spriteIdx].Frames[0].rectNumber;
core::rect<s32> srcRect = SpriteBank->getPositions()[rectIdx];
IGUISkin *skin = Environment->getSkin();
s32 scale = std::max(std::floor(skin->getScale()), 1.0f);
core::rect<s32> rect(center, srcRect.getSize() * scale);
rect -= rect.getSize() / 2;
const video::SColor colors[] = {
ButtonSprites[stateIdx].Color,
ButtonSprites[stateIdx].Color,
ButtonSprites[stateIdx].Color,
ButtonSprites[stateIdx].Color,
};
SpriteBank->draw2DSprite(spriteIdx, rect, &AbsoluteClippingRect, colors,
os::Timer::getTime() - startTime, ButtonSprites[stateIdx].Loop);
}
EGUI_BUTTON_IMAGE_STATE CGUIButton::getImageState(bool pressed) const

@ -92,7 +92,7 @@ class CGUIButton : public IGUIButton
*/
virtual void setSprite(EGUI_BUTTON_STATE state, s32 index,
video::SColor color = video::SColor(255, 255, 255, 255),
bool loop = false, bool scale = false) override;
bool loop = false) override;
//! Get the sprite-index for the given state or -1 when no sprite is set
s32 getSpriteIndex(EGUI_BUTTON_STATE state) const override;
@ -103,9 +103,6 @@ class CGUIButton : public IGUIButton
//! Returns if the sprite in the given state does loop
bool getSpriteLoop(EGUI_BUTTON_STATE state) const override;
//! Returns if the sprite in the given state is scaled
bool getSpriteScale(EGUI_BUTTON_STATE state) const override;
//! Sets if the button should behave like a push button. Which means it
//! can be in two states: Normal or Pressed. With a click on the button,
//! the user can change the state of the button.
@ -158,19 +155,18 @@ class CGUIButton : public IGUIButton
struct ButtonSprite
{
ButtonSprite() :
Index(-1), Loop(false), Scale(false)
Index(-1), Loop(false)
{
}
bool operator==(const ButtonSprite &other) const
{
return Index == other.Index && Color == other.Color && Loop == other.Loop && Scale == other.Scale;
return Index == other.Index && Color == other.Color && Loop == other.Loop;
}
s32 Index;
video::SColor Color;
bool Loop;
bool Scale;
};
ButtonSprite ButtonSprites[EGBS_COUNT];

@ -24,6 +24,12 @@ class CGUISkin : public IGUISkin
//! destructor
virtual ~CGUISkin();
//! returns display density scaling factor
virtual float getScale() const override { return Scale; }
//! sets display density scaling factor
virtual void setScale(float scale) override { Scale = scale; }
//! returns default color
video::SColor getColor(EGUI_DEFAULT_COLOR color) const override;
@ -210,6 +216,7 @@ class CGUISkin : public IGUISkin
EGUI_SKIN_TYPE getType() const override;
private:
float Scale = 1.0f;
video::SColor Colors[EGDC_COUNT];
s32 Sizes[EGDS_COUNT];
u32 Icons[EGDI_COUNT];

@ -129,9 +129,9 @@ EM_BOOL CIrrDeviceSDL::MouseLeaveCallback(int eventType, const EmscriptenMouseEv
}
#endif
bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE irrlichtKey)
{
switch (key) {
switch (irrlichtKey) {
// keys which are known to have safe special character interpretation
// could need changes over time (removals and additions!)
case KEY_RETURN:
@ -189,24 +189,68 @@ bool CIrrDeviceSDL::keyIsKnownSpecial(EKEY_CODE key)
}
}
int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
int CIrrDeviceSDL::findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock)
{
switch (irrlichtKey) {
// special cases that always return a char regardless of how the SDL keycode
// looks
switch (key) {
case KEY_RETURN:
case KEY_ESCAPE:
return (int)key;
return (int)irrlichtKey;
// This is necessary for keys on the numpad because they don't use the same
// keycodes as their non-numpad versions (whose keycodes correspond to chars),
// but have their own SDL keycodes and their own Irrlicht keycodes (which
// don't correspond to chars).
case KEY_MULTIPLY:
return '*';
case KEY_ADD:
return '+';
case KEY_SUBTRACT:
return '-';
case KEY_DIVIDE:
return '/';
default:
break;
}
if (numlock) {
// Number keys on the numpad are also affected, but we only want them
// to produce number chars when numlock is enabled.
switch (irrlichtKey) {
case KEY_NUMPAD0:
return '0';
case KEY_NUMPAD1:
return '1';
case KEY_NUMPAD2:
return '2';
case KEY_NUMPAD3:
return '3';
case KEY_NUMPAD4:
return '4';
case KEY_NUMPAD5:
return '5';
case KEY_NUMPAD6:
return '6';
case KEY_NUMPAD7:
return '7';
case KEY_NUMPAD8:
return '8';
case KEY_NUMPAD9:
return '9';
default:
break;
}
}
// SDL in-place ORs values with no character representation with 1<<30
// https://wiki.libsdl.org/SDL2/SDLKeycodeLookup
if (assumedChar & (1 << 30))
// This also affects the numpad keys btw.
if (sdlKey & (1 << 30))
return 0;
switch (key) {
switch (irrlichtKey) {
case KEY_PRIOR:
case KEY_NEXT:
case KEY_HOME:
@ -218,7 +262,7 @@ int CIrrDeviceSDL::findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key)
case KEY_NUMLOCK:
return 0;
default:
return assumedChar;
return sdlKey;
}
}
@ -825,7 +869,8 @@ bool CIrrDeviceSDL::run()
irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN);
irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0;
irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL) != 0;
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key);
irrevent.KeyInput.Char = findCharToPassToIrrlicht(mp.SDLKey, key,
(SDL_event.key.keysym.mod & KMOD_NUM) != 0);
postEventFromUser(irrevent);
} break;

@ -273,10 +273,10 @@ class CIrrDeviceSDL : public CIrrDeviceStub
#endif
// Check if a key is a known special character with no side effects on text boxes.
static bool keyIsKnownSpecial(EKEY_CODE key);
static bool keyIsKnownSpecial(EKEY_CODE irrlichtKey);
// Return the Char that should be sent to Irrlicht for the given key (either the one passed in or 0).
static int findCharToPassToIrrlicht(int assumedChar, EKEY_CODE key);
static int findCharToPassToIrrlicht(uint32_t sdlKey, EKEY_CODE irrlichtKey, bool numlock);
// Check if a text box is in focus. Enable or disable SDL_TEXTINPUT events only if in focus.
void resetReceiveTextInputEvents();

@ -352,6 +352,7 @@ void ClientLauncher::config_guienv()
float density = rangelim(g_settings->getFloat("gui_scaling"), 0.5f, 20) *
RenderingEngine::getDisplayDensity();
skin->setScale(density);
skin->setSize(gui::EGDS_CHECK_BOX_WIDTH, (s32)(17.0f * density));
skin->setSize(gui::EGDS_SCROLLBAR_SIZE, (s32)(21.0f * density));
skin->setSize(gui::EGDS_WINDOW_BUTTON_WIDTH, (s32)(15.0f * density));

@ -1145,7 +1145,6 @@ void drawItemStack(
}
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
material.Lighting = false;
driver->setMaterial(material);
driver->drawMeshBuffer(buf);

@ -526,26 +526,7 @@ ShaderInfo ShaderSource::generateShader(const std::string &name,
shaderinfo.name = name;
shaderinfo.material_type = material_type;
shaderinfo.drawtype = drawtype;
switch (material_type) {
case TILE_MATERIAL_OPAQUE:
case TILE_MATERIAL_LIQUID_OPAQUE:
case TILE_MATERIAL_WAVING_LIQUID_OPAQUE:
shaderinfo.base_material = video::EMT_SOLID;
break;
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_PLAIN_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_PLAIN:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
case TILE_MATERIAL_WAVING_LIQUID_BASIC:
shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
}
shaderinfo.base_material = MaterialType_to_irr(material_type);
shaderinfo.material = shaderinfo.base_material;
bool enable_shaders = g_settings->getBool("enable_shaders");

@ -18,31 +18,54 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "tile.h"
#include "EMaterialTypes.h"
#include "SMaterial.h"
// Sets everything else except the texture in the material
void TileLayer::applyMaterialOptions(video::SMaterial &material) const
void MaterialType_to_irr(MaterialType material_type,
video::E_MATERIAL_TYPE &irr_mat_type, f32 &irr_mat_param)
{
switch (material_type) {
case TILE_MATERIAL_OPAQUE:
case TILE_MATERIAL_LIQUID_OPAQUE:
case TILE_MATERIAL_WAVING_LIQUID_OPAQUE:
material.MaterialType = video::EMT_SOLID;
irr_mat_type = video::EMT_SOLID;
irr_mat_param = 0.0f;
break;
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_PLAIN_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
default:
irr_mat_type = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
irr_mat_param = 0.0f;
break;
case TILE_MATERIAL_BASIC:
case TILE_MATERIAL_PLAIN:
case TILE_MATERIAL_WAVING_LEAVES:
case TILE_MATERIAL_WAVING_PLANTS:
case TILE_MATERIAL_WAVING_LIQUID_BASIC:
material.MaterialTypeParam = 0.5;
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
break;
case TILE_MATERIAL_ALPHA:
case TILE_MATERIAL_LIQUID_TRANSPARENT:
case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
break;
default:
irr_mat_type = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
irr_mat_param = 0.5f;
break;
}
}
video::E_MATERIAL_TYPE MaterialType_to_irr(MaterialType material_type) {
video::E_MATERIAL_TYPE res;
f32 unused;
MaterialType_to_irr(material_type, res, unused);
return res;
}
void MaterialType_to_irr(MaterialType material_type, video::SMaterial &material) {
MaterialType_to_irr(material_type,
material.MaterialType, material.MaterialTypeParam);
}
// Sets everything else except the texture in the material
void TileLayer::applyMaterialOptions(video::SMaterial &material) const
{
MaterialType_to_irr(static_cast<MaterialType>(material_type), material);
material.BackfaceCulling = (material_flags & MATERIAL_FLAG_BACKFACE_CULLING) != 0;
if (!(material_flags & MATERIAL_FLAG_TILEABLE_HORIZONTAL)) {
material.TextureLayers[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;

@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#pragma once
#include "EMaterialTypes.h"
#include "irrlichttypes.h"
#include <ITexture.h>
#include <vector>
@ -39,6 +40,11 @@ enum MaterialType{
TILE_MATERIAL_PLAIN_ALPHA
};
void MaterialType_to_irr(MaterialType material_type,
video::E_MATERIAL_TYPE &irr_mat_type, f32 &irr_mat_param);
video::E_MATERIAL_TYPE MaterialType_to_irr(MaterialType material_type);
void MaterialType_to_irr(MaterialType material_type, video::SMaterial &mat);
// Material flags
// Should backface culling be enabled?
#define MATERIAL_FLAG_BACKFACE_CULLING 0x01

@ -18,8 +18,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "wieldmesh.h"
#include "EMaterialTypes.h"
#include "SMaterial.h"
#include "settings.h"
#include "shader.h"
#include "inventory.h"
#include "client.h"
#include "itemdef.h"
@ -29,7 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapblock_mesh.h"
#include "client/meshgen/collector.h"
#include "client/tile.h"
#include "log.h"
#include "util/numeric.h"
#include <map>
#include <IMeshManipulator.h>
@ -41,6 +41,56 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MIN_EXTRUSION_MESH_RESOLUTION 16
#define MAX_EXTRUSION_MESH_RESOLUTION 512
/*!
* Applies overlays, textures and optionally materials to the given mesh and
* extracts tile colors for colorization.
* \param mattype overrides the buffer's material type, but can also
* be NULL to leave the original material.
* \param colors returns the colors of the mesh buffers in the mesh.
*/
static void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
std::vector<ItemPartColor> *colors)
{
const u32 mc = mesh->getMeshBufferCount();
// Allocate colors for existing buffers
colors->clear();
colors->resize(mc);
for (u32 i = 0; i < mc; ++i) {
const TileSpec *tile = &(f.tiles[i]);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
const TileLayer *layer = &tile->layers[layernum];
if (layer->texture_id == 0)
continue;
if (layernum != 0) {
scene::IMeshBuffer *copy = cloneMeshBuffer(buf);
copy->getMaterial() = buf->getMaterial();
mesh->addMeshBuffer(copy);
copy->drop();
buf = copy;
colors->emplace_back(layer->has_color, layer->color);
} else {
(*colors)[i] = ItemPartColor(layer->has_color, layer->color);
}
video::SMaterial &material = buf->getMaterial();
if (layer->animation_frame_count > 1) {
const FrameSpec &animation_frame = (*layer->frames)[0];
material.setTexture(0, animation_frame.texture);
} else {
material.setTexture(0, layer->texture);
}
if (tile->world_aligned) {
u32 n = buf->getVertexCount();
for (u32 k = 0; k != n; ++k)
buf->getTCoords(k) /= layer->scale;
}
}
}
}
static scene::IMesh *createExtrusionMesh(int resolution_x, int resolution_y)
{
const f32 r = 0.5;
@ -193,10 +243,55 @@ class ExtrusionMeshCache: public IReferenceCounted
static ExtrusionMeshCache *g_extrusion_mesh_cache = nullptr;
static scene::SMesh *getExtrudedMesh(ITextureSource *tsrc,
const std::string &imagename, const std::string &overlay_name)
{
// check textures
video::ITexture *texture = tsrc->getTextureForMesh(imagename);
if (!texture) {
return NULL;
}
video::ITexture *overlay_texture =
(overlay_name.empty()) ? NULL : tsrc->getTexture(overlay_name);
// get mesh
core::dimension2d<u32> dim = texture->getSize();
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
scene::SMesh *mesh = cloneMesh(original);
original->drop();
//set texture
mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
tsrc->getTexture(imagename));
if (overlay_texture) {
scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0));
copy->getMaterial().setTexture(0, overlay_texture);
mesh->addMeshBuffer(copy);
copy->drop();
}
// Customize materials
for (u32 layer = 0; layer < mesh->getMeshBufferCount(); layer++) {
video::SMaterial &material = mesh->getMeshBuffer(layer)->getMaterial();
material.TextureLayers[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
material.TextureLayers[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
material.forEachTexture([] (auto &tex) {
tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
tex.MagFilter = video::ETMAGF_NEAREST;
});
material.BackfaceCulling = true;
material.Lighting = false;
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.MaterialTypeParam = 0.0f; // render everything with alpha > 0
material.ZWriteEnable = video::EZW_ON;
}
scaleMesh(mesh, v3f(2.0, 2.0, 2.0));
return mesh;
}
WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id, bool lighting):
scene::ISceneNode(mgr->getRootSceneNode(), mgr, id),
m_material_type(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF),
m_material_type(video::EMT_TRANSPARENT_ALPHA_CHANNEL),
m_lighting(lighting)
{
m_enable_shaders = g_settings->getBool("enable_shaders");
@ -248,7 +343,17 @@ void WieldMeshSceneNode::setCube(const ContentFeatures &f,
scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube();
scene::SMesh *copy = cloneMesh(cubemesh);
cubemesh->drop();
postProcessNodeMesh(copy, f, false, true, &m_material_type, &m_colors, true);
postProcessNodeMesh(copy, f, &m_colors);
// Customize materials
for (u32 i = 0; i < cubemesh->getMeshBufferCount(); ++i) {
// It suffices to look at the first layer;
// a special overlay layer needs to be consistent with it -
// otherwise we would be overwriting the material options of the fist layer
scene::IMeshBuffer *buf = cubemesh->getMeshBuffer(i);
f.tiles[i].layers[0].applyMaterialOptions(buf->getMaterial());
}
changeToMesh(copy);
copy->drop();
m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR);
@ -295,7 +400,8 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
material.TextureLayers[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
material.TextureLayers[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
material.MaterialType = m_material_type;
material.MaterialTypeParam = 0.5f;
material.MaterialTypeParam = m_material_type_param;
material.ZWriteEnable = video::EZW_ON;
material.BackfaceCulling = true;
// Enable bi/trilinear filtering only for high resolution textures
bool bilinear_filter = dim.Width > 32 && m_bilinear_filter;
@ -350,13 +456,11 @@ static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n,
}
scene::SMeshBuffer *buf = new scene::SMeshBuffer();
buf->Material.setTexture(0, p.layer.texture);
p.layer.applyMaterialOptions(buf->Material);
mesh->addMeshBuffer(buf);
buf->append(&p.vertices[0], p.vertices.size(),
&p.indices[0], p.indices.size());
buf->drop();
colors->push_back(
ItemPartColor(p.layer.has_color, p.layer.color));
colors->emplace_back(p.layer.has_color, p.layer.color);
}
return mesh;
}
@ -365,7 +469,6 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
{
ITextureSource *tsrc = client->getTextureSource();
IItemDefManager *idef = client->getItemDefManager();
IShaderSource *shdrsrc = client->getShaderSource();
const NodeDefManager *ndef = client->getNodeDefManager();
const ItemDefinition &def = item.getDefinition(idef);
const ContentFeatures &f = ndef->get(def.name);
@ -373,8 +476,16 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
scene::SMesh *mesh = nullptr;
const auto material_type = def.type == ITEM_NODE
? f.getMaterialType() : TILE_MATERIAL_ALPHA;
MaterialType_to_irr(material_type, m_material_type, m_material_type_param);
if (m_enable_shaders) {
u32 shader_id = shdrsrc->getShader("object_shader", TILE_MATERIAL_BASIC, NDT_NORMAL);
IShaderSource *shdrsrc = client->getShaderSource();
u32 shader_id = shdrsrc->getShader("object_shader",
material_type, def.type == ITEM_NODE ? f.drawtype : NDT_MESH);
// The shader gives a material ID (different from our material type) back.
// Strictly speaking, "m_material type" is a misnomer,
// since either a material type or ID is stored.
m_material_type = shdrsrc->getShaderInfo(shader_id).material;
}
@ -463,7 +574,8 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
for (u32 i = 0; i < material_count; ++i) {
video::SMaterial &material = m_meshnode->getMaterial(i);
material.MaterialType = m_material_type;
material.MaterialTypeParam = 0.5f;
material.MaterialTypeParam = m_material_type_param;
material.ZWriteEnable = video::EZW_ON;
material.BackfaceCulling = cull_backface;
material.forEachTexture([this] (auto &tex) {
setMaterialFilters(tex, m_bilinear_filter, m_trilinear_filter,
@ -624,8 +736,7 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
} else
scaleMesh(mesh, v3f(1.2, 1.2, 1.2));
// add overlays
postProcessNodeMesh(mesh, f, false, false, nullptr,
&result->buffer_colors, true);
postProcessNodeMesh(mesh, f, &result->buffer_colors);
if (f.drawtype == NDT_ALLFACES)
scaleMesh(mesh, v3f(f.visual_scale));
break;
@ -664,14 +775,17 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
for (u32 i = 0; i < mesh->getMeshBufferCount(); ++i) {
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
video::SMaterial &material = buf->getMaterial();
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.MaterialTypeParam = 0.5f;
// Note: This overwrites material types / type params
// of plantlike extrusion meshes for alpha blending consistency.
// It suffices to look at the first layer; overlays need to be consistent.
f.tiles[i].layers[0].applyMaterialOptions(material);
material.ZWriteEnable = video::EZW_ON;
material.forEachTexture([] (auto &tex) {
tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
tex.MagFilter = video::ETMAGF_NEAREST;
});
material.BackfaceCulling = cull_backface;
material.Lighting = false;
material.Lighting = false; // no lighting in the inventory
}
rotateMeshXZby(mesh, -45);
@ -685,109 +799,3 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
}
result->mesh = mesh;
}
scene::SMesh *getExtrudedMesh(ITextureSource *tsrc,
const std::string &imagename, const std::string &overlay_name)
{
// check textures
video::ITexture *texture = tsrc->getTextureForMesh(imagename);
if (!texture) {
return NULL;
}
video::ITexture *overlay_texture =
(overlay_name.empty()) ? NULL : tsrc->getTexture(overlay_name);
// get mesh
core::dimension2d<u32> dim = texture->getSize();
scene::IMesh *original = g_extrusion_mesh_cache->create(dim);
scene::SMesh *mesh = cloneMesh(original);
original->drop();
//set texture
mesh->getMeshBuffer(0)->getMaterial().setTexture(0,
tsrc->getTexture(imagename));
if (overlay_texture) {
scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0));
copy->getMaterial().setTexture(0, overlay_texture);
mesh->addMeshBuffer(copy);
copy->drop();
}
// Customize materials
for (u32 layer = 0; layer < mesh->getMeshBufferCount(); layer++) {
video::SMaterial &material = mesh->getMeshBuffer(layer)->getMaterial();
material.TextureLayers[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE;
material.TextureLayers[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE;
material.forEachTexture([] (auto &tex) {
tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST;
tex.MagFilter = video::ETMAGF_NEAREST;
});
material.BackfaceCulling = true;
material.Lighting = false;
material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
material.MaterialTypeParam = 0.5f;
}
scaleMesh(mesh, v3f(2.0, 2.0, 2.0));
return mesh;
}
void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f,
bool use_shaders, bool set_material, const video::E_MATERIAL_TYPE *mattype,
std::vector<ItemPartColor> *colors, bool apply_scale)
{
const u32 mc = mesh->getMeshBufferCount();
// Allocate colors for existing buffers
colors->clear();
colors->resize(mc);
for (u32 i = 0; i < mc; ++i) {
const TileSpec *tile = &(f.tiles[i]);
scene::IMeshBuffer *buf = mesh->getMeshBuffer(i);
for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
const TileLayer *layer = &tile->layers[layernum];
if (layer->texture_id == 0)
continue;
if (layernum != 0) {
scene::IMeshBuffer *copy = cloneMeshBuffer(buf);
copy->getMaterial() = buf->getMaterial();
mesh->addMeshBuffer(copy);
copy->drop();
buf = copy;
colors->emplace_back(layer->has_color, layer->color);
} else {
(*colors)[i] = ItemPartColor(layer->has_color, layer->color);
}
video::SMaterial &material = buf->getMaterial();
if (set_material)
layer->applyMaterialOptions(material);
if (mattype) {
material.MaterialType = *mattype;
}
if (layer->animation_frame_count > 1) {
const FrameSpec &animation_frame = (*layer->frames)[0];
material.setTexture(0, animation_frame.texture);
} else {
material.setTexture(0, layer->texture);
}
if (use_shaders) {
if (layer->normal_texture) {
if (layer->animation_frame_count > 1) {
const FrameSpec &animation_frame = (*layer->frames)[0];
material.setTexture(1, animation_frame.normal_texture);
} else
material.setTexture(1, layer->normal_texture);
}
material.setTexture(2, layer->flags_texture);
}
if (apply_scale && tile->world_aligned) {
u32 n = buf->getVertexCount();
for (u32 k = 0; k != n; ++k)
buf->getTCoords(k) /= layer->scale;
}
}
}
}

@ -118,6 +118,7 @@ class WieldMeshSceneNode : public scene::ISceneNode
// Child scene node with the current wield mesh
scene::IMeshSceneNode *m_meshnode = nullptr;
video::E_MATERIAL_TYPE m_material_type;
f32 m_material_type_param = 0.0f;
// True if SMaterial::Lighting should be enabled.
bool m_lighting;
@ -146,17 +147,3 @@ class WieldMeshSceneNode : public scene::ISceneNode
};
void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result);
scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename,
const std::string &overlay_name);
/*!
* Applies overlays, textures and optionally materials to the given mesh and
* extracts tile colors for colorization.
* \param mattype overrides the buffer's material type, but can also
* be NULL to leave the original material.
* \param colors returns the colors of the mesh buffers in the mesh.
*/
void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, bool use_shaders,
bool set_material, const video::E_MATERIAL_TYPE *mattype,
std::vector<ItemPartColor> *colors, bool apply_scale = false);

@ -89,12 +89,11 @@ void GUIButton::setSpriteBank(IGUISpriteBank* sprites)
SpriteBank = sprites;
}
void GUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop, bool scale)
void GUIButton::setSprite(EGUI_BUTTON_STATE state, s32 index, video::SColor color, bool loop)
{
ButtonSprites[(u32)state].Index = index;
ButtonSprites[(u32)state].Color = color;
ButtonSprites[(u32)state].Loop = loop;
ButtonSprites[(u32)state].Scale = scale;
}
//! Get the sprite-index for the given state or -1 when no sprite is set
@ -115,12 +114,6 @@ bool GUIButton::getSpriteLoop(EGUI_BUTTON_STATE state) const
return ButtonSprites[(u32)state].Loop;
}
//! Returns if the sprite in the given state is scaled
bool GUIButton::getSpriteScale(EGUI_BUTTON_STATE state) const
{
return ButtonSprites[(u32)state].Scale;
}
//! called if an event happened.
bool GUIButton::OnEvent(const SEvent& event)
{
@ -354,23 +347,26 @@ void GUIButton::draw()
void GUIButton::drawSprite(EGUI_BUTTON_STATE state, u32 startTime, const core::position2di& center)
{
u32 stateIdx = (u32)state;
s32 spriteIdx = ButtonSprites[stateIdx].Index;
if (spriteIdx == -1)
return;
if (ButtonSprites[stateIdx].Index != -1)
{
if ( ButtonSprites[stateIdx].Scale )
{
const video::SColor colors[] = {ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color,ButtonSprites[stateIdx].Color};
SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, AbsoluteRect.UpperLeftCorner,
&AbsoluteClippingRect, colors[0], // FIXME: remove [0]
porting::getTimeMs()-startTime, ButtonSprites[stateIdx].Loop);
}
else
{
SpriteBank->draw2DSprite(ButtonSprites[stateIdx].Index, center,
&AbsoluteClippingRect, ButtonSprites[stateIdx].Color, startTime, porting::getTimeMs(),
ButtonSprites[stateIdx].Loop, true);
}
}
u32 rectIdx = SpriteBank->getSprites()[spriteIdx].Frames[0].rectNumber;
core::rect<s32> srcRect = SpriteBank->getPositions()[rectIdx];
IGUISkin *skin = Environment->getSkin();
s32 scale = std::max(std::floor(skin->getScale()), 1.0f);
core::rect<s32> rect(center, srcRect.getSize() * scale);
rect -= rect.getSize() / 2;
const video::SColor colors[] = {
ButtonSprites[stateIdx].Color,
ButtonSprites[stateIdx].Color,
ButtonSprites[stateIdx].Color,
ButtonSprites[stateIdx].Color,
};
SpriteBank->draw2DSprite(spriteIdx, rect, &AbsoluteClippingRect, colors,
porting::getTimeMs() - startTime, ButtonSprites[stateIdx].Loop);
}
EGUI_BUTTON_IMAGE_STATE GUIButton::getImageState(bool pressed) const

@ -92,7 +92,7 @@ class GUIButton : public gui::IGUIButton
*/
virtual void setSprite(gui::EGUI_BUTTON_STATE state, s32 index,
video::SColor color=video::SColor(255,255,255,255),
bool loop=false, bool scale=false) override;
bool loop=false) override;
//! Get the sprite-index for the given state or -1 when no sprite is set
virtual s32 getSpriteIndex(gui::EGUI_BUTTON_STATE state) const override;
@ -103,9 +103,6 @@ class GUIButton : public gui::IGUIButton
//! Returns if the sprite in the given state does loop
virtual bool getSpriteLoop(gui::EGUI_BUTTON_STATE state) const override;
//! Returns if the sprite in the given state is scaled
virtual bool getSpriteScale(gui::EGUI_BUTTON_STATE state) const override;
//! Sets if the button should behave like a push button. Which means it
//! can be in two states: Normal or Pressed. With a click on the button,
//! the user can change the state of the button.
@ -230,13 +227,12 @@ class GUIButton : public gui::IGUIButton
{
bool operator==(const ButtonSprite &other) const
{
return Index == other.Index && Color == other.Color && Loop == other.Loop && Scale == other.Scale;
return Index == other.Index && Color == other.Color && Loop == other.Loop;
}
s32 Index = -1;
video::SColor Color;
bool Loop = false;
bool Scale = false;
};
ButtonSprite ButtonSprites[gui::EGBS_COUNT];

@ -27,6 +27,12 @@ namespace gui
//! destructor
virtual ~GUISkin();
//! returns display density scaling factor
virtual float getScale() const { return Scale; }
//! sets display density scaling factor
virtual void setScale(float scale) { Scale = scale; }
//! returns default color
virtual video::SColor getColor(EGUI_DEFAULT_COLOR color) const;
@ -292,6 +298,7 @@ namespace gui
private:
float Scale = 1.0f;
video::SColor Colors[EGDC_COUNT];
s32 Sizes[EGDS_COUNT];
u32 Icons[EGDI_COUNT];

@ -784,6 +784,80 @@ bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype
return false;
}
void ContentFeatures::updateAlpha(bool translucent_liquids) {
if ((drawtype == NDT_LIQUID || drawtype == NDT_FLOWINGLIQUID) &&
!translucent_liquids)
alpha = ALPHAMODE_OPAQUE;
}
MaterialType ContentFeatures::getMaterialType() const
{
MaterialType material_type;
switch (alpha) {
case ALPHAMODE_OPAQUE:
material_type = TILE_MATERIAL_OPAQUE;
break;
case ALPHAMODE_CLIP:
material_type = TILE_MATERIAL_BASIC;
break;
case ALPHAMODE_BLEND:
default:
material_type = TILE_MATERIAL_ALPHA;
break;
}
switch (drawtype) {
case NDT_LIQUID:
case NDT_FLOWINGLIQUID:
switch (alpha) {
case ALPHAMODE_OPAQUE:
return (waving == 3)
? TILE_MATERIAL_WAVING_LIQUID_OPAQUE
: TILE_MATERIAL_LIQUID_OPAQUE;
case ALPHAMODE_CLIP:
return (waving == 3)
? TILE_MATERIAL_WAVING_LIQUID_BASIC
: TILE_MATERIAL_LIQUID_TRANSPARENT;
case ALPHAMODE_BLEND:
default:
return (waving == 3)
? TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT
: TILE_MATERIAL_LIQUID_TRANSPARENT;
}
break;
case NDT_ALLFACES_OPTIONAL:
if (waving >= 1)
return TILE_MATERIAL_WAVING_LEAVES;
break;
case NDT_PLANTLIKE:
if (waving >= 1)
return TILE_MATERIAL_WAVING_PLANTS;
break;
case NDT_MESH:
case NDT_NODEBOX:
switch (waving) {
case 1:
return TILE_MATERIAL_WAVING_PLANTS;
case 2:
return TILE_MATERIAL_WAVING_LEAVES;
case 3:
switch (alpha) {
case ALPHAMODE_OPAQUE:
return TILE_MATERIAL_WAVING_LIQUID_OPAQUE;
case ALPHAMODE_CLIP:
return TILE_MATERIAL_WAVING_LIQUID_BASIC;
case ALPHAMODE_BLEND:
return TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT;
default:
assert(false);
}
break;
}
break;
default: break;
}
return material_type;
}
void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
{
@ -810,11 +884,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
tdef_spec[j] = tiledef_special[j];
}
bool is_liquid = false;
MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
TILE_MATERIAL_ALPHA);
updateAlpha(tsettings.translucent_liquids);
MaterialType material_type = getMaterialType();
switch (drawtype) {
default:
@ -825,16 +896,10 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
solidness = 0;
break;
case NDT_LIQUID:
if (!tsettings.translucent_liquids)
alpha = ALPHAMODE_OPAQUE;
solidness = 1;
is_liquid = true;
break;
case NDT_FLOWINGLIQUID:
solidness = 0;
if (!tsettings.translucent_liquids)
alpha = ALPHAMODE_OPAQUE;
is_liquid = true;
break;
case NDT_GLASSLIKE:
solidness = 0;
@ -879,13 +944,9 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
for (TileDef &td : tdef)
td.name += std::string("^[noalpha");
}
if (waving >= 1)
material_type = TILE_MATERIAL_WAVING_LEAVES;
break;
case NDT_PLANTLIKE:
solidness = 0;
if (waving >= 1)
material_type = TILE_MATERIAL_WAVING_PLANTS;
break;
case NDT_FIRELIKE:
solidness = 0;
@ -893,15 +954,6 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
case NDT_MESH:
case NDT_NODEBOX:
solidness = 0;
if (waving == 1) {
material_type = TILE_MATERIAL_WAVING_PLANTS;
} else if (waving == 2) {
material_type = TILE_MATERIAL_WAVING_LEAVES;
} else if (waving == 3) {
material_type = alpha == ALPHAMODE_OPAQUE ?
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
}
break;
case NDT_TORCHLIKE:
case NDT_SIGNLIKE:
@ -914,17 +966,6 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
break;
}
if (is_liquid) {
if (waving == 3) {
material_type = alpha == ALPHAMODE_OPAQUE ?
TILE_MATERIAL_WAVING_LIQUID_OPAQUE : (alpha == ALPHAMODE_CLIP ?
TILE_MATERIAL_WAVING_LIQUID_BASIC : TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
} else {
material_type = alpha == ALPHAMODE_OPAQUE ? TILE_MATERIAL_LIQUID_OPAQUE :
TILE_MATERIAL_LIQUID_TRANSPARENT;
}
}
u32 tile_shader = shdsrc->getShader("nodes_shader", material_type, drawtype);
MaterialType overlay_material = material_type;
@ -1082,6 +1123,7 @@ void NodeDefManager::clear()
f.name = "air";
f.drawtype = NDT_AIRLIKE;
f.param_type = CPT_LIGHT;
f.alpha = ALPHAMODE_CLIP;
f.light_propagates = true;
f.sunlight_propagates = true;
f.walkable = false;

@ -531,6 +531,8 @@ struct ContentFeatures
}
#ifndef SERVER
void updateAlpha(bool translucent_liquids);
MaterialType getMaterialType() const;
void updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings);
#endif