diff --git a/.github/workflows/png_file_checks.yml b/.github/workflows/png_file_checks.yml new file mode 100644 index 000000000..86cd93527 --- /dev/null +++ b/.github/workflows/png_file_checks.yml @@ -0,0 +1,26 @@ +name: png_file_checks + +# Check whether all png files are in a valid format +on: + push: + paths: + - '**.png' + - '.github/workflows/**.yml' + pull_request: + paths: + - '**.png' + - '.github/workflows/**.yml' + +jobs: + png_optimized: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install deps + run: | + sudo apt-get update + sudo apt install -y optipng + + - name: Check whether all png files are optimized + run: | + ./util/ci/check_png_optimized.sh diff --git a/LICENSE.txt b/LICENSE.txt index f7930f528..503dd62d2 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -14,6 +14,9 @@ https://www.apache.org/licenses/LICENSE-2.0.html Textures by Zughy are under CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/ +Textures by cx384 are under CC BY-SA 4.0 +https://creativecommons.org/licenses/by-sa/4.0/ + Media files by DS are under CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0/ @@ -64,7 +67,13 @@ Zughy: textures/base/pack/settings_info.png textures/base/pack/settings_reset.png textures/base/pack/server_url.png + textures/base/pack/server_url_unavailable.png textures/base/pack/server_view_clients.png + textures/base/pack/server_view_clients_unavailable.png + +cx384: + textures/base/pack/server_view_mods.png + textures/base/pack/server_view_mods_unavailable.png appgurueu: textures/base/pack/server_incompatible.png diff --git a/builtin/game/falling.lua b/builtin/game/falling.lua index bb6ca1a64..6abd16c58 100644 --- a/builtin/game/falling.lua +++ b/builtin/game/falling.lua @@ -308,23 +308,26 @@ core.register_entity(":__builtin:falling_node", { core.remove_node(bcp) else + -- We are placing on top so check what's there np.y = np.y + 1 - end - -- Check what's here - local n2 = core.get_node(np) - local nd = core.registered_nodes[n2.name] - -- If it's not air or liquid, remove node and replace it with - -- it's drops - if n2.name ~= "air" and (not nd or nd.liquidtype ~= "source") then - if nd and nd.buildable_to == false then + local n2 = core.get_node(np) + local nd = core.registered_nodes[n2.name] + if not nd or nd.buildable_to then + core.remove_node(np) + else + -- 'walkable' is used to mean "falling nodes can't replace this" + -- here. Normally we would collide with the walkable node itself + -- and place our node on top (so `n2.name == "air"`), but we + -- re-check this in case we ended up inside a node. + if not nd.diggable or nd.walkable then + return false + end nd.on_dig(np, n2, nil) -- If it's still there, it might be protected if core.get_node(np).name == n2.name then return false end - else - core.remove_node(np) end end diff --git a/builtin/mainmenu/dlg_server_list_mods.lua b/builtin/mainmenu/dlg_server_list_mods.lua new file mode 100644 index 000000000..ad44ac37f --- /dev/null +++ b/builtin/mainmenu/dlg_server_list_mods.lua @@ -0,0 +1,105 @@ +-- Luanti +-- Copyright (C) 2024 cx384 +-- SPDX-License-Identifier: LGPL-2.1-or-later + +local function get_formspec(dialogdata) + local TOUCH_GUI = core.settings:get_bool("touch_gui") + local server = dialogdata.server + local group_by_prefix = dialogdata.group_by_prefix + local expand_all = dialogdata.expand_all + + -- A wrongly behaving server may send ill formed mod names + table.sort(server.mods) + + local cells = {} + if group_by_prefix then + local function get_prefix(mod) + return mod:match("[^_]*") + end + local count = {} + for _, mod in ipairs(server.mods) do + local prefix = get_prefix(mod) + count[prefix] = (count[prefix] or 0) + 1 + end + local last_prefix + local function add_row(depth, mod) + table.insert(cells, ("%d"):format(depth)) + table.insert(cells, mod) + end + for i, mod in ipairs(server.mods) do + local prefix = get_prefix(mod) + if last_prefix == prefix then + add_row(1, mod) + elseif count[prefix] > 1 then + add_row(0, prefix) + add_row(1, mod) + else + add_row(0, mod) + end + last_prefix = prefix + end + else + cells = table.copy(server.mods) + end + + for i, cell in ipairs(cells) do + cells[i] = core.formspec_escape(cell) + end + cells = table.concat(cells, ",") + + local heading + if server.gameid then + heading = fgettext("The $1 server uses a game called $2 and the following mods:", + "" .. core.hypertext_escape(server.name) .. "", + "") + else + heading = fgettext("The $1 server uses the following mods:", + "" .. core.hypertext_escape(server.name) .. "") + end + + local formspec = { + "formspec_version[8]", + "size[8,9.5]", + TOUCH_GUI and "padding[0.01,0.01]" or "", + "hypertext[0,0;8,1.5;;", heading, "]", + "tablecolumns[", group_by_prefix and + (expand_all and "indent;text" or "tree;text") or "text", "]", + "table[0.5,1.5;7,6.8;mods;", cells, "]", + "checkbox[0.5,8.7;group_by_prefix;", fgettext("Group by prefix"), ";", + group_by_prefix and "true" or "false", "]", + group_by_prefix and ("checkbox[0.5,9.15;expand_all;" .. fgettext("Expand all") .. ";" .. + (expand_all and "true" or "false") .. "]") or "", + "button[5.5,8.5;2,0.8;quit;OK]" + } + return table.concat(formspec, "") +end + +local function buttonhandler(this, fields) + if fields.quit then + this:delete() + return true + end + + if fields.group_by_prefix then + this.data.group_by_prefix = core.is_yes(fields.group_by_prefix) + return true + end + + if fields.expand_all then + this.data.expand_all = core.is_yes(fields.expand_all) + return true + end + + return false +end + +function create_server_list_mods_dialog(server) + local retval = dialog_create("dlg_server_list_mods", + get_formspec, + buttonhandler, + nil) + retval.data.group_by_prefix = false + retval.data.expand_all = false + retval.data.server = server + return retval +end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 4e1c201cd..9eceb41a8 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -56,6 +56,7 @@ dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua") dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua") dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua") dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua") +dofile(menupath .. DIR_DELIM .. "dlg_server_list_mods.lua") local tabs = { content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"), diff --git a/builtin/mainmenu/tab_online.lua b/builtin/mainmenu/tab_online.lua index 02c6a9c24..bd7f45342 100644 --- a/builtin/mainmenu/tab_online.lua +++ b/builtin/mainmenu/tab_online.lua @@ -161,6 +161,26 @@ local function get_formspec(tabview, name, tabdata) core.formspec_escape(gamedata.serverdescription) .. "]" end + -- Mods button + local mods = selected_server.mods + if mods and #mods > 0 then + local tooltip = "" + if selected_server.gameid then + tooltip = fgettext("Game: $1", selected_server.gameid) .. "\n" + end + tooltip = tooltip .. fgettext("Number of mods: $1", #mods) + + retval = retval .. + "tooltip[btn_view_mods;" .. tooltip .. "]" .. + "style[btn_view_mods;padding=6]" .. + "image_button[4,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. + "server_view_mods.png") .. ";btn_view_mods;]" + else + retval = retval .. "image[4.1,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir .. + "server_view_mods_unavailable.png") .. "]" + end + + -- Clients list button local clients_list = selected_server.clients_list local can_view_clients_list = clients_list and #clients_list > 0 if can_view_clients_list then @@ -178,15 +198,23 @@ local function get_formspec(tabview, name, tabdata) retval = retval .. "style[btn_view_clients;padding=6]" retval = retval .. "image_button[4.5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. "server_view_clients.png") .. ";btn_view_clients;]" + else + retval = retval .. "image[4.6,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir .. + "server_view_clients_unavailable.png") .. "]" end + -- URL button if selected_server.url then retval = retval .. "tooltip[btn_server_url;" .. fgettext("Open server website") .. "]" retval = retval .. "style[btn_server_url;padding=6]" - retval = retval .. "image_button[" .. (can_view_clients_list and "4" or "4.5") .. ",1.3;0.5,0.5;" .. + retval = retval .. "image_button[3.5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir .. "server_url.png") .. ";btn_server_url;]" + else + retval = retval .. "image[3.6,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir .. + "server_url_unavailable.png") .. "]" end + -- Favorites toggle button if is_selected_fav() then retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]" retval = retval .. "style[btn_delete_favorite;padding=6]" @@ -468,6 +496,14 @@ local function main_button_handler(tabview, fields, name, tabdata) return true end + if fields.btn_view_mods then + local dlg = create_server_list_mods_dialog(find_selected_server()) + dlg:set_parent(tabview) + tabview:hide() + dlg:show() + return true + end + if fields.btn_mp_clear then tabdata.search_for = "" menudata.search_result = nil diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index f13f7f69e..d8afc4b78 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -377,17 +377,12 @@ fog_start (Fog start) float 0.4 0.0 0.99 [**Clouds] -# Clouds are a client-side effect. -enable_clouds (Clouds) bool true - -# Use 3D cloud look instead of flat. -# -# Requires: enable_clouds +# Allow clouds to look 3D instead of flat. enable_3d_clouds (3D clouds) bool true # Use smooth cloud shading. # -# Requires: enable_3d_clouds, enable_clouds +# Requires: enable_3d_clouds soft_clouds (Soft clouds) bool false [**Filtering and Antialiasing] @@ -472,9 +467,6 @@ smooth_lighting (Smooth lighting) bool true # at the expense of minor visual glitches that do not impact game playability. performance_tradeoffs (Tradeoffs for performance) bool false -# Adds particles when digging a node. -enable_particles (Digging particles) bool true - [**Waving Nodes] @@ -667,8 +659,7 @@ sound_volume (Volume) float 0.8 0.0 1.0 # Volume multiplier when the window is unfocused. sound_volume_unfocused (Volume when unfocused) float 0.3 0.0 1.0 -# Whether to mute sounds. You can unmute sounds at any time, unless the -# sound system is disabled (enable_sound=false). +# Whether to mute sounds. You can unmute sounds at any time. # In-game, you can toggle the mute state with the mute key or by using the # pause menu. mute_sound (Mute sound) bool false @@ -709,12 +700,6 @@ formspec_fullscreen_bg_color (Formspec Full-Screen Background Color) string (0,0 # to hardware (e.g. render-to-texture for nodes in inventory). gui_scaling_filter (GUI scaling filter) bool false -# When gui_scaling_filter_txr2img is true, copy those images -# from hardware to software for scaling. When false, fall back -# to the old scaling method, for video drivers that don't -# properly support downloading textures back from hardware. -gui_scaling_filter_txr2img (GUI scaling filter txr2img) bool true - # Delay showing tooltips, stated in milliseconds. tooltip_show_delay (Tooltip delay) int 400 0 18446744073709551615 @@ -1869,10 +1854,7 @@ transparency_sorting_group_by_buffers (Transparency Sorting Group by Buffers) bo # Radius of cloud area stated in number of 64 node cloud squares. # Values larger than 26 will start to produce sharp cutoffs at cloud area corners. -cloud_radius (Cloud radius) int 12 1 62 - -# Whether node texture animations should be desynchronized per mapblock. -desynchronize_mapblock_texture_animation (Desynchronize block animation) bool false +cloud_radius (Cloud radius) int 12 8 62 # Delay between mesh updates on the client in ms. Increasing this will slow # down the rate of mesh updates, thus reducing jitter on slower clients. @@ -1942,7 +1924,7 @@ shadow_poisson_filter (Poisson filtering) bool true # Minimum value: 1; maximum value: 16 # # Requires: enable_dynamic_shadows, opengl -shadow_update_frames (Map shadows update frames) int 8 1 16 +shadow_update_frames (Map shadows update frames) int 16 1 32 # Set to true to render debugging breakdown of the bloom effect. # In debug mode, the screen is split into 4 quadrants: @@ -2126,9 +2108,6 @@ max_block_send_distance (Max block send distance) int 12 1 65535 # Set this to -1 to disable the limit. max_forceloaded_blocks (Maximum forceloaded blocks) int 16 -1 -# Interval of sending time of day to clients, stated in seconds. -time_send_interval (Time send interval) float 5.0 0.001 - # Interval of saving important changes in the world, stated in seconds. server_map_save_interval (Map save interval) float 5.3 0.001 @@ -2137,7 +2116,7 @@ server_map_save_interval (Map save interval) float 5.3 0.001 server_unload_unused_data_timeout (Unload unused server data) int 29 0 4294967295 # Maximum number of statically stored objects in a block. -max_objects_per_block (Maximum objects per block) int 256 1 65535 +max_objects_per_block (Maximum objects per block) int 256 256 65535 # Length of time between active block management cycles, stated in seconds. active_block_mgmt_interval (Active block management interval) float 2.0 0.0 @@ -2356,12 +2335,6 @@ show_technical_names (Show technical names) bool false # Controlled by a checkbox in the settings menu. show_advanced (Show advanced settings) bool false -# Enables the sound system. -# If disabled, this completely disables all sounds everywhere and the in-game -# sound controls will be non-functional. -# Changing this setting requires a restart. -enable_sound (Sound) bool true - # Key for moving the player forward. keymap_forward (Forward key) key KEY_KEY_W diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 9027c5fa1..8a10f34c4 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -53,7 +53,6 @@ centroid varying float nightRatio; varying highp vec3 eyeVec; varying float nightFactor; -#ifdef ENABLE_DYNAMIC_SHADOWS #if (MATERIAL_WAVING_LIQUID && defined(ENABLE_WATER_REFLECTIONS)) vec4 perm(vec4 x) { @@ -536,7 +535,7 @@ void main(void) col.rgb += water_reflect_color * f_shadow_factor * brightness_factor; #endif -#if (defined(ENABLE_NODE_SPECULAR) && !MATERIAL_WAVING_LIQUID) +#if (defined(ENABLE_NODE_SPECULAR) && !MATERIAL_WATER_REFLECTIONS) // Apply specular to blocks. if (dot(v_LightDirection, vNormal) < 0.0) { // This intensity is a placeholder and should be replaced by proper specular maps. diff --git a/doc/lua_api.md b/doc/lua_api.md index a4e6c7c69..eceb11937 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5044,7 +5044,7 @@ inside the VoxelManip. can use `core.emerge_area` to make sure that the area you want to read/write is already generated. -* Other mods, or the core itself, could possibly modify the area of the map +* Other mods, or the engine itself, could possibly modify the area of the map currently loaded into a VoxelManip object. With the exception of Mapgen VoxelManips (see above section), the internal buffers are not updated. For this reason, it is strongly encouraged to complete the usage of a particular @@ -5059,9 +5059,11 @@ inside the VoxelManip. Methods ------- -* `read_from_map(p1, p2)`: Loads a chunk of map into the VoxelManip object +* `read_from_map(p1, p2)`: Loads a chunk of map into the VoxelManip object containing the region formed by `p1` and `p2`. * returns actual emerged `pmin`, actual emerged `pmax` + * Note that calling this multiple times will *add* to the area loaded in the + VoxelManip, and not reset it. * `write_to_map([light])`: Writes the data loaded from the `VoxelManip` back to the map. * **important**: data must be set using `VoxelManip:set_data()` before @@ -5120,8 +5122,8 @@ Methods generated mapchunk above are propagated down into the mapchunk, defaults to `true` if left out. * `update_liquids()`: Update liquid flow -* `was_modified()`: Returns `true` if the data in the voxel manipulator has been modified - since it was last read from the map. This means you have to call `get_data` again. +* `was_modified()`: Returns `true` if the data in the VoxelManip has been modified + since it was last read from the map. This means you have to call `get_data()` again. This only applies to a `VoxelManip` object from `core.get_mapgen_object`, 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! diff --git a/games/devtest/mods/testabms/textures/testabms_after_node.png b/games/devtest/mods/testabms/textures/testabms_after_node.png index dab87594b..2a1efd53e 100644 Binary files a/games/devtest/mods/testabms/textures/testabms_after_node.png and b/games/devtest/mods/testabms/textures/testabms_after_node.png differ diff --git a/games/devtest/mods/testabms/textures/testabms_wait_node.png b/games/devtest/mods/testabms/textures/testabms_wait_node.png index a9bd9a36f..67ef96b6e 100644 Binary files a/games/devtest/mods/testabms/textures/testabms_wait_node.png and b/games/devtest/mods/testabms/textures/testabms_wait_node.png differ diff --git a/games/devtest/mods/testnodes/textures/testnodes_attachedwr_bottom.png b/games/devtest/mods/testnodes/textures/testnodes_attachedwr_bottom.png index 1a2e1e90e..9318c9454 100644 Binary files a/games/devtest/mods/testnodes/textures/testnodes_attachedwr_bottom.png and b/games/devtest/mods/testnodes/textures/testnodes_attachedwr_bottom.png differ diff --git a/games/devtest/mods/testnodes/textures/testnodes_attachedwr_side.png b/games/devtest/mods/testnodes/textures/testnodes_attachedwr_side.png index 382e2dafa..7701dc9e2 100644 Binary files a/games/devtest/mods/testnodes/textures/testnodes_attachedwr_side.png and b/games/devtest/mods/testnodes/textures/testnodes_attachedwr_side.png differ diff --git a/games/devtest/mods/testnodes/textures/testnodes_attachedwr_top.png b/games/devtest/mods/testnodes/textures/testnodes_attachedwr_top.png index 39ea67b8b..70976ca15 100644 Binary files a/games/devtest/mods/testnodes/textures/testnodes_attachedwr_top.png and b/games/devtest/mods/testnodes/textures/testnodes_attachedwr_top.png differ diff --git a/games/devtest/mods/testnodes/textures/testnodes_sign3d.png b/games/devtest/mods/testnodes/textures/testnodes_sign3d.png index e4ad9479f..d37a0de7e 100644 Binary files a/games/devtest/mods/testnodes/textures/testnodes_sign3d.png and b/games/devtest/mods/testnodes/textures/testnodes_sign3d.png differ diff --git a/games/devtest/mods/testtools/textures/testtools_particle_clip.png b/games/devtest/mods/testtools/textures/testtools_particle_clip.png index 5fb9ad09a..773e55e50 100644 Binary files a/games/devtest/mods/testtools/textures/testtools_particle_clip.png and b/games/devtest/mods/testtools/textures/testtools_particle_clip.png differ diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 724334326..a971632c9 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -12,6 +12,7 @@ unittests.list = {} -- player = false, -- Does test require a player? -- map = false, -- Does test require map access? -- async = false, -- Does the test run asynchronously? (read notes above!) +-- random = false, -- Does the test use math.random directly or indirectly? -- } function unittests.register(name, func, opts) local def = table.copy(opts or {}) @@ -47,8 +48,18 @@ local function await(invoke) return coroutine.yield() end +local function printf(fmt, ...) + print(fmt:format(...)) +end + function unittests.run_one(idx, counters, out_callback, player, pos) local def = unittests.list[idx] + local seed + if def.random then + seed = core.get_us_time() + math.randomseed(seed) + end + if not def.player then player = nil elseif player == nil then @@ -70,8 +81,10 @@ function unittests.run_one(idx, counters, out_callback, player, pos) if not status then core.log("error", err) end - print(string.format("[%s] %s - %dms", - status and "PASS" or "FAIL", def.name, ms_taken)) + printf("[%s] %s - %dms", status and "PASS" or "FAIL", def.name, ms_taken) + if seed and not status then + printf("Random was seeded to %d", seed) + end counters.time = counters.time + ms_taken counters.total = counters.total + 1 if status then @@ -160,11 +173,11 @@ function unittests.run_all() -- Print stats assert(#unittests.list == counters.total) print(string.rep("+", 80)) - print(string.format("Devtest Unit Test Results: %s", - counters.total == counters.passed and "PASSED" or "FAILED")) - print(string.format(" %d / %d failed tests.", - counters.total - counters.passed, counters.total)) - print(string.format(" Testing took %dms total.", counters.time)) + local passed = counters.total == counters.passed + printf("Devtest Unit Test Results: %s", passed and "PASSED" or "FAILED") + printf(" %d / %d failed tests.", + counters.total - counters.passed, counters.total) + printf(" Testing took %dms total.", counters.time) print(string.rep("+", 80)) unittests.on_finished(counters.total == counters.passed) return counters.total == counters.passed diff --git a/games/devtest/mods/unittests/raycast.lua b/games/devtest/mods/unittests/raycast.lua index 08d6a1120..1dc196cc5 100644 --- a/games/devtest/mods/unittests/raycast.lua +++ b/games/devtest/mods/unittests/raycast.lua @@ -34,3 +34,70 @@ local function test_raycast_pointabilities(player, pos1) end unittests.register("test_raycast_pointabilities", test_raycast_pointabilities, {map=true}) + +local function test_raycast_noskip(_, pos) + local function random_point_in_area(min, max) + local extents = max - min + local v = extents:multiply(vector.new( + math.random(), + math.random(), + math.random() + )) + return min + v + end + + -- FIXME a variation of this unit test fails in an edge case. + -- This is because Luanti does not handle perfectly diagonal raycasts correctly: + -- Perfect diagonals collide with neither "outside" face and may thus "pierce" nodes. + -- Enable the following code to reproduce: + if 0 == 1 then + pos = vector.new(6, 32, -3) + math.randomseed(1596190898) + function random_point_in_area(min, max) + return min:combine(max, math.random) + end + end + + local function cuboid_minmax(extent) + return pos:offset(-extent, -extent, -extent), + pos:offset(extent, extent, extent) + end + + -- Carve out a 3x3x3 dirt cuboid in a larger air cuboid + local r = 8 + local min, max = cuboid_minmax(r + 1) + local vm = core.get_voxel_manip(min, max) + local old_data = vm:get_data() + local data = vm:get_data() + local emin, emax = vm:get_emerged_area() + local va = VoxelArea:new({MinEdge = emin, MaxEdge = emax}) + for index in va:iterp(min, max) do + data[index] = core.CONTENT_AIR + end + for index in va:iterp(cuboid_minmax(1)) do + data[index] = core.get_content_id("basenodes:dirt") + end + vm:set_data(data) + vm:write_to_map() + + -- Raycast many times from outside the cuboid + for _ = 1, 100 do + local ray_start + repeat + ray_start = random_point_in_area(cuboid_minmax(r)) + until not ray_start:in_area(cuboid_minmax(1.501)) + -- Pick a random position inside the dirt + local ray_end = random_point_in_area(cuboid_minmax(1.499)) + -- The first pointed thing should have only air "in front" of it, + -- or a dirt node got falsely skipped. + local pt = core.raycast(ray_start, ray_end, false, false):next() + if pt then + assert(core.get_node(pt.above).name == "air") + end + end + + vm:set_data(old_data) + vm:write_to_map() +end + +unittests.register("test_raycast_noskip", test_raycast_noskip, {map = true, random = true}) diff --git a/irr/include/EDebugSceneTypes.h b/irr/include/EDebugSceneTypes.h index 29ca8ad0e..2994eca6c 100644 --- a/irr/include/EDebugSceneTypes.h +++ b/irr/include/EDebugSceneTypes.h @@ -34,7 +34,7 @@ enum E_DEBUG_SCENE_TYPE EDS_BBOX_ALL = EDS_BBOX | EDS_BBOX_BUFFERS, //! Show all debug infos - EDS_FULL = 0xffffffff + EDS_FULL = 0xffff }; } // end namespace scene diff --git a/irr/include/IGUIButton.h b/irr/include/IGUIButton.h index 8870f8b1c..fdee609d9 100644 --- a/irr/include/IGUIButton.h +++ b/irr/include/IGUIButton.h @@ -5,6 +5,7 @@ #pragma once #include "IGUIElement.h" +#include "SColor.h" namespace irr { diff --git a/irr/include/IGUIElement.h b/irr/include/IGUIElement.h index 429bc06b3..cdd3d7487 100644 --- a/irr/include/IGUIElement.h +++ b/irr/include/IGUIElement.h @@ -10,7 +10,6 @@ #include "IEventReceiver.h" #include "EGUIElementTypes.h" #include "EGUIAlignment.h" -#include "IGUIEnvironment.h" #include #include #include @@ -19,6 +18,8 @@ namespace irr { namespace gui { +class IGUIEnvironment; + //! Base class of all GUI elements. class IGUIElement : virtual public IReferenceCounted, public IEventReceiver { diff --git a/irr/include/IGUIImage.h b/irr/include/IGUIImage.h index 33453a3cd..cc3c66eb9 100644 --- a/irr/include/IGUIImage.h +++ b/irr/include/IGUIImage.h @@ -5,6 +5,7 @@ #pragma once #include "IGUIElement.h" +#include "SColor.h" namespace irr { diff --git a/irr/include/ISceneManager.h b/irr/include/ISceneManager.h index 25e1e5fe4..31604214f 100644 --- a/irr/include/ISceneManager.h +++ b/irr/include/ISceneManager.h @@ -387,6 +387,14 @@ public: pass currently is active they can render the correct part of their geometry. */ virtual E_SCENE_NODE_RENDER_PASS getSceneNodeRenderPass() const = 0; + /** + * Sets debug data flags that will be set on every rendered scene node. + * Refer to `E_DEBUG_SCENE_TYPE`. + * @param setBits bit mask of types to enable + * @param unsetBits bit mask of types to disable + */ + virtual void setGlobalDebugData(u16 setBits, u16 unsetBits) = 0; + //! Creates a new scene manager. /** This can be used to easily draw and/or store two independent scenes at the same time. The mesh cache will be diff --git a/irr/include/ISceneNode.h b/irr/include/ISceneNode.h index 0438d855f..897cfb350 100644 --- a/irr/include/ISceneNode.h +++ b/irr/include/ISceneNode.h @@ -403,14 +403,14 @@ public: their geometry because it is their only reason for existence, for example the OctreeSceneNode. \param state The culling state to be used. Check E_CULLING_TYPE for possible values.*/ - void setAutomaticCulling(u32 state) + void setAutomaticCulling(u16 state) { AutomaticCullingState = state; } //! Gets the automatic culling state. /** \return The automatic culling state. */ - u32 getAutomaticCulling() const + u16 getAutomaticCulling() const { return AutomaticCullingState; } @@ -419,7 +419,7 @@ public: /** A bitwise OR of the types from @ref irr::scene::E_DEBUG_SCENE_TYPE. Please note that not all scene nodes support all debug data types. \param state The debug data visibility state to be used. */ - virtual void setDebugDataVisible(u32 state) + virtual void setDebugDataVisible(u16 state) { DebugDataVisible = state; } @@ -427,7 +427,7 @@ public: //! Returns if debug data like bounding boxes are drawn. /** \return A bitwise OR of the debug data values from @ref irr::scene::E_DEBUG_SCENE_TYPE that are currently visible. */ - u32 isDebugDataVisible() const + u16 isDebugDataVisible() const { return DebugDataVisible; } @@ -581,10 +581,10 @@ protected: s32 ID; //! Automatic culling state - u32 AutomaticCullingState; + u16 AutomaticCullingState; //! Flag if debug data should be drawn, such as Bounding Boxes. - u32 DebugDataVisible; + u16 DebugDataVisible; //! Is the node visible? bool IsVisible; diff --git a/irr/include/SMaterial.h b/irr/include/SMaterial.h index 867557154..3bbc6e946 100644 --- a/irr/include/SMaterial.h +++ b/irr/include/SMaterial.h @@ -20,7 +20,7 @@ class ITexture; //! Flag for MaterialTypeParam (in combination with EMT_ONETEXTURE_BLEND) or for BlendFactor //! BlendFunc = source * sourceFactor + dest * destFactor -enum E_BLEND_FACTOR +enum E_BLEND_FACTOR : u8 { EBF_ZERO = 0, //!< src & dest (0, 0, 0, 0) EBF_ONE, //!< src & dest (1, 1, 1, 1) @@ -36,7 +36,7 @@ enum E_BLEND_FACTOR }; //! Values defining the blend operation -enum E_BLEND_OPERATION +enum E_BLEND_OPERATION : u8 { EBO_NONE = 0, //!< No blending happens EBO_ADD, //!< Default blending adds the color values @@ -51,7 +51,7 @@ enum E_BLEND_OPERATION }; //! MaterialTypeParam: e.g. DirectX: D3DTOP_MODULATE, D3DTOP_MODULATE2X, D3DTOP_MODULATE4X -enum E_MODULATE_FUNC +enum E_MODULATE_FUNC : u8 { EMFN_MODULATE_1X = 1, EMFN_MODULATE_2X = 2, @@ -59,7 +59,7 @@ enum E_MODULATE_FUNC }; //! Comparison function, e.g. for depth buffer test -enum E_COMPARISON_FUNC +enum E_COMPARISON_FUNC : u8 { //! Depth test disabled (disable also write to depth buffer) ECFN_DISABLED = 0, @@ -82,7 +82,7 @@ enum E_COMPARISON_FUNC }; //! Enum values for enabling/disabling color planes for rendering -enum E_COLOR_PLANE +enum E_COLOR_PLANE : u8 { //! No color enabled ECP_NONE = 0, @@ -103,7 +103,7 @@ enum E_COLOR_PLANE //! Source of the alpha value to take /** This is currently only supported in EMT_ONETEXTURE_BLEND. You can use an or'ed combination of values. Alpha values are modulated (multiplied). */ -enum E_ALPHA_SOURCE +enum E_ALPHA_SOURCE : u8 { //! Use no alpha, somewhat redundant with other settings EAS_NONE = 0, @@ -181,7 +181,7 @@ Some drivers don't support a per-material setting of the anti-aliasing modes. In those cases, FSAA/multisampling is defined by the device mode chosen upon creation via irr::SIrrCreationParameters. */ -enum E_ANTI_ALIASING_MODE +enum E_ANTI_ALIASING_MODE : u8 { //! Use to turn off anti-aliasing for this material EAAM_OFF = 0, @@ -202,7 +202,7 @@ const c8 *const PolygonOffsetDirectionNames[] = { }; //! For SMaterial.ZWriteEnable -enum E_ZWRITE +enum E_ZWRITE : u8 { //! zwrite always disabled for this material EZW_OFF = 0, @@ -240,10 +240,10 @@ public: //! Default constructor. Creates a solid material SMaterial() : MaterialType(EMT_SOLID), ColorParam(0, 0, 0, 0), - MaterialTypeParam(0.0f), Thickness(1.0f), ZBuffer(ECFN_LESSEQUAL), - AntiAliasing(EAAM_SIMPLE), ColorMask(ECP_ALL), - BlendOperation(EBO_NONE), BlendFactor(0.0f), PolygonOffsetDepthBias(0.f), - PolygonOffsetSlopeScale(0.f), Wireframe(false), PointCloud(false), + MaterialTypeParam(0.0f), Thickness(1.0f), BlendFactor(0.0f), + PolygonOffsetDepthBias(0.f), PolygonOffsetSlopeScale(0.f), + ZBuffer(ECFN_LESSEQUAL), AntiAliasing(EAAM_SIMPLE), ColorMask(ECP_ALL), + BlendOperation(EBO_NONE), Wireframe(false), PointCloud(false), ZWriteEnable(EZW_AUTO), BackfaceCulling(true), FrontfaceCulling(false), FogEnable(false), UseMipMaps(true) @@ -268,28 +268,6 @@ public: //! Thickness of non-3dimensional elements such as lines and points. f32 Thickness; - //! Is the ZBuffer enabled? Default: ECFN_LESSEQUAL - /** If you want to disable depth test for this material - just set this parameter to ECFN_DISABLED. - Values are from E_COMPARISON_FUNC. */ - u8 ZBuffer; - - //! Sets the antialiasing mode - /** Values are chosen from E_ANTI_ALIASING_MODE. Default is - EAAM_SIMPLE, i.e. simple multi-sample anti-aliasing. */ - u8 AntiAliasing; - - //! Defines the enabled color planes - /** Values are defined as or'ed values of the E_COLOR_PLANE enum. - Only enabled color planes will be rendered to the current render - target. Typical use is to disable all colors when rendering only to - depth or stencil buffer, or using Red and Green for Stereo rendering. */ - u8 ColorMask : 4; - - //! Store the blend operation of choice - /** Values to be chosen from E_BLEND_OPERATION. */ - E_BLEND_OPERATION BlendOperation : 4; - //! Store the blend factors /** textureBlendFunc/textureBlendFuncSeparate functions should be used to write properly blending factors to this parameter. @@ -304,11 +282,7 @@ public: //! A constant z-buffer offset for a polygon/line/point /** The range of the value is driver specific. - On OpenGL you get units which are multiplied by the smallest value that is guaranteed to produce a resolvable offset. - On D3D9 you can pass a range between -1 and 1. But you should likely divide it by the range of the depthbuffer. - Like dividing by 65535.0 for a 16 bit depthbuffer. Thought it still might produce too large of a bias. - Some article (https://aras-p.info/blog/2008/06/12/depth-bias-and-the-power-of-deceiving-yourself/) - recommends multiplying by 2.0*4.8e-7 (and strangely on both 16 bit and 24 bit). */ + On OpenGL you get units which are multiplied by the smallest value that is guaranteed to produce a resolvable offset. */ f32 PolygonOffsetDepthBias; //! Variable Z-Buffer offset based on the slope of the polygon. @@ -320,6 +294,25 @@ public: and -1.f to pull them towards the camera. */ f32 PolygonOffsetSlopeScale; + //! Is the ZBuffer enabled? Default: ECFN_LESSEQUAL + /** If you want to disable depth test for this material + just set this parameter to ECFN_DISABLED. */ + E_COMPARISON_FUNC ZBuffer : 4; + + //! Sets the antialiasing mode + /** Default is EAAM_SIMPLE, i.e. simple multi-sample anti-aliasing. */ + E_ANTI_ALIASING_MODE AntiAliasing : 4; + + //! Defines the enabled color planes + /** Values are defined as or'ed values of the E_COLOR_PLANE enum. + Only enabled color planes will be rendered to the current render + target. Typical use is to disable all colors when rendering only to + depth or stencil buffer, or using Red and Green for Stereo rendering. */ + E_COLOR_PLANE ColorMask : 4; + + //! Store the blend operation of choice + E_BLEND_OPERATION BlendOperation : 4; + //! Draw as wireframe or filled triangles? Default: false bool Wireframe : 1; diff --git a/irr/include/SMaterialLayer.h b/irr/include/SMaterialLayer.h index 419a8f1e9..4d43bd820 100644 --- a/irr/include/SMaterialLayer.h +++ b/irr/include/SMaterialLayer.h @@ -45,7 +45,7 @@ static const char *const aTextureClampNames[] = { //! Texture minification filter. /** Used when scaling textures down. See the documentation on OpenGL's `GL_TEXTURE_MIN_FILTER` for more information. */ -enum E_TEXTURE_MIN_FILTER +enum E_TEXTURE_MIN_FILTER : u8 { //! Aka nearest-neighbor. ETMINF_NEAREST_MIPMAP_NEAREST = 0, @@ -61,7 +61,7 @@ enum E_TEXTURE_MIN_FILTER /** Used when scaling textures up. See the documentation on OpenGL's `GL_TEXTURE_MAG_FILTER` for more information. Note that mipmaps are only used for minification, not for magnification. */ -enum E_TEXTURE_MAG_FILTER +enum E_TEXTURE_MAG_FILTER : u8 { //! Aka nearest-neighbor. ETMAGF_NEAREST = 0, diff --git a/irr/src/CAnimatedMeshSceneNode.cpp b/irr/src/CAnimatedMeshSceneNode.cpp index 3871d52a3..6facfcd06 100644 --- a/irr/src/CAnimatedMeshSceneNode.cpp +++ b/irr/src/CAnimatedMeshSceneNode.cpp @@ -253,7 +253,7 @@ void CAnimatedMeshSceneNode::render() // for debug purposes only: if (DebugDataVisible && PassCount == 1) { video::SMaterial debug_mat; - debug_mat.AntiAliasing = 0; + debug_mat.AntiAliasing = video::EAAM_OFF; driver->setMaterial(debug_mat); // show normals if (DebugDataVisible & scene::EDS_NORMALS) { @@ -276,9 +276,6 @@ void CAnimatedMeshSceneNode::render() debug_mat.ZBuffer = video::ECFN_DISABLED; driver->setMaterial(debug_mat); - if (DebugDataVisible & scene::EDS_BBOX) - driver->draw3DBox(Box, video::SColor(255, 255, 255, 255)); - // show bounding box if (DebugDataVisible & scene::EDS_BBOX_BUFFERS) { for (u32 g = 0; g < m->getMeshBufferCount(); ++g) { @@ -290,6 +287,9 @@ void CAnimatedMeshSceneNode::render() } } + if (DebugDataVisible & scene::EDS_BBOX) + driver->draw3DBox(Box, video::SColor(255, 255, 255, 255)); + // show skeleton if (DebugDataVisible & scene::EDS_SKELETON) { if (Mesh->getMeshType() == EAMT_SKINNED) { diff --git a/irr/src/CFileSystem.h b/irr/src/CFileSystem.h index 9400d85a3..d2a627e49 100644 --- a/irr/src/CFileSystem.h +++ b/irr/src/CFileSystem.h @@ -91,7 +91,7 @@ public: private: //! Currently used FileSystemType - EFileSystemType FileSystemType; + EFileSystemType FileSystemType = FILESYSTEM_NATIVE; //! WorkingDirectory for Native and Virtual filesystems io::path WorkingDirectory[2]; //! currently attached ArchiveLoaders diff --git a/irr/src/CGUIEnvironment.cpp b/irr/src/CGUIEnvironment.cpp index b40896327..31e7038f4 100644 --- a/irr/src/CGUIEnvironment.cpp +++ b/irr/src/CGUIEnvironment.cpp @@ -948,7 +948,7 @@ IGUIElement *CGUIEnvironment::getNextElement(bool reverse, bool group) // this element is not part of the tab cycle, // but its parent might be... IGUIElement *el = Focus; - while (el && el->getParent() && startOrder == -1) { + while (el->getParent() && startOrder == -1) { el = el->getParent(); startOrder = el->getTabOrder(); } diff --git a/irr/src/CMeshSceneNode.cpp b/irr/src/CMeshSceneNode.cpp index 2d9e400e9..89220cdc7 100644 --- a/irr/src/CMeshSceneNode.cpp +++ b/irr/src/CMeshSceneNode.cpp @@ -109,12 +109,10 @@ void CMeshSceneNode::render() // for debug purposes only: if (DebugDataVisible && PassCount == 1) { video::SMaterial m; - m.AntiAliasing = 0; + m.AntiAliasing = video::EAAM_OFF; + m.ZBuffer = video::ECFN_DISABLED; driver->setMaterial(m); - if (DebugDataVisible & scene::EDS_BBOX) { - driver->draw3DBox(Box, video::SColor(255, 255, 255, 255)); - } if (DebugDataVisible & scene::EDS_BBOX_BUFFERS) { for (u32 g = 0; g < Mesh->getMeshBufferCount(); ++g) { driver->draw3DBox( @@ -123,6 +121,10 @@ void CMeshSceneNode::render() } } + if (DebugDataVisible & scene::EDS_BBOX) { + driver->draw3DBox(Box, video::SColor(255, 255, 255, 255)); + } + if (DebugDataVisible & scene::EDS_NORMALS) { // draw normals const f32 debugNormalLength = 1.f; diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index 385e978b1..c87d5ae93 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -619,20 +619,17 @@ void CNullDriver::draw3DBox(const core::aabbox3d &box, SColor color) core::vector3df edges[8]; box.getEdges(edges); - // TODO: optimize into one big drawIndexPrimitive call. + video::S3DVertex v[8]; + for (u32 i = 0; i < 8; i++) { + v[i].Pos = edges[i]; + v[i].Color = color; + } - draw3DLine(edges[5], edges[1], color); - draw3DLine(edges[1], edges[3], color); - draw3DLine(edges[3], edges[7], color); - draw3DLine(edges[7], edges[5], color); - draw3DLine(edges[0], edges[2], color); - draw3DLine(edges[2], edges[6], color); - draw3DLine(edges[6], edges[4], color); - draw3DLine(edges[4], edges[0], color); - draw3DLine(edges[1], edges[0], color); - draw3DLine(edges[3], edges[2], color); - draw3DLine(edges[7], edges[6], color); - draw3DLine(edges[5], edges[4], color); + const static u16 box_indices[24] = { + 5, 1, 1, 3, 3, 7, 7, 5, 0, 2, 2, 6, 6, 4, 4, 0, 1, 0, 3, 2, 7, 6, 5, 4 + }; + + drawVertexPrimitiveList(v, 8, box_indices, 12, EVT_STANDARD, scene::EPT_LINES); } //! draws an 2d image @@ -1314,7 +1311,7 @@ void CNullDriver::runOcclusionQuery(scene::ISceneNode *node, bool visible) OcclusionQueries[index].Run = 0; if (!visible) { SMaterial mat; - mat.AntiAliasing = 0; + mat.AntiAliasing = video::EAAM_OFF; mat.ColorMask = ECP_NONE; mat.ZWriteEnable = EZW_OFF; setMaterial(mat); diff --git a/irr/src/COpenGLCoreTexture.h b/irr/src/COpenGLCoreTexture.h index d8a813d5d..2254076a7 100644 --- a/irr/src/COpenGLCoreTexture.h +++ b/irr/src/COpenGLCoreTexture.h @@ -137,6 +137,9 @@ public: Images.clear(); } + if (!name.empty()) + Driver->irrGlObjectLabel(GL_TEXTURE, TextureName, name.c_str()); + Driver->getCacheHandler()->getTextureCache().set(0, prevTexture); TEST_GL_ERROR(Driver); @@ -247,6 +250,9 @@ public: break; } + if (!name.empty()) + Driver->irrGlObjectLabel(GL_TEXTURE, TextureName, name.c_str()); + Driver->getCacheHandler()->getTextureCache().set(0, prevTexture); if (TEST_GL_ERROR(Driver)) { char msg[256]; diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index 73eb22095..e9cd10b49 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -2462,66 +2462,6 @@ void COpenGLDriver::setFog(SColor c, E_FOG_TYPE fogType, f32 start, glFogfv(GL_FOG_COLOR, data); } -//! Draws a 3d box. -void COpenGLDriver::draw3DBox(const core::aabbox3d &box, SColor color) -{ - core::vector3df edges[8]; - box.getEdges(edges); - - setRenderStates3DMode(); - - video::S3DVertex v[24]; - - for (u32 i = 0; i < 24; i++) - v[i].Color = color; - - v[0].Pos = edges[5]; - v[1].Pos = edges[1]; - v[2].Pos = edges[1]; - v[3].Pos = edges[3]; - v[4].Pos = edges[3]; - v[5].Pos = edges[7]; - v[6].Pos = edges[7]; - v[7].Pos = edges[5]; - v[8].Pos = edges[0]; - v[9].Pos = edges[2]; - v[10].Pos = edges[2]; - v[11].Pos = edges[6]; - v[12].Pos = edges[6]; - v[13].Pos = edges[4]; - v[14].Pos = edges[4]; - v[15].Pos = edges[0]; - v[16].Pos = edges[1]; - v[17].Pos = edges[0]; - v[18].Pos = edges[3]; - v[19].Pos = edges[2]; - v[20].Pos = edges[7]; - v[21].Pos = edges[6]; - v[22].Pos = edges[5]; - v[23].Pos = edges[4]; - - if (!FeatureAvailable[IRR_ARB_vertex_array_bgra] && !FeatureAvailable[IRR_EXT_vertex_array_bgra]) - getColorBuffer(v, 24, EVT_STANDARD); - - CacheHandler->setClientState(true, false, true, false); - - glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex), &(static_cast(v))[0].Pos); - -#ifdef GL_BGRA - const GLint colorSize = (FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra]) ? GL_BGRA : 4; -#else - const GLint colorSize = 4; -#endif - if (FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra]) - glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex), &(static_cast(v))[0].Color); - else { - _IRR_DEBUG_BREAK_IF(ColorBuffer.size() == 0); - glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]); - } - - glDrawArrays(GL_LINES, 0, 24); -} - //! Draws a 3d line. void COpenGLDriver::draw3DLine(const core::vector3df &start, const core::vector3df &end, SColor color) diff --git a/irr/src/COpenGLDriver.h b/irr/src/COpenGLDriver.h index 0fdd15d16..1b5c0c6d3 100644 --- a/irr/src/COpenGLDriver.h +++ b/irr/src/COpenGLDriver.h @@ -170,9 +170,6 @@ public: const core::position2d &end, SColor color = SColor(255, 255, 255, 255)) override; - //! Draws a 3d box - void draw3DBox(const core::aabbox3d &box, SColor color = SColor(255, 255, 255, 255)) override; - //! Draws a 3d line. virtual void draw3DLine(const core::vector3df &start, const core::vector3df &end, diff --git a/irr/src/COpenGLExtensionHandler.h b/irr/src/COpenGLExtensionHandler.h index a1754a328..63c55f26c 100644 --- a/irr/src/COpenGLExtensionHandler.h +++ b/irr/src/COpenGLExtensionHandler.h @@ -1065,6 +1065,10 @@ public: void irrGlCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data); + inline void irrGlObjectLabel(GLenum identifier, GLuint name, const char *label) + { + // unimplemented + } // shader programming void extGlGenPrograms(GLsizei n, GLuint *programs); diff --git a/irr/src/CSceneManager.cpp b/irr/src/CSceneManager.cpp index d8a147b34..23e5335ce 100644 --- a/irr/src/CSceneManager.cpp +++ b/irr/src/CSceneManager.cpp @@ -490,13 +490,19 @@ void CSceneManager::drawAll() // let all nodes register themselves OnRegisterSceneNode(); + const auto &render_node = [this] (ISceneNode *node) { + u32 flags = node->isDebugDataVisible(); + node->setDebugDataVisible((flags & DebugDataMask) | DebugDataBits); + node->render(); + }; + // render camera scenes { CurrentRenderPass = ESNRP_CAMERA; Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); for (auto *node : CameraList) - node->render(); + render_node(node); CameraList.clear(); } @@ -507,7 +513,7 @@ void CSceneManager::drawAll() Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); for (auto *node : SkyBoxList) - node->render(); + render_node(node); SkyBoxList.clear(); } @@ -520,7 +526,7 @@ void CSceneManager::drawAll() std::sort(SolidNodeList.begin(), SolidNodeList.end()); for (auto &it : SolidNodeList) - it.Node->render(); + render_node(it.Node); SolidNodeList.clear(); } @@ -533,7 +539,7 @@ void CSceneManager::drawAll() std::sort(TransparentNodeList.begin(), TransparentNodeList.end()); for (auto &it : TransparentNodeList) - it.Node->render(); + render_node(it.Node); TransparentNodeList.clear(); } @@ -546,7 +552,7 @@ void CSceneManager::drawAll() std::sort(TransparentEffectNodeList.begin(), TransparentEffectNodeList.end()); for (auto &it : TransparentEffectNodeList) - it.Node->render(); + render_node(it.Node); TransparentEffectNodeList.clear(); } @@ -557,7 +563,7 @@ void CSceneManager::drawAll() Driver->getOverrideMaterial().Enabled = ((Driver->getOverrideMaterial().EnablePasses & CurrentRenderPass) != 0); for (auto *node : GuiNodeList) - node->render(); + render_node(node); GuiNodeList.clear(); } diff --git a/irr/src/CSceneManager.h b/irr/src/CSceneManager.h index 2a4fb7f7b..8f03a1fec 100644 --- a/irr/src/CSceneManager.h +++ b/irr/src/CSceneManager.h @@ -179,6 +179,11 @@ public: //! Set current render time. void setCurrentRenderPass(E_SCENE_NODE_RENDER_PASS nextPass) override { CurrentRenderPass = nextPass; } + void setGlobalDebugData(u16 setBits, u16 unsetBits) override { + DebugDataMask = ~unsetBits; + DebugDataBits = setBits; + } + //! returns if node is culled bool isCulled(const ISceneNode *node) const override; @@ -268,6 +273,9 @@ private: //! Mesh cache IMeshCache *MeshCache; + //! Global debug render state + u16 DebugDataMask = 0, DebugDataBits = 0; + E_SCENE_NODE_RENDER_PASS CurrentRenderPass; }; diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index c33b3c9d9..25c9a14f6 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -164,13 +164,6 @@ COpenGL3DriverBase::COpenGL3DriverBase(const SIrrlichtCreationParameters ¶ms ExposedData = ContextManager->getContext(); ContextManager->activateContext(ExposedData, false); GL.LoadAllProcedures(ContextManager); - if (EnableErrorTest && GL.IsExtensionPresent("GL_KHR_debug")) { - GL.Enable(GL_DEBUG_OUTPUT); - GL.DebugMessageCallback(debugCb, this); - } else if (EnableErrorTest) { - os::Printer::log("GL debug extension not available"); - } - initQuadsIndices(); TEST_GL_ERROR(this); } @@ -248,6 +241,20 @@ bool COpenGL3DriverBase::genericDriverInit(const core::dimension2d &screenS initFeatures(); printTextureFormats(); + if (EnableErrorTest) { + if (KHRDebugSupported) { + GL.Enable(GL_DEBUG_OUTPUT); + GL.DebugMessageCallback(debugCb, this); + } else { + os::Printer::log("GL debug extension not available"); + } + } else { + // don't do debug things if they are not wanted (even if supported) + KHRDebugSupported = false; + } + + initQuadsIndices(); + // reset cache handler delete CacheHandler; CacheHandler = new COpenGL3CacheHandler(this); @@ -1025,9 +1032,7 @@ void COpenGL3DriverBase::drawGeneric(const void *vertices, const void *indexList GL.DrawElements(GL_TRIANGLE_FAN, primitiveCount + 2, indexSize, indexList); break; case scene::EPT_TRIANGLES: - GL.DrawElements((LastMaterial.Wireframe) ? GL_LINES : (LastMaterial.PointCloud) ? GL_POINTS - : GL_TRIANGLES, - primitiveCount * 3, indexSize, indexList); + GL.DrawElements(GL_TRIANGLES, primitiveCount * 3, indexSize, indexList); break; default: break; @@ -1306,7 +1311,28 @@ void COpenGL3DriverBase::setBasicRenderStates(const SMaterial &material, const S getGLBlend(srcAlphaFact), getGLBlend(dstAlphaFact)); } - // TODO: Polygon Offset. Not sure if it was left out deliberately or if it won't work with this driver. + // fillmode + if (Version.Spec != OpenGLSpec::ES && // not supported in gles + (resetAllRenderStates || + lastmaterial.Wireframe != material.Wireframe || + lastmaterial.PointCloud != material.PointCloud)) { + GL.PolygonMode(GL_FRONT_AND_BACK, + material.Wireframe ? GL_LINE : + material.PointCloud ? GL_POINT : + GL_FILL); + } + + // Polygon Offset + if (resetAllRenderStates || + lastmaterial.PolygonOffsetDepthBias != material.PolygonOffsetDepthBias || + lastmaterial.PolygonOffsetSlopeScale != material.PolygonOffsetSlopeScale) { + if (material.PolygonOffsetDepthBias || material.PolygonOffsetSlopeScale) { + GL.Enable(GL.POLYGON_OFFSET_FILL); + GL.PolygonOffset(material.PolygonOffsetSlopeScale, material.PolygonOffsetDepthBias); + } else { + GL.Disable(GL.POLYGON_OFFSET_FILL); + } + } if (resetAllRenderStates || lastmaterial.Thickness != material.Thickness) GL.LineWidth(core::clamp(static_cast(material.Thickness), DimAliasedLine[0], DimAliasedLine[1])); @@ -1615,7 +1641,7 @@ s32 COpenGL3DriverBase::addHighLevelShaderMaterial( s32 nr = -1; COpenGL3MaterialRenderer *r = new COpenGL3MaterialRenderer( this, nr, vertexShaderProgram, - pixelShaderProgram, + pixelShaderProgram, shaderName, callback, baseMaterial, userData); r->drop(); diff --git a/irr/src/OpenGL/ExtensionHandler.h b/irr/src/OpenGL/ExtensionHandler.h index 0e3a0af2d..209fd415b 100644 --- a/irr/src/OpenGL/ExtensionHandler.h +++ b/irr/src/OpenGL/ExtensionHandler.h @@ -161,10 +161,23 @@ public: GL.BlendEquation(mode); } + inline void irrGlObjectLabel(GLenum identifier, GLuint name, const char *label) + { + if (KHRDebugSupported) { + u32 len = strlen(label); + // Since our texture strings can get quite long we also truncate + // to a hardcoded limit of 82 + len = std::min(len, std::min(MaxLabelLength, 82U)); + GL.ObjectLabel(identifier, name, len, label); + } + } + bool LODBiasSupported = false; bool AnisotropicFilterSupported = false; bool BlendMinMaxSupported = false; bool TextureMultisampleSupported = false; + bool KHRDebugSupported = false; + u32 MaxLabelLength = 0; }; } diff --git a/irr/src/OpenGL/MaterialRenderer.cpp b/irr/src/OpenGL/MaterialRenderer.cpp index 7439dba61..269f28ae9 100644 --- a/irr/src/OpenGL/MaterialRenderer.cpp +++ b/irr/src/OpenGL/MaterialRenderer.cpp @@ -24,6 +24,7 @@ COpenGL3MaterialRenderer::COpenGL3MaterialRenderer(COpenGL3DriverBase *driver, s32 &outMaterialTypeNr, const c8 *vertexShaderProgram, const c8 *pixelShaderProgram, + const c8 *debugName, IShaderConstantSetCallBack *callback, E_MATERIAL_TYPE baseMaterial, s32 userData) : @@ -45,7 +46,7 @@ COpenGL3MaterialRenderer::COpenGL3MaterialRenderer(COpenGL3DriverBase *driver, if (CallBack) CallBack->grab(); - init(outMaterialTypeNr, vertexShaderProgram, pixelShaderProgram); + init(outMaterialTypeNr, vertexShaderProgram, pixelShaderProgram, debugName); } COpenGL3MaterialRenderer::COpenGL3MaterialRenderer(COpenGL3DriverBase *driver, @@ -98,6 +99,7 @@ GLuint COpenGL3MaterialRenderer::getProgram() const void COpenGL3MaterialRenderer::init(s32 &outMaterialTypeNr, const c8 *vertexShaderProgram, const c8 *pixelShaderProgram, + const c8 *debugName, bool addMaterial) { outMaterialTypeNr = -1; @@ -121,6 +123,9 @@ void COpenGL3MaterialRenderer::init(s32 &outMaterialTypeNr, if (!linkProgram()) return; + if (debugName) + Driver->irrGlObjectLabel(GL_PROGRAM, Program, debugName); + if (addMaterial) outMaterialTypeNr = Driver->addMaterialRenderer(this); } diff --git a/irr/src/OpenGL/MaterialRenderer.h b/irr/src/OpenGL/MaterialRenderer.h index fca71478a..0916f1ad5 100644 --- a/irr/src/OpenGL/MaterialRenderer.h +++ b/irr/src/OpenGL/MaterialRenderer.h @@ -28,6 +28,7 @@ public: s32 &outMaterialTypeNr, const c8 *vertexShaderProgram = 0, const c8 *pixelShaderProgram = 0, + const c8 *debugName = nullptr, IShaderConstantSetCallBack *callback = 0, E_MATERIAL_TYPE baseMaterial = EMT_SOLID, s32 userData = 0); @@ -66,7 +67,9 @@ protected: E_MATERIAL_TYPE baseMaterial = EMT_SOLID, s32 userData = 0); - void init(s32 &outMaterialTypeNr, const c8 *vertexShaderProgram, const c8 *pixelShaderProgram, bool addMaterial = true); + void init(s32 &outMaterialTypeNr, const c8 *vertexShaderProgram, + const c8 *pixelShaderProgram, const c8 *debugName = nullptr, + bool addMaterial = true); bool createShader(GLenum shaderType, const char *shader); bool linkProgram(); diff --git a/irr/src/OpenGL/Renderer2D.cpp b/irr/src/OpenGL/Renderer2D.cpp index b7b8edf1b..ec53cc9f9 100644 --- a/irr/src/OpenGL/Renderer2D.cpp +++ b/irr/src/OpenGL/Renderer2D.cpp @@ -23,8 +23,8 @@ COpenGL3Renderer2D::COpenGL3Renderer2D(const c8 *vertexShaderProgram, const c8 * WithTexture(withTexture) { int Temp = 0; - - init(Temp, vertexShaderProgram, pixelShaderProgram, false); + init(Temp, vertexShaderProgram, pixelShaderProgram, + withTexture ? "2DTexture" : "2DNoTexture", false); COpenGL3CacheHandler *cacheHandler = Driver->getCacheHandler(); diff --git a/irr/src/OpenGL3/Driver.cpp b/irr/src/OpenGL3/Driver.cpp index 767ce5992..7a62f4a12 100644 --- a/irr/src/OpenGL3/Driver.cpp +++ b/irr/src/OpenGL3/Driver.cpp @@ -72,6 +72,9 @@ void COpenGL3Driver::initFeatures() LODBiasSupported = true; BlendMinMaxSupported = true; TextureMultisampleSupported = true; + KHRDebugSupported = isVersionAtLeast(4, 6) || queryExtension("GL_KHR_debug"); + if (KHRDebugSupported) + MaxLabelLength = GetInteger(GL.MAX_LABEL_LENGTH); // COGLESCoreExtensionHandler::Feature static_assert(MATERIAL_MAX_TEXTURES <= 16, "Only up to 16 textures are guaranteed"); diff --git a/irr/src/OpenGLES2/Driver.cpp b/irr/src/OpenGLES2/Driver.cpp index 25c4f485d..6b234842a 100644 --- a/irr/src/OpenGLES2/Driver.cpp +++ b/irr/src/OpenGLES2/Driver.cpp @@ -124,6 +124,9 @@ void COpenGLES2Driver::initFeatures() AnisotropicFilterSupported = queryExtension("GL_EXT_texture_filter_anisotropic"); BlendMinMaxSupported = (Version.Major >= 3) || FeatureAvailable[IRR_GL_EXT_blend_minmax]; TextureMultisampleSupported = isVersionAtLeast(3, 1); + KHRDebugSupported = queryExtension("GL_KHR_debug"); + if (KHRDebugSupported) + MaxLabelLength = GetInteger(GL.MAX_LABEL_LENGTH); // COGLESCoreExtensionHandler::Feature static_assert(MATERIAL_MAX_TEXTURES <= 8, "Only up to 8 textures are guaranteed"); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 692651049..88c0c5a45 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -417,7 +417,6 @@ set(independent_SRCS hud.cpp inventory.cpp itemstackmetadata.cpp - lighting.cpp log.cpp metadata.cpp modchannels.cpp diff --git a/src/activeobjectmgr.h b/src/activeobjectmgr.h index 943147160..a9b007018 100644 --- a/src/activeobjectmgr.h +++ b/src/activeobjectmgr.h @@ -5,7 +5,6 @@ #pragma once #include -#include "debug.h" #include "util/container.h" #include "irrlichttypes.h" #include "util/basic_macros.h" diff --git a/src/chat.h b/src/chat.h index d328732c3..97b391ccb 100644 --- a/src/chat.h +++ b/src/chat.h @@ -112,7 +112,8 @@ public: void resize(u32 scrollback); -protected: + // Get the current scroll position + s32 getScrollPosition() const { return m_scroll; } s32 getTopScrollPos() const; s32 getBottomScrollPos() const; diff --git a/src/chat_interface.h b/src/chat_interface.h index 1276c3a23..6ee1451db 100644 --- a/src/chat_interface.h +++ b/src/chat_interface.h @@ -4,10 +4,9 @@ #pragma once -#include "util/container.h" -#include -#include #include "irrlichttypes.h" +#include "util/container.h" // MutexedQueue +#include enum ChatEventType { CET_CHAT, diff --git a/src/client/client.cpp b/src/client/client.cpp index 222c1a2ac..b709f8cbf 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -37,6 +37,7 @@ #include "profiler.h" #include "shader.h" #include "gettext.h" +#include "gettime.h" #include "clientdynamicinfo.h" #include "clientmap.h" #include "clientmedia.h" diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index b68edb790..725ff4323 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -91,8 +91,7 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) init_args(start_data, cmd_args); #if USE_SOUND - if (g_settings->getBool("enable_sound")) - g_sound_manager_singleton = createSoundManagerSingleton(); + g_sound_manager_singleton = createSoundManagerSingleton(); #endif if (!init_engine()) diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index 55e89410d..b65970f95 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -445,8 +445,8 @@ void Clouds::readSettings() // chosen to avoid exactly that. // refer to vertex_count in updateMesh() m_enable_3d = g_settings->getBool("enable_3d_clouds"); - const u16 maximum = m_enable_3d ? 62 : 25; - m_cloud_radius_i = rangelim(g_settings->getU16("cloud_radius"), 1, maximum); + const u16 maximum = !m_enable_3d ? 62 : 25; + m_cloud_radius_i = rangelim(g_settings->getU16("cloud_radius"), 8, maximum); invalidateMesh(); } diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index d90d4e8b0..5307047c4 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -656,25 +656,33 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) } } else if (m_prop.visual == "upright_sprite") { grabMatrixNode(); - scene::SMesh *mesh = new scene::SMesh(); - double dx = BS * m_prop.visual_size.X / 2; - double dy = BS * m_prop.visual_size.Y / 2; + auto mesh = make_irr(); + f32 dx = BS * m_prop.visual_size.X / 2; + f32 dy = BS * m_prop.visual_size.Y / 2; video::SColor c(0xFFFFFFFF); - { // Front - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - video::S3DVertex vertices[4] = { - video::S3DVertex(-dx, -dy, 0, 0,0,1, c, 1,1), - video::S3DVertex( dx, -dy, 0, 0,0,1, c, 0,1), - video::S3DVertex( dx, dy, 0, 0,0,1, c, 0,0), - video::S3DVertex(-dx, dy, 0, 0,0,1, c, 1,0), - }; - if (m_is_player) { - // Move minimal Y position to 0 (feet position) - for (video::S3DVertex &vertex : vertices) - vertex.Pos.Y += dy; + video::S3DVertex vertices[4] = { + video::S3DVertex(-dx, -dy, 0, 0,0,1, c, 1,1), + video::S3DVertex( dx, -dy, 0, 0,0,1, c, 0,1), + video::S3DVertex( dx, dy, 0, 0,0,1, c, 0,0), + video::S3DVertex(-dx, dy, 0, 0,0,1, c, 1,0), + }; + if (m_is_player) { + // Move minimal Y position to 0 (feet position) + for (auto &vertex : vertices) + vertex.Pos.Y += dy; + } + const u16 indices[] = {0,1,2,2,3,0}; + + for (int face : {0, 1}) { + auto buf = make_irr(); + // Front (0) or Back (1) + if (face == 1) { + for (auto &v : vertices) + v.Normal *= -1; + for (int i : {0, 2}) + std::swap(vertices[i].Pos, vertices[i+1].Pos); } - u16 indices[] = {0,1,2,2,3,0}; buf->append(vertices, 4, indices, 6); // Set material @@ -682,36 +690,12 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) buf->getMaterial().ColorParam = c; // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); + mesh->addMeshBuffer(buf.get()); } - { // Back - scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - video::S3DVertex vertices[4] = { - video::S3DVertex( dx,-dy, 0, 0,0,-1, c, 1,1), - video::S3DVertex(-dx,-dy, 0, 0,0,-1, c, 0,1), - video::S3DVertex(-dx, dy, 0, 0,0,-1, c, 0,0), - video::S3DVertex( dx, dy, 0, 0,0,-1, c, 1,0), - }; - if (m_is_player) { - // Move minimal Y position to 0 (feet position) - for (video::S3DVertex &vertex : vertices) - vertex.Pos.Y += dy; - } - u16 indices[] = {0,1,2,2,3,0}; - buf->append(vertices, 4, indices, 6); - // Set material - setMaterial(buf->getMaterial()); - buf->getMaterial().ColorParam = c; - - // Add to mesh - mesh->addMeshBuffer(buf); - buf->drop(); - } - m_meshnode = m_smgr->addMeshSceneNode(mesh, m_matrixnode); + mesh->recalculateBoundingBox(); + m_meshnode = m_smgr->addMeshSceneNode(mesh.get(), m_matrixnode); m_meshnode->grab(); - mesh->drop(); } else if (m_prop.visual == "cube") { grabMatrixNode(); scene::IMesh *mesh = createCubeMesh(v3f(BS,BS,BS)); diff --git a/src/client/content_cao.h b/src/client/content_cao.h index 1115b6819..a6b9beeab 100644 --- a/src/client/content_cao.h +++ b/src/client/content_cao.h @@ -4,15 +4,23 @@ #pragma once -#include -#include "irrlichttypes_extrabloated.h" -#include "clientobject.h" +#include "EMaterialTypes.h" +#include "IDummyTransformationSceneNode.h" +#include "irrlichttypes.h" + #include "object_properties.h" -#include "itemgroup.h" +#include "clientobject.h" #include "constants.h" +#include "itemgroup.h" #include +#include #include +namespace irr::scene { + class IMeshSceneNode; + class IBillboardSceneNode; +} + class Camera; class Client; struct Nametag; diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 4d1eeec47..d8f1ff3b6 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -62,46 +62,44 @@ const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_railli MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output): data(input), collector(output), - nodedef(data->nodedef), - blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE), - smooth_liquids(g_settings->getBool("enable_water_reflections")) + nodedef(data->m_nodedef), + blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE) { } -void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, bool special) +void MapblockMeshGenerator::useTile(TileSpec *tile_ret, int index, u8 set_flags, + u8 reset_flags, bool special) { if (special) - getSpecialTile(index, &cur_node.tile, cur_node.p == data->m_crack_pos_relative); + getSpecialTile(index, tile_ret, cur_node.p == data->m_crack_pos_relative); else - getTile(index, &cur_node.tile); - if (!data->m_smooth_lighting) - cur_node.color = encode_light(cur_node.light, cur_node.f->light_source); + getTile(index, tile_ret); - for (auto &layer : cur_node.tile.layers) { + for (auto &layer : tile_ret->layers) { layer.material_flags |= set_flags; layer.material_flags &= ~reset_flags; } } // Returns a tile, ready for use, non-rotated. -void MapblockMeshGenerator::getTile(int index, TileSpec *tile) +void MapblockMeshGenerator::getTile(int index, TileSpec *tile_ret) { - getNodeTileN(cur_node.n, cur_node.p, index, data, *tile); + getNodeTileN(cur_node.n, cur_node.p, index, data, *tile_ret); } // Returns a tile, ready for use, rotated according to the node facedir. -void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile) +void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile_ret) { - getNodeTile(cur_node.n, cur_node.p, direction, data, *tile); + getNodeTile(cur_node.n, cur_node.p, direction, data, *tile_ret); } // Returns a special tile, ready for use, non-rotated. -void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack) +void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile_ret, bool apply_crack) { - *tile = cur_node.f->special_tiles[index]; + *tile_ret = cur_node.f->special_tiles[index]; TileLayer *top_layer = nullptr; - for (auto &layernum : tile->layers) { + for (auto &layernum : tile_ret->layers) { TileLayer *layer = &layernum; if (layer->texture_id == 0) continue; @@ -114,7 +112,7 @@ void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply top_layer->material_flags |= MATERIAL_FLAG_CRACK; } -void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal, +void MapblockMeshGenerator::drawQuad(const TileSpec &tile, v3f *coords, const v3s16 &normal, float vertical_tiling) { const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0), @@ -128,15 +126,17 @@ void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal, if (data->m_smooth_lighting) vertices[j].Color = blendLightColor(coords[j]); else - vertices[j].Color = cur_node.color; + vertices[j].Color = cur_node.lcolor; if (shade_face) applyFacesShading(vertices[j].Color, normal2); vertices[j].TCoords = tcoords[j]; } - collector->append(cur_node.tile, vertices, 4, quad_indices, 6); + collector->append(tile, vertices, 4, quad_indices, 6); } -static std::array setupCuboidVertices(const aabb3f &box, const f32 *txc, TileSpec *tiles, int tilecount) { +static std::array setupCuboidVertices(const aabb3f &box, + const f32 *txc, const TileSpec *tiles, int tilecount) +{ v3f min = box.MinEdge; v3f max = box.MaxEdge; @@ -218,7 +218,7 @@ enum class QuadDiagonal { // and to choose diagonal to split the quad at. template void MapblockMeshGenerator::drawCuboid(const aabb3f &box, - TileSpec *tiles, int tilecount, const f32 *txc, u8 mask, Fn &&face_lighter) + const TileSpec *tiles, int tilecount, const f32 *txc, u8 mask, Fn &&face_lighter) { assert(tilecount >= 1 && tilecount <= 6); // pre-condition @@ -238,16 +238,16 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box, void MapblockMeshGenerator::getSmoothLightFrame() { for (int k = 0; k < 8; ++k) - cur_node.frame.sunlight[k] = false; + cur_node.lframe.sunlight[k] = false; for (int k = 0; k < 8; ++k) { LightPair light(getSmoothLightTransparent(blockpos_nodes + cur_node.p, light_dirs[k], data)); - cur_node.frame.lightsDay[k] = light.lightDay; - cur_node.frame.lightsNight[k] = light.lightNight; + cur_node.lframe.lightsDay[k] = light.lightDay; + cur_node.lframe.lightsNight[k] = light.lightNight; // If there is direct sunlight and no ambient occlusion at some corner, // mark the vertical edge (top and bottom corners) containing it. if (light.lightDay == 255) { - cur_node.frame.sunlight[k] = true; - cur_node.frame.sunlight[k ^ 2] = true; + cur_node.lframe.sunlight[k] = true; + cur_node.lframe.sunlight[k ^ 2] = true; } } } @@ -270,9 +270,9 @@ LightInfo MapblockMeshGenerator::blendLight(const v3f &vertex_pos) f32 dy = (k & 2) ? y : 1 - y; f32 dz = (k & 1) ? z : 1 - z; // Use direct sunlight (255), if any; use daylight otherwise. - f32 light_boosted = cur_node.frame.sunlight[k] ? 255 : cur_node.frame.lightsDay[k]; - lightDay += dx * dy * dz * cur_node.frame.lightsDay[k]; - lightNight += dx * dy * dz * cur_node.frame.lightsNight[k]; + f32 light_boosted = cur_node.lframe.sunlight[k] ? 255 : cur_node.lframe.lightsDay[k]; + lightDay += dx * dy * dz * cur_node.lframe.lightsDay[k]; + lightNight += dx * dy * dz * cur_node.lframe.lightsNight[k]; lightBoosted += dx * dy * dz * light_boosted; } return LightInfo{lightDay, lightNight, lightBoosted}; @@ -280,7 +280,6 @@ LightInfo MapblockMeshGenerator::blendLight(const v3f &vertex_pos) // Calculates vertex color to be used in mapblock mesh // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so) -// tile_color - node's tile color video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos) { LightInfo light = blendLight(vertex_pos); @@ -323,8 +322,14 @@ static inline int lightDiff(LightPair a, LightPair b) return abs(a.lightDay - b.lightDay) + abs(a.lightNight - b.lightNight); } -void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc, - TileSpec *tiles, int tile_count, u8 mask) +void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const TileSpec &tile, + const f32 *txc, u8 mask) +{ + drawAutoLightedCuboid(box, &tile, 1, txc, mask); +} + +void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, + const TileSpec *tiles, int tile_count, const f32 *txc, u8 mask) { bool scale = std::fabs(cur_node.f->visual_scale - 1.0f) > 1e-3f; f32 texture_coord_buf[24]; @@ -348,10 +353,6 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc, generateCuboidTextureCoords(box, texture_coord_buf); txc = texture_coord_buf; } - if (!tiles) { - tiles = &cur_node.tile; - tile_count = 1; - } if (data->m_smooth_lighting) { LightInfo lights[8]; for (int j = 0; j < 8; ++j) { @@ -377,7 +378,7 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc, }); } else { drawCuboid(box, tiles, tile_count, txc, mask, [&] (int face, video::S3DVertex vertices[4]) { - video::SColor color = encode_light(cur_node.light, cur_node.f->light_source); + video::SColor color = cur_node.lcolor; if (!cur_node.f->light_source) applyFacesShading(color, vertices[0].Normal); for (int j = 0; j < 4; j++) { @@ -540,19 +541,20 @@ void MapblockMeshGenerator::prepareLiquidNodeDrawing() if (data->m_smooth_lighting) return; // don't need to pre-compute anything in this case + auto light = LightPair(getInteriorLight(cur_node.n, 0, nodedef)); if (cur_node.f->light_source != 0) { // If this liquid emits light and doesn't contain light, draw // it at what it emits, for an increased effect u8 e = decode_light(cur_node.f->light_source); - cur_node.light = LightPair(std::max(e, cur_node.light.lightDay), - std::max(e, cur_node.light.lightNight)); + light = LightPair(std::max(e, light.lightDay), + std::max(e, light.lightNight)); } else if (nodedef->getLightingFlags(ntop).has_light) { // Otherwise, use the light of the node on top if possible - cur_node.light = LightPair(getInteriorLight(ntop, 0, nodedef)); + light = LightPair(getInteriorLight(ntop, 0, nodedef)); } - cur_liquid.color_top = encode_light(cur_node.light, cur_node.f->light_source); - cur_node.color = encode_light(cur_node.light, cur_node.f->light_source); + cur_liquid.color_top = encode_light(light, cur_node.f->light_source); + cur_node.lcolor = encode_light(light, cur_node.f->light_source); } void MapblockMeshGenerator::getLiquidNeighborhood() @@ -695,10 +697,18 @@ void MapblockMeshGenerator::drawLiquidSides() v += 0.5f - cur_liquid.corner_levels[base.Z][base.X]; } + video::SColor color; if (data->m_smooth_lighting) - cur_node.color = blendLightColor(pos); + color = blendLightColor(pos); + else + color = cur_node.lcolor; + pos += cur_node.origin; - vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, face.dir.X, face.dir.Y, face.dir.Z, cur_node.color, vertex.u, v); + + vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, + face.dir.X, face.dir.Y, face.dir.Z, + color, + vertex.u, v); }; collector->append(cur_liquid.tile, vertices, 4, quad_indices, 6); } @@ -722,7 +732,7 @@ void MapblockMeshGenerator::drawLiquidTop() int u = corner_resolve[i][0]; int w = corner_resolve[i][1]; - if (smooth_liquids) { + if (data->m_enable_water_reflections) { int x = vertices[i].Pos.X > 0; int z = vertices[i].Pos.Z > 0; @@ -774,7 +784,7 @@ void MapblockMeshGenerator::drawLiquidTop() vertex.TCoords += tcoord_translate; - if (!smooth_liquids) { + if (!data->m_enable_water_reflections) { vertex.Normal = v3f(dx, 1., dz).normalize(); } } @@ -816,7 +826,8 @@ void MapblockMeshGenerator::drawLiquidNode() void MapblockMeshGenerator::drawGlasslikeNode() { - useTile(0, 0, 0); + TileSpec tile; + useTile(&tile, 0, 0, 0); for (int face = 0; face < 6; face++) { // Check this neighbor @@ -850,7 +861,7 @@ void MapblockMeshGenerator::drawGlasslikeNode() vertex.rotateXZBy(-90); break; } } - drawQuad(vertices, dir); + drawQuad(tile, vertices, dir); } } @@ -860,9 +871,6 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() for (int face = 0; face < 6; face++) getTile(g_6dirs[face], &tiles[face]); - if (!data->m_smooth_lighting) - cur_node.color = encode_light(cur_node.light, cur_node.f->light_source); - TileSpec glass_tiles[6]; for (auto &glass_tile : glass_tiles) glass_tile = tiles[4]; @@ -934,7 +942,6 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9}, }; - cur_node.tile = tiles[1]; for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) { bool edge_invisible; if (nb[nb_triplet[edge][2]]) @@ -943,14 +950,13 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]]; if (edge_invisible) continue; - drawAutoLightedCuboid(frame_edges[edge]); + drawAutoLightedCuboid(frame_edges[edge], tiles[1]); } for (int face = 0; face < 6; face++) { if (nb[face]) continue; - cur_node.tile = glass_tiles[face]; // Face at Z- v3f vertices[4] = { v3f(-a, a, -g), @@ -976,7 +982,7 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() } } v3s16 dir = g_6dirs[face]; - drawQuad(vertices, dir); + drawQuad(glass_tiles[face], vertices, dir); } // Optionally render internal liquid level defined by param2 @@ -986,13 +992,18 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() // Internal liquid level has param2 range 0 .. 63, // convert it to -0.5 .. 0.5 float vlev = (param2 / 63.0f) * 2.0f - 1.0f; - getSpecialTile(0, &cur_node.tile); - drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b), - -(nb[4] ? g : b), - -(nb[3] ? g : b), - (nb[2] ? g : b), - (nb[1] ? g : b) * vlev, - (nb[0] ? g : b))); + TileSpec tile; + getSpecialTile(0, &tile); + drawAutoLightedCuboid( + aabb3f( + -(nb[5] ? g : b), + -(nb[4] ? g : b), + -(nb[3] ? g : b), + (nb[2] ? g : b), + (nb[1] ? g : b) * vlev, + (nb[0] ? g : b) + ), + tile); } } @@ -1007,7 +1018,8 @@ void MapblockMeshGenerator::drawTorchlikeNode() case DWM_S2: tileindex = 0; break; // floor, but rotated default: tileindex = 2; // side (or invalid, shouldn't happen) } - useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING); + TileSpec tile; + useTile(&tile, tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING); float size = BS / 2 * cur_node.f->visual_scale; v3f vertices[4] = { @@ -1054,13 +1066,14 @@ void MapblockMeshGenerator::drawTorchlikeNode() break; } } - drawQuad(vertices); + drawQuad(tile, vertices); } void MapblockMeshGenerator::drawSignlikeNode() { u8 wall = cur_node.n.getWallMounted(nodedef); - useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING); + TileSpec tile; + useTile(&tile, 0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING); static const float offset = BS / 16; float size = BS / 2 * cur_node.f->visual_scale; // Wall at X+ of node @@ -1091,13 +1104,13 @@ void MapblockMeshGenerator::drawSignlikeNode() vertex.rotateXYBy(-90); vertex.rotateXZBy(-90); break; } } - drawQuad(vertices); + drawQuad(tile, vertices); } -void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset, - bool offset_top_only) +void MapblockMeshGenerator::drawPlantlikeQuad(const TileSpec &tile, + float rotation, float quad_offset, bool offset_top_only) { - const f32 scale = cur_node.scale; + const f32 scale = cur_plant.scale; v3f vertices[4] = { v3f(-scale, -BS / 2 + 2.0 * scale * cur_plant.plant_height, 0), v3f( scale, -BS / 2 + 2.0 * scale * cur_plant.plant_height, 0), @@ -1147,14 +1160,14 @@ void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset, } } - drawQuad(vertices, v3s16(0, 0, 0), cur_plant.plant_height); + drawQuad(tile, vertices, v3s16(0, 0, 0), cur_plant.plant_height); } -void MapblockMeshGenerator::drawPlantlike(bool is_rooted) +void MapblockMeshGenerator::drawPlantlike(const TileSpec &tile, bool is_rooted) { cur_plant.draw_style = PLANT_STYLE_CROSS; - cur_node.scale = BS / 2 * cur_node.f->visual_scale; cur_plant.offset = v3f(0, 0, 0); + cur_plant.scale = BS / 2 * cur_node.f->visual_scale; cur_plant.rotate_degree = 0.0f; cur_plant.random_offset_Y = false; cur_plant.face_num = 0; @@ -1164,7 +1177,7 @@ void MapblockMeshGenerator::drawPlantlike(bool is_rooted) case CPT2_MESHOPTIONS: cur_plant.draw_style = PlantlikeStyle(cur_node.n.param2 & MO_MASK_STYLE); if (cur_node.n.param2 & MO_BIT_SCALE_SQRT2) - cur_node.scale *= 1.41421; + cur_plant.scale *= 1.41421; if (cur_node.n.param2 & MO_BIT_RANDOM_OFFSET) { PseudoRandom rng(cur_node.p.X << 8 | cur_node.p.Z | cur_node.p.Y << 16); cur_plant.offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145); @@ -1205,63 +1218,66 @@ void MapblockMeshGenerator::drawPlantlike(bool is_rooted) switch (cur_plant.draw_style) { case PLANT_STYLE_CROSS: - drawPlantlikeQuad(46); - drawPlantlikeQuad(-44); + drawPlantlikeQuad(tile, 46); + drawPlantlikeQuad(tile, -44); break; case PLANT_STYLE_CROSS2: - drawPlantlikeQuad(91); - drawPlantlikeQuad(1); + drawPlantlikeQuad(tile, 91); + drawPlantlikeQuad(tile, 1); break; case PLANT_STYLE_STAR: - drawPlantlikeQuad(121); - drawPlantlikeQuad(241); - drawPlantlikeQuad(1); + drawPlantlikeQuad(tile, 121); + drawPlantlikeQuad(tile, 241); + drawPlantlikeQuad(tile, 1); break; case PLANT_STYLE_HASH: - drawPlantlikeQuad( 1, BS / 4); - drawPlantlikeQuad( 91, BS / 4); - drawPlantlikeQuad(181, BS / 4); - drawPlantlikeQuad(271, BS / 4); + drawPlantlikeQuad(tile, 1, BS / 4); + drawPlantlikeQuad(tile, 91, BS / 4); + drawPlantlikeQuad(tile, 181, BS / 4); + drawPlantlikeQuad(tile, 271, BS / 4); break; case PLANT_STYLE_HASH2: - drawPlantlikeQuad( 1, -BS / 2, true); - drawPlantlikeQuad( 91, -BS / 2, true); - drawPlantlikeQuad(181, -BS / 2, true); - drawPlantlikeQuad(271, -BS / 2, true); + drawPlantlikeQuad(tile, 1, -BS / 2, true); + drawPlantlikeQuad(tile, 91, -BS / 2, true); + drawPlantlikeQuad(tile, 181, -BS / 2, true); + drawPlantlikeQuad(tile, 271, -BS / 2, true); break; } } void MapblockMeshGenerator::drawPlantlikeNode() { - useTile(); - drawPlantlike(); + TileSpec tile; + useTile(&tile); + drawPlantlike(tile); } void MapblockMeshGenerator::drawPlantlikeRootedNode() { drawSolidNode(); - useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true); + TileSpec tile; + useTile(&tile, 0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true); cur_node.origin += v3f(0.0, BS, 0.0); cur_node.p.Y++; if (data->m_smooth_lighting) { getSmoothLightFrame(); } else { MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + cur_node.p); - cur_node.light = LightPair(getInteriorLight(ntop, 0, nodedef)); + auto light = LightPair(getInteriorLight(ntop, 0, nodedef)); + cur_node.lcolor = encode_light(light, cur_node.f->light_source); } - drawPlantlike(true); + drawPlantlike(tile, true); cur_node.p.Y--; } -void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle, - float offset_h, float offset_v) +void MapblockMeshGenerator::drawFirelikeQuad(const TileSpec &tile, float rotation, + float opening_angle, float offset_h, float offset_v) { - const f32 scale = cur_node.scale; + const f32 scale = BS / 2 * cur_node.f->visual_scale; v3f vertices[4] = { v3f(-scale, -BS / 2 + scale * 2, 0), v3f( scale, -BS / 2 + scale * 2, 0), @@ -1275,13 +1291,13 @@ void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle vertex.rotateXZBy(rotation); vertex.Y += offset_v; } - drawQuad(vertices); + drawQuad(tile, vertices); } void MapblockMeshGenerator::drawFirelikeNode() { - useTile(); - cur_node.scale = BS / 2 * cur_node.f->visual_scale; + TileSpec tile; + useTile(&tile); // Check for adjacent nodes bool neighbors = false; @@ -1300,41 +1316,41 @@ void MapblockMeshGenerator::drawFirelikeNode() bool drawBottomFire = neighbor[D6D_YP]; if (drawBasicFire || neighbor[D6D_ZP]) - drawFirelikeQuad(0, -10, 0.4 * BS); + drawFirelikeQuad(tile, 0, -10, 0.4 * BS); else if (drawBottomFire) - drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS); + drawFirelikeQuad(tile, 0, 70, 0.47 * BS, 0.484 * BS); if (drawBasicFire || neighbor[D6D_XN]) - drawFirelikeQuad(90, -10, 0.4 * BS); + drawFirelikeQuad(tile, 90, -10, 0.4 * BS); else if (drawBottomFire) - drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS); + drawFirelikeQuad(tile, 90, 70, 0.47 * BS, 0.484 * BS); if (drawBasicFire || neighbor[D6D_ZN]) - drawFirelikeQuad(180, -10, 0.4 * BS); + drawFirelikeQuad(tile, 180, -10, 0.4 * BS); else if (drawBottomFire) - drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS); + drawFirelikeQuad(tile, 180, 70, 0.47 * BS, 0.484 * BS); if (drawBasicFire || neighbor[D6D_XP]) - drawFirelikeQuad(270, -10, 0.4 * BS); + drawFirelikeQuad(tile, 270, -10, 0.4 * BS); else if (drawBottomFire) - drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS); + drawFirelikeQuad(tile, 270, 70, 0.47 * BS, 0.484 * BS); if (drawBasicFire) { - drawFirelikeQuad(45, 0, 0.0); - drawFirelikeQuad(-45, 0, 0.0); + drawFirelikeQuad(tile, 45, 0, 0.0); + drawFirelikeQuad(tile, -45, 0, 0.0); } } void MapblockMeshGenerator::drawFencelikeNode() { - useTile(0, 0, 0); - TileSpec tile_nocrack = cur_node.tile; + TileSpec tile_nocrack; + useTile(&tile_nocrack, 0, 0, 0); for (auto &layer : tile_nocrack.layers) layer.material_flags &= ~MATERIAL_FLAG_CRACK; // Put wood the right way around in the posts - TileSpec tile_rot = cur_node.tile; + TileSpec tile_rot = tile_nocrack; tile_rot.rotation = TileRotation::R90; static const f32 post_rad = BS / 8; @@ -1352,10 +1368,7 @@ void MapblockMeshGenerator::drawFencelikeNode() 0.500, 0.000, 0.750, 1.000, 0.750, 0.000, 1.000, 1.000, }; - cur_node.tile = tile_rot; - drawAutoLightedCuboid(post, postuv); - - cur_node.tile = tile_nocrack; + drawAutoLightedCuboid(post, tile_rot, postuv); // Now a section of fence, +X, if there's a post there v3s16 p2 = cur_node.p; @@ -1375,8 +1388,8 @@ void MapblockMeshGenerator::drawFencelikeNode() 0.000, 0.500, 1.000, 0.625, 0.000, 0.875, 1.000, 1.000, }; - drawAutoLightedCuboid(bar_x1, xrailuv); - drawAutoLightedCuboid(bar_x2, xrailuv); + drawAutoLightedCuboid(bar_x1, tile_nocrack, xrailuv); + drawAutoLightedCuboid(bar_x2, tile_nocrack, xrailuv); } // Now a section of fence, +Z, if there's a post there @@ -1397,8 +1410,8 @@ void MapblockMeshGenerator::drawFencelikeNode() 0.3750, 0.3750, 0.5000, 0.5000, 0.6250, 0.6250, 0.7500, 0.7500, }; - drawAutoLightedCuboid(bar_z1, zrailuv); - drawAutoLightedCuboid(bar_z2, zrailuv); + drawAutoLightedCuboid(bar_z1, tile_nocrack, zrailuv); + drawAutoLightedCuboid(bar_z2, tile_nocrack, zrailuv); } } @@ -1480,7 +1493,8 @@ void MapblockMeshGenerator::drawRaillikeNode() angle = rail_kinds[code].angle; } - useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING); + TileSpec tile; + useTile(&tile, tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING); static const float offset = BS / 64; static const float size = BS / 2; @@ -1494,7 +1508,7 @@ void MapblockMeshGenerator::drawRaillikeNode() if (angle) for (v3f &vertex : vertices) vertex.rotateXZBy(angle); - drawQuad(vertices); + drawQuad(tile, vertices); } namespace { @@ -1526,7 +1540,7 @@ void MapblockMeshGenerator::drawAllfacesNode() getTile(nodebox_tile_dirs[face], &tiles[face]); if (data->m_smooth_lighting) getSmoothLightFrame(); - drawAutoLightedCuboid(box, nullptr, tiles, 6); + drawAutoLightedCuboid(box, tiles, 6); } void MapblockMeshGenerator::drawNodeboxNode() @@ -1633,7 +1647,7 @@ void MapblockMeshGenerator::drawNodeboxNode() for (auto &box : boxes) { u8 mask = getNodeBoxMask(box, solid_neighbors, sametype_neighbors); - drawAutoLightedCuboid(box, nullptr, tiles, 6, mask); + drawAutoLightedCuboid(box, tiles, 6, nullptr, mask); } } @@ -1678,29 +1692,35 @@ void MapblockMeshGenerator::drawMeshNode() for (u32 j = 0; j < mesh->getMeshBufferCount(); j++) { // Only up to 6 tiles are supported - const u32 tile = mesh->getTextureSlot(j); - useTile(MYMIN(tile, 5)); + const u32 tile_idx = mesh->getTextureSlot(j); + TileSpec tile; + useTile(&tile, MYMIN(tile_idx, 5)); scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices(); u32 vertex_count = buf->getVertexCount(); + // Mesh is always private here. So the lighting is applied to each + // vertex right here. if (data->m_smooth_lighting) { - // Mesh is always private here. So the lighting is applied to each - // vertex right here. for (u32 k = 0; k < vertex_count; k++) { video::S3DVertex &vertex = vertices[k]; vertex.Color = blendLightColor(vertex.Pos, vertex.Normal); vertex.Pos += cur_node.origin; } - collector->append(cur_node.tile, vertices, vertex_count, - buf->getIndices(), buf->getIndexCount()); } else { - // Let the collector process colors, etc. - collector->append(cur_node.tile, vertices, vertex_count, - buf->getIndices(), buf->getIndexCount(), cur_node.origin, - cur_node.color, cur_node.f->light_source); + bool is_light_source = cur_node.f->light_source != 0; + for (u32 k = 0; k < vertex_count; k++) { + video::S3DVertex &vertex = vertices[k]; + video::SColor color = cur_node.lcolor; + if (!is_light_source) + applyFacesShading(color, vertex.Normal); + vertex.Color = color; + vertex.Pos += cur_node.origin; + } } + collector->append(tile, vertices, vertex_count, + buf->getIndices(), buf->getIndexCount()); } mesh->drop(); } @@ -1725,10 +1745,12 @@ void MapblockMeshGenerator::drawNode() break; } cur_node.origin = intToFloat(cur_node.p, BS); - if (data->m_smooth_lighting) + if (data->m_smooth_lighting) { getSmoothLightFrame(); - else - cur_node.light = LightPair(getInteriorLight(cur_node.n, 0, nodedef)); + } else { + auto light = LightPair(getInteriorLight(cur_node.n, 0, nodedef)); + cur_node.lcolor = encode_light(light, cur_node.f->light_source); + } switch (cur_node.f->drawtype) { case NDT_FLOWINGLIQUID: drawLiquidNode(); break; case NDT_GLASSLIKE: drawGlasslikeNode(); break; @@ -1751,19 +1773,11 @@ void MapblockMeshGenerator::generate() { ZoneScoped; - for (cur_node.p.Z = 0; cur_node.p.Z < data->side_length; cur_node.p.Z++) - for (cur_node.p.Y = 0; cur_node.p.Y < data->side_length; cur_node.p.Y++) - for (cur_node.p.X = 0; cur_node.p.X < data->side_length; cur_node.p.X++) { + for (cur_node.p.Z = 0; cur_node.p.Z < data->m_side_length; cur_node.p.Z++) + for (cur_node.p.Y = 0; cur_node.p.Y < data->m_side_length; cur_node.p.Y++) + for (cur_node.p.X = 0; cur_node.p.X < data->m_side_length; cur_node.p.X++) { cur_node.n = data->m_vmanip.getNodeNoEx(blockpos_nodes + cur_node.p); cur_node.f = &nodedef->get(cur_node.n); drawNode(); } } - -void MapblockMeshGenerator::renderSingle(content_t node, u8 param2) -{ - cur_node.p = {0, 0, 0}; - cur_node.n = MapNode(node, 0xff, param2); - cur_node.f = &nodedef->get(cur_node.n); - drawNode(); -} diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index 327a7ea05..a4ed6a0fc 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -47,7 +47,6 @@ class MapblockMeshGenerator public: MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output); void generate(); - void renderSingle(content_t node, u8 param2 = 0x00); private: MeshMakeData *const data; @@ -59,15 +58,12 @@ private: // current node struct { - v3s16 p; - v3f origin; + v3s16 p; // relative to blockpos_nodes + v3f origin; // p in BS space MapNode n; const ContentFeatures *f; - LightPair light; - LightFrame frame; - video::SColor color; - TileSpec tile; - f32 scale; + LightFrame lframe; // smooth lighting + video::SColor lcolor; // unsmooth lighting } cur_node; // lighting @@ -76,21 +72,23 @@ private: video::SColor blendLightColor(const v3f &vertex_pos); video::SColor blendLightColor(const v3f &vertex_pos, const v3f &vertex_normal); - void useTile(int index = 0, u8 set_flags = MATERIAL_FLAG_CRACK_OVERLAY, + void useTile(TileSpec *tile_ret, int index = 0, u8 set_flags = MATERIAL_FLAG_CRACK_OVERLAY, u8 reset_flags = 0, bool special = false); - void getTile(int index, TileSpec *tile); - void getTile(v3s16 direction, TileSpec *tile); - void getSpecialTile(int index, TileSpec *tile, bool apply_crack = false); + void getTile(int index, TileSpec *tile_ret); + void getTile(v3s16 direction, TileSpec *tile_ret); + void getSpecialTile(int index, TileSpec *tile_ret, bool apply_crack = false); // face drawing - void drawQuad(v3f *vertices, const v3s16 &normal = v3s16(0, 0, 0), + void drawQuad(const TileSpec &tile, v3f *vertices, const v3s16 &normal = v3s16(0, 0, 0), float vertical_tiling = 1.0); // cuboid drawing! template - void drawCuboid(const aabb3f &box, TileSpec *tiles, int tilecount, const f32 *txc, u8 mask, Fn &&face_lighter); + void drawCuboid(const aabb3f &box, const TileSpec *tiles, int tilecount, + const f32 *txc, u8 mask, Fn &&face_lighter); void generateCuboidTextureCoords(aabb3f const &box, f32 *coords); - void drawAutoLightedCuboid(aabb3f box, f32 const *txc = nullptr, TileSpec *tiles = nullptr, int tile_count = 0, 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); u8 getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const; // liquid-specific @@ -113,7 +111,6 @@ private: f32 corner_levels[2][2]; }; LiquidData cur_liquid; - bool smooth_liquids = false; void prepareLiquidNodeDrawing(); void getLiquidNeighborhood(); @@ -136,6 +133,7 @@ private: struct PlantlikeData { PlantlikeStyle draw_style; v3f offset; + float scale; float rotate_degree; bool random_offset_Y; int face_num; @@ -143,12 +141,12 @@ private: }; PlantlikeData cur_plant; - void drawPlantlikeQuad(float rotation, float quad_offset = 0, + void drawPlantlikeQuad(const TileSpec &tile, float rotation, float quad_offset = 0, bool offset_top_only = false); - void drawPlantlike(bool is_rooted = false); + void drawPlantlike(const TileSpec &tile, bool is_rooted = false); // firelike-specific - void drawFirelikeQuad(float rotation, float opening_angle, + void drawFirelikeQuad(const TileSpec &tile, float rotation, float opening_angle, float offset_h, float offset_v = 0.0); // drawtypes diff --git a/src/client/game.cpp b/src/client/game.cpp index f907744c7..1e1f1cb1c 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -472,6 +472,8 @@ public: const static float object_hit_delay = 0.2; +const static u16 bbox_debug_flag = scene::EDS_BBOX_ALL; + /* The reason the following structs are not anonymous structs within the * class is that they are not used by the majority of member functions and * many functions that do require objects of thse types do not modify them @@ -680,6 +682,8 @@ protected: private: struct Flags { bool disable_camera_update = false; + /// 0 = no debug text active, see toggleDebug() for the rest + int debug_state = 0; }; void pauseAnimation(); @@ -782,11 +786,8 @@ private: * (as opposed to the this local caching). This can be addressed in * a later release. */ - bool m_cache_disable_escape_sequences; bool m_cache_doubletap_jump; - bool m_cache_enable_clouds; bool m_cache_enable_joysticks; - bool m_cache_enable_particles; bool m_cache_enable_fog; bool m_cache_enable_noclip; bool m_cache_enable_free_move; @@ -828,16 +829,10 @@ Game::Game() : { g_settings->registerChangedCallback("chat_log_level", &settingChangedCallback, this); - g_settings->registerChangedCallback("disable_escape_sequences", - &settingChangedCallback, this); g_settings->registerChangedCallback("doubletap_jump", &settingChangedCallback, this); - g_settings->registerChangedCallback("enable_clouds", - &settingChangedCallback, this); g_settings->registerChangedCallback("enable_joysticks", &settingChangedCallback, this); - g_settings->registerChangedCallback("enable_particles", - &settingChangedCallback, this); g_settings->registerChangedCallback("enable_fog", &settingChangedCallback, this); g_settings->registerChangedCallback("mouse_sensitivity", @@ -937,6 +932,10 @@ bool Game::startup(bool *kill, runData.time_from_last_punch = 10.0; m_game_ui->initFlags(); + if (g_settings->getBool("show_debug")) { + m_flags.debug_state = 1; + m_game_ui->m_flags.show_minimal_debug = true; + } m_first_loop_after_window_activation = true; @@ -1175,7 +1174,7 @@ bool Game::init( bool Game::initSound() { #if USE_SOUND - if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) { + if (g_sound_manager_singleton.get()) { infostream << "Attempting to use OpenAL audio" << std::endl; sound_manager = createOpenALSoundManager(g_sound_manager_singleton.get(), std::make_unique()); @@ -1333,8 +1332,7 @@ bool Game::createClient(const GameStartData &start_data) /* Clouds */ - if (m_cache_enable_clouds) - clouds = make_irr(smgr, shader_src, -1, rand()); + clouds = make_irr(smgr, shader_src, -1, myrand()); /* Skybox */ @@ -1708,6 +1706,7 @@ void Game::updateDebugState() hud->disableBlockBounds(); if (!has_debug) { draw_control->show_wireframe = false; + smgr->setGlobalDebugData(0, bbox_debug_flag); m_flags.disable_camera_update = false; m_game_formspec.disableDebugView(); } @@ -1845,7 +1844,9 @@ void Game::processUserInput(f32 dtime) m_game_focused = true; } - if (!guienv->hasFocus(gui_chat_console.get()) && gui_chat_console->isOpen()) { + if (!guienv->hasFocus(gui_chat_console.get()) && gui_chat_console->isOpen() + && !gui_chat_console->isMyChild(guienv->getFocus())) + { gui_chat_console->closeConsoleAtOnce(); } @@ -1909,34 +1910,22 @@ void Game::processKeyInput() toggleNoClip(); #if USE_SOUND } else if (wasKeyDown(KeyType::MUTE)) { - if (g_settings->getBool("enable_sound")) { - bool new_mute_sound = !g_settings->getBool("mute_sound"); - g_settings->setBool("mute_sound", new_mute_sound); - if (new_mute_sound) - m_game_ui->showTranslatedStatusText("Sound muted"); - else - m_game_ui->showTranslatedStatusText("Sound unmuted"); - } else { - m_game_ui->showTranslatedStatusText("Sound system is disabled"); - } + bool new_mute_sound = !g_settings->getBool("mute_sound"); + g_settings->setBool("mute_sound", new_mute_sound); + if (new_mute_sound) + m_game_ui->showTranslatedStatusText("Sound muted"); + else + m_game_ui->showTranslatedStatusText("Sound unmuted"); } else if (wasKeyDown(KeyType::INC_VOLUME)) { - if (g_settings->getBool("enable_sound")) { - float new_volume = g_settings->getFloat("sound_volume", 0.0f, 0.9f) + 0.1f; - g_settings->setFloat("sound_volume", new_volume); - std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100)); - m_game_ui->showStatusText(msg); - } else { - m_game_ui->showTranslatedStatusText("Sound system is disabled"); - } + float new_volume = g_settings->getFloat("sound_volume", 0.0f, 0.9f) + 0.1f; + g_settings->setFloat("sound_volume", new_volume); + std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100)); + m_game_ui->showStatusText(msg); } else if (wasKeyDown(KeyType::DEC_VOLUME)) { - if (g_settings->getBool("enable_sound")) { - float new_volume = g_settings->getFloat("sound_volume", 0.1f, 1.0f) - 0.1f; - g_settings->setFloat("sound_volume", new_volume); - std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100)); - m_game_ui->showStatusText(msg); - } else { - m_game_ui->showTranslatedStatusText("Sound system is disabled"); - } + float new_volume = g_settings->getFloat("sound_volume", 0.1f, 1.0f) - 0.1f; + g_settings->setFloat("sound_volume", new_volume); + std::wstring msg = fwgettext("Volume changed to %d%%", myround(new_volume * 100)); + m_game_ui->showStatusText(msg); #else } else if (wasKeyDown(KeyType::MUTE) || wasKeyDown(KeyType::INC_VOLUME) || wasKeyDown(KeyType::DEC_VOLUME)) { @@ -2267,45 +2256,44 @@ void Game::toggleDebug() LocalPlayer *player = client->getEnv().getLocalPlayer(); bool has_debug = client->checkPrivilege("debug"); bool has_basic_debug = has_debug || (player->hud_flags & HUD_FLAG_BASIC_DEBUG); + // Initial: No debug info // 1x toggle: Debug text // 2x toggle: Debug text with profiler graph // 3x toggle: Debug text and wireframe (needs "debug" priv) - // Next toggle: Back to initial + // 4x toggle: Debug text and bbox (needs "debug" priv) // // The debug text can be in 2 modes: minimal and basic. // * Minimal: Only technical client info that not gameplay-relevant // * Basic: Info that might give gameplay advantage, e.g. pos, angle // Basic mode is used when player has the debug HUD flag set, // otherwise the Minimal mode is used. - if (!m_game_ui->m_flags.show_minimal_debug) { - m_game_ui->m_flags.show_minimal_debug = true; - if (has_basic_debug) - m_game_ui->m_flags.show_basic_debug = true; - m_game_ui->m_flags.show_profiler_graph = false; - draw_control->show_wireframe = false; + + auto &state = m_flags.debug_state; + state = (state + 1) % 5; + if (state >= 3 && !has_debug) + state = 0; + + m_game_ui->m_flags.show_minimal_debug = state > 0; + m_game_ui->m_flags.show_basic_debug = state > 0 && has_basic_debug; + m_game_ui->m_flags.show_profiler_graph = state == 2; + draw_control->show_wireframe = state == 3; + smgr->setGlobalDebugData(state == 4 ? bbox_debug_flag : 0, + state == 4 ? 0 : bbox_debug_flag); + + if (state == 1) { m_game_ui->showTranslatedStatusText("Debug info shown"); - } else if (!m_game_ui->m_flags.show_profiler_graph && !draw_control->show_wireframe) { - if (has_basic_debug) - m_game_ui->m_flags.show_basic_debug = true; - m_game_ui->m_flags.show_profiler_graph = true; + } else if (state == 2) { m_game_ui->showTranslatedStatusText("Profiler graph shown"); - } else if (!draw_control->show_wireframe && client->checkPrivilege("debug")) { - if (has_basic_debug) - m_game_ui->m_flags.show_basic_debug = true; - m_game_ui->m_flags.show_profiler_graph = false; - draw_control->show_wireframe = true; - m_game_ui->showTranslatedStatusText("Wireframe shown"); + } else if (state == 3) { + if (driver->getDriverType() == video::EDT_OGLES2) + m_game_ui->showTranslatedStatusText("Wireframe not supported by video driver"); + else + m_game_ui->showTranslatedStatusText("Wireframe shown"); + } else if (state == 4) { + m_game_ui->showTranslatedStatusText("Bounding boxes shown"); } else { - m_game_ui->m_flags.show_minimal_debug = false; - m_game_ui->m_flags.show_basic_debug = false; - m_game_ui->m_flags.show_profiler_graph = false; - draw_control->show_wireframe = false; - if (has_debug) { - m_game_ui->showTranslatedStatusText("Debug info, profiler graph, and wireframe hidden"); - } else { - m_game_ui->showTranslatedStatusText("Debug info and profiler graph hidden"); - } + m_game_ui->showTranslatedStatusText("All debug info hidden"); } } @@ -2896,9 +2884,6 @@ void Game::handleClientEvent_OverrideDayNigthRatio(ClientEvent *event, void Game::handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation *cam) { - if (!clouds) - return; - clouds->setDensity(event->cloud_params.density); clouds->setColorBright(video::SColor(event->cloud_params.color_bright)); clouds->setColorAmbient(video::SColor(event->cloud_params.color_ambient)); @@ -2935,10 +2920,7 @@ void Game::updateChat(f32 dtime) std::vector entries = m_chat_log_buf.take(); for (const auto& entry : entries) { std::string line; - if (!m_cache_disable_escape_sequences) { - line.append(color_for(entry.level)); - } - line.append(entry.combined); + line.append(color_for(entry.level)).append(entry.combined); chat_backend->addMessage(L"", utf8_to_wide(line)); } @@ -3023,8 +3005,7 @@ void Game::updateCamera(f32 dtime) client->updateCameraOffset(camera_offset); client->getEnv().updateCameraOffset(camera_offset); - if (clouds) - clouds->updateCameraOffset(camera_offset); + clouds->updateCameraOffset(camera_offset); } } } @@ -3683,10 +3664,8 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, } else { runData.dig_time_complete = params.time; - if (m_cache_enable_particles) { - client->getParticleManager()->addNodeParticle(client, - player, nodepos, n, features); - } + client->getParticleManager()->addNodeParticle(client, + player, nodepos, n, features); } if (!runData.digging) { @@ -3771,11 +3750,8 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, client->interact(INTERACT_DIGGING_COMPLETED, pointed); - if (m_cache_enable_particles) { - client->getParticleManager()->addDiggingParticles(client, - player, nodepos, n, features); - } - + client->getParticleManager()->addDiggingParticles(client, + player, nodepos, n, features); // Send event to trigger sound client->getEventManager()->put(new NodeDugEvent(nodepos, n)); @@ -3866,8 +3842,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, /* Update clouds */ - if (clouds) - updateClouds(dtime); + updateClouds(dtime); /* Update particles @@ -4129,11 +4104,8 @@ void Game::readSettings() } m_chat_log_buf.setLogLevel(chat_log_level); - m_cache_disable_escape_sequences = g_settings->getBool("disable_escape_sequences"); m_cache_doubletap_jump = g_settings->getBool("doubletap_jump"); - m_cache_enable_clouds = g_settings->getBool("enable_clouds"); m_cache_enable_joysticks = g_settings->getBool("enable_joysticks"); - m_cache_enable_particles = g_settings->getBool("enable_particles"); m_cache_enable_fog = g_settings->getBool("enable_fog"); m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity", 0.001f, 10.0f); m_cache_joystick_frustum_sensitivity = std::max(g_settings->getFloat("joystick_frustum_sensitivity"), 0.001f); diff --git a/src/client/game_formspec.cpp b/src/client/game_formspec.cpp index 4c3bbb04a..cc5ddc0ed 100644 --- a/src/client/game_formspec.cpp +++ b/src/client/game_formspec.cpp @@ -333,12 +333,11 @@ void GameFormSpec::showPauseMenu() #ifndef __ANDROID__ #if USE_SOUND - if (g_settings->getBool("enable_sound")) { - os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" - << strgettext("Sound Volume") << "]"; - } + os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" + << strgettext("Sound Volume") << "]"; #endif #endif + if (g_touchcontrols) { os << "button_exit[4," << (ypos++) << ";3,0.5;btn_touchscreen_layout;" << strgettext("Touchscreen Layout") << "]"; diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 6fc7ac267..53fae50c0 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -215,7 +215,6 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ void GameUI::initFlags() { m_flags = GameUI::Flags(); - m_flags.show_minimal_debug = g_settings->getBool("show_debug"); } void GameUI::showTranslatedStatusText(const char *str) diff --git a/src/client/guiscalingfilter.cpp b/src/client/guiscalingfilter.cpp index a8d17722b..33b175d09 100644 --- a/src/client/guiscalingfilter.cpp +++ b/src/client/guiscalingfilter.cpp @@ -94,8 +94,7 @@ video::ITexture *guiScalingResizeCached(video::IVideoDriver *driver, auto it_img = g_imgCache.find(origname); video::IImage *srcimg = (it_img != g_imgCache.end()) ? it_img->second : nullptr; if (!srcimg) { - if (!g_settings->getBool("gui_scaling_filter_txr2img")) - return src; + // Download image from GPU srcimg = driver->createImageFromData(src->getColorFormat(), src->getSize(), src->lock(video::ETLM_READ_ONLY), false); src->unlock(); diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index b34c22d78..542c41bc7 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -5,6 +5,7 @@ #pragma once #include "irrlichttypes.h" +#include "irr_v2d.h" #include "joystick_controller.h" #include #include "keycode.h" diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h index c5302b533..d7bf4230e 100644 --- a/src/client/joystick_controller.h +++ b/src/client/joystick_controller.h @@ -4,7 +4,9 @@ #pragma once -#include "irrlichttypes_extrabloated.h" +#include +#include "irrlichttypes.h" + #include "keys.h" #include #include diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 43c6e22e2..a47bf5ae9 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -26,10 +26,14 @@ MeshMakeData */ -MeshMakeData::MeshMakeData(const NodeDefManager *ndef, u16 side_length): - side_length(side_length), - nodedef(ndef) -{} +MeshMakeData::MeshMakeData(const NodeDefManager *ndef, + u16 side_length, MeshGrid mesh_grid) : + m_side_length(side_length), + m_mesh_grid(mesh_grid), + m_nodedef(ndef) +{ + assert(m_side_length > 0); +} void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos) { @@ -38,8 +42,9 @@ void MeshMakeData::fillBlockDataBegin(const v3s16 &blockpos) v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE; m_vmanip.clear(); + // extra 1 block thick layer around the mesh VoxelArea voxel_area(blockpos_nodes - v3s16(1,1,1) * MAP_BLOCKSIZE, - blockpos_nodes + v3s16(1,1,1) * (side_length + MAP_BLOCKSIZE /* extra layer of blocks around the mesh */) - v3s16(1,1,1)); + blockpos_nodes + v3s16(1,1,1) * (m_side_length + MAP_BLOCKSIZE) - v3s16(1,1,1)); m_vmanip.addArea(voxel_area); } @@ -52,17 +57,30 @@ void MeshMakeData::fillBlockData(const v3s16 &bp, MapNode *data) m_vmanip.copyFrom(data, data_area, v3s16(0,0,0), blockpos_nodes, data_size); } +void MeshMakeData::fillSingleNode(MapNode data, MapNode padding) +{ + m_blockpos = {0, 0, 0}; + + m_vmanip.clear(); + // area around 0,0,0 so that this positon has neighbors + const s16 sz = 3; + m_vmanip.addArea({v3s16(-sz), v3s16(sz)}); + + u32 count = m_vmanip.m_area.getVolume(); + for (u32 i = 0; i < count; i++) { + m_vmanip.m_data[i] = padding; + m_vmanip.m_flags[i] &= ~VOXELFLAG_NO_DATA; + } + + m_vmanip.setNodeNoEmerge({0, 0, 0}, data); +} + void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos) { if (crack_level >= 0) m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE; } -void MeshMakeData::setSmoothLighting(bool smooth_lighting) -{ - m_smooth_lighting = smooth_lighting; -} - /* Light and vertex color functions */ @@ -133,7 +151,7 @@ u16 getFaceLight(MapNode n, MapNode n2, const NodeDefManager *ndef) static u16 getSmoothLightCombined(const v3s16 &p, const std::array &dirs, MeshMakeData *data) { - const NodeDefManager *ndef = data->nodedef; + const NodeDefManager *ndef = data->m_nodedef; u16 ambient_occlusion = 0; u16 light_count = 0; @@ -321,7 +339,7 @@ void final_color_blend(video::SColor *result, */ void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile) { - const NodeDefManager *ndef = data->nodedef; + const NodeDefManager *ndef = data->m_nodedef; const ContentFeatures &f = ndef->get(mn); tile = f.tiles[tileindex]; bool has_crack = p == data->m_crack_pos_relative; @@ -341,7 +359,7 @@ void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, */ void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile) { - const NodeDefManager *ndef = data->nodedef; + const NodeDefManager *ndef = data->m_nodedef; // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0), // (0,0,1), (0,0,-1) or (0,0,0) @@ -593,7 +611,7 @@ void PartialMeshBuffer::draw(video::IVideoDriver *driver) const MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offset): m_tsrc(client->getTextureSource()), m_shdrsrc(client->getShaderSource()), - m_bounding_sphere_center((data->side_length * 0.5f - 0.5f) * BS), + m_bounding_sphere_center((data->m_side_length * 0.5f - 0.5f) * BS), m_animation_force_timer(0), // force initial animation m_last_crack(-1) { @@ -602,10 +620,12 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs for (auto &m : m_mesh) m = make_irr(); - auto mesh_grid = client->getMeshGrid(); + auto mesh_grid = data->m_mesh_grid; v3s16 bp = data->m_blockpos; - // Only generate minimap mapblocks at even coordinates. - if (mesh_grid.isMeshPos(bp) && client->getMinimap()) { + // Only generate minimap mapblocks at grid aligned coordinates. + // FIXME: ^ doesn't really make sense. and in practice, bp is always aligned + if (mesh_grid.isMeshPos(bp) && data->m_generate_minimap) { + // meshgen area always fits into a grid cell m_minimap_mapblocks.resize(mesh_grid.getCellVolume(), nullptr); v3s16 ofs; @@ -622,17 +642,13 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs } } + // algin vertices to mesh grid, not meshgen area v3f offset = intToFloat((data->m_blockpos - mesh_grid.getMeshPos(data->m_blockpos)) * MAP_BLOCKSIZE, BS); + MeshCollector collector(m_bounding_sphere_center, offset); - /* - Add special graphics: - - torches - - flowing water - - fences - - whatever - */ { + // Generate everything MapblockMeshGenerator(data, &collector).generate(); } @@ -640,9 +656,6 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs Convert MeshCollector to SMesh */ - const bool desync_animations = g_settings->getBool( - "desynchronize_mapblock_texture_animation"); - m_bounding_radius = std::sqrt(collector.m_bounding_radius_sq); for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { @@ -679,16 +692,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs auto &info = m_animation_info[{layer, i}]; info.tile = p.layer; info.frame = 0; - if (desync_animations) { - // Get starting position from noise - info.frame_offset = - 100000 * (2.0 + noise3d( - data->m_blockpos.X, data->m_blockpos.Y, - data->m_blockpos.Z, 0)); - } else { - // Play all synchronized - info.frame_offset = 0; - } + info.frame_offset = 0; // Replace tile texture with the first animation frame p.layer.texture = (*p.layer.frames)[0].texture; } @@ -702,6 +706,16 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; tex.MagFilter = video::ETMAGF_NEAREST; }); + /* + * The second layer is for overlays, but uses the same vertex positions + * as the first, which quickly leads to z-fighting. + * To fix this we can offset the polygons in the direction of the camera. + * This only affects the depth buffer and leads to no visual gaps in geometry. + */ + if (layer == 1) { + material.PolygonOffsetSlopeScale = -1; + material.PolygonOffsetDepthBias = -1; + } { material.MaterialType = m_shdrsrc->getShaderInfo( @@ -738,7 +752,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs } } - m_bsp_tree.buildTree(&m_transparent_triangles, data->side_length); + m_bsp_tree.buildTree(&m_transparent_triangles, data->m_side_length); // Check if animation is required for this mesh m_has_animation = @@ -947,21 +961,22 @@ video::SColor encode_light(u16 light, u8 emissive_light) u8 get_solid_sides(MeshMakeData *data) { - std::unordered_map results; v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE; - const NodeDefManager *ndef = data->nodedef; + const NodeDefManager *ndef = data->m_nodedef; - u8 result = 0x3F; // all sides solid; + const u16 side = data->m_side_length; + assert(data->m_vmanip.m_area.contains(blockpos_nodes + v3s16(side - 1))); - for (s16 i = 0; i < data->side_length && result != 0; i++) - for (s16 j = 0; j < data->side_length && result != 0; j++) { + u8 result = 0x3F; // all sides solid + for (s16 i = 0; i < side && result != 0; i++) + for (s16 j = 0; j < side && result != 0; j++) { v3s16 positions[6] = { v3s16(0, i, j), - v3s16(data->side_length - 1, i, j), + v3s16(side - 1, i, j), v3s16(i, 0, j), - v3s16(i, data->side_length - 1, j), + v3s16(i, side - 1, j), v3s16(i, j, 0), - v3s16(i, j, data->side_length - 1) + v3s16(i, j, side - 1) }; for (u8 k = 0; k < 6; k++) { diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index e7cadb3db..55aa172bd 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -4,8 +4,11 @@ #pragma once -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes.h" #include "irr_ptr.h" +#include "IMesh.h" +#include "SMeshBuffer.h" + #include "util/numeric.h" #include "client/tile.h" #include "voxel.h" @@ -13,6 +16,10 @@ #include #include +namespace irr::video { + class IVideoDriver; +} + class Client; class NodeDefManager; class IShaderSource; @@ -29,14 +36,25 @@ struct MinimapMapblock; struct MeshMakeData { VoxelManipulator m_vmanip; + + // base pos of meshgen area, in blocks v3s16 m_blockpos = v3s16(-1337,-1337,-1337); + // size of meshgen area, in nodes. + // vmanip will have at least an extra 1 node onion layer. + // area is expected to fit into mesh grid cell. + u16 m_side_length; + // vertex positions will be relative to this grid + MeshGrid m_mesh_grid; + + // relative to blockpos v3s16 m_crack_pos_relative = v3s16(-1337,-1337,-1337); + bool m_generate_minimap = false; bool m_smooth_lighting = false; - u16 side_length; + bool m_enable_water_reflections = false; - const NodeDefManager *nodedef; + const NodeDefManager *m_nodedef; - MeshMakeData(const NodeDefManager *ndef, u16 side_length); + MeshMakeData(const NodeDefManager *ndef, u16 side_lingth, MeshGrid mesh_grid); /* Copy block data manually (to allow optimizations by the caller) @@ -44,15 +62,15 @@ struct MeshMakeData void fillBlockDataBegin(const v3s16 &blockpos); void fillBlockData(const v3s16 &bp, MapNode *data); + /* + Prepare block data for rendering a single node located at (0,0,0). + */ + void fillSingleNode(MapNode data, MapNode padding = MapNode(CONTENT_AIR)); + /* Set the (node) position of a crack */ void setCrack(int crack_level, v3s16 crack_pos); - - /* - Enable or disable smooth lighting - */ - void setSmoothLighting(bool smooth_lighting); }; // represents a triangle as indexes into the vertex buffer in SMeshBuffer diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index cd2e9a91d..a2c0ae327 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -3,7 +3,6 @@ // Copyright (C) 2010-2013 celeron55, Perttu Ahola #include "mesh.h" -#include "S3DVertex.h" #include "debug.h" #include "log.h" #include @@ -11,6 +10,9 @@ #include #include #include +#include "S3DVertex.h" +#include "SMesh.h" +#include "SMeshBuffer.h" inline static void applyShadeFactor(video::SColor& color, float factor) { diff --git a/src/client/mesh.h b/src/client/mesh.h index 3345e24f7..d8eb6080e 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -4,10 +4,20 @@ #pragma once +#include "SColor.h" #include "SMaterialLayer.h" -#include "irrlichttypes_extrabloated.h" #include "nodedef.h" +namespace irr { + namespace scene { + class IAnimatedMesh; + class IMesh; + class IMeshBuffer; + } +} + +using namespace irr; + /*! * Applies shading to a color based on the surface's * normal vector. diff --git a/src/client/mesh_generator_thread.cpp b/src/client/mesh_generator_thread.cpp index 3d80f8e67..70f4287ab 100644 --- a/src/client/mesh_generator_thread.cpp +++ b/src/client/mesh_generator_thread.cpp @@ -40,6 +40,7 @@ MeshUpdateQueue::MeshUpdateQueue(Client *client): m_client(client) { m_cache_smooth_lighting = g_settings->getBool("smooth_lighting"); + m_cache_enable_water_reflections = g_settings->getBool("enable_water_reflections"); } MeshUpdateQueue::~MeshUpdateQueue() @@ -176,7 +177,8 @@ void MeshUpdateQueue::done(v3s16 pos) void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q) { auto mesh_grid = m_client->getMeshGrid(); - MeshMakeData *data = new MeshMakeData(m_client->ndef(), MAP_BLOCKSIZE * mesh_grid.cell_size); + MeshMakeData *data = new MeshMakeData(m_client->ndef(), + MAP_BLOCKSIZE * mesh_grid.cell_size, mesh_grid); q->data = data; data->fillBlockDataBegin(q->p); @@ -191,7 +193,9 @@ void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q) } data->setCrack(q->crack_level, q->crack_pos); - data->setSmoothLighting(m_cache_smooth_lighting); + data->m_generate_minimap = !!m_client->getMinimap(); + data->m_smooth_lighting = m_cache_smooth_lighting; + data->m_enable_water_reflections = m_cache_enable_water_reflections; } /* diff --git a/src/client/mesh_generator_thread.h b/src/client/mesh_generator_thread.h index fb9b5ae9e..72e4c7bed 100644 --- a/src/client/mesh_generator_thread.h +++ b/src/client/mesh_generator_thread.h @@ -69,8 +69,9 @@ private: std::unordered_set m_inflight_blocks; std::mutex m_mutex; - // TODO: Add callback to update these when g_settings changes + // TODO: Add callback to update these when g_settings changes, and update all meshes bool m_cache_smooth_lighting; + bool m_cache_enable_water_reflections; void fillDataFromMapBlocks(QueuedMeshUpdate *q); }; diff --git a/src/client/meshgen/collector.cpp b/src/client/meshgen/collector.cpp index 476f3112b..5a4fb8489 100644 --- a/src/client/meshgen/collector.cpp +++ b/src/client/meshgen/collector.cpp @@ -41,45 +41,6 @@ void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *verti p.indices.push_back(indices[i] + vertex_count); } -void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertices, - u32 numVertices, const u16 *indices, u32 numIndices, v3f pos, - video::SColor c, u8 light_source) -{ - for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) { - const TileLayer *layer = &tile.layers[layernum]; - if (layer->texture_id == 0) - continue; - append(*layer, vertices, numVertices, indices, numIndices, pos, c, - light_source, layernum, tile.world_aligned); - } -} - -void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *vertices, - u32 numVertices, const u16 *indices, u32 numIndices, v3f pos, - video::SColor c, u8 light_source, u8 layernum, bool use_scale) -{ - PreMeshBuffer &p = findBuffer(layer, layernum, numVertices); - - f32 scale = 1.0f; - if (use_scale) - scale = 1.0f / layer.scale; - - u32 vertex_count = p.vertices.size(); - for (u32 i = 0; i < numVertices; i++) { - video::SColor color = c; - if (!light_source) - applyFacesShading(color, vertices[i].Normal); - auto vpos = vertices[i].Pos + pos + offset; - p.vertices.emplace_back(vpos, vertices[i].Normal, color, - scale * vertices[i].TCoords); - m_bounding_radius_sq = std::max(m_bounding_radius_sq, - (vpos - m_center_pos).getLengthSQ()); - } - - for (u32 i = 0; i < numIndices; i++) - p.indices.push_back(indices[i] + vertex_count); -} - PreMeshBuffer &MeshCollector::findBuffer( const TileLayer &layer, u8 layernum, u32 numVertices) { diff --git a/src/client/meshgen/collector.h b/src/client/meshgen/collector.h index 79256e262..693e2be04 100644 --- a/src/client/meshgen/collector.h +++ b/src/client/meshgen/collector.h @@ -35,21 +35,12 @@ struct MeshCollector void append(const TileSpec &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices); - void append(const TileSpec &material, - const video::S3DVertex *vertices, u32 numVertices, - const u16 *indices, u32 numIndices, - v3f pos, video::SColor c, u8 light_source); private: void append(const TileLayer &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, u8 layernum, bool use_scale = false); - void append(const TileLayer &material, - const video::S3DVertex *vertices, u32 numVertices, - const u16 *indices, u32 numIndices, - v3f pos, video::SColor c, u8 light_source, - u8 layernum, bool use_scale = false); PreMeshBuffer &findBuffer(const TileLayer &layer, u8 layernum, u32 numVertices); }; diff --git a/src/client/minimap.h b/src/client/minimap.h index 15112d565..36c900134 100644 --- a/src/client/minimap.h +++ b/src/client/minimap.h @@ -4,18 +4,35 @@ #pragma once -#include "../hud.h" -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes.h" #include "irr_ptr.h" +#include "rect.h" +#include "SMeshBuffer.h" + +#include "../hud.h" +#include "mapnode.h" #include "util/thread.h" -#include "voxel.h" #include #include #include +namespace irr { + namespace video { + class IVideoDriver; + class IImage; + class ITexture; + } + + namespace scene { + class ISceneNode; + } +} + class Client; +class NodeDefManager; class ITextureSource; class IShaderSource; +class VoxelManipulator; #define MINIMAP_MAX_SX 512 #define MINIMAP_MAX_SY 512 diff --git a/src/client/particles.cpp b/src/client/particles.cpp index cd9b9b736..693e5f00c 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -22,6 +22,8 @@ #include "settings.h" #include "profiler.h" +#include "SMeshBuffer.h" + using BlendMode = ParticleParamTypes::BlendMode; ClientParticleTexture::ClientParticleTexture(const ServerParticleTexture& p, ITextureSource *tsrc) diff --git a/src/client/particles.h b/src/client/particles.h index 619877744..72294a552 100644 --- a/src/client/particles.h +++ b/src/client/particles.h @@ -4,12 +4,21 @@ #pragma once +#include "irrlichttypes_bloated.h" +#include "irr_ptr.h" +#include "ISceneNode.h" +#include "S3DVertex.h" +#include "SMeshBuffer.h" + +#include #include #include -#include "irrlichttypes_extrabloated.h" -#include "irr_ptr.h" #include "../particles.h" +namespace irr::video { + class ITexture; +} + struct ClientEvent; class ParticleManager; class ClientEnvironment; diff --git a/src/client/render/anaglyph.cpp b/src/client/render/anaglyph.cpp index 7baf40322..833ad0114 100644 --- a/src/client/render/anaglyph.cpp +++ b/src/client/render/anaglyph.cpp @@ -18,7 +18,7 @@ void SetColorMaskStep::run(PipelineContext &context) { video::SOverrideMaterial &mat = context.device->getVideoDriver()->getOverrideMaterial(); mat.reset(); - mat.Material.ColorMask = color_mask; + mat.Material.ColorMask = static_cast(color_mask); mat.EnableProps = video::EMP_COLOR_MASK; mat.EnablePasses = scene::ESNRP_SKY_BOX | scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT | scene::ESNRP_TRANSPARENT_EFFECT; diff --git a/src/client/render/secondstage.cpp b/src/client/render/secondstage.cpp index 42858fa84..b8744f694 100644 --- a/src/client/render/secondstage.cpp +++ b/src/client/render/secondstage.cpp @@ -21,7 +21,7 @@ PostProcessingStep::PostProcessingStep(u32 _shader_id, const std::vector &_t void PostProcessingStep::configureMaterial() { material.UseMipMaps = false; - material.ZBuffer = true; + material.ZBuffer = video::ECFN_LESSEQUAL; material.ZWriteEnable = video::EZW_ON; for (u32 k = 0; k < texture_map.size(); ++k) { material.TextureLayers[k].AnisotropicFilter = 0; @@ -196,7 +196,7 @@ RenderStep *addPostProcessing(RenderPipeline *pipeline, RenderStep *previousStep } if (enable_volumetric_light) { - buffer->setTexture(TEXTURE_VOLUME, scale, "volume", color_format); + buffer->setTexture(TEXTURE_VOLUME, scale, "volume", bloom_format); shader_id = client->getShaderSource()->getShaderRaw("volumetric_light"); auto volume = pipeline->addStep(shader_id, std::vector { source, TEXTURE_DEPTH }); diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index fe3c17936..807c5816a 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -50,8 +50,7 @@ void FpsControl::limit(IrrlichtDevice *device, f32 *dtime, bool assume_paused) if (busy_time < frametime_min) { sleep_time = frametime_min - busy_time; - if (sleep_time > 0) - sleep_us(sleep_time); + porting::preciseSleepUs(sleep_time); } else { sleep_time = 0; } diff --git a/src/client/shader.cpp b/src/client/shader.cpp index d35f02ec5..656b060bd 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -687,13 +687,23 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: case TILE_MATERIAL_WAVING_LIQUID_OPAQUE: case TILE_MATERIAL_WAVING_LIQUID_BASIC: - case TILE_MATERIAL_LIQUID_TRANSPARENT: shaders_header << "#define MATERIAL_WAVING_LIQUID 1\n"; break; default: shaders_header << "#define MATERIAL_WAVING_LIQUID 0\n"; break; } + switch (material_type) { + case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT: + case TILE_MATERIAL_WAVING_LIQUID_OPAQUE: + case TILE_MATERIAL_WAVING_LIQUID_BASIC: + case TILE_MATERIAL_LIQUID_TRANSPARENT: + shaders_header << "#define MATERIAL_WATER_REFLECTIONS 1\n"; + break; + default: + shaders_header << "#define MATERIAL_WATER_REFLECTIONS 0\n"; + break; + } shaders_header << "#define ENABLE_WAVING_LEAVES " << g_settings->getBool("enable_waving_leaves") << "\n"; shaders_header << "#define ENABLE_WAVING_PLANTS " << g_settings->getBool("enable_waving_plants") << "\n"; diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp index a5eea8f38..a186fc41b 100644 --- a/src/client/shadows/dynamicshadows.cpp +++ b/src/client/shadows/dynamicshadows.cpp @@ -13,18 +13,6 @@ using m4f = core::matrix4; -static v3f quantizeDirection(v3f direction, float step) -{ - - float yaw = std::atan2(direction.Z, direction.X); - float pitch = std::asin(direction.Y); // assume look is normalized - - yaw = std::floor(yaw / step) * step; - pitch = std::floor(pitch / step) * step; - - return v3f(std::cos(yaw)*std::cos(pitch), std::sin(pitch), std::sin(yaw)*std::cos(pitch)); -} - void DirectionalLight::createSplitMatrices(const Camera *cam) { static const float COS_15_DEG = 0.965926f; @@ -74,7 +62,7 @@ void DirectionalLight::createSplitMatrices(const Camera *cam) v3f boundVec = (cam_pos_scene + farCorner * sfFar) - center_scene; float radius = boundVec.getLength(); float length = radius * 3.0f; - v3f eye_displacement = quantizeDirection(direction, M_PI / 2880 /*15 seconds*/) * length; + v3f eye_displacement = direction * length; // we must compute the viewmat with the position - the camera offset // but the future_frustum position must be the actual world position diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 2c9e4234d..958ffa953 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -27,7 +27,7 @@ static video::SMaterial baseMaterial() video::SMaterial mat; mat.ZBuffer = video::ECFN_DISABLED; mat.ZWriteEnable = video::EZW_OFF; - mat.AntiAliasing = 0; + mat.AntiAliasing = video::EAAM_OFF; mat.TextureLayers[0].TextureWrapU = video::ETC_CLAMP_TO_EDGE; mat.TextureLayers[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; mat.BackfaceCulling = false; diff --git a/src/client/tile.h b/src/client/tile.h index cb42efa02..4f5691bc5 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -50,6 +50,11 @@ struct FrameSpec video::ITexture *texture = nullptr; }; +/** + * We have two tile layers: + * layer 0 = base + * layer 1 = overlay + */ #define MAX_TILE_LAYERS 2 //! Defines a layer of a tile. diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 4239c4121..f03de2a5b 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -21,8 +21,8 @@ #include #include "client/renderingengine.h" -#define WIELD_SCALE_FACTOR 30.0 -#define WIELD_SCALE_FACTOR_EXTRUDED 40.0 +#define WIELD_SCALE_FACTOR 30.0f +#define WIELD_SCALE_FACTOR_EXTRUDED 40.0f #define MIN_EXTRUSION_MESH_RESOLUTION 16 #define MAX_EXTRUSION_MESH_RESOLUTION 512 @@ -225,18 +225,6 @@ WieldMeshSceneNode::~WieldMeshSceneNode() g_extrusion_mesh_cache = nullptr; } -void WieldMeshSceneNode::setCube(const ContentFeatures &f, - v3f wield_scale) -{ - scene::IMesh *cubemesh = g_extrusion_mesh_cache->createCube(); - scene::SMesh *copy = cloneMesh(cubemesh); - cubemesh->drop(); - postProcessNodeMesh(copy, f, false, &m_material_type, &m_colors, true); - changeToMesh(copy); - copy->drop(); - m_meshnode->setScale(wield_scale * WIELD_SCALE_FACTOR); -} - void WieldMeshSceneNode::setExtruded(const std::string &imagename, const std::string &overlay_name, v3f wield_scale, ITextureSource *tsrc, u8 num_frames) @@ -251,6 +239,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, core::dimension2d dim = texture->getSize(); // Detect animation texture and pull off top frame instead of using entire thing + // FIXME: this is quite unportable, we should be working with the original TileLayer if there's one if (num_frames > 1) { u32 frame_height = dim.Height / num_frames; dim = core::dimension2d(dim.Width, frame_height); @@ -262,11 +251,13 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, mesh->getMeshBuffer(0)->getMaterial().setTexture(0, tsrc->getTexture(imagename)); if (overlay_texture) { + // duplicate the extruded mesh for the overlay scene::IMeshBuffer *copy = cloneMeshBuffer(mesh->getMeshBuffer(0)); copy->getMaterial().setTexture(0, overlay_texture); mesh->addMeshBuffer(copy); copy->drop(); } + mesh->recalculateBoundingBox(); changeToMesh(mesh); mesh->drop(); @@ -292,14 +283,10 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, } } -static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n, +static scene::SMesh *createGenericNodeMesh(Client *client, MapNode n, std::vector *colors, const ContentFeatures &f) { - MeshMakeData mesh_make_data(client->ndef(), 1); - MeshCollector collector(v3f(0.0f * BS), v3f()); - mesh_make_data.setSmoothLighting(false); - MapblockMeshGenerator gen(&mesh_make_data, &collector); - + n.setParam1(0xff); if (n.getParam2()) { // keep it } else if (f.param_type_2 == CPT2_WALLMOUNTED || @@ -313,29 +300,43 @@ static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n, } else if (f.drawtype == NDT_SIGNLIKE || f.drawtype == NDT_TORCHLIKE) { n.setParam2(1); } - gen.renderSingle(n.getContent(), n.getParam2()); + + MeshCollector collector(v3f(0), v3f()); + { + MeshMakeData mmd(client->ndef(), 1, MeshGrid{1}); + mmd.fillSingleNode(n); + MapblockMeshGenerator(&mmd, &collector).generate(); + } colors->clear(); scene::SMesh *mesh = new scene::SMesh(); - for (auto &prebuffers : collector.prebuffers) + for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { + auto &prebuffers = collector.prebuffers[layer]; for (PreMeshBuffer &p : prebuffers) { if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) { const FrameSpec &frame = (*p.layer.frames)[0]; p.layer.texture = frame.texture; } - for (video::S3DVertex &v : p.vertices) { + for (video::S3DVertex &v : p.vertices) v.Color.setAlpha(255); - } - scene::SMeshBuffer *buf = new scene::SMeshBuffer(); - buf->Material.setTexture(0, p.layer.texture); - p.layer.applyMaterialOptions(buf->Material); - mesh->addMeshBuffer(buf); + + auto buf = make_irr(); 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)); + + // Set up material + buf->Material.setTexture(0, p.layer.texture); + if (layer == 1) { + buf->Material.PolygonOffsetSlopeScale = -1; + buf->Material.PolygonOffsetDepthBias = -1; + } + p.layer.applyMaterialOptions(buf->Material); + + mesh->addMeshBuffer(buf.get()); + colors->emplace_back(p.layer.has_color, p.layer.color); } + } + mesh->recalculateBoundingBox(); return mesh; } @@ -375,15 +376,12 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che } // Handle nodes - // See also CItemDefManager::createClientCached() if (def.type == ITEM_NODE) { - bool cull_backface = f.needsBackfaceCulling(); - // Select rendering method switch (f.drawtype) { case NDT_AIRLIKE: setExtruded("no_texture_airlike.png", "", - v3f(1.0, 1.0, 1.0), tsrc, 1); + v3f(1), tsrc, 1); break; case NDT_SIGNLIKE: case NDT_TORCHLIKE: @@ -393,38 +391,33 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che v3f wscale = wield_scale; if (f.drawtype == NDT_FLOWINGLIQUID) wscale.Z *= 0.1f; - setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id), - tsrc->getTextureName(f.tiles[0].layers[1].texture_id), - wscale, tsrc, - f.tiles[0].layers[0].animation_frame_count); - // Add color const TileLayer &l0 = f.tiles[0].layers[0]; - m_colors.emplace_back(l0.has_color, l0.color); const TileLayer &l1 = f.tiles[0].layers[1]; + setExtruded(tsrc->getTextureName(l0.texture_id), + tsrc->getTextureName(l1.texture_id), + wscale, tsrc, + l0.animation_frame_count); + // Add color + m_colors.emplace_back(l0.has_color, l0.color); m_colors.emplace_back(l1.has_color, l1.color); break; } case NDT_PLANTLIKE_ROOTED: { - setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), - "", wield_scale, tsrc, - f.special_tiles[0].layers[0].animation_frame_count); - // Add color + // use the plant tile const TileLayer &l0 = f.special_tiles[0].layers[0]; + setExtruded(tsrc->getTextureName(l0.texture_id), + "", wield_scale, tsrc, + l0.animation_frame_count); m_colors.emplace_back(l0.has_color, l0.color); break; } - case NDT_NORMAL: - case NDT_ALLFACES: - case NDT_LIQUID: - setCube(f, wield_scale); - break; default: { - // Render non-trivial drawtypes like the actual node + // Render all other drawtypes like the actual node MapNode n(id); if (def.place_param2) n.setParam2(*def.place_param2); - mesh = createSpecialNodeMesh(client, n, &m_colors, f); + mesh = createGenericNodeMesh(client, n, &m_colors, f); changeToMesh(mesh); mesh->drop(); m_meshnode->setScale( @@ -437,9 +430,9 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che u32 material_count = m_meshnode->getMaterialCount(); for (u32 i = 0; i < material_count; ++i) { video::SMaterial &material = m_meshnode->getMaterial(i); + // FIXME: overriding this breaks different alpha modes the mesh may have material.MaterialType = m_material_type; material.MaterialTypeParam = 0.5f; - material.BackfaceCulling = cull_backface; material.forEachTexture([this] (auto &tex) { setMaterialFilters(tex, m_bilinear_filter, m_trilinear_filter, m_anisotropic_filter); @@ -550,8 +543,6 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) // Shading is on by default result->needs_shading = true; - bool cull_backface = f.needsBackfaceCulling(); - // If inventory_image is defined, it overrides everything else const std::string inventory_image = item.getInventoryImage(idef); const std::string inventory_overlay = item.getInventoryOverlay(idef); @@ -568,51 +559,32 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) result->needs_shading = false; } else if (def.type == ITEM_NODE) { switch (f.drawtype) { - case NDT_NORMAL: - case NDT_ALLFACES: - case NDT_LIQUID: - case NDT_FLOWINGLIQUID: { - scene::IMesh *cube = g_extrusion_mesh_cache->createCube(); - mesh = cloneMesh(cube); - cube->drop(); - if (f.drawtype == NDT_FLOWINGLIQUID) { - scaleMesh(mesh, v3f(1.2, 0.03, 1.2)); - translateMesh(mesh, v3f(0, -0.57, 0)); - } else - scaleMesh(mesh, v3f(1.2, 1.2, 1.2)); - // add overlays - postProcessNodeMesh(mesh, f, false, nullptr, - &result->buffer_colors, true); - if (f.drawtype == NDT_ALLFACES) - scaleMesh(mesh, v3f(f.visual_scale)); - break; - } case NDT_PLANTLIKE: { - mesh = getExtrudedMesh(tsrc, - tsrc->getTextureName(f.tiles[0].layers[0].texture_id), - tsrc->getTextureName(f.tiles[0].layers[1].texture_id)); - // Add color const TileLayer &l0 = f.tiles[0].layers[0]; - result->buffer_colors.emplace_back(l0.has_color, l0.color); const TileLayer &l1 = f.tiles[0].layers[1]; + mesh = getExtrudedMesh(tsrc, + tsrc->getTextureName(l0.texture_id), + tsrc->getTextureName(l1.texture_id)); + // Add color + result->buffer_colors.emplace_back(l0.has_color, l0.color); result->buffer_colors.emplace_back(l1.has_color, l1.color); break; } case NDT_PLANTLIKE_ROOTED: { - mesh = getExtrudedMesh(tsrc, - tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id), ""); - // Add color + // Use the plant tile const TileLayer &l0 = f.special_tiles[0].layers[0]; + mesh = getExtrudedMesh(tsrc, + tsrc->getTextureName(l0.texture_id), ""); result->buffer_colors.emplace_back(l0.has_color, l0.color); break; } default: { - // Render non-trivial drawtypes like the actual node + // Render all other drawtypes like the actual node MapNode n(id); if (def.place_param2) n.setParam2(*def.place_param2); - mesh = createSpecialNodeMesh(client, n, &result->buffer_colors, f); + mesh = createGenericNodeMesh(client, n, &result->buffer_colors, f); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); break; } @@ -621,13 +593,13 @@ 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(); + // FIXME: overriding this breaks different alpha modes the mesh may have material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.MaterialTypeParam = 0.5f; material.forEachTexture([] (auto &tex) { tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; tex.MagFilter = video::ETMAGF_NEAREST; }); - material.BackfaceCulling = cull_backface; } rotateMeshXZby(mesh, -45); @@ -648,7 +620,7 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename, const std::string &overlay_name) { // check textures - video::ITexture *texture = tsrc->getTextureForMesh(imagename); + video::ITexture *texture = tsrc->getTexture(imagename); if (!texture) { return NULL; } @@ -683,56 +655,7 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.MaterialTypeParam = 0.5f; } - scaleMesh(mesh, v3f(2.0, 2.0, 2.0)); + scaleMesh(mesh, v3f(2)); return mesh; } - -void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, - bool set_material, const video::E_MATERIAL_TYPE *mattype, - std::vector *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 (apply_scale && tile->world_aligned) { - u32 n = buf->getVertexCount(); - for (u32 k = 0; k != n; ++k) - buf->getTCoords(k) /= layer->scale; - } - } - } -} diff --git a/src/client/wieldmesh.h b/src/client/wieldmesh.h index 6e92af9f6..0b65dfbd9 100644 --- a/src/client/wieldmesh.h +++ b/src/client/wieldmesh.h @@ -92,7 +92,6 @@ public: WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id = -1); virtual ~WieldMeshSceneNode(); - void setCube(const ContentFeatures &f, v3f wield_scale); void setExtruded(const std::string &imagename, const std::string &overlay_image, v3f wield_scale, ITextureSource *tsrc, u8 num_frames); void setItem(const ItemStack &item, Client *client, @@ -143,14 +142,3 @@ 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 set_material, const video::E_MATERIAL_TYPE *mattype, - std::vector *colors, bool apply_scale = false); diff --git a/src/content/content.h b/src/content/content.h index 2a98efe11..215a28174 100644 --- a/src/content/content.h +++ b/src/content/content.h @@ -3,9 +3,8 @@ // Copyright (C) 2018 rubenwardy #pragma once -#include "config.h" -#include "convert_json.h" #include "irrlichttypes.h" +#include enum class ContentType { diff --git a/src/content/mod_configuration.cpp b/src/content/mod_configuration.cpp index 37d2eadd4..810ea7626 100644 --- a/src/content/mod_configuration.cpp +++ b/src/content/mod_configuration.cpp @@ -47,7 +47,7 @@ void ModConfiguration::addMods(const std::vector &new_mods) } // Add new mods - for (int want_from_modpack = 1; want_from_modpack >= 0; --want_from_modpack) { + for (bool want_from_modpack : {true, false}) { // First iteration: // Add all the mods that come from modpacks // Second iteration: @@ -56,9 +56,12 @@ void ModConfiguration::addMods(const std::vector &new_mods) std::set seen_this_iteration; for (const ModSpec &mod : new_mods) { - if (mod.part_of_modpack != (bool)want_from_modpack) + if (mod.part_of_modpack != want_from_modpack) continue; + // unrelated to this code, but we want to assert it somewhere + assert(fs::IsPathAbsolute(mod.path)); + if (existing_mods.count(mod.name) == 0) { // GOOD CASE: completely new mod. m_unsatisfied_mods.push_back(mod); diff --git a/src/content/mods.cpp b/src/content/mods.cpp index a95ea0227..1c100f5a1 100644 --- a/src/content/mods.cpp +++ b/src/content/mods.cpp @@ -167,6 +167,7 @@ std::map getModsInPath( mod_path.clear(); mod_path.append(path).append(DIR_DELIM).append(modname); + mod_path = fs::AbsolutePath(mod_path); mod_virtual_path.clear(); // Intentionally uses / to keep paths same on different platforms @@ -174,7 +175,7 @@ std::map getModsInPath( ModSpec spec(modname, mod_path, part_of_modpack, mod_virtual_path); parseModContents(spec); - result.insert(std::make_pair(modname, spec)); + result[modname] = std::move(spec); } return result; } diff --git a/src/content/mods.h b/src/content/mods.h index b6083fe19..a7e1e5041 100644 --- a/src/content/mods.h +++ b/src/content/mods.h @@ -25,7 +25,7 @@ struct ModSpec { std::string name; std::string author; - std::string path; + std::string path; // absolute path on disk std::string desc; int release = 0; diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp index d0644248e..980f0188c 100644 --- a/src/content/subgames.cpp +++ b/src/content/subgames.cpp @@ -22,9 +22,8 @@ namespace { -bool getGameMinetestConfig(const std::string &game_path, Settings &conf) +bool getGameConfig(const std::string &game_path, Settings &conf) { - // TODO: rename this std::string conf_path = game_path + DIR_DELIM + "minetest.conf"; return conf.readConfigFile(conf_path.c_str()); } @@ -355,7 +354,7 @@ void loadGameConfAndInitWorld(const std::string &path, const std::string &name, game_settings = Settings::createLayer(SL_GAME); } - getGameMinetestConfig(gamespec.path, *game_settings); + getGameConfig(gamespec.path, *game_settings); game_settings->removeSecureSettings(); infostream << "Initializing world at " << final_path << std::endl; diff --git a/src/debug.h b/src/debug.h index e1c9c6298..ecd3f8e18 100644 --- a/src/debug.h +++ b/src/debug.h @@ -6,8 +6,7 @@ #include #include -#include "gettime.h" -#include "log.h" +#include "log.h" // unused. for convenience. #ifdef _MSC_VER #define FUNCTION_NAME __FUNCTION__ diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index db758593c..349e0a430 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -82,7 +82,6 @@ void set_default_settings() // Client settings->setDefault("address", ""); - settings->setDefault("enable_sound", "true"); #if defined(__unix__) && !defined(__APPLE__) && !defined (__ANDROID__) // On Linux+X11 (not Linux+Wayland or Linux+XWayland), I've encountered a bug // where fake mouse events were generated from touch events if in relative @@ -270,7 +269,6 @@ void set_default_settings() settings->setDefault("cinematic", "false"); settings->setDefault("camera_smoothing", "0.0"); settings->setDefault("cinematic_camera_smoothing", "0.7"); - settings->setDefault("enable_clouds", "true"); settings->setDefault("view_bobbing_amount", "1.0"); settings->setDefault("fall_bobbing_amount", "0.03"); settings->setDefault("enable_3d_clouds", "true"); @@ -292,14 +290,11 @@ void set_default_settings() settings->setDefault("hud_scaling", "1.0"); settings->setDefault("gui_scaling", "1.0"); settings->setDefault("gui_scaling_filter", "false"); - settings->setDefault("gui_scaling_filter_txr2img", "true"); settings->setDefault("smooth_scrolling", "true"); - settings->setDefault("desynchronize_mapblock_texture_animation", "false"); settings->setDefault("hud_hotbar_max_width", "1.0"); settings->setDefault("enable_local_map_saving", "false"); settings->setDefault("show_entity_selectionbox", "false"); settings->setDefault("ambient_occlusion_gamma", "1.8"); - settings->setDefault("enable_particles", "true"); settings->setDefault("arm_inertia", "true"); settings->setDefault("show_nametag_backgrounds", "true"); settings->setDefault("show_block_bounds_radius_near", "4"); @@ -350,7 +345,7 @@ void set_default_settings() settings->setDefault("shadow_map_color", "false"); settings->setDefault("shadow_filters", "1"); settings->setDefault("shadow_poisson_filter", "true"); - settings->setDefault("shadow_update_frames", "8"); + settings->setDefault("shadow_update_frames", "16"); settings->setDefault("shadow_soft_radius", "5.0"); settings->setDefault("shadow_sky_body_orbit_tilt", "0.0"); settings->setDefault("enable_sun_tint", "false"); @@ -420,7 +415,6 @@ void set_default_settings() #endif // Server - settings->setDefault("disable_escape_sequences", "false"); settings->setDefault("strip_color_codes", "false"); #ifndef NDEBUG settings->setDefault("random_mod_load_order", "true"); @@ -440,7 +434,6 @@ void set_default_settings() settings->setDefault("protocol_version_min", "1"); settings->setDefault("player_transfer_distance", "0"); settings->setDefault("max_simultaneous_block_sends_per_client", "40"); - settings->setDefault("time_send_interval", "5"); settings->setDefault("motd", ""); settings->setDefault("max_users", "15"); diff --git a/src/emerge.h b/src/emerge.h index 85ce9875f..c7a7d62d4 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -8,7 +8,6 @@ #include #include "network/networkprotocol.h" #include "irr_v3d.h" -#include "util/container.h" #include "util/metricsbackend.h" #include "mapgen/mapgen.h" // for MapgenParams #include "map.h" diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 689ad22e0..31df2a944 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -16,6 +16,7 @@ #include "gettext.h" #include "irrlicht_changes/CGUITTFont.h" #include "util/string.h" +#include "guiScrollBar.h" #include inline u32 clamp_u8(s32 value) @@ -28,6 +29,11 @@ inline bool isInCtrlKeys(const irr::EKEY_CODE& kc) return kc == KEY_LCONTROL || kc == KEY_RCONTROL || kc == KEY_CONTROL; } +inline u32 getScrollbarSize(IGUIEnvironment* env) +{ + return env->getSkin()->getSize(gui::EGDS_SCROLLBAR_SIZE); +} + GUIChatConsole::GUIChatConsole( gui::IGUIEnvironment* env, gui::IGUIElement* parent, @@ -62,15 +68,14 @@ GUIChatConsole::GUIChatConsole( } const u16 chat_font_size = g_settings->getU16("chat_font_size"); - m_font = g_fontengine->getFont(chat_font_size != 0 ? - rangelim(chat_font_size, 5, 72) : FONT_SIZE_UNSPECIFIED, FM_Mono); + m_font.grab(g_fontengine->getFont(chat_font_size != 0 ? + rangelim(chat_font_size, 5, 72) : FONT_SIZE_UNSPECIFIED, FM_Mono)); if (!m_font) { errorstream << "GUIChatConsole: Unable to load mono font" << std::endl; } else { core::dimension2d dim = m_font->getDimension(L"M"); m_fontsize = v2u32(dim.Width, dim.Height); - m_font->grab(); } m_fontsize.X = MYMAX(m_fontsize.X, 1); m_fontsize.Y = MYMAX(m_fontsize.Y, 1); @@ -81,12 +86,11 @@ GUIChatConsole::GUIChatConsole( // track ctrl keys for mouse event m_is_ctrl_down = false; m_cache_clickable_chat_weblinks = g_settings->getBool("clickable_chat_weblinks"); -} -GUIChatConsole::~GUIChatConsole() -{ - if (m_font) - m_font->drop(); + m_scrollbar.reset(new GUIScrollBar(env, this, -1, core::rect(0, 0, 30, m_height), false, true, tsrc)); + m_scrollbar->setSubElement(true); + m_scrollbar->setLargeStep(1); + m_scrollbar->setSmallStep(1); } void GUIChatConsole::openConsole(f32 scale) @@ -121,6 +125,7 @@ void GUIChatConsole::closeConsole() m_open = false; Environment->removeFocus(this); m_menumgr->deletingMenu(this); + m_scrollbar->setVisible(false); } void GUIChatConsole::closeConsoleAtOnce() @@ -180,6 +185,10 @@ void GUIChatConsole::draw() m_screensize = screensize; m_desired_height = m_desired_height_fraction * m_screensize.Y; reformatConsole(); + } else if (!m_scrollbar->getAbsolutePosition().isPointInside(core::vector2di(screensize.X, m_height))) { + // the height of the chat window is no longer the height of the scrollbar + // happens while opening/closing the window + updateScrollbar(true); } // Animation @@ -204,6 +213,9 @@ void GUIChatConsole::reformatConsole() s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt if (cols <= 0 || rows <= 0) cols = rows = 0; + + updateScrollbar(true); + recalculateConsolePosition(); m_chat_backend->reformat(cols, rows); } @@ -293,10 +305,17 @@ void GUIChatConsole::drawBackground() void GUIChatConsole::drawText() { - if (m_font == NULL) + if (!m_font) return; ChatBuffer& buf = m_chat_backend->getConsoleBuffer(); + + core::recti rect; + if (m_scrollbar->isVisible()) + rect = core::rect (0, 0, m_screensize.X - getScrollbarSize(Environment), m_height); + else + rect = AbsoluteClippingRect; + for (u32 row = 0; row < buf.getRows(); ++row) { const ChatFormattedLine& line = buf.getFormattedLine(row); @@ -315,13 +334,13 @@ void GUIChatConsole::drawText() if (m_font->getType() == irr::gui::EGFT_CUSTOM) { // Draw colored text if possible - gui::CGUITTFont *tmp = static_cast(m_font); + auto *tmp = static_cast(m_font.get()); tmp->draw( fragment.text, destrect, false, false, - &AbsoluteClippingRect); + &rect); } else { // Otherwise use standard text m_font->draw( @@ -330,10 +349,12 @@ void GUIChatConsole::drawText() video::SColor(255, 255, 255, 255), false, false, - &AbsoluteClippingRect); + &rect); } } } + + updateScrollbar(); } void GUIChatConsole::drawPrompt() @@ -680,6 +701,11 @@ bool GUIChatConsole::OnEvent(const SEvent& event) prompt.input(std::wstring(event.StringInput.Str->c_str())); return true; } + else if (event.EventType == EET_GUI_EVENT && event.GUIEvent.EventType == EGET_SCROLL_BAR_CHANGED && + (void*) event.GUIEvent.Caller == (void*) m_scrollbar.get()) + { + m_chat_backend->getConsoleBuffer().scrollAbsolute(m_scrollbar->getPos()); + } return Parent ? Parent->OnEvent(event) : false; } @@ -692,6 +718,7 @@ void GUIChatConsole::setVisible(bool visible) m_height = 0; recalculateConsolePosition(); } + m_scrollbar->setVisible(visible); } bool GUIChatConsole::weblinkClick(s32 col, s32 row) @@ -763,3 +790,18 @@ void GUIChatConsole::updatePrimarySelection() std::string selected = wide_to_utf8(wselected); Environment->getOSOperator()->copyToPrimarySelection(selected.c_str()); } + +void GUIChatConsole::updateScrollbar(bool update_size) +{ + ChatBuffer &buf = m_chat_backend->getConsoleBuffer(); + m_scrollbar->setMin(buf.getTopScrollPos()); + m_scrollbar->setMax(buf.getBottomScrollPos()); + m_scrollbar->setPos(buf.getScrollPosition()); + m_scrollbar->setPageSize(m_fontsize.Y * buf.getLineCount()); + m_scrollbar->setVisible(m_scrollbar->getMin() != m_scrollbar->getMax()); + + if (update_size) { + const core::rect rect (m_screensize.X - getScrollbarSize(Environment), 0, m_screensize.X, m_height); + m_scrollbar->setRelativePosition(rect); + } +} diff --git a/src/gui/guiChatConsole.h b/src/gui/guiChatConsole.h index 8e6c32fcd..9b1309a6e 100644 --- a/src/gui/guiChatConsole.h +++ b/src/gui/guiChatConsole.h @@ -8,8 +8,10 @@ #include "modalMenu.h" #include "chat.h" #include "config.h" +#include "irr_ptr.h" class Client; +class GUIScrollBar; class GUIChatConsole : public gui::IGUIElement { @@ -20,7 +22,6 @@ public: ChatBackend* backend, Client* client, IMenuManager* menumgr); - virtual ~GUIChatConsole(); // Open the console (height = desired fraction of screen size) // This doesn't open immediately but initiates an animation. @@ -76,10 +77,13 @@ private: // If the selected text changed, we need to update the (X11) primary selection. void updatePrimarySelection(); + void updateScrollbar(bool update_size = false); + private: ChatBackend* m_chat_backend; Client* m_client; IMenuManager* m_menumgr; + irr_ptr m_scrollbar; // current screen size v2u32 m_screensize; @@ -116,7 +120,7 @@ private: video::SColor m_background_color = video::SColor(255, 0, 0, 0); // font - gui::IGUIFont *m_font = nullptr; + irr_ptr m_font; v2u32 m_fontsize; // Enable clickable chat weblinks diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 912bdbc75..21b529d0b 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -130,7 +130,7 @@ GUIEngine::GUIEngine(JoystickController *joystick, // create soundmanager #if USE_SOUND - if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) { + if (g_sound_manager_singleton.get()) { m_sound_manager = createOpenALSoundManager(g_sound_manager_singleton.get(), std::make_unique()); } diff --git a/src/gui/mainmenumanager.h b/src/gui/mainmenumanager.h index 9d10e3960..87751bb62 100644 --- a/src/gui/mainmenumanager.h +++ b/src/gui/mainmenumanager.h @@ -11,6 +11,12 @@ #include #include +#include "IGUIEnvironment.h" + +namespace irr::gui { + class IGUIStaticText; +} + class IGameCallback { public: diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h index 62cbcfc1d..81888e866 100644 --- a/src/gui/modalMenu.h +++ b/src/gui/modalMenu.h @@ -4,8 +4,10 @@ #pragma once -#include "irrlichttypes_extrabloated.h" +#include "IGUIElement.h" +#include "irrlichttypes_bloated.h" #include "irr_ptr.h" + #include "util/string.h" #ifdef __ANDROID__ #include diff --git a/src/gui/touchscreeneditor.h b/src/gui/touchscreeneditor.h index dc06fb224..6c70eb693 100644 --- a/src/gui/touchscreeneditor.h +++ b/src/gui/touchscreeneditor.h @@ -13,6 +13,7 @@ class ISimpleTextureSource; namespace irr::gui { + class IGUIButton; class IGUIImage; } diff --git a/src/lighting.cpp b/src/lighting.cpp deleted file mode 100644 index b8def7728..000000000 --- a/src/lighting.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// Luanti -// SPDX-License-Identifier: LGPL-2.1-or-later -// Copyright (C) 2021 x2048, Dmitry Kostenko - -#include "lighting.h" - -AutoExposure::AutoExposure() - : luminance_min(-3.f), - luminance_max(-3.f), - exposure_correction(0.0f), - speed_dark_bright(1000.f), - speed_bright_dark(1000.f), - center_weight_power(1.f) -{} diff --git a/src/lighting.h b/src/lighting.h index aaf49983f..216bbfb59 100644 --- a/src/lighting.h +++ b/src/lighting.h @@ -29,7 +29,14 @@ struct AutoExposure /// @brief Power value for center-weighted metering. Value of 1.0 measures entire screen uniformly float center_weight_power; - AutoExposure(); + constexpr AutoExposure() + : luminance_min(-3.f), + luminance_max(-3.f), + exposure_correction(0.0f), + speed_dark_bright(1000.f), + speed_bright_dark(1000.f), + center_weight_power(1.f) + {} }; /** diff --git a/src/map.cpp b/src/map.cpp index 240788944..e8ccac0cc 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -752,17 +752,12 @@ MMVManip::MMVManip(Map *map): assert(map); } -void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, - bool load_if_inexistent) +void MMVManip::initialEmerge(v3s16 p_min, v3s16 p_max, bool load_if_inexistent) { TimeTaker timer1("initialEmerge", &emerge_time); assert(m_map); - // Units of these are MapBlocks - v3s16 p_min = blockpos_min; - v3s16 p_max = blockpos_max; - VoxelArea block_area_nodes (p_min*MAP_BLOCKSIZE, (p_max+1)*MAP_BLOCKSIZE-v3s16(1,1,1)); @@ -775,6 +770,7 @@ void MMVManip::initialEmerge(v3s16 blockpos_min, v3s16 blockpos_max, infostream<getNode(0, 0, 0).getContent() == CONTENT_IGNORE) - { - // Mark that block was loaded as blank - flags |= VMANIP_BLOCK_CONTAINS_CIGNORE; - }*/ m_loaded_blocks[p] = flags; } - m_is_dirty = false; + if (all_new) + m_is_dirty = false; } void MMVManip::blitBackAll(std::map *modified_blocks, @@ -834,6 +826,7 @@ void MMVManip::blitBackAll(std::map *modified_blocks, /* Copy data of all blocks */ + assert(!m_loaded_blocks.empty()); for (auto &loaded_block : m_loaded_blocks) { v3s16 p = loaded_block.first; MapBlock *block = m_map->getBlockNoCreateNoEx(p); diff --git a/src/map.h b/src/map.h index 37d1a713d..a4f0e4524 100644 --- a/src/map.h +++ b/src/map.h @@ -298,9 +298,6 @@ protected: u32 needed_count); }; -#define VMANIP_BLOCK_DATA_INEXIST 1 -#define VMANIP_BLOCK_CONTAINS_CIGNORE 2 - class MMVManip : public VoxelManipulator { public: @@ -344,4 +341,8 @@ protected: value = flags describing the block */ std::map m_loaded_blocks; + + enum : u8 { + VMANIP_BLOCK_DATA_INEXIST = 1 << 0, + }; }; diff --git a/src/mapblock.cpp b/src/mapblock.cpp index f68657d9d..3039ff3e1 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -73,6 +73,12 @@ MapBlock::~MapBlock() porting::TrackFreedMemory(sizeof(MapNode) * nodecount); } +static inline size_t get_max_objects_per_block() +{ + u16 ret = g_settings->getU16("max_objects_per_block"); + return MYMAX(256, ret); +} + bool MapBlock::onObjectsActivation() { // Ignore if no stored objects (to not set changed flag) @@ -84,7 +90,7 @@ bool MapBlock::onObjectsActivation() << "activating " << count << " objects in block " << getPos() << std::endl; - if (count > g_settings->getU16("max_objects_per_block")) { + if (count > get_max_objects_per_block()) { errorstream << "suspiciously large amount of objects detected: " << count << " in " << getPos() << "; removing all of them." << std::endl; @@ -99,7 +105,7 @@ bool MapBlock::onObjectsActivation() bool MapBlock::saveStaticObject(u16 id, const StaticObject &obj, u32 reason) { - if (m_static_objects.getStoredSize() >= g_settings->getU16("max_objects_per_block")) { + if (m_static_objects.getStoredSize() >= get_max_objects_per_block()) { warningstream << "MapBlock::saveStaticObject(): Trying to store id = " << id << " statically but block " << getPos() << " already contains " << m_static_objects.getStoredSize() << " objects." diff --git a/src/mapnode.cpp b/src/mapnode.cpp index 5f706a489..22fc36086 100644 --- a/src/mapnode.cpp +++ b/src/mapnode.cpp @@ -179,130 +179,52 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, if (nodebox.type == NODEBOX_FIXED || nodebox.type == NODEBOX_LEVELED) { const auto &fixed = nodebox.fixed; int facedir = n.getFaceDir(nodemgr, true); - u8 axisdir = facedir>>2; - facedir&=0x03; + u8 axisdir = facedir >> 2; + facedir &= 0x03; boxes.reserve(boxes.size() + fixed.size()); for (aabb3f box : fixed) { if (nodebox.type == NODEBOX_LEVELED) box.MaxEdge.Y = (-0.5f + n.getLevel(nodemgr) / 64.0f) * BS; + if(facedir == 1) { + box.MinEdge.rotateXZBy(-90); + box.MaxEdge.rotateXZBy(-90); + } else if(facedir == 2) { + box.MinEdge.rotateXZBy(180); + box.MaxEdge.rotateXZBy(180); + } else if(facedir == 3) { + box.MinEdge.rotateXZBy(90); + box.MaxEdge.rotateXZBy(90); + } + switch (axisdir) { case 0: - if(facedir == 1) - { - box.MinEdge.rotateXZBy(-90); - box.MaxEdge.rotateXZBy(-90); - } - else if(facedir == 2) - { - box.MinEdge.rotateXZBy(180); - box.MaxEdge.rotateXZBy(180); - } - else if(facedir == 3) - { - box.MinEdge.rotateXZBy(90); - box.MaxEdge.rotateXZBy(90); - } break; case 1: // z+ box.MinEdge.rotateYZBy(90); box.MaxEdge.rotateYZBy(90); - if(facedir == 1) - { - box.MinEdge.rotateXYBy(90); - box.MaxEdge.rotateXYBy(90); - } - else if(facedir == 2) - { - box.MinEdge.rotateXYBy(180); - box.MaxEdge.rotateXYBy(180); - } - else if(facedir == 3) - { - box.MinEdge.rotateXYBy(-90); - box.MaxEdge.rotateXYBy(-90); - } break; case 2: //z- box.MinEdge.rotateYZBy(-90); box.MaxEdge.rotateYZBy(-90); - if(facedir == 1) - { - box.MinEdge.rotateXYBy(-90); - box.MaxEdge.rotateXYBy(-90); - } - else if(facedir == 2) - { - box.MinEdge.rotateXYBy(180); - box.MaxEdge.rotateXYBy(180); - } - else if(facedir == 3) - { - box.MinEdge.rotateXYBy(90); - box.MaxEdge.rotateXYBy(90); - } break; case 3: //x+ box.MinEdge.rotateXYBy(-90); box.MaxEdge.rotateXYBy(-90); - if(facedir == 1) - { - box.MinEdge.rotateYZBy(90); - box.MaxEdge.rotateYZBy(90); - } - else if(facedir == 2) - { - box.MinEdge.rotateYZBy(180); - box.MaxEdge.rotateYZBy(180); - } - else if(facedir == 3) - { - box.MinEdge.rotateYZBy(-90); - box.MaxEdge.rotateYZBy(-90); - } break; case 4: //x- box.MinEdge.rotateXYBy(90); box.MaxEdge.rotateXYBy(90); - if(facedir == 1) - { - box.MinEdge.rotateYZBy(-90); - box.MaxEdge.rotateYZBy(-90); - } - else if(facedir == 2) - { - box.MinEdge.rotateYZBy(180); - box.MaxEdge.rotateYZBy(180); - } - else if(facedir == 3) - { - box.MinEdge.rotateYZBy(90); - box.MaxEdge.rotateYZBy(90); - } break; case 5: box.MinEdge.rotateXYBy(-180); box.MaxEdge.rotateXYBy(-180); - if(facedir == 1) - { - box.MinEdge.rotateXZBy(90); - box.MaxEdge.rotateXZBy(90); - } - else if(facedir == 2) - { - box.MinEdge.rotateXZBy(180); - box.MaxEdge.rotateXZBy(180); - } - else if(facedir == 3) - { - box.MinEdge.rotateXZBy(-90); - box.MaxEdge.rotateXZBy(-90); - } break; default: break; } + box.repair(); boxes.push_back(box); } @@ -419,57 +341,47 @@ void transformNodeBox(const MapNode &n, const NodeBox &nodebox, boxes.reserve(boxes_size); -#define BOXESPUSHBACK(c) \ - for (std::vector::const_iterator \ - it = (c).begin(); \ - it != (c).end(); ++it) \ - (boxes).push_back(*it); + auto boxes_insert = [&](const std::vector &boxes_src) { + boxes.insert(boxes.end(), boxes_src.begin(), boxes_src.end()); + }; - BOXESPUSHBACK(nodebox.fixed); + boxes_insert(nodebox.fixed); - if (neighbors & 1) { - BOXESPUSHBACK(c.connect_top); - } else { - BOXESPUSHBACK(c.disconnected_top); - } + if (neighbors & 1) + boxes_insert(c.connect_top); + else + boxes_insert(c.disconnected_top); - if (neighbors & 2) { - BOXESPUSHBACK(c.connect_bottom); - } else { - BOXESPUSHBACK(c.disconnected_bottom); - } + if (neighbors & 2) + boxes_insert(c.connect_bottom); + else + boxes_insert(c.disconnected_bottom); - if (neighbors & 4) { - BOXESPUSHBACK(c.connect_front); - } else { - BOXESPUSHBACK(c.disconnected_front); - } + if (neighbors & 4) + boxes_insert(c.connect_front); + else + boxes_insert(c.disconnected_front); - if (neighbors & 8) { - BOXESPUSHBACK(c.connect_left); - } else { - BOXESPUSHBACK(c.disconnected_left); - } + if (neighbors & 8) + boxes_insert(c.connect_left); + else + boxes_insert(c.disconnected_left); - if (neighbors & 16) { - BOXESPUSHBACK(c.connect_back); - } else { - BOXESPUSHBACK(c.disconnected_back); - } + if (neighbors & 16) + boxes_insert(c.connect_back); + else + boxes_insert(c.disconnected_back); - if (neighbors & 32) { - BOXESPUSHBACK(c.connect_right); - } else { - BOXESPUSHBACK(c.disconnected_right); - } + if (neighbors & 32) + boxes_insert(c.connect_right); + else + boxes_insert(c.disconnected_right); - if (neighbors == 0) { - BOXESPUSHBACK(c.disconnected); - } + if (neighbors == 0) + boxes_insert(c.disconnected); - if (neighbors < 4) { - BOXESPUSHBACK(c.disconnected_sides); - } + if (neighbors < 4) + boxes_insert(c.disconnected_sides); } else // NODEBOX_REGULAR diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index cf1dcacb1..89fe3bf22 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -18,6 +18,7 @@ #include "version.h" #include "irrlicht_changes/printing.h" #include "network/connection.h" +#include "network/networkpacket.h" #include "network/networkprotocol.h" #include "network/serveropcodes.h" #include "server/player_sao.h" diff --git a/src/nodedef.h b/src/nodedef.h index d819be815..24bb0c460 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -474,21 +474,6 @@ struct ContentFeatures } } - bool needsBackfaceCulling() const - { - switch (drawtype) { - case NDT_TORCHLIKE: - case NDT_SIGNLIKE: - case NDT_FIRELIKE: - case NDT_RAILLIKE: - case NDT_PLANTLIKE: - case NDT_PLANTLIKE_ROOTED: - case NDT_MESH: - return false; - default: - return true; - } - } bool isLiquid() const{ return (liquid_type != LIQUID_NONE); diff --git a/src/objdef.cpp b/src/objdef.cpp index f61d12f30..b8b11b7b7 100644 --- a/src/objdef.cpp +++ b/src/objdef.cpp @@ -6,6 +6,9 @@ #include "util/numeric.h" #include "log.h" #include "gamedef.h" +#include "porting.h" // strcasecmp + +#include ObjDefManager::ObjDefManager(IGameDef *gamedef, ObjDefType type) { diff --git a/src/objdef.h b/src/objdef.h index 93dd78837..ec52aa831 100644 --- a/src/objdef.h +++ b/src/objdef.h @@ -4,8 +4,9 @@ #pragma once -#include "util/basic_macros.h" -#include "porting.h" +#include "util/basic_macros.h" // DISABLE_CLASS_COPY +#include "irrlichttypes.h" +#include #include class IGameDef; diff --git a/src/porting.h b/src/porting.h index d6cec6c1a..8b753f518 100644 --- a/src/porting.h +++ b/src/porting.h @@ -32,12 +32,16 @@ #define sleep_ms(x) Sleep(x) #define sleep_us(x) Sleep((x)/1000) + #define SLEEP_ACCURACY_US 2000 + #define setenv(n,v,o) _putenv_s(n,v) #define unsetenv(n) _putenv_s(n,"") #else #include #include // setenv + #define SLEEP_ACCURACY_US 200 + #define sleep_ms(x) usleep((x)*1000) #define sleep_us(x) usleep(x) #endif @@ -220,6 +224,21 @@ inline u64 getDeltaMs(u64 old_time_ms, u64 new_time_ms) return (old_time_ms - new_time_ms); } +inline void preciseSleepUs(u64 sleep_time) +{ + if (sleep_time > 0) + { + u64 target_time = porting::getTimeUs() + sleep_time; + if (sleep_time > SLEEP_ACCURACY_US) + sleep_us(sleep_time - SLEEP_ACCURACY_US); + + // Busy-wait the remaining time to adjust for sleep inaccuracies + // The target - now > 0 construct will handle overflow gracefully (even though it should + // never happen) + while ((s64)(target_time - porting::getTimeUs()) > 0) {} + } +} + inline const char *getPlatformName() { return diff --git a/src/reflowscan.h b/src/reflowscan.h index 66fec9ea6..70890c9bc 100644 --- a/src/reflowscan.h +++ b/src/reflowscan.h @@ -5,7 +5,8 @@ #pragma once #include "util/container.h" -#include "irrlichttypes_bloated.h" +#include "irrlichttypes.h" +#include "irr_v3d.h" class NodeDefManager; class Map; diff --git a/src/server.cpp b/src/server.cpp index 91f921e0c..b9914900f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -8,6 +8,7 @@ #include #include "irr_v2d.h" #include "network/connection.h" +#include "network/networkpacket.h" #include "network/networkprotocol.h" #include "network/serveropcodes.h" #include "server/ban.h" @@ -624,6 +625,11 @@ void Server::AsyncRunStep(float dtime, bool initial_step) ZoneScoped; auto framemarker = FrameMarker("Server::AsyncRunStep()-frame").started(); + if (!m_async_fatal_error.get().empty()) { + infostream << "Refusing server step in error state" << std::endl; + return; + } + { // Send blocks to clients SendBlocks(dtime); @@ -649,9 +655,10 @@ void Server::AsyncRunStep(float dtime, bool initial_step) Send to clients at constant intervals */ + static const float time_send_interval = 5.0f; m_time_of_day_send_timer -= dtime; - if (m_time_of_day_send_timer < 0.0) { - m_time_of_day_send_timer = g_settings->getFloat("time_send_interval"); + if (m_time_of_day_send_timer < 0) { + m_time_of_day_send_timer = time_send_interval; u16 time = m_env->getTimeOfDay(); float time_speed = g_settings->getFloat("time_speed"); SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed); @@ -3862,6 +3869,14 @@ std::string Server::getBuiltinLuaPath() return porting::path_share + DIR_DELIM + "builtin"; } +void Server::setAsyncFatalError(const std::string &error) +{ + m_async_fatal_error.set(error); + // make sure server steps stop happening immediately + if (m_thread) + m_thread->stop(); +} + // Not thread-safe. void Server::addShutdownError(const ModError &e) { diff --git a/src/server.h b/src/server.h index 560a3452d..407d43d1b 100644 --- a/src/server.h +++ b/src/server.h @@ -344,8 +344,7 @@ public: void setStepSettings(StepSettings spdata) { m_step_settings.store(spdata); } StepSettings getStepSettings() { return m_step_settings.load(); } - inline void setAsyncFatalError(const std::string &error) - { m_async_fatal_error.set(error); } + void setAsyncFatalError(const std::string &error); inline void setAsyncFatalError(const LuaError &e) { setAsyncFatalError(std::string("Lua: ") + e.what()); diff --git a/src/server/clientiface.cpp b/src/server/clientiface.cpp index b114c6c84..d05fedbb2 100644 --- a/src/server/clientiface.cpp +++ b/src/server/clientiface.cpp @@ -6,6 +6,7 @@ #include "clientiface.h" #include "debug.h" #include "network/connection.h" +#include "network/networkpacket.h" #include "network/serveropcodes.h" #include "remoteplayer.h" #include "serialization.h" // SER_FMT_VER_INVALID diff --git a/src/server/clientiface.h b/src/server/clientiface.h index 294bcbd26..5324ee727 100644 --- a/src/server/clientiface.h +++ b/src/server/clientiface.h @@ -6,25 +6,25 @@ #include "irr_v3d.h" // for irrlicht datatypes -#include "constants.h" -#include "network/networkpacket.h" -#include "network/networkprotocol.h" #include "network/address.h" +#include "network/networkprotocol.h" // session_t #include "porting.h" #include "threading/mutex_auto_lock.h" #include "clientdynamicinfo.h" #include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include -class MapBlock; -class ServerEnvironment; class EmergeManager; +class MapBlock; +class NetworkPacket; +class ServerEnvironment; /* * State Transitions diff --git a/src/servermap.h b/src/servermap.h index 203020006..73b35e38f 100644 --- a/src/servermap.h +++ b/src/servermap.h @@ -8,8 +8,8 @@ #include #include "map.h" -#include "util/container.h" -#include "util/metricsbackend.h" +#include "util/container.h" // UniqueQueue +#include "util/metricsbackend.h" // ptr typedefs #include "map_settings_manager.h" class Settings; diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 8c20b0ed5..8f0eaf855 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -212,8 +212,6 @@ bool run_tests() u64 t1 = porting::getTimeMs(); TestGameDef gamedef; - g_logger.setLevelSilenced(LL_ERROR, true); - u32 num_modules_failed = 0; u32 num_total_tests_failed = 0; u32 num_total_tests_run = 0; @@ -243,11 +241,9 @@ bool run_tests() u64 tdiff = porting::getTimeMs() - t1; - g_logger.setLevelSilenced(LL_ERROR, false); - const char *overall_status = (num_modules_failed == 0) ? "PASSED" : "FAILED"; - rawstream + rawstream << "\n" << "++++++++++++++++++++++++++++++++++++++++" << "++++++++++++++++++++++++++++++++++++++++" << std::endl << "Unit Test Results: " << overall_status << std::endl @@ -283,17 +279,15 @@ bool run_tests(const std::string &module_name) return catch_test_failures == 0; } - g_logger.setLevelSilenced(LL_ERROR, true); u64 t1 = porting::getTimeMs(); bool ok = testmod->testModule(&gamedef); u64 tdiff = porting::getTimeMs() - t1; - g_logger.setLevelSilenced(LL_ERROR, false); const char *overall_status = ok ? "PASSED" : "FAILED"; - rawstream + rawstream << "\n" << "++++++++++++++++++++++++++++++++++++++++" << "++++++++++++++++++++++++++++++++++++++++" << std::endl << "Unit Test Results: " << overall_status << std::endl diff --git a/src/unittest/test.h b/src/unittest/test.h index cc772f670..dcecb9fb4 100644 --- a/src/unittest/test.h +++ b/src/unittest/test.h @@ -9,7 +9,7 @@ #include #include -#include "irrlichttypes_extrabloated.h" +#include "irrlichttypes_bloated.h" #include "porting.h" #include "filesys.h" #include "mapnode.h" diff --git a/src/unittest/test_compression.cpp b/src/unittest/test_compression.cpp index ad8dba2fc..35c500a52 100644 --- a/src/unittest/test_compression.cpp +++ b/src/unittest/test_compression.cpp @@ -6,7 +6,6 @@ #include -#include "irrlichttypes_extrabloated.h" #include "log.h" #include "serialization.h" #include "nodedef.h" diff --git a/src/unittest/test_content_mapblock.cpp b/src/unittest/test_content_mapblock.cpp index c357c5ea7..68f66eabf 100644 --- a/src/unittest/test_content_mapblock.cpp +++ b/src/unittest/test_content_mapblock.cpp @@ -38,8 +38,10 @@ public: MeshMakeData makeSingleNodeMMD(bool smooth_lighting = true) { - MeshMakeData data{ndef(), 1}; - data.setSmoothLighting(smooth_lighting); + MeshMakeData data{ndef(), 1, MeshGrid{1}}; + data.m_generate_minimap = false; + data.m_smooth_lighting = smooth_lighting; + data.m_enable_water_reflections = false; data.m_blockpos = {0, 0, 0}; for (s16 x = -1; x <= 1; x++) for (s16 y = -1; y <= 1; y++) diff --git a/src/unittest/test_voxelarea.cpp b/src/unittest/test_voxelarea.cpp index f594a9be7..d9380caf9 100644 --- a/src/unittest/test_voxelarea.cpp +++ b/src/unittest/test_voxelarea.cpp @@ -98,10 +98,12 @@ void TestVoxelArea::test_addarea() void TestVoxelArea::test_pad() { VoxelArea v1(v3s16(-1447, -9547, -875), v3s16(-147, 8854, 669)); + auto old_extent = v1.getExtent(); v1.pad(v3s16(100, 200, 300)); UASSERT(v1.MinEdge == v3s16(-1547, -9747, -1175)); UASSERT(v1.MaxEdge == v3s16(-47, 9054, 969)); + UASSERT(v1.getExtent() > old_extent); } void TestVoxelArea::test_extent() @@ -165,6 +167,12 @@ void TestVoxelArea::test_contains_point() UASSERTEQ(bool, v1.contains(v3s16(-100, 100, 10000)), false); UASSERTEQ(bool, v1.contains(v3s16(-100, 100, -10000)), false); UASSERTEQ(bool, v1.contains(v3s16(10000, 100, 10)), false); + + VoxelArea v2; + UASSERTEQ(bool, v2.contains(v3s16(-200, 10, 10)), false); + UASSERTEQ(bool, v2.contains(v3s16(0, 0, 0)), false); + UASSERTEQ(bool, v2.contains(v3s16(1, 1, 1)), false); + UASSERTEQ(bool, v2.contains(v3s16(-1, -1, -1)), false); } void TestVoxelArea::test_contains_i() @@ -180,6 +188,12 @@ void TestVoxelArea::test_contains_i() UASSERTEQ(bool, v2.contains(10), true); UASSERTEQ(bool, v2.contains(0), true); UASSERTEQ(bool, v2.contains(-1), false); + + VoxelArea v3; + UASSERTEQ(bool, v3.contains(0), false); + UASSERTEQ(bool, v3.contains(-1), false); + UASSERTEQ(bool, v3.contains(1), false); + UASSERTEQ(bool, v3.contains(2), false); } void TestVoxelArea::test_equal() @@ -244,6 +258,7 @@ void TestVoxelArea::test_intersect() VoxelArea v3({11, 11, 11}, {11, 11, 11}); VoxelArea v4({-11, -2, -10}, {10, 2, 11}); UASSERT(v2.intersect(v1) == v2); + UASSERT(!v2.intersect(v1).hasEmptyExtent()); UASSERT(v1.intersect(v2) == v2.intersect(v1)); UASSERT(v1.intersect(v3).hasEmptyExtent()); UASSERT(v3.intersect(v1) == v1.intersect(v3)); diff --git a/src/voxel.cpp b/src/voxel.cpp index 8f3858a1f..f74129260 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -113,6 +113,20 @@ void VoxelManipulator::print(std::ostream &o, const NodeDefManager *ndef, } } +static inline void checkArea(const VoxelArea &a) +{ + // won't overflow since cbrt(2^64) > 2^16 + u64 real_volume = static_cast(a.getExtent().X) * a.getExtent().Y * a.getExtent().Z; + + // Volume limit equal to 8 default mapchunks, (80 * 2) ^ 3 = 4,096,000 + // Note: the hard limit is somewhere around 2^31 due to s32 type + constexpr u64 MAX_ALLOWED = 4096000; + if (real_volume > MAX_ALLOWED) { + throw BaseException("VoxelManipulator: " + "Area volume exceeds allowed value of " + std::to_string(MAX_ALLOWED)); + } +} + void VoxelManipulator::addArea(const VoxelArea &area) { // Cancel if requested area has zero volume @@ -124,18 +138,10 @@ void VoxelManipulator::addArea(const VoxelArea &area) return; // Calculate new area - VoxelArea new_area; - // New area is the requested area if m_area has zero volume - if(m_area.hasEmptyExtent()) - { - new_area = area; - } - // Else add requested area to m_area - else - { - new_area = m_area; - new_area.addArea(area); - } + VoxelArea new_area = m_area; + new_area.addArea(area); + + checkArea(new_area); u32 new_size = new_area.getVolume(); diff --git a/src/voxel.h b/src/voxel.h index a35be3e19..c47f97a30 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -103,6 +103,7 @@ public: { MinEdge -= d; MaxEdge += d; + cacheExtent(); } /* @@ -188,6 +189,7 @@ public: ret.MaxEdge.Y = std::min(a.MaxEdge.Y, MaxEdge.Y); ret.MinEdge.Z = std::max(a.MinEdge.Z, MinEdge.Z); ret.MaxEdge.Z = std::min(a.MaxEdge.Z, MaxEdge.Z); + ret.cacheExtent(); return ret; } @@ -457,7 +459,9 @@ public: { if(!m_area.contains(p)) return false; - m_data[m_area.index(p)] = n; + const s32 index = m_area.index(p); + m_data[index] = n; + m_flags[index] &= ~VOXELFLAG_NO_DATA; return true; } diff --git a/textures/base/pack/cdb_update_cropped.png b/textures/base/pack/cdb_update_cropped.png index 8161dd7e4..2285c8ffc 100644 Binary files a/textures/base/pack/cdb_update_cropped.png and b/textures/base/pack/cdb_update_cropped.png differ diff --git a/textures/base/pack/checkbox_16.png b/textures/base/pack/checkbox_16.png index 567151860..fa84ff0e3 100644 Binary files a/textures/base/pack/checkbox_16.png and b/textures/base/pack/checkbox_16.png differ diff --git a/textures/base/pack/checkbox_32.png b/textures/base/pack/checkbox_32.png index 00208a0f1..bc6b2721b 100644 Binary files a/textures/base/pack/checkbox_32.png and b/textures/base/pack/checkbox_32.png differ diff --git a/textures/base/pack/server_url_unavailable.png b/textures/base/pack/server_url_unavailable.png new file mode 100644 index 000000000..8010ad469 Binary files /dev/null and b/textures/base/pack/server_url_unavailable.png differ diff --git a/textures/base/pack/server_view_clients.png b/textures/base/pack/server_view_clients.png index 87b569f93..120067074 100644 Binary files a/textures/base/pack/server_view_clients.png and b/textures/base/pack/server_view_clients.png differ diff --git a/textures/base/pack/server_view_clients_unavailable.png b/textures/base/pack/server_view_clients_unavailable.png new file mode 100644 index 000000000..964efcde1 Binary files /dev/null and b/textures/base/pack/server_view_clients_unavailable.png differ diff --git a/textures/base/pack/server_view_mods.png b/textures/base/pack/server_view_mods.png new file mode 100644 index 000000000..a2611d92d Binary files /dev/null and b/textures/base/pack/server_view_mods.png differ diff --git a/textures/base/pack/server_view_mods_unavailable.png b/textures/base/pack/server_view_mods_unavailable.png new file mode 100644 index 000000000..4238bb786 Binary files /dev/null and b/textures/base/pack/server_view_mods_unavailable.png differ diff --git a/util/ci/check_png_optimized.sh b/util/ci/check_png_optimized.sh new file mode 100755 index 000000000..856262797 --- /dev/null +++ b/util/ci/check_png_optimized.sh @@ -0,0 +1,31 @@ +#!/bin/bash -e + +# Only warn if decrease is more than 3% +optimization_requirement=3 + +git ls-files "*.png" | sort -u | ( + optimized=1 + temp_file=$(mktemp) + echo "Optimizing png files:" + while read file; do + # Does only run a fuzzy check without -o7 -zm1-9 since it would be too slow otherwise + decrease=($(optipng -nc -strip all -out "$temp_file" -clobber "$file" |& \ + sed -n 's/.*(\([0-9]\{1,\}\) bytes\? = \([0-9]\{1,\}\)\.[0-9]\{2\}% decrease).*/\1 \2/p')) + if [[ -n "${decrease[*]}" ]]; then + if [ "${decrease[1]}" -ge "$optimization_requirement" ]; then + echo -en "\033[31m" + optimized=0 + else + echo -en "\033[32m" + fi + echo -e "Decrease: ${decrease[0]}B ${decrease[1]}%\033[0m $file" + fi + done + rm "$temp_file" + + if [ "$optimized" -eq 0 ]; then + echo -e "\033[1;31mWarning: Could optimized png file(s) by more than $optimization_requirement%.\033[0m" \ + "Apply 'optipng -o7 -zm1-9 -nc -strip all -clobber '" + exit 1 + fi +)