diff --git a/doc/en/block-properties.md b/doc/en/block-properties.md
index d98a8cac5..61f44e844 100644
--- a/doc/en/block-properties.md
+++ b/doc/en/block-properties.md
@@ -69,6 +69,7 @@ Rotation profile (set of available block rotations and behaviour of placing bloc
- "pipe" - wood logs, pipes, pillars
- "pane" - panels, doors, signs
- "stairs" - "pane" + flipped variants
+- "ladder" - like a "pipe", but on horizontal surfaces it is installed like "pane"
### *particles*
diff --git a/doc/en/scripting/builtins/libgui.md b/doc/en/scripting/builtins/libgui.md
index 53d6e4ad2..a7ddbeaec 100644
--- a/doc/en/scripting/builtins/libgui.md
+++ b/doc/en/scripting/builtins/libgui.md
@@ -139,7 +139,7 @@ gui.confirm(
## Documents and templates
```lua
--- Loads a UI document with its script, returns the name of the document if successfully loaded.
+-- Loads a UI document with its script. Returns document environment table
gui.load_document(
-- Path to the xml file of the page. Example: `core:layouts/pages/main.xml`
path: str,
@@ -147,7 +147,7 @@ gui.load_document(
name: str
-- Table of parameters passed to the on_open event
args: table
-) -> str
+) -> table
-- Loads and processes layout template from file
gui.template(
diff --git a/doc/en/scripting/builtins/libhud.md b/doc/en/scripting/builtins/libhud.md
index 1d6264b2d..550659ccc 100644
--- a/doc/en/scripting/builtins/libhud.md
+++ b/doc/en/scripting/builtins/libhud.md
@@ -57,6 +57,10 @@ hud.get_block_inventory() -> int
-- Gives the ID of the second open inventory (block, virtual inventory...) or 0.
hud.get_second_inventory() -> int
+-- Get the inventory ID of exchange item (grabbed item in inventory) or 0.
+-- It has only one slot - 0
+hud.get_exchange_inventory() -> int
+
-- Gives the ID of the player that the UI is bound to.
hud.get_player() -> int
diff --git a/doc/en/scripting/events.md b/doc/en/scripting/events.md
index 75180edc3..9baa9fd85 100644
--- a/doc/en/scripting/events.md
+++ b/doc/en/scripting/events.md
@@ -24,7 +24,7 @@ Called on block broken by player
function on_replaced(x, y, z, playerid)
```
-Called on block replaced with other by player
+Called before a player replaces a block
```lua
function on_interact(x, y, z, playerid) -> bool
diff --git a/doc/en/world-generator.md b/doc/en/world-generator.md
index cd868b404..3c0a774f3 100644
--- a/doc/en/world-generator.md
+++ b/doc/en/world-generator.md
@@ -212,10 +212,27 @@ local map = Heightmap(width, height)
Operations apply to all height values.
```lua
+-- Convert to absolute values
map:abs()
-```
-Casts height values to absolute.
+-- Round down
+map:floor()
+
+-- Round up
+map:ceil()
+
+-- Round to the nearest integer
+map:round()
+
+-- Sine of height values (in radians)
+map:sin()
+
+-- Cosine of height values (in radians)
+map:cos()
+
+-- Tangent of height values (in radians)
+map:tan()
+```
### Binary Operations
diff --git a/doc/en/xml-ui-layouts.md b/doc/en/xml-ui-layouts.md
index 0bd1b5353..704a0b786 100644
--- a/doc/en/xml-ui-layouts.md
+++ b/doc/en/xml-ui-layouts.md
@@ -39,6 +39,8 @@ Examples:
- `position-func` - position supplier for an element (two numbers), called on every parent container size update or on element adding on a container. May be called before *on_hud_open*
- `size-func` - element size provider (two numbers), called when the size of the container in which the element is located changes, or when an element is added to the container. Can be called before on_hud_open is called.
- `onclick` - lua function called when an element is clicked.
+- `onrightclick` - lua function called when the right mouse button is pressed on an element.
+- `onmiddleclick` - lua function called when the middle mouse button is pressed on an element.
- `ondoubleclick` - lua function called when you double click on an element.
- `onfocus` - lua function called when focusing on an element.
- `ondefocus` - lua function called when the element loses focus.
@@ -192,6 +194,9 @@ Example of list description:
- `width` - minimum content width. Default: 100.
- `selected` - initially selected value. Default: "".
- `onselect` - function to which the user-selected value is passed
+- `mode` - behaviour / display mode:
+ - `select` - default mode.
+ - `button` - button mode: the text is not replaced with the selected option; the element rendered as a regular button.
## *bindbox*
diff --git a/doc/ru/block-properties.md b/doc/ru/block-properties.md
index e139f635d..6d613d888 100644
--- a/doc/ru/block-properties.md
+++ b/doc/ru/block-properties.md
@@ -70,6 +70,7 @@
- "pipe" - профиль "труба". Примеры блоков: бревно, труба, лампочка
- "pane" - профиль "панель". Примеры блоков: панель, дверь, табличка
- "stairs" - профиль "ступеньки" ("pane" + перевернутые варианты)
+- "ladder" - профиль "лестница" (как "pipe", но на горизонтальных поверхностях как "pane")
### Испускаемые частицы - *particles*
diff --git a/doc/ru/scripting/builtins/libgui.md b/doc/ru/scripting/builtins/libgui.md
index 9c2eb7418..ea1221415 100644
--- a/doc/ru/scripting/builtins/libgui.md
+++ b/doc/ru/scripting/builtins/libgui.md
@@ -140,12 +140,12 @@ gui.confirm(
## Документы и шаблоны
```lua
--- Загружает UI документ и его скрипт. Возвращает имя документа.
+-- Загружает UI документ и его скрипт. Возвращает пространство имён документа
gui.load_document(
path: string, -- путь к xml файлу, например: core:layouts/pages/main.xml
name: string, -- id документа, например: core:pages/main
args: table -- параметры для события on_open
-) -> string
+) -> table
-- Обрабатывает xml шаблон макета из файла
gui.template(
diff --git a/doc/ru/scripting/builtins/libhud.md b/doc/ru/scripting/builtins/libhud.md
index bad0e93c6..7a93c93e4 100644
--- a/doc/ru/scripting/builtins/libhud.md
+++ b/doc/ru/scripting/builtins/libhud.md
@@ -58,6 +58,11 @@ hud.get_block_inventory() -> int
-- Дает ID второго открытого инвентаря (блок, виртуальный инвентарь...) или 0.
hud.get_second_inventory() -> int
+-- Получить ID инвентаря, в котором находится предмет во время
+-- его перемещения в инвентаре, или 0 если его не существует.
+-- Имеет единственный слот - 0
+hud.get_exchange_inventory() -> int
+
-- Дает ID игрока, к которому привязан пользовательский интерфейс.
hud.get_player() -> int
@@ -81,4 +86,4 @@ hud.set_allow_pause(flag: boolean)
-- Функция, управляющая именованным скелетом 'hand' (см. gfx.skeletons)
hud.hand_controller: function()
-```
\ No newline at end of file
+```
diff --git a/doc/ru/scripting/events.md b/doc/ru/scripting/events.md
index ff4bfb456..bfa0b9388 100644
--- a/doc/ru/scripting/events.md
+++ b/doc/ru/scripting/events.md
@@ -34,7 +34,7 @@ function on_broken(x, y, z, playerid)
function on_replaced(x, y, z, playerid)
```
-Вызывается после замены блока игроком
+Вызывается перед заменой блока игроком
```lua
function on_interact(x, y, z, playerid) -> bool
diff --git a/doc/ru/world-generator.md b/doc/ru/world-generator.md
index 30e4b1098..3415a81d6 100644
--- a/doc/ru/world-generator.md
+++ b/doc/ru/world-generator.md
@@ -215,12 +215,28 @@ local map = Heightmap(ширина, высота)
Операции применяются ко всем значениям высоты.
```lua
+-- Приведение значений высот к абсолютным
map:abs()
+
+-- Округление к меньшему целому
+map:floor()
+
+-- Округление к большему целому
+map:ceil()
+
+-- Округление к ближайшему целому
+map:round()
+
+-- Синус от значений высот (в радианах)
+map:sin()
+
+-- Косинус от значений высот (в радианах)
+map:cos()
+
+-- Тангенс от значений высот (в радианах)
+map:tan()
```
-Приводит значения высот к абсолютным.
-
-
### Бинарные операции
Операции с применением второй карты или скаляра.
diff --git a/doc/ru/xml-ui-layouts.md b/doc/ru/xml-ui-layouts.md
index 2ca6da1ce..9be788e7f 100644
--- a/doc/ru/xml-ui-layouts.md
+++ b/doc/ru/xml-ui-layouts.md
@@ -43,6 +43,8 @@
- `position-func` - поставщик позиции элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
- `size-func` - поставщик размера элемента (два числа), вызываемый при изменении размера контейнера, в котором находится элемент, либо при добавлении элемента в контейнер. Может быть вызван до вызова on_hud_open.
- `onclick` - lua функция вызываемая при нажатии на элемент.
+- `onrightclick` - lua функция вызываемая при нажатии правой кнопки мыши на элемент.
+- `onmiddleclick` - lua функция вызываемая при нажатии средней кнопки мыши на элемент.
- `ondoubleclick` - lua функция вызываемая при двойном нажатии на элемент.
- `onfocus` - lua функция вызываемая при фокусировке на элемент.
- `ondefocus` - lua функция вызываемая при потере фокуса элеметом.
@@ -193,6 +195,9 @@
- `width` - минимальная ширина содержимого. По-умолчанию: 100.
- `selected` - изначально выбранное значение. По-умолчанию: "".
- `onselect` - функция, в которую передаётся выбранное пользователем значение
+- `mode` - режим работы:
+ - `select` - режим по-умолчанию.
+ - `button` - режим кнопки: текст не заменяется на выбранную опцию, а элемент выглядит как обычная кнопка.
## Привязка ввода - *bindbox*
diff --git a/res/content/base/generators/demo.files/script.lua b/res/content/base/generators/demo.files/script.lua
index 7a3282c3c..ff05e08cc 100644
--- a/res/content/base/generators/demo.files/script.lua
+++ b/res/content/base/generators/demo.files/script.lua
@@ -86,12 +86,27 @@ function generate_heightmap(x, y, w, h, s, inputs)
local rivermap = Heightmap(w, h)
rivermap.noiseSeed = SEED
- rivermap:noise({x+21, y+12}, 0.1*s, 4)
- rivermap:abs()
- rivermap:mul(2.0)
- rivermap:pow(0.15)
- rivermap:max(0.5)
- map:mul(rivermap)
+ rivermap:noise({ x + 21, y + 12 }, 0.1 * s, 4)
+ rivermap:pow(4)
+ rivermap:mul(3500)
+ rivermap:add(1)
+ rivermap:pow(-1)
+ rivermap:mul(-1)
+ rivermap:add(1)
+
+ local rivermap_bottom = Heightmap(w, h)
+ rivermap_bottom:add(0.23)
+ rivermap_bottom:noise({ x, y }, 2 * s, 4, 0.005)
+
+ map:sub(rivermap_bottom)
+
+ local map_multiplied = Heightmap(w, h)
+ map_multiplied:add(map)
+ map_multiplied:mul(rivermap)
+
+ map:min(map_multiplied)
+
+ map:add(rivermap_bottom)
local desertmap = Heightmap(w, h)
desertmap.noiseSeed = SEED
diff --git a/res/content/base/modules/util.lua b/res/content/base/modules/util.lua
index 874e6d3d6..4d25ccb91 100644
--- a/res/content/base/modules/util.lua
+++ b/res/content/base/modules/util.lua
@@ -1,5 +1,44 @@
local util = {}
+local DROP_FORCE = 8
+local DROP_INIT_VEL = {0, 3, 0}
+
+---@param mode integer 0 - whole stack, 1 - single item, 2 - dupe whole stack
+function util.drop_from_slot(invid, slot, mode)
+ if invid == 0 then
+ return
+ end
+ local pid = hud.get_player()
+ if mode == 2 and not player.is_infinite_items(pid) then
+ return
+ end
+ local itemid, itemcount = inventory.get(invid, slot)
+ if itemid == 0 then
+ return
+ end
+
+ local drop_itemcount = 1
+ if mode == 0 then
+ drop_itemcount = itemcount
+ end
+ if mode == 2 then
+ drop_itemcount = item.stack_size(itemid)
+ else
+ inventory.set(invid, slot, itemid, itemcount - drop_itemcount)
+ end
+
+ local data = inventory.get_all_data(invid, slot)
+ local pvel = { player.get_vel(pid) }
+ local ppos = vec3.add({ player.get_pos(pid) }, { 0, 0.7, 0 })
+ local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE)
+ local drop = util.drop(ppos, itemid, drop_itemcount, data, 1.5)
+ if not drop then
+ return
+ end
+ local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL))
+ drop.rigidbody:set_vel(velocity)
+end
+
function util.drop(ppos, itemid, count, data, pickup_delay)
if itemid == 0 or not itemid then
return nil
diff --git a/res/content/base/scripts/hud.lua b/res/content/base/scripts/hud.lua
index 9fc10f2a1..d0e3d1532 100644
--- a/res/content/base/scripts/hud.lua
+++ b/res/content/base/scripts/hud.lua
@@ -1,30 +1,21 @@
local base_util = require "util"
-local DROP_FORCE = 8
-local DROP_INIT_VEL = {0, 3, 0}
-
function on_hud_open()
- input.add_callback("player.drop", function ()
+ events.on("core:drop_outside_inventory", function(mode)
+ local invid = hud.get_exchange_inventory()
+ base_util.drop_from_slot(invid, 0, mode)
+ end)
+ input.add_callback("player.drop", function()
if hud.is_paused() or hud.is_inventory_open() then
return
end
local pid = hud.get_player()
local invid, slot = player.get_inventory(pid)
- local itemid, itemcount = inventory.get(invid, slot)
- if itemid == 0 then
- return
+ local mode = 1
+ if input.is_pressed("key:left-ctrl") or input.is_pressed("key:right-ctrl") then
+ mode = 0
end
- local data = inventory.get_all_data(invid, slot)
- inventory.set(invid, slot, itemid, itemcount-1)
-
- local pvel = {player.get_vel(pid)}
- local ppos = vec3.add({player.get_pos(pid)}, {0, 0.7, 0})
- local throw_force = vec3.mul(player.get_dir(pid), DROP_FORCE)
- local drop = base_util.drop(ppos, itemid, 1, data, 1.5)
- if not drop then
- return
- end
- local velocity = vec3.add(throw_force, vec3.add(pvel, DROP_INIT_VEL))
- drop.rigidbody:set_vel(velocity)
+ base_util.drop_from_slot(invid, slot, mode)
end)
+ rules.create("do-loot-non-player", true)
end
diff --git a/res/content/base/scripts/world.lua b/res/content/base/scripts/world.lua
index 419dcc769..3da6f6c2a 100644
--- a/res/content/base/scripts/world.lua
+++ b/res/content/base/scripts/world.lua
@@ -16,6 +16,4 @@ function on_block_broken(id, x, y, z, playerid)
spawn_spread=vec3.mul(size, 0.4)
})
end
-
- rules.create("do-loot-non-player", true)
end
diff --git a/res/layouts/templates/pack.xml b/res/layouts/templates/pack.xml
index 56bd50e50..d4e721d0c 100644
--- a/res/layouts/templates/pack.xml
+++ b/res/layouts/templates/pack.xml
@@ -1,6 +1,6 @@
-
- ]], id, string.escape_xml(text)), {on_yes=yes_callback, on_no=no_callback},
- string.escape_xml(yes_text), string.escape_xml(no_text))
+ ]], id,
+ string.escape_xml(text),
+ string.escape_xml(yes_text),
+ string.escape_xml(no_text)), {on_yes=yes_callback, on_no=no_callback})
input.add_callback("key:escape", no_callback, gui.root[id])
end
diff --git a/res/shaders/effects/deferred_lighting.glsl b/res/shaders/effects/deferred_lighting.glsl
index c314df02a..55e267bf3 100644
--- a/res/shaders/effects/deferred_lighting.glsl
+++ b/res/shaders/effects/deferred_lighting.glsl
@@ -1,6 +1,9 @@
#include
#include
+#define CLOUDS_FOG_FACTOR_MUL 0.3f
+#define CLOUDS_FOG_CURVE_MUL 0.4f
+
vec4 effect() {
vec4 pos = texture(u_position, v_uv);
float light = 1.0;
@@ -32,6 +35,10 @@ vec4 effect() {
light = pow(light, u_gamma);
vec3 fogColor = texture(u_skybox, dir).rgb;
- float fog = calc_fog(length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0);
+ float fog = calc_fog(
+ length(u_view * vec4((modelpos.xyz - u_cameraPos) * FOG_POS_SCALE, 0.0)) / 256.0,
+ mix(1.0, CLOUDS_FOG_FACTOR_MUL, emission),
+ mix(1.0, CLOUDS_FOG_CURVE_MUL, emission)
+ );
return vec4(mix(texture(u_screen, v_uv).rgb * mix(1.0, light, 1.0), fogColor, fog), 1.0);
}
diff --git a/res/shaders/entity.glslf b/res/shaders/entity.glslf
index f3293adb7..a817d963a 100644
--- a/res/shaders/entity.glslf
+++ b/res/shaders/entity.glslf
@@ -15,11 +15,33 @@ uniform float u_fogCurve;
uniform bool u_alphaClip;
uniform vec3 u_sunDir;
+const float BAYER_MATRIX[64] = float[](
+ 0.0, 32.0, 8.0, 40.0, 2.0, 34.0, 10.0, 42.0,
+ 48.0, 16.0, 56.0, 24.0, 50.0, 18.0, 58.0, 26.0,
+ 12.0, 44.0, 4.0, 36.0, 14.0, 46.0, 6.0, 38.0,
+ 60.0, 28.0, 52.0, 20.0, 62.0, 30.0, 54.0, 22.0,
+ 3.0, 35.0, 11.0, 43.0, 1.0, 33.0, 9.0, 41.0,
+ 51.0, 19.0, 59.0, 27.0, 49.0, 17.0, 57.0, 25.0,
+ 15.0, 47.0, 7.0, 39.0, 13.0, 45.0, 5.0, 37.0,
+ 63.0, 31.0, 55.0, 23.0, 61.0, 29.0, 53.0, 21.0
+);
+
+float calc_bayer(vec2 pos){
+ ivec2 p = ivec2(mod(pos, 8.0));
+ return BAYER_MATRIX[p.y * 8 + p.x] / 64.0;
+}
+
void main() {
vec4 texColor = texture(u_texture0, a_texCoord);
float alpha = a_color.a * texColor.a;
// anyway it's any alpha-test alternative required
- if (alpha < (u_alphaClip ? 0.5f : 0.15f)) {
+ if (u_alphaClip) {
+ float bayer = calc_bayer(gl_FragCoord.xy);
+ if (alpha - bayer < 0.01f) {
+ discard;
+ }
+ alpha = 1.0;
+ } else if (alpha < 0.05f) {
discard;
}
f_color = a_color * texColor;
diff --git a/res/shaders/lib/fog.glsl b/res/shaders/lib/fog.glsl
index e5ff18bbd..597380bb6 100644
--- a/res/shaders/lib/fog.glsl
+++ b/res/shaders/lib/fog.glsl
@@ -7,13 +7,17 @@ uniform float u_weatherFogOpacity;
uniform float u_weatherFogDencity;
uniform float u_weatherFogCurve;
-float calc_fog(float depth) {
+float calc_fog(float depth, float fogFactor, float fogCurve) {
return min(
1.0,
- max(pow(depth * u_fogFactor, u_fogCurve),
+ max(pow(depth * u_fogFactor * fogFactor, u_fogCurve * fogCurve),
min(pow(depth * u_weatherFogDencity, u_weatherFogCurve),
u_weatherFogOpacity))
);
}
+float calc_fog(float depth) {
+ return calc_fog(depth, 1.0, 1.0);
+}
+
#endif // FOG_GLSL_
diff --git a/src/content/loading/BlockLoader.cpp b/src/content/loading/BlockLoader.cpp
index 56903db79..d6bcad92b 100644
--- a/src/content/loading/BlockLoader.cpp
+++ b/src/content/loading/BlockLoader.cpp
@@ -151,6 +151,8 @@ template<> void ContentUnitLoader::loadUnit(
def.rotations = BlockRotProfile::PANE;
} else if (profile == BlockRotProfile::STAIRS_NAME) {
def.rotations = BlockRotProfile::STAIRS;
+ } else if (profile == BlockRotProfile::LADDER_NAME) {
+ def.rotations = BlockRotProfile::LADDER;
} else if (profile != "none") {
logger.error() << "unknown rotation profile " << profile;
def.rotatable = false;
diff --git a/src/engine/ServerMainloop.cpp b/src/engine/ServerMainloop.cpp
index c1a95330d..9790723a9 100644
--- a/src/engine/ServerMainloop.cpp
+++ b/src/engine/ServerMainloop.cpp
@@ -48,7 +48,7 @@ void ServerMainloop::run() {
delta = time.getDelta();
}
if (controller) {
- controller->getLevel()->getWorld()->updateTimers(delta);
+ controller->getLevel()->getWorld().updateTimers(delta);
controller->update(glm::min(delta, 0.2), false);
}
engine.applicationTick();
diff --git a/src/frontend/LevelFrontend.cpp b/src/frontend/LevelFrontend.cpp
index d70f09ba2..2143a4778 100644
--- a/src/frontend/LevelFrontend.cpp
+++ b/src/frontend/LevelFrontend.cpp
@@ -147,3 +147,7 @@ const ContentGfxCache& LevelFrontend::getContentGfxCache() const {
LevelController& LevelFrontend::getController() const {
return controller;
}
+
+Weather& LevelFrontend::getWeather() {
+ return weather;
+}
diff --git a/src/frontend/LevelFrontend.hpp b/src/frontend/LevelFrontend.hpp
index 451f88675..28c8a5832 100644
--- a/src/frontend/LevelFrontend.hpp
+++ b/src/frontend/LevelFrontend.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include "world/Weather.hpp"
+
#include
class Assets;
@@ -15,6 +17,7 @@ class LevelFrontend {
LevelController& controller;
Assets& assets;
std::unique_ptr contentCache;
+ Weather weather {};
public:
LevelFrontend(
Engine& engine,
@@ -28,4 +31,5 @@ public:
const ContentGfxCache& getContentGfxCache() const;
ContentGfxCache& getContentGfxCache();
LevelController& getController() const;
+ Weather& getWeather();
};
diff --git a/src/frontend/debug_panel.cpp b/src/frontend/debug_panel.cpp
index 780bbf00a..84e85f3e9 100644
--- a/src/frontend/debug_panel.cpp
+++ b/src/frontend/debug_panel.cpp
@@ -254,7 +254,7 @@ std::shared_ptr create_debug_panel(
}
}));
panel->add(create_label(gui, [&]() {
- return L"seed: " + std::to_wstring(level.getWorld()->getSeed());
+ return L"seed: " + std::to_wstring(level.getWorld().getSeed());
}));
for (int ax = 0; ax < 3; ax++) {
@@ -294,7 +294,7 @@ std::shared_ptr create_debug_panel(
sub->add(box, glm::vec2(20, 0));
panel->add(sub);
}
- auto& worldInfo = level.getWorld()->getInfo();
+ auto& worldInfo = level.getWorld().getInfo();
panel->add(create_label(gui, [&]() {
int hour, minute, second;
timeutil::from_value(worldInfo.daytime, hour, minute, second);
@@ -310,12 +310,6 @@ std::shared_ptr create_debug_panel(
bar->setConsumer([&](double val) { worldInfo.daytime = val; });
panel->add(bar);
}
- if (allowDebugCheats) {
- auto bar = std::make_shared(gui, 0.0f, 1.0f, 0.0f, 0.005f, 8);
- bar->setSupplier([&]() { return worldInfo.fog; });
- bar->setConsumer([&](double val) { worldInfo.fog = val; });
- panel->add(bar);
- }
{
auto checkbox = std::make_shared(
gui, L"Show Chunk Borders", glm::vec2(400, 24)
diff --git a/src/frontend/hud.cpp b/src/frontend/hud.cpp
index 1aa03436b..08a4b8455 100644
--- a/src/frontend/hud.cpp
+++ b/src/frontend/hud.cpp
@@ -174,6 +174,15 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
gui,
""
);
+
+ inventoryDropArea = guiutil::create(
+ gui,
+ R"()"
+ );
uicamera = std::make_unique(glm::vec3(), 1);
uicamera->perspective = false;
@@ -188,6 +197,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
gui.add(debugPanel);
gui.add(darkOverlay);
+ gui.add(inventoryDropArea);
gui.add(hotbarView);
gui.add(contentAccessPanel);
@@ -213,6 +223,7 @@ Hud::~Hud() {
}
gui.remove(hotbarView);
gui.remove(darkOverlay);
+ gui.remove(inventoryDropArea);
gui.remove(contentAccessPanel);
gui.remove(debugPanel);
}
@@ -358,6 +369,7 @@ void Hud::update(bool visible) {
contentAccess->setMinSize(glm::vec2(1, windowSize.y));
hotbarView->setVisible(visible && !(secondUI && !inventoryView));
darkOverlay->setVisible(isMenuOpen);
+ inventoryDropArea->setVisible(inventoryOpen);
menu.setVisible(isMenuOpen);
if (visible) {
@@ -739,6 +751,10 @@ std::shared_ptr Hud::getSecondInventory() {
return nullptr;
}
+std::shared_ptr Hud::getExchangeInventory() {
+ return exchangeSlotInv;
+}
+
bool Hud::isContentAccess() const {
return showContentPanel;
}
diff --git a/src/frontend/hud.hpp b/src/frontend/hud.hpp
index f80c36369..f40a53581 100644
--- a/src/frontend/hud.hpp
+++ b/src/frontend/hud.hpp
@@ -93,6 +93,8 @@ class Hud : public util::ObjectsKeeper {
std::shared_ptr debugPanel;
/// @brief Overlay used in pause mode
std::shared_ptr darkOverlay;
+ /// @brief Overlay used in inventory
+ std::shared_ptr inventoryDropArea;
/// @brief Inventories interaction agent (grabbed item)
std::shared_ptr exchangeSlot;
/// @brief Exchange slot inventory (1 slot only)
@@ -207,6 +209,8 @@ public:
std::shared_ptr getSecondInventory();
+ std::shared_ptr getExchangeInventory();
+
bool isContentAccess() const;
void setContentAccess(bool flag);
diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp
index 33b637531..24ee44378 100644
--- a/src/frontend/screens/LevelScreen.cpp
+++ b/src/frontend/screens/LevelScreen.cpp
@@ -43,7 +43,7 @@ LevelScreen::LevelScreen(
Engine& engine, std::unique_ptr levelPtr, int64_t localPlayer
)
: Screen(engine),
- world(*levelPtr->getWorld()),
+ world(levelPtr->getWorld()),
postProcessing(std::make_unique(
levelPtr->content.getIndices(ResourceType::POST_EFFECT_SLOT).size()
)),
@@ -81,7 +81,7 @@ LevelScreen::LevelScreen(
auto resetChunks = [=](bool) {
player->chunks->saveAndClear();
- renderer->clear();
+ renderer->resetCache();
};
keepAlive(settings.graphics.backlight.observe(resetChunks));
keepAlive(settings.graphics.softLighting.observe(resetChunks));
@@ -94,11 +94,11 @@ LevelScreen::LevelScreen(
}));
keepAlive(input.addCallback(BIND_CHUNKS_RELOAD, [=]() {
player->chunks->saveAndClear();
- renderer->clear();
+ renderer->resetCache();
return false;
}));
controller->preQuitCallbacks.listen([this]() {
- if (!controller->getLevel()->getWorld()->isNameless()) {
+ if (!controller->getLevel()->getWorld().isNameless()) {
saveWorldPreview();
}
});
@@ -109,7 +109,7 @@ LevelScreen::LevelScreen(
}
LevelScreen::~LevelScreen() {
- if (!controller->getLevel()->getWorld()->isNameless()) {
+ if (!controller->getLevel()->getWorld().isNameless()) {
saveDecorations();
}
scripting::on_frontend_close();
@@ -153,7 +153,7 @@ void LevelScreen::loadDecorations() {
}
auto data = io::read_object(CLIENT_FILE);
if (data.has("weather")) {
- renderer->getWeather().deserialize(data["weather"]);
+ frontend->getWeather().deserialize(data["weather"]);
}
}
@@ -161,7 +161,7 @@ void LevelScreen::saveDecorations() {
io::create_directory("world:client");
auto data = dv::object();
- data["weather"] = renderer->getWeather().serialize();
+ data["weather"] = frontend->getWeather().serialize();
io::write_json(CLIENT_FILE, data, true);
}
@@ -257,7 +257,7 @@ void LevelScreen::update(float delta) {
hud->update(hudVisible);
- const Weather& weather = renderer->getWeather();
+ const Weather& weather = frontend->getWeather();
const Player& player = playerController->getPlayer();
const Camera& camera = *player.currentCamera;
decorator->update(paused ? 0.0f : delta, camera, weather);
diff --git a/src/graphics/core/Batch2D.cpp b/src/graphics/core/Batch2D.cpp
index 7aa856f3f..33265b49f 100644
--- a/src/graphics/core/Batch2D.cpp
+++ b/src/graphics/core/Batch2D.cpp
@@ -372,7 +372,7 @@ void Batch2D::sprite(
void Batch2D::flush() {
if (index == 0)
return;
- mesh->reload(buffer.get(), index);
+ mesh->reload(buffer.get(), index, true);
mesh->draw(gl::to_glenum(primitive));
index = 0;
}
diff --git a/src/graphics/core/Batch3D.cpp b/src/graphics/core/Batch3D.cpp
index ce9f176c7..3aa996706 100644
--- a/src/graphics/core/Batch3D.cpp
+++ b/src/graphics/core/Batch3D.cpp
@@ -292,13 +292,13 @@ void Batch3D::point(const glm::vec3& coord, const glm::vec4& tint) {
}
void Batch3D::flush() {
- mesh->reload(buffer.get(), index);
+ mesh->reload(buffer.get(), index, true);
mesh->draw();
index = 0;
}
void Batch3D::flushPoints() {
- mesh->reload(buffer.get(), index);
+ mesh->reload(buffer.get(), index, true);
mesh->draw(GL_POINTS);
index = 0;
}
diff --git a/src/graphics/core/LineBatch.cpp b/src/graphics/core/LineBatch.cpp
index 9e33398ac..514f550d6 100644
--- a/src/graphics/core/LineBatch.cpp
+++ b/src/graphics/core/LineBatch.cpp
@@ -57,7 +57,7 @@ void LineBatch::box(float x, float y, float z, float w, float h, float d,
void LineBatch::flush(){
if (index == 0)
return;
- mesh->reload(buffer.get(), index);
+ mesh->reload(buffer.get(), index, true);
mesh->draw(GL_LINES);
index = 0;
}
diff --git a/src/graphics/core/Mesh.hpp b/src/graphics/core/Mesh.hpp
index 48c029696..2ff15cc2a 100644
--- a/src/graphics/core/Mesh.hpp
+++ b/src/graphics/core/Mesh.hpp
@@ -47,12 +47,15 @@ public:
void reload(
const VertexStructure* vertexBuffer,
size_t vertexCount,
- const std::vector& indices
+ const std::vector& indices,
+ bool streaming
);
- void reload(const VertexStructure* vertexBuffer, size_t vertexCount) {
+ void reload(
+ const VertexStructure* vertexBuffer, size_t vertexCount, bool streaming
+ ) {
static const std::vector indices {};
- reload(vertexBuffer, vertexCount, indices);
+ reload(vertexBuffer, vertexCount, indices, streaming);
}
/// @brief Draw mesh with specified primitives type
@@ -61,6 +64,8 @@ public:
/// @brief Draw mesh as triangles
void draw() const;
+private:
+
};
#include "graphics/core/Mesh.inl"
diff --git a/src/graphics/core/Mesh.inl b/src/graphics/core/Mesh.inl
index 913da2bce..4c6424dd1 100644
--- a/src/graphics/core/Mesh.inl
+++ b/src/graphics/core/Mesh.inl
@@ -46,9 +46,9 @@ Mesh::Mesh(
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
- reload(vertexBuffer, vertices, std::move(indices));
-
glBindVertexArray(vao);
+ reload(vertexBuffer, vertices, std::move(indices), false);
+
// attributes
int offset = 0;
for (int i = 0; attrs[i].count; i++) {
@@ -82,20 +82,22 @@ template
void Mesh::reload(
const VertexStructure* vertexBuffer,
size_t vertexCount,
- const std::vector& indices
+ const std::vector& indices,
+ bool streaming
) {
this->vertexCount = vertexCount;
- glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
if (vertexBuffer != nullptr && vertexCount != 0) {
glBufferData(
GL_ARRAY_BUFFER,
vertexCount * sizeof(VertexStructure),
vertexBuffer,
- GL_STREAM_DRAW
+ streaming ? GL_STREAM_DRAW : GL_STATIC_DRAW
);
} else {
- glBufferData(GL_ARRAY_BUFFER, 0, {}, GL_STREAM_DRAW);
+ glBufferData(
+ GL_ARRAY_BUFFER, 0, {}, streaming ? GL_STREAM_DRAW : GL_STATIC_DRAW
+ );
}
for (int i = 0; i < ibos.size(); i++) {
@@ -116,7 +118,6 @@ void Mesh::reload(
GL_STATIC_DRAW
);
}
- glBindVertexArray(0);
}
template
diff --git a/src/graphics/core/Shader.cpp b/src/graphics/core/Shader.cpp
index 8cc6f2025..1c752ef5a 100644
--- a/src/graphics/core/Shader.cpp
+++ b/src/graphics/core/Shader.cpp
@@ -186,7 +186,8 @@ void Shader::recompile(const std::vector& defines) {
glDeleteProgram(id);
id = newProgram;
uniformLocations.clear();
- logger.info() << "shader " << id << " has been recompiled";
+ logger.debug() << "shader program (" << vertexSource.file << ", "
+ << fragmentSource.file << ") has been recompiled";
}
std::unique_ptr Shader::create(
diff --git a/src/graphics/core/Shadows.cpp b/src/graphics/core/Shadows.cpp
index c9ecc108d..8defa40f8 100644
--- a/src/graphics/core/Shadows.cpp
+++ b/src/graphics/core/Shadows.cpp
@@ -99,8 +99,8 @@ void Shadows::setup(Shader& shader, const Weather& weather) {
if (!shadows) {
return;
}
- const auto& worldInfo = level.getWorld()->getInfo();
- float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
+ const auto& worldInfo = level.getWorld().getInfo();
+ float cloudsIntensity = weather.clouds();
float shadowsOpacity = 1.0f - cloudsIntensity;
shadowsOpacity *= glm::sqrt(glm::abs(
glm::mod((worldInfo.daytime + 0.5f) * 2.0f, 1.0f) * 2.0f - 1.0f
@@ -158,8 +158,8 @@ void Shadows::generateShadowsMap(
float scale,
const std::function& renderShadowPass
) {
- auto world = level.getWorld();
- const auto& worldInfo = world->getInfo();
+ const auto& world = level.getWorld();
+ const auto& worldInfo = world.getInfo();
int resolution = shadowMap.getResolution();
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
diff --git a/src/graphics/render/BlocksRenderer.cpp b/src/graphics/render/BlocksRenderer.cpp
index 33ecc8e17..f75bd675c 100644
--- a/src/graphics/render/BlocksRenderer.cpp
+++ b/src/graphics/render/BlocksRenderer.cpp
@@ -10,15 +10,14 @@
#include "frontend/ContentGfxCache.hpp"
const glm::vec3 BlocksRenderer::SUN_VECTOR(0.528265, 0.833149, -0.163704);
-const float DIRECTIONAL_LIGHT_FACTOR = 0.3f;
+constexpr float DIRECTIONAL_LIGHT_FACTOR = 0.3f;
BlocksRenderer::BlocksRenderer(
size_t capacity,
- const Content& content,
+ const Block* const* blockDefs,
const ContentGfxCache& cache,
const EngineSettings& settings
-) : content(content),
- vertexBuffer(std::make_unique(capacity)),
+) : vertexBuffer(std::make_unique(capacity)),
indexBuffer(std::make_unique(capacity)),
denseIndexBuffer(std::make_unique(capacity)),
vertexCount(0),
@@ -28,7 +27,7 @@ BlocksRenderer::BlocksRenderer(
cache(cache),
settings(settings)
{
- blockDefsCache = content.getIndices()->blocks.getDefs();
+ blockDefsCache = blockDefs;
}
BlocksRenderer::~BlocksRenderer() = default;
@@ -482,83 +481,78 @@ glm::vec4 BlocksRenderer::pickSoftLight(
}
void BlocksRenderer::render(
- const voxel* voxels, const int beginEnds[256][2]
+ const voxel* voxels, int totalBegin, int totalEnd
) {
bool denseRender = this->denseRender;
bool densePass = this->densePass;
bool enableAO = settings.graphics.softLighting.get();
- for (const auto drawGroup : *content.drawGroups) {
- int begin = beginEnds[drawGroup][0];
- if (begin == 0) {
+
+ for (int i = totalBegin; i < totalEnd; i++) {
+ const voxel& vox = voxels[i];
+ blockid_t id = vox.id;
+ blockstate state = vox.state;
+ const auto& def = *blockDefsCache[id];
+ uint8_t variantId = def.getVariantIndex(state.userbits);
+ const auto& variant = def.getVariant(variantId);
+ if (id == 0 || state.segment) {
continue;
}
- int end = beginEnds[drawGroup][1];
- for (int i = begin-1; i <= end; i++) {
- const voxel& vox = voxels[i];
- blockid_t id = vox.id;
- blockstate state = vox.state;
- const auto& def = *blockDefsCache[id];
- uint8_t variantId = def.getVariantIndex(state.userbits);
- const auto& variant = def.getVariant(variantId);
- if (id == 0 || variant.drawGroup != drawGroup || state.segment) {
- continue;
+ if (denseRender != (variant.culling == CullingMode::OPTIONAL)) {
+ continue;
+ }
+ if (def.translucent) {
+ continue;
+ }
+ const UVRegion texfaces[6] {
+ cache.getRegion(id, variantId, 0, densePass),
+ cache.getRegion(id, variantId, 1, densePass),
+ cache.getRegion(id, variantId, 2, densePass),
+ cache.getRegion(id, variantId, 3, densePass),
+ cache.getRegion(id, variantId, 4, densePass),
+ cache.getRegion(id, variantId, 5, densePass)
+ };
+ int x = i % CHUNK_W;
+ int y = i / (CHUNK_D * CHUNK_W);
+ int z = (i / CHUNK_D) % CHUNK_W;
+
+ switch (def.getModel(state.userbits).type) {
+ case BlockModelType::BLOCK:
+ blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
+ def.ambientOcclusion && enableAO);
+ break;
+ case BlockModelType::XSPRITE: {
+ if (!denseRender)
+ blockXSprite(x, y, z, glm::vec3(1.0f),
+ texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
+ break;
}
- if (denseRender != (variant.culling == CullingMode::OPTIONAL)) {
- continue;
+ case BlockModelType::AABB: {
+ if (!denseRender)
+ blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
+ !def.shadeless, def.ambientOcclusion && enableAO);
+ break;
}
- if (def.translucent) {
- continue;
- }
- const UVRegion texfaces[6] {
- cache.getRegion(id, variantId, 0, densePass),
- cache.getRegion(id, variantId, 1, densePass),
- cache.getRegion(id, variantId, 2, densePass),
- cache.getRegion(id, variantId, 3, densePass),
- cache.getRegion(id, variantId, 4, densePass),
- cache.getRegion(id, variantId, 5, densePass)
- };
- int x = i % CHUNK_W;
- int y = i / (CHUNK_D * CHUNK_W);
- int z = (i / CHUNK_D) % CHUNK_W;
- switch (def.getModel(state.userbits).type) {
- case BlockModelType::BLOCK:
- blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
- def.ambientOcclusion && enableAO);
- break;
- case BlockModelType::XSPRITE: {
- if (!denseRender)
- blockXSprite(x, y, z, glm::vec3(1.0f),
- texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
- break;
- }
- case BlockModelType::AABB: {
- if (!denseRender)
- blockAABB({x, y, z}, texfaces, &def, vox.state.rotation,
- !def.shadeless, def.ambientOcclusion && enableAO);
- break;
- }
- case BlockModelType::CUSTOM: {
- blockCustomModel(
- {x, y, z},
- def,
- vox.state,
- !def.shadeless,
- def.ambientOcclusion && enableAO
- );
- break;
- }
- default:
- break;
- }
- if (overflow) {
- return;
+ case BlockModelType::CUSTOM: {
+ blockCustomModel(
+ {x, y, z},
+ def,
+ vox.state,
+ !def.shadeless,
+ def.ambientOcclusion && enableAO
+ );
+ break;
}
+ default:
+ break;
+ }
+ if (overflow) {
+ return;
}
}
}
SortingMeshData BlocksRenderer::renderTranslucent(
- const voxel* voxels, int beginEnds[256][2]
+ const voxel* voxels, int totalBegin, int totalEnd
) {
SortingMeshData sortingMesh {{}};
@@ -568,106 +562,98 @@ SortingMeshData BlocksRenderer::renderTranslucent(
bool densePass = this->densePass;
bool enableAO = settings.graphics.softLighting.get();
- for (const auto drawGroup : *content.drawGroups) {
- int begin = beginEnds[drawGroup][0];
- if (begin == 0) {
+ for (int i = totalBegin; i < totalEnd; i++) {
+ const voxel& vox = voxels[i];
+ blockid_t id = vox.id;
+ blockstate state = vox.state;
+ const auto& def = *blockDefsCache[id];
+ uint8_t variantId = def.getVariantIndex(state.userbits);
+ if (id == 0 || state.segment) {
continue;
}
- int end = beginEnds[drawGroup][1];
- for (int i = begin-1; i <= end; i++) {
- const voxel& vox = voxels[i];
- blockid_t id = vox.id;
- blockstate state = vox.state;
- const auto& def = *blockDefsCache[id];
- uint8_t variantId = def.getVariantIndex(state.userbits);
- const auto& variant = def.getVariant(variantId);
- if (id == 0 || variant.drawGroup != drawGroup || state.segment) {
- continue;
- }
- if (!def.translucent) {
- continue;
- }
- const UVRegion texfaces[6] {
- cache.getRegion(id, variantId, 0, densePass),
- cache.getRegion(id, variantId, 1, densePass),
- cache.getRegion(id, variantId, 2, densePass),
- cache.getRegion(id, variantId, 3, densePass),
- cache.getRegion(id, variantId, 4, densePass),
- cache.getRegion(id, variantId, 5, densePass)
- };
- int x = i % CHUNK_W;
- int y = i / (CHUNK_D * CHUNK_W);
- int z = (i / CHUNK_D) % CHUNK_W;
- switch (def.getModel(state.userbits).type) {
- case BlockModelType::BLOCK:
- blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
- def.ambientOcclusion && enableAO);
- break;
- case BlockModelType::XSPRITE: {
- blockXSprite(x, y, z, glm::vec3(1.0f),
- texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
- break;
- }
- case BlockModelType::AABB: {
- blockAABB(
- {x, y, z},
- texfaces,
- &def,
- vox.state.rotation,
- !def.shadeless,
- def.ambientOcclusion && enableAO
- );
- break;
- }
- case BlockModelType::CUSTOM: {
- blockCustomModel(
- {x, y, z},
- def,
- vox.state,
- !def.shadeless,
- def.ambientOcclusion && enableAO
- );
- break;
- }
- default:
- break;
- }
- if (vertexCount == 0) {
- continue;
- }
- SortingMeshEntry entry {
- glm::vec3(
- x + chunk->x * CHUNK_W + 0.5f,
- y + 0.5f,
- z + chunk->z * CHUNK_D + 0.5f
- ),
- util::Buffer(indexCount), 0};
-
- totalSize += entry.vertexData.size();
-
- for (int j = 0; j < indexCount; j++) {
- std::memcpy(
- entry.vertexData.data() + j,
- vertexBuffer.get() + indexBuffer[j],
- sizeof(ChunkVertex)
- );
- ChunkVertex& vertex = entry.vertexData[j];
-
- if (!aabbInit) {
- aabbInit = true;
- aabb.a = aabb.b = vertex.position;
- } else {
- aabb.addPoint(vertex.position);
- }
-
- vertex.position.x += chunk->x * CHUNK_W + 0.5f;
- vertex.position.y += 0.5f;
- vertex.position.z += chunk->z * CHUNK_D + 0.5f;
- }
- sortingMesh.entries.push_back(std::move(entry));
- vertexCount = 0;
- vertexOffset = indexCount = 0;
+ if (!def.translucent) {
+ continue;
}
+ const UVRegion texfaces[6] {
+ cache.getRegion(id, variantId, 0, densePass),
+ cache.getRegion(id, variantId, 1, densePass),
+ cache.getRegion(id, variantId, 2, densePass),
+ cache.getRegion(id, variantId, 3, densePass),
+ cache.getRegion(id, variantId, 4, densePass),
+ cache.getRegion(id, variantId, 5, densePass)
+ };
+ int x = i % CHUNK_W;
+ int y = i / (CHUNK_D * CHUNK_W);
+ int z = (i / CHUNK_D) % CHUNK_W;
+ switch (def.getModel(state.userbits).type) {
+ case BlockModelType::BLOCK:
+ blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
+ def.ambientOcclusion && enableAO);
+ break;
+ case BlockModelType::XSPRITE: {
+ blockXSprite(x, y, z, glm::vec3(1.0f),
+ texfaces[FACE_MX], texfaces[FACE_MZ], 1.0f);
+ break;
+ }
+ case BlockModelType::AABB: {
+ blockAABB(
+ {x, y, z},
+ texfaces,
+ &def,
+ vox.state.rotation,
+ !def.shadeless,
+ def.ambientOcclusion && enableAO
+ );
+ break;
+ }
+ case BlockModelType::CUSTOM: {
+ blockCustomModel(
+ {x, y, z},
+ def,
+ vox.state,
+ !def.shadeless,
+ def.ambientOcclusion && enableAO
+ );
+ break;
+ }
+ default:
+ break;
+ }
+ if (vertexCount == 0) {
+ continue;
+ }
+ SortingMeshEntry entry {
+ glm::vec3(
+ x + chunk->x * CHUNK_W + 0.5f,
+ y + 0.5f,
+ z + chunk->z * CHUNK_D + 0.5f
+ ),
+ util::Buffer(indexCount), 0};
+
+ totalSize += entry.vertexData.size();
+
+ for (int j = 0; j < indexCount; j++) {
+ std::memcpy(
+ entry.vertexData.data() + j,
+ vertexBuffer.get() + indexBuffer[j],
+ sizeof(ChunkVertex)
+ );
+ ChunkVertex& vertex = entry.vertexData[j];
+
+ if (!aabbInit) {
+ aabbInit = true;
+ aabb.a = aabb.b = vertex.position;
+ } else {
+ aabb.addPoint(vertex.position);
+ }
+
+ vertex.position.x += chunk->x * CHUNK_W + 0.5f;
+ vertex.position.y += 0.5f;
+ vertex.position.z += chunk->z * CHUNK_D + 0.5f;
+ }
+ sortingMesh.entries.push_back(std::move(entry));
+ vertexCount = 0;
+ vertexOffset = indexCount = 0;
}
// additional powerful optimization
@@ -709,19 +695,16 @@ void BlocksRenderer::build(
int totalBegin = chunk->bottom * (CHUNK_W * CHUNK_D);
int totalEnd = chunk->top * (CHUNK_W * CHUNK_D);
+
bool hasTranslucent = false;
- int beginEnds[256][2] {};
for (int i = totalBegin; i < totalEnd; i++) {
const voxel& vox = voxels[i];
blockid_t id = vox.id;
const auto& def = *blockDefsCache[id];
- const auto& variant = def.getVariantByBits(vox.state.userbits);
- hasTranslucent = def.translucent || hasTranslucent;
-
- if (beginEnds[variant.drawGroup][0] == 0) {
- beginEnds[variant.drawGroup][0] = i + 1;
+ if (def.translucent) {
+ hasTranslucent = true;
+ break;
}
- beginEnds[variant.drawGroup][1] = i;
}
cancelled = false;
@@ -733,7 +716,7 @@ void BlocksRenderer::build(
densePass = false;
if (hasTranslucent) {
- sortingMesh = renderTranslucent(voxels, beginEnds);
+ sortingMesh = renderTranslucent(voxels, totalBegin, totalEnd);
} else {
sortingMesh = {};
}
@@ -746,13 +729,13 @@ void BlocksRenderer::build(
denseRender = false;
densePass = false;
- render(voxels, beginEnds);
+ render(voxels, totalBegin, totalEnd);
size_t endIndex = indexCount;
denseRender = true;
densePass = settings.graphics.denseRender.get();
- render(voxels, beginEnds);
+ render(voxels, totalBegin, totalEnd);
denseIndexCount = indexCount;
for (size_t i = 0; i < denseIndexCount; i++) {
@@ -761,7 +744,7 @@ void BlocksRenderer::build(
indexCount = endIndex;
densePass = false;
- render(voxels, beginEnds);
+ render(voxels, totalBegin, totalEnd);
}
ChunkMeshData BlocksRenderer::createMesh() {
diff --git a/src/graphics/render/BlocksRenderer.hpp b/src/graphics/render/BlocksRenderer.hpp
index 94b708a54..0488f0080 100644
--- a/src/graphics/render/BlocksRenderer.hpp
+++ b/src/graphics/render/BlocksRenderer.hpp
@@ -25,7 +25,7 @@ class BlocksRenderer final {
public:
BlocksRenderer(
size_t capacity,
- const Content& content,
+ const Block* const* blockDefs,
const ContentGfxCache& cache,
const EngineSettings& settings
);
@@ -44,7 +44,6 @@ public:
}
private:
static const glm::vec3 SUN_VECTOR;
- const Content& content;
std::unique_ptr vertexBuffer;
std::unique_ptr indexBuffer;
std::unique_ptr denseIndexBuffer;
@@ -145,13 +144,16 @@ private:
);
// Does block allow to see other blocks sides (is it transparent)
- inline bool isOpen(const glm::ivec3& pos, const Block& def, const Variant& variant) const {
+ bool isOpen(const glm::ivec3& pos, const Block& def, const Variant& variant) const {
const auto& vox = voxelsBuffer->pickBlock(
chunk->x * CHUNK_W + pos.x, pos.y, chunk->z * CHUNK_D + pos.z
);
if (vox.id == BLOCK_VOID) {
return false;
}
+ if (def.rt.id == vox.id && def.rt.solid && variant.culling == CullingMode::DEFAULT) {
+ return false;
+ }
const auto& block = *blockDefsCache[vox.id];
const auto& blockVariant = block.getVariantByBits(vox.state.userbits);
uint8_t otherDrawGroup = blockVariant.drawGroup;
@@ -178,6 +180,6 @@ private:
float x, float y, float z, const glm::ivec3& right, const glm::ivec3& up
) const;
- void render(const voxel* voxels, const int beginEnds[256][2]);
- SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]);
+ void render(const voxel* voxels, int totalBegin, int totalEnd);
+ SortingMeshData renderTranslucent(const voxel* voxels, int totalBegin, int totalEnd);
};
diff --git a/src/graphics/render/ChunksRenderer.cpp b/src/graphics/render/ChunksRenderer.cpp
index baf4cf356..82a7fa6d6 100644
--- a/src/graphics/render/ChunksRenderer.cpp
+++ b/src/graphics/render/ChunksRenderer.cpp
@@ -11,9 +11,12 @@
#include "world/Level.hpp"
#include "window/Camera.hpp"
#include "maths/FrustumCulling.hpp"
+#include "maths/util.hpp"
#include "util/listutil.hpp"
#include "util/ObjectsPool.hpp"
+#include "util/timeutil.hpp"
#include "settings.hpp"
+#include "content/Content.hpp"
static debug::Logger logger("chunks-render");
@@ -33,7 +36,7 @@ public:
settings.graphics.denseRender.get()
? settings.graphics.chunkMaxVerticesDense.get()
: settings.graphics.chunkMaxVertices.get(),
- level.content,
+ level.content.getIndices()->blocks.getDefs(),
cache,
settings
) {
@@ -92,7 +95,7 @@ ChunksRenderer::ChunksRenderer(
threadPool.setStopOnFail(false);
renderer = std::make_unique(
settings.graphics.chunkMaxVertices.get(),
- level.content, cache, settings
+ level.content.getIndices()->blocks.getDefs(), cache, settings
);
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
logger.info() << "memory consumption is "
@@ -122,30 +125,30 @@ std::shared_ptr ChunksRenderer::prepareVoxelsVolume(
return voxelsBuffer;
}
-const ChunkMesh* ChunksRenderer::render(
- const std::shared_ptr& chunk, bool important, bool lowPriority
+void ChunksRenderer::renderBlocking(const std::shared_ptr& chunk) {
+ glm::ivec2 key(chunk->x, chunk->z);
+ ChunkMesh mesh {};
+ auto voxelsBuffer = prepareVoxelsVolume(*chunk);
+ mesh = renderer->render(chunk.get(), *voxelsBuffer);
+ meshes[key] = std::move(mesh);
+ chunk->flags.modified = false;
+}
+
+void ChunksRenderer::render(
+ const std::shared_ptr& chunk, bool lowPriority
) {
glm::ivec2 key(chunk->x, chunk->z);
- if (important) {
- ChunkMesh mesh {};
- auto voxelsBuffer = prepareVoxelsVolume(*chunk);
- mesh = renderer->render(chunk.get(), *voxelsBuffer);
- meshes[key] = std::move(mesh);
- chunk->flags.modified = false;
- return &meshes[key];
- }
if (inwork.find(key) != inwork.end() ||
((inwork.size() >= threadPool.getWorkersCount() ||
enqueuedInFrame >= MAX_CHUNKS_ENQUEUED_IN_FRAME) &&
lowPriority)) {
- return nullptr;
+ return;
}
chunk->flags.modified = false;
enqueuedInFrame++;
auto voxelsBuffer = prepareVoxelsVolume(*chunk);
threadPool.enqueueJob({chunk, std::move(voxelsBuffer)});
inwork[key] = true;
- return nullptr;
}
void ChunksRenderer::unload(const Chunk* chunk) {
@@ -161,76 +164,69 @@ void ChunksRenderer::clear() {
threadPool.clearQueue();
}
-const ChunkMesh* ChunksRenderer::getOrRender(
- const std::shared_ptr& chunk, bool important, bool lowPriority
-) {
- auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
- if (found == meshes.end()) {
- return render(chunk, important, lowPriority);
- }
- if (chunk->flags.modified && chunk->flags.lighted) {
- render(chunk, important, lowPriority);
- }
- return &found->second;
-}
-
void ChunksRenderer::update() {
threadPool.pullResults();
enqueuedInFrame = 0;
-}
-const Mesh* ChunksRenderer::retrieveChunk(
- size_t index, const Camera& camera, bool culling
-) {
- auto chunk = chunks.getChunks()[index];
- if (chunk == nullptr) {
- return nullptr;
- }
- if (!chunk->flags.lighted) {
- const auto& found = meshes.find({chunk->x, chunk->z});
- if (found == meshes.end()) {
- return nullptr;
- } else {
- return found->second.mesh.get();
+ int width = chunks.getWidth();
+ int halfWidth = width / 2;
+ int halfHeight = chunks.getHeight() / 2;
+ int centerX = chunks.getOffsetX() + halfWidth;
+ int centerY = chunks.getOffsetY() + halfHeight;
+
+ meshBuildQueue.clear();
+ auto& chunksArray = chunks.getChunks();
+ for (int index = 0; index < chunks.getVolume(); index++) {
+ const auto& chunk = chunks.getChunks()[index];
+ if (chunk == nullptr || !chunk->flags.lighted) {
+ continue;
+ }
+ int x = chunk->x;
+ int z = chunk->z;
+ if (chunk->flags.modified || meshes.find({x, z}) == meshes.end()) {
+ meshBuildQueue.emplace_back(x - centerX, z - centerY);
}
}
- float distance = glm::distance(
- camera.position,
- glm::vec3(
- (chunk->x + 0.5f) * CHUNK_W,
- camera.position.y,
- (chunk->z + 0.5f) * CHUNK_D
- )
+ std::sort(
+ meshBuildQueue.begin(),
+ meshBuildQueue.end(),
+ [](const glm::ivec2& a, const glm::ivec2& b) {
+ return util::length2(a) < util::length2(b);
+ }
);
- auto mesh = getOrRender(
- chunk,
- distance < CHUNK_W * 1.5f,
- distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5
- );
- if (mesh == nullptr) {
- return nullptr;
- }
- if (chunk->flags.dirtyHeights) {
- chunk->updateHeights();
- }
- if (culling) {
- const auto& meshAABB = mesh->meshAABB;
- auto aabbMin = meshAABB.min();
- auto aabbMax = meshAABB.max();
- glm::vec3 min(
- chunk->x * CHUNK_W + std::min(0.0f, aabbMin.x),
- chunk->bottom,
- chunk->z * CHUNK_D + std::min(0.0f, aabbMin.z)
- );
- glm::vec3 max(
- chunk->x * CHUNK_W + aabbMax.x,
- chunk->top,
- chunk->z * CHUNK_D + aabbMax.z
- );
- if (!frustum.isBoxVisible(min, max)) return nullptr;
+ int loadDistance = settings.chunks.loadDistance.get();
+
+ const int topN = 10;
+ int top = std::min(meshBuildQueue.size(), topN);
+ for (int i = 0; i < top; i++) {
+ glm::ivec2 offset = meshBuildQueue[i];
+ size_t index =
+ (offset.y + halfHeight) * width + offset.x + halfWidth;
+
+ auto& chunk = chunksArray[index];
+ assert(chunk != nullptr);
+
+ float distance = glm::distance(
+ glm::vec3 (centerX, 0.0f, centerY),
+ glm::vec3(
+ (chunk->x + 0.5f) * CHUNK_W,
+ 0,
+ (chunk->z + 0.5f) * CHUNK_D
+ )
+ );
+ bool important = distance < CHUNK_W * 1.5f;
+ bool lowPriority = distance > CHUNK_W * loadDistance * 0.5;
+
+ if (chunk->flags.dirtyHeights) {
+ chunk->updateHeights();
+ }
+ if (important) {
+ renderBlocking(chunk);
+ } else {
+ render(chunk, lowPriority);
+ }
}
- return mesh->mesh.get();
}
void ChunksRenderer::drawShadowsPass(
@@ -317,16 +313,43 @@ void ChunksRenderer::drawChunks(
// TODO: minimize draw calls number
for (int i = indices.size()-1; i >= 0; i--) {
auto& chunk = chunks.getChunks()[indices[i].index];
- auto mesh = retrieveChunk(indices[i].index, camera, culling);
- if (mesh == nullptr) {
+ if (chunk == nullptr) {
continue;
}
+ auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
+ if (found == meshes.end()) {
+ continue;
+ }
+ auto& mesh = found->second;
+ if (mesh.mesh == nullptr) {
+ continue;
+ }
+ if (culling) {
+ const auto& meshAABB = mesh.meshAABB;
+ auto aabbMin = meshAABB.min();
+ auto aabbMax = meshAABB.max();
+ glm::vec3 min(
+ chunk->x * CHUNK_W + std::min(0.0f, aabbMin.x),
+ chunk->bottom,
+ chunk->z * CHUNK_D + std::min(0.0f, aabbMin.z)
+ );
+ glm::vec3 max(
+ chunk->x * CHUNK_W + aabbMax.x,
+ chunk->top,
+ chunk->z * CHUNK_D + aabbMax.z
+ );
+
+ if (!frustum.isBoxVisible(min, max)) {
+ continue;
+ }
+ }
+
glm::vec3 coord(
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
);
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
shader.uniformMatrix("u_model", model);
- mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
+ mesh.mesh->draw(GL_TRIANGLES, glm::distance2(camera.position * glm::vec3(1, 0, 1),
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
visibleChunks++;
}
diff --git a/src/graphics/render/ChunksRenderer.hpp b/src/graphics/render/ChunksRenderer.hpp
index 5e6783d97..5e26adc43 100644
--- a/src/graphics/render/ChunksRenderer.hpp
+++ b/src/graphics/render/ChunksRenderer.hpp
@@ -1,16 +1,17 @@
#pragma once
+#define GLM_ENABLE_EXPERIMENTAL
+
+#include "util/ThreadPool.hpp"
+#include "commons.hpp"
+
#include
#include
#include
#include
-#define GLM_ENABLE_EXPERIMENTAL
#include
-#include "util/ThreadPool.hpp"
-#include "commons.hpp"
-
template class Mesh;
class Chunk;
class Level;
@@ -44,22 +45,6 @@ struct RendererJob {
};
class ChunksRenderer {
- const Chunks& chunks;
- const Assets& assets;
- const Frustum& frustum;
- const EngineSettings& settings;
-
- std::unique_ptr renderer;
- std::unordered_map meshes;
- std::unordered_map inwork;
- std::vector indices;
- util::ThreadPool threadPool;
- const Mesh* retrieveChunk(
- size_t index, const Camera& camera, bool culling
- );
- std::shared_ptr prepareVoxelsVolume(const Chunk& chunk);
-
- size_t enqueuedInFrame = 0;
public:
ChunksRenderer(
const Level& level,
@@ -69,18 +54,11 @@ public:
const ContentGfxCache& cache,
const EngineSettings& settings
);
- virtual ~ChunksRenderer();
+ ~ChunksRenderer();
- const ChunkMesh* render(
- const std::shared_ptr& chunk, bool important, bool lowPriority
- );
void unload(const Chunk* chunk);
void clear();
- const ChunkMesh* getOrRender(
- const std::shared_ptr& chunk, bool important, bool lowPriority
- );
-
void drawShadowsPass(
const Camera& camera, Shader& shader, const Camera& playerCamera
);
@@ -92,4 +70,25 @@ public:
void update();
static size_t visibleChunks;
+
+private:
+ const Chunks& chunks;
+ const Assets& assets;
+ const Frustum& frustum;
+ const EngineSettings& settings;
+
+ std::unique_ptr renderer;
+ std::unordered_map meshes;
+ std::unordered_map inwork;
+ std::vector indices;
+ util::ThreadPool threadPool;
+ std::vector meshBuildQueue;
+
+ size_t enqueuedInFrame = 0;
+
+ std::shared_ptr prepareVoxelsVolume(const Chunk& chunk);
+
+ void render(const std::shared_ptr& chunk, bool lowPriority);
+
+ void renderBlocking(const std::shared_ptr& chunk);
};
diff --git a/src/graphics/render/CloudsRenderer.cpp b/src/graphics/render/CloudsRenderer.cpp
index e2047eb63..bfc53e4bb 100755
--- a/src/graphics/render/CloudsRenderer.cpp
+++ b/src/graphics/render/CloudsRenderer.cpp
@@ -329,7 +329,7 @@ void CloudsRenderer::draw(
shader.uniform4f("u_tint", glm::vec4(weather.cloudsTint(), 1.0f));
shader.uniform1f("u_fogFactor", fogFactor * 0.03f);
- shader.uniform1f("u_fogCurve", 0.7f - 0.3f);
+ shader.uniform1f("u_fogCurve", 0.4f);
for (int i = 0; i < std::min(quality, layers.size()); i++) {
draw(layers[i], frustum, shader, camera, timer, i);
diff --git a/src/graphics/render/DebugLinesRenderer.cpp b/src/graphics/render/DebugLinesRenderer.cpp
index df0b1af78..3750b2cd0 100644
--- a/src/graphics/render/DebugLinesRenderer.cpp
+++ b/src/graphics/render/DebugLinesRenderer.cpp
@@ -93,7 +93,7 @@ void DebugLinesRenderer::drawCoordSystem(
}
void DebugLinesRenderer::render(
- DrawContext& pctx,
+ const DrawContext& pctx,
const Camera& camera,
LineBatch& linesBatch,
Shader& linesShader,
diff --git a/src/graphics/render/DebugLinesRenderer.hpp b/src/graphics/render/DebugLinesRenderer.hpp
index 7a9f0a324..bc6609c5a 100644
--- a/src/graphics/render/DebugLinesRenderer.hpp
+++ b/src/graphics/render/DebugLinesRenderer.hpp
@@ -20,7 +20,7 @@ public:
/// @param linesShader Shader used for rendering lines
/// @param showChunkBorders Whether to show chunk borders
void render(
- DrawContext& ctx,
+ const DrawContext& ctx,
const Camera& camera,
LineBatch& linesBatch,
Shader& linesShader,
diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp
index 1fa2abd0b..26a2b302e 100644
--- a/src/graphics/render/Decorator.cpp
+++ b/src/graphics/render/Decorator.cpp
@@ -25,6 +25,7 @@
#include "voxels/blocks_agent.hpp"
#include "window/Camera.hpp"
#include "world/Level.hpp"
+#include "world/Weather.hpp"
#include "WorldRenderer.hpp"
namespace fs = std::filesystem;
diff --git a/src/graphics/render/MainBatch.cpp b/src/graphics/render/MainBatch.cpp
index 1a501ca15..91364e510 100644
--- a/src/graphics/render/MainBatch.cpp
+++ b/src/graphics/render/MainBatch.cpp
@@ -46,7 +46,7 @@ void MainBatch::flush() {
texture = blank.get();
}
texture->bind();
- mesh->reload(buffer.get(), index);
+ mesh->reload(buffer.get(), index, true);
mesh->draw();
index = 0;
}
diff --git a/src/graphics/render/WorldRenderer.cpp b/src/graphics/render/WorldRenderer.cpp
index 72c9cdb32..5c5616fc2 100644
--- a/src/graphics/render/WorldRenderer.cpp
+++ b/src/graphics/render/WorldRenderer.cpp
@@ -51,7 +51,9 @@
#include "window/Window.hpp"
#include "world/Level.hpp"
#include "world/LevelEvents.hpp"
+#include "world/Weather.hpp"
#include "world/World.hpp"
+#include "debug/Logger.hpp"
#include
#include
@@ -62,9 +64,20 @@
inline constexpr size_t BATCH3D_CAPACITY = 4096;
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
+static debug::Logger logger("world-renderer");
+
bool WorldRenderer::showChunkBorders = false;
bool WorldRenderer::showEntitiesDebug = false;
+template
+static ObserverHandler observe_setting(
+ ObservableSetting& setting, bool& dirtySettings
+) {
+ return setting.observe([&dirtySettings](auto const&) {
+ dirtySettings = true;
+ });
+}
+
WorldRenderer::WorldRenderer(
Engine& engine, LevelFrontend& frontend, Player& player
)
@@ -72,6 +85,7 @@ WorldRenderer::WorldRenderer(
level(frontend.getLevel()),
player(player),
assets(*engine.getAssets()),
+ weather(frontend.getWeather()),
frustumCulling(std::make_unique()),
lineBatch(std::make_unique()),
batch3d(std::make_unique(BATCH3D_CAPACITY)),
@@ -120,10 +134,41 @@ WorldRenderer::WorldRenderer(
shadowMapping = std::make_unique(level);
debugLines = std::make_unique(level);
cloudsRenderer = std::make_unique();
+
+ keepAlive(observe_setting(settings.graphics.advancedRender, dirtySettings));
+ keepAlive(observe_setting(settings.graphics.shadowsQuality, dirtySettings));
+ keepAlive(observe_setting(settings.graphics.ssao, dirtySettings));
}
WorldRenderer::~WorldRenderer() = default;
+void WorldRenderer::refreshSettings() {
+ Shader* affectedShaders[] {
+ &assets.require("main"),
+ &assets.require("entity"),
+ &assets.require("clouds"),
+ &assets.require("translucent"),
+ &assets.require("deferred_lighting").getShader(),
+ nullptr
+ };
+
+ const auto& graphics = engine.getSettings().graphics;
+ gbufferPipeline = graphics.advancedRender.get();
+
+ int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline;
+ shadowMapping->setQuality(shadowsQuality);
+
+ std::vector defines;
+ if (shadowsQuality != 0) defines.emplace_back("ENABLE_SHADOWS");
+ if (graphics.ssao.get()) defines.emplace_back("ENABLE_SSAO");
+ if (gbufferPipeline) defines.emplace_back("ADVANCED_RENDER");
+
+ logger.info() << "recompiling shaders due to settings change";
+ for (size_t i = 0; affectedShaders[i]; i++) {
+ affectedShaders[i]->recompile(defines);
+ }
+}
+
static void setup_weather(Shader& shader, const Weather& weather) {
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
@@ -156,7 +201,7 @@ void WorldRenderer::setupWorldShader(
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
shader.uniform1i("u_debugLights", lightsDebug);
shader.uniform1i("u_debugNormals", false);
- shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
+ shader.uniform1f("u_dayTime", level.getWorld().getInfo().daytime);
shader.uniform2f("u_lightDir", skybox->getLightDir());
shader.uniform1i("u_skybox", advanced_pipeline::TARGET_SKYBOX);
@@ -188,8 +233,6 @@ void WorldRenderer::renderOpaque(
const EngineSettings& settings,
bool hudVisible
) {
- texts->render(ctx, camera, settings, hudVisible, false);
-
float fogFactor = calcFogFactor();
auto& entityShader = assets.require("entity");
@@ -212,12 +255,12 @@ void WorldRenderer::renderOpaque(
modelBatch->render();
particles->render(camera);
- auto& shader = assets.require("main");
+ auto& mainShader = assets.require("main");
auto& cloudsShader = assets.require("clouds");
- setupWorldShader(shader, camera, settings, fogFactor);
+ setupWorldShader(mainShader, camera, settings, fogFactor);
- chunksRenderer->drawChunks(camera, shader);
+ chunksRenderer->drawChunks(camera, mainShader);
blockWraps->draw(ctx);
if (level.environment.sky.clouds) {
@@ -232,7 +275,7 @@ void WorldRenderer::renderOpaque(
if (hudVisible) {
auto& linesShader = assets.require("lines");
- renderLines(camera, linesShader, ctx);
+ renderInWorldLines(camera, linesShader, ctx);
}
}
@@ -265,7 +308,7 @@ void WorldRenderer::renderBlockSelection() {
lineBatch->flush();
}
-void WorldRenderer::renderLines(
+void WorldRenderer::renderInWorldLines(
const Camera& camera, Shader& linesShader, const DrawContext& pctx
) {
linesShader.use();
@@ -282,193 +325,144 @@ void WorldRenderer::renderLines(
}
}
-void WorldRenderer::refreshSettings(Shader** shaders) {
- const auto& graphics = engine.getSettings().graphics;
- gbufferPipeline = graphics.advancedRender.get();
-
- int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline;
- shadowMapping->setQuality(shadowsQuality);
-
- CompileTimeShaderSettings currentSettings {
- gbufferPipeline,
- shadowsQuality != 0,
- graphics.ssao.get() && gbufferPipeline};
- if (prevCTShaderSettings.advancedRender != currentSettings.advancedRender ||
- prevCTShaderSettings.shadows != currentSettings.shadows ||
- prevCTShaderSettings.ssao != currentSettings.ssao) {
- std::vector defines;
- if (currentSettings.shadows) defines.emplace_back("ENABLE_SHADOWS");
- if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO");
- if (currentSettings.advancedRender)
- defines.emplace_back("ADVANCED_RENDER");
-
- for (size_t i = 0; shaders[i]; i++) {
- shaders[i]->recompile(defines);
- }
- prevCTShaderSettings = currentSettings;
- }
-}
-
void WorldRenderer::update(const Camera& camera, float delta) {
timer += delta;
+ chunksRenderer->update();
weather.update(delta);
precipitation->update(delta);
particles->update(camera, delta);
}
+void WorldRenderer::renderFrameClassic(
+ const DrawContext& pctx,
+ Camera& camera,
+ bool hudVisible,
+ PostProcessing& postProcessing
+) {
+ const auto& settings = engine.getSettings();
+ const auto& worldInfo = level.getWorld().getInfo();
+
+ DrawContext ctx = pctx.sub();
+ ctx.setDepthTest(true);
+ ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
+ ctx.setFramebuffer(postProcessing.getFramebuffer());
+
+ skybox->draw(
+ level.environment, ctx, camera, worldInfo.daytime, weather.clouds()
+ );
+ texts->render(ctx, camera, settings, hudVisible, false);
+
+ if (debug && hudVisible) {
+ renderDebugLines(ctx, camera);
+ }
+
+ DrawContext wctx = ctx.sub();
+ wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
+ wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
+ // Translucent blocks
+ {
+ const auto& settings = engine.getSettings();
+ auto& translucentShader = assets.require("translucent");
+ auto sctx = wctx.sub();
+ sctx.setCullFace(true);
+ translucentShader.use();
+ setupWorldShader(translucentShader, camera, settings, calcFogFactor());
+ chunksRenderer->drawSortedMeshes(camera, translucentShader);
+ }
+
+ renderWeatherEffects(camera);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void WorldRenderer::renderFrameAdvanced(
+ const DrawContext& pctx,
+ Camera& camera,
+ bool hudVisible,
+ PostProcessing& postProcessing
+) {
+ const auto& settings = engine.getSettings();
+ const auto& worldInfo = level.getWorld().getInfo();
+ float fogFactor = calcFogFactor();
+
+ shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) {
+ auto& shader = assets.require("shadows");
+ setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f);
+ chunksRenderer->drawShadowsPass(shadowCamera, shader, camera);
+ });
+
+ auto& deferredShader =
+ assets.require("deferred_lighting").getShader();
+ deferredShader.use();
+ setupWorldShader(deferredShader, camera, settings, fogFactor);
+ postProcessing.renderDeferredShading(pctx, assets, timer, camera);
+
+ DrawContext ctx = pctx.sub();
+ ctx.setDepthTest(true);
+ ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
+
+ postProcessing.bindDepthBuffer();
+ skybox->draw(
+ level.environment, ctx, camera, worldInfo.daytime, weather.clouds()
+ );
+ texts->render(ctx, camera, settings, hudVisible, false);
+ if (debug && hudVisible) {
+ renderDebugLines(ctx, camera);
+ }
+
+ DrawContext wctx = ctx.sub();
+ wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
+ wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
+ // Translucent blocks
+ {
+ const auto& settings = engine.getSettings();
+ auto& translucentShader = assets.require("translucent");
+ auto sctx = wctx.sub();
+ sctx.setCullFace(true);
+ translucentShader.use();
+ setupWorldShader(translucentShader, camera, settings, fogFactor);
+ chunksRenderer->drawSortedMeshes(camera, translucentShader);
+ }
+
+ renderWeatherEffects(camera);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
void WorldRenderer::renderFrame(
const DrawContext& pctx,
Camera& camera,
bool hudVisible,
PostProcessing& postProcessing
) {
- auto projView = camera.getProjView();
- auto world = level.getWorld();
-
const auto& vp = pctx.getViewport();
camera.setAspectRatio(vp.x / static_cast(vp.y));
+ if (dirtySettings) {
+ refreshSettings();
+ dirtySettings = false;
+ }
- auto& mainShader = assets.require("main");
- auto& entityShader = assets.require("entity");
- auto& cloudsShader = assets.require("clouds");
- auto& translucentShader = assets.require("translucent");
- auto& deferredShader =
- assets.require("deferred_lighting").getShader();
-
- const auto& settings = engine.getSettings();
-
- Shader* affectedShaders[] {
- &mainShader,
- &entityShader,
- &cloudsShader,
- &translucentShader,
- &deferredShader,
- nullptr};
-
- refreshSettings(affectedShaders);
-
- const auto& worldInfo = world->getInfo();
-
+ const auto& worldInfo = level.getWorld().getInfo();
float clouds = weather.clouds();
- clouds = glm::max(worldInfo.fog, clouds);
- float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f;
float random = rand() / static_cast(RAND_MAX);
skybox->refresh(
level.environment,
pctx,
worldInfo.daytime,
- mie,
+ 1.0f + clouds,
weather.skyTint(),
weather.highlight * random,
4
);
-
- chunksRenderer->update();
-
- shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) {
- auto& shader = assets.require("shadows");
- setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f);
- chunksRenderer->drawShadowsPass(shadowCamera, shader, camera);
- });
- {
- DrawContext wctx = pctx.sub();
- postProcessing.use(wctx, gbufferPipeline);
- display::clearDepth();
-
- /* Main opaque pass (GBuffer pass) */ {
- DrawContext ctx = wctx.sub();
- ctx.setDepthTest(true);
- ctx.setCullFace(true);
- ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
- ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
- renderOpaque(ctx, camera, settings, hudVisible);
- }
- texts->render(pctx, camera, settings, hudVisible, true);
- }
- float fogFactor = calcFogFactor();
+ renderOpaquePass(pctx, camera, hudVisible, postProcessing);
if (gbufferPipeline) {
- deferredShader.use();
- setupWorldShader(deferredShader, camera, settings, fogFactor);
- postProcessing.renderDeferredShading(pctx, assets, timer, camera);
- }
- {
- DrawContext ctx = pctx.sub();
- ctx.setDepthTest(true);
- ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
-
- if (gbufferPipeline) {
- postProcessing.bindDepthBuffer();
- } else {
- ctx.setFramebuffer(postProcessing.getFramebuffer());
- }
-
- // Background sky plane
- skybox->draw(
- level.environment, ctx, camera, worldInfo.daytime, clouds
- );
-
- auto& linesShader = assets.require("lines");
- linesShader.use();
- if (debug && hudVisible) {
- debugLines->render(
- ctx, camera, *lineBatch, linesShader, showChunkBorders
- );
- }
- linesShader.uniformMatrix("u_projview", projView);
- lineBatch->flush();
-
- DrawContext wctx = ctx.sub();
- wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
- wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
- // Translucent blocks
- {
- auto sctx = wctx.sub();
- sctx.setCullFace(true);
- translucentShader.use();
- setupWorldShader(translucentShader, camera, settings, fogFactor);
- chunksRenderer->drawSortedMeshes(camera, translucentShader);
- }
-
- // Weather effects
- entityShader.use();
- setupWorldShader(entityShader, camera, settings, fogFactor);
-
- std::array weatherInstances {
- &weather.a, &weather.b};
- for (const auto& weather : weatherInstances) {
- float maxIntensity = weather->fall.maxIntensity;
- float zero = weather->fall.minOpacity;
- float one = weather->fall.maxOpacity;
- float t = (weather->intensity * (one - zero)) * maxIntensity + zero;
- entityShader.uniform1i("u_alphaClip", weather->fall.opaque);
- entityShader.uniform1f(
- "u_opacity", weather->fall.opaque ? t * t : t
- );
- if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) {
- precipitation->render(camera, *weather);
- }
- }
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ renderFrameAdvanced(pctx, camera, hudVisible, postProcessing);
+ } else {
+ renderFrameClassic(pctx, camera, hudVisible, postProcessing);
}
postProcessing.render(pctx, assets, timer, camera);
if (player.currentCamera == player.fpCamera) {
- DrawContext ctx = pctx.sub();
- ctx.setDepthTest(true);
- ctx.setCullFace(true);
- ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
- ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
- display::clearDepth();
-
- // prepare modified HUD camera
- Camera hudcam = camera;
- hudcam.far = 10.0f;
- hudcam.setFov(0.9f);
- hudcam.position = {};
-
- setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);
- hands->render(camera);
+ renderHandsPass(pctx, camera);
}
{
auto bctx = pctx.sub();
@@ -478,6 +472,85 @@ void WorldRenderer::renderFrame(
glActiveTexture(GL_TEXTURE0);
}
+
+void WorldRenderer::renderOpaquePass(
+ const DrawContext& pctx,
+ Camera& camera,
+ bool hudVisible,
+ PostProcessing& postProcessing
+) {
+ const auto& settings = engine.getSettings();
+
+ DrawContext wctx = pctx.sub();
+ postProcessing.use(wctx, gbufferPipeline);
+ display::clearDepth();
+
+ /* Main opaque pass (GBuffer pass) */ {
+ DrawContext ctx = wctx.sub();
+ ctx.setDepthTest(true);
+ ctx.setCullFace(true);
+ ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
+ ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
+ renderOpaque(ctx, camera, settings, hudVisible);
+
+ ctx.setDepthMask(false);
+ texts->render(wctx, camera, settings, hudVisible, true);
+ }
+}
+
+void WorldRenderer::renderWeatherEffects(Camera& camera) {
+ const auto& settings = engine.getSettings();
+ auto& entityShader = assets.require("entity");
+ entityShader.use();
+ setupWorldShader(entityShader, camera, settings, calcFogFactor());
+
+ std::array weatherInstances {
+ &weather.a, &weather.b};
+ for (const auto& weather : weatherInstances) {
+ float maxIntensity = weather->fall.maxIntensity;
+ float zero = weather->fall.minOpacity;
+ float one = weather->fall.maxOpacity;
+ float t = (weather->intensity * (one - zero)) * maxIntensity + zero;
+ entityShader.uniform1i("u_alphaClip", weather->fall.opaque);
+ entityShader.uniform1f(
+ "u_opacity", weather->fall.opaque ? t * t : t
+ );
+ if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) {
+ precipitation->render(camera, *weather);
+ }
+ }
+}
+
+void WorldRenderer::renderDebugLines(const DrawContext& ctx, Camera& camera) {
+ auto& linesShader = assets.require("lines");
+ linesShader.use();
+ debugLines->render(
+ ctx, camera, *lineBatch, linesShader, showChunkBorders
+ );
+ linesShader.uniformMatrix("u_projview", camera.getProjView());
+ lineBatch->flush();
+}
+
+void WorldRenderer::renderHandsPass(const DrawContext& pctx, Camera& camera) {
+ auto& entityShader = assets.require("entity");
+
+ DrawContext ctx = pctx.sub();
+ ctx.setDepthTest(true);
+ ctx.setCullFace(true);
+ ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
+ ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
+ display::clearDepth();
+
+ // prepare modified HUD camera
+ Camera hudcam = camera;
+ hudcam.far = 10.0f;
+ hudcam.setFov(0.9f);
+ hudcam.position = {};
+
+ setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);
+ hands->render(camera);
+}
+
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
int x = std::floor(player.currentCamera->position.x);
int y = std::floor(player.currentCamera->position.y);
@@ -523,7 +596,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
batch3d->flush();
}
-void WorldRenderer::clear() {
+void WorldRenderer::resetCache() {
chunksRenderer->clear();
}
diff --git a/src/graphics/render/WorldRenderer.hpp b/src/graphics/render/WorldRenderer.hpp
index c3e6b1669..b3b10f88c 100644
--- a/src/graphics/render/WorldRenderer.hpp
+++ b/src/graphics/render/WorldRenderer.hpp
@@ -4,12 +4,11 @@
#include "typedefs.hpp"
#include "presets/WeatherPreset.hpp"
-#include "world/Weather.hpp"
#include "window/Camera.hpp"
+#include "util/ObjectsKeeper.hpp"
#include
#include
-#include
#include
class Assets;
@@ -36,18 +35,38 @@ class Skybox;
class TextsRenderer;
class CloudsRenderer;
struct EngineSettings;
+struct Weather;
-struct CompileTimeShaderSettings {
- bool advancedRender = false;
- bool shadows = false;
- bool ssao = false;
-};
+class WorldRenderer final : public util::ObjectsKeeper {
+public:
+ static bool showChunkBorders;
+ static bool showEntitiesDebug;
-class WorldRenderer {
+ WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
+ ~WorldRenderer();
+
+ void update(const Camera& camera, float delta);
+
+ void renderFrame(
+ const DrawContext& context,
+ Camera& camera,
+ bool hudVisible,
+ PostProcessing& postProcessing
+ );
+
+ void resetCache();
+
+ void setDebug(bool flag);
+
+ void toggleLightsDebug();
+
+ Weather& getWeather();
+private:
Engine& engine;
const Level& level;
Player& player;
const Assets& assets;
+ Weather& weather;
std::unique_ptr frustumCulling;
std::unique_ptr lineBatch;
std::unique_ptr batch3d;
@@ -59,14 +78,12 @@ class WorldRenderer {
std::unique_ptr debugLines;
std::unique_ptr precipitation;
std::unique_ptr cloudsRenderer;
- Weather weather {};
float timer = 0.0f;
bool debug = false;
bool lightsDebug = false;
bool gbufferPipeline = false;
-
- CompileTimeShaderSettings prevCTShaderSettings {};
+ bool dirtySettings = true;
/// @brief Render block selection lines
void renderBlockSelection();
@@ -74,7 +91,7 @@ class WorldRenderer {
/// @brief Render lines (selection and debug)
/// @param camera active camera
/// @param linesShader shader used
- void renderLines(
+ void renderInWorldLines(
const Camera& camera, Shader& linesShader, const DrawContext& pctx
);
@@ -98,7 +115,34 @@ class WorldRenderer {
bool hudVisible
);
- void refreshSettings(Shader** shaders);
+ void renderOpaquePass(
+ const DrawContext& context,
+ Camera& camera,
+ bool hudVisible,
+ PostProcessing& postProcessing
+ );
+
+ void renderWeatherEffects(Camera& camera);
+
+ void renderHandsPass(const DrawContext& pctx, Camera& camera);
+
+ void renderDebugLines(const DrawContext& context, Camera& camera);
+
+ void renderFrameClassic(
+ const DrawContext& context,
+ Camera& camera,
+ bool hudVisible,
+ PostProcessing& postProcessing
+ );
+
+ void renderFrameAdvanced(
+ const DrawContext& context,
+ Camera& camera,
+ bool hudVisible,
+ PostProcessing& postProcessing
+ );
+
+ void refreshSettings();
float calcFogFactor() const;
public:
@@ -106,27 +150,4 @@ public:
std::unique_ptr texts;
std::unique_ptr blockWraps;
std::unique_ptr skeletons;
-
- static bool showChunkBorders;
- static bool showEntitiesDebug;
-
- WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
- ~WorldRenderer();
-
- void update(const Camera& camera, float delta);
-
- void renderFrame(
- const DrawContext& context,
- Camera& camera,
- bool hudVisible,
- PostProcessing& postProcessing
- );
-
- void clear();
-
- void setDebug(bool flag);
-
- void toggleLightsDebug();
-
- Weather& getWeather();
};
diff --git a/src/graphics/ui/GUI.cpp b/src/graphics/ui/GUI.cpp
index 6169a2578..944c32fb5 100644
--- a/src/graphics/ui/GUI.cpp
+++ b/src/graphics/ui/GUI.cpp
@@ -14,7 +14,6 @@
#include "frontend/UiDocument.hpp"
#include "frontend/locale.hpp"
#include "graphics/core/Batch2D.hpp"
-#include "graphics/core/LineBatch.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/Font.hpp"
#include "graphics/core/DrawContext.hpp"
@@ -260,7 +259,7 @@ void GUI::act(float delta, const glm::uvec2& vp) {
hover = nullptr;
}
}
- if (focus) {
+ if (focus && focusedOnStart == focus.get()) {
actFocused();
}
if (focus && !focus->isFocused()) {
@@ -274,6 +273,7 @@ void GUI::postAct() {
postRunnables.pop();
callback();
}
+ focusedOnStart = focus.get();
}
void GUI::draw(const DrawContext& pctx, Assets& assets) {
diff --git a/src/graphics/ui/GUI.hpp b/src/graphics/ui/GUI.hpp
index 7d120c9cc..97fc0ed37 100644
--- a/src/graphics/ui/GUI.hpp
+++ b/src/graphics/ui/GUI.hpp
@@ -72,6 +72,7 @@ namespace gui {
std::shared_ptr hover;
std::shared_ptr pressed;
std::shared_ptr focus;
+ const UINode* focusedOnStart = nullptr;
std::shared_ptr tooltip;
std::shared_ptr rootDocument;
std::unique_ptr syntaxColorScheme;
diff --git a/src/graphics/ui/elements/InlineFrame.cpp b/src/graphics/ui/elements/InlineFrame.cpp
index 319a16b1b..eeef50e47 100644
--- a/src/graphics/ui/elements/InlineFrame.cpp
+++ b/src/graphics/ui/elements/InlineFrame.cpp
@@ -14,6 +14,7 @@ void InlineFrame::setSrc(const std::string& src) {
this->src = src;
if (document) {
scripting::on_ui_close(*document, nullptr);
+ clear();
document = nullptr;
root = nullptr;
}
diff --git a/src/graphics/ui/elements/InventoryView.cpp b/src/graphics/ui/elements/InventoryView.cpp
index 144d80397..395c8d34e 100644
--- a/src/graphics/ui/elements/InventoryView.cpp
+++ b/src/graphics/ui/elements/InventoryView.cpp
@@ -12,11 +12,8 @@
#include "graphics/core/Batch2D.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Font.hpp"
-#include "graphics/core/Shader.hpp"
-#include "graphics/core/Texture.hpp"
#include "graphics/render/BlocksPreview.hpp"
#include "graphics/ui/GUI.hpp"
-#include "items/Inventories.hpp"
#include "items/Inventory.hpp"
#include "items/ItemDef.hpp"
#include "logic/scripting/scripting.hpp"
diff --git a/src/graphics/ui/elements/ModelViewer.cpp b/src/graphics/ui/elements/ModelViewer.cpp
index 727110f8b..748eabae5 100644
--- a/src/graphics/ui/elements/ModelViewer.cpp
+++ b/src/graphics/ui/elements/ModelViewer.cpp
@@ -13,6 +13,7 @@
#include "window/Window.hpp"
#include "../GUI.hpp"
+// TODO: remove
// TODO: remove
#include
@@ -24,8 +25,7 @@ ModelViewer::ModelViewer(
: Container(gui, size),
modelName(modelName),
camera(),
- batch(std::make_unique(1024)),
- fbo(std::make_unique(size.x, size.y)) {
+ batch(std::make_unique(1024)) {
camera.perspective = true;
camera.position = glm::vec3(2, 2, 2);
}
@@ -115,7 +115,13 @@ void ModelViewer::draw(const DrawContext& pctx, const Assets& assets) {
return;
}
auto& prevShader = Shader::getUsed();
-
+
+ if (!fbo) {
+ if (size.x <= 0 || size.y <= 0) {
+ return;
+ }
+ fbo = std::make_unique(size.x, size.y);
+ }
fbo->resize(size.x, size.y);
{
glDisable(GL_SCISSOR_TEST);
@@ -154,6 +160,13 @@ void ModelViewer::draw(const DrawContext& pctx, const Assets& assets) {
Container::draw(pctx, assets);
}
+void ModelViewer::setVisible(bool flag) {
+ if (!flag && fbo) {
+ fbo.reset();
+ }
+ Container::setVisible(flag);
+}
+
void ModelViewer::setCenter(const glm::vec3& center) {
this->center = center;
}
diff --git a/src/graphics/ui/elements/ModelViewer.hpp b/src/graphics/ui/elements/ModelViewer.hpp
index 4e841faae..8cf999bb9 100644
--- a/src/graphics/ui/elements/ModelViewer.hpp
+++ b/src/graphics/ui/elements/ModelViewer.hpp
@@ -23,7 +23,6 @@ namespace gui {
bool grabbing = false;
public:
ModelViewer(GUI& gui, const glm::vec2& size, const std::string& modelName);
-
~ModelViewer();
void setModel(const std::string& modelName);
@@ -34,6 +33,7 @@ namespace gui {
void act(float delta) override;
void draw(const DrawContext& pctx, const Assets& assets) override;
+ void setVisible(bool flag) override;
void setRotation(const glm::vec3& euler);
void setCenter(const glm::vec3& center);
diff --git a/src/graphics/ui/elements/SelectBox.cpp b/src/graphics/ui/elements/SelectBox.cpp
index b8205d806..0a6940943 100644
--- a/src/graphics/ui/elements/SelectBox.cpp
+++ b/src/graphics/ui/elements/SelectBox.cpp
@@ -13,14 +13,21 @@ SelectBox::SelectBox(
GUI& gui,
std::vector