mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-07 12:01:50 +00:00
Merge pull request #748 from MihailRis/volume-renderer
yet another something renderer
This commit is contained in:
commit
99106df230
104 changed files with 1889 additions and 704 deletions
|
|
@ -40,6 +40,37 @@ gui.get_locales_info() -> table of tables {
|
|||
|
||||
Returns information about all loaded locales (res/texts/\*).
|
||||
|
||||
## Frames
|
||||
|
||||
```lua
|
||||
-- A replacement for menu:reset() to close the pause menu, deactivating the main UI frame.
|
||||
gui.close_menu()
|
||||
|
||||
-- Creates a frame.
|
||||
gui.create_frame(
|
||||
-- Global frame id (not related to the UI 'id' property).
|
||||
id: str,
|
||||
-- The texture to render the frame to.
|
||||
-- If the string is empty, the frame is rendered to the screen.
|
||||
output_texture: str,
|
||||
-- Frame size. For example: {640, 480}
|
||||
size: vec2
|
||||
) -> Element, Document
|
||||
|
||||
-- Returns the id of the active frame (not the element id).
|
||||
gui.get_active_frame() -> str
|
||||
|
||||
-- Sets the active frame, receiving user input.
|
||||
-- An empty string specifies the null frame capturing the cursor.
|
||||
gui.set_active_frame(
|
||||
-- ID of the frame created via gui.create_frame
|
||||
id: str,
|
||||
-- Function providing the cursor position in the frame.
|
||||
-- Used for custom projection (e.g., in 3D)
|
||||
[optional] cursorLocator
|
||||
)
|
||||
```
|
||||
|
||||
```lua
|
||||
gui.clear_markup(
|
||||
-- markup language ("md" - Markdown)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,37 @@ gui.get_env(document: string) -> table
|
|||
gui.get_locales_info() -> table
|
||||
```
|
||||
|
||||
## Фреймы
|
||||
|
||||
```lua
|
||||
-- Замена для menu:reset() для закрытия меню паузы, деактивирующая основной фрейм UI.
|
||||
gui.close_menu()
|
||||
|
||||
-- Создаёт фрейм.
|
||||
gui.create_frame(
|
||||
-- Глобальный id фрейма (не связан с UI свойством 'id').
|
||||
id: str,
|
||||
-- Текстура, в которую будет производиться рендер фрейма.
|
||||
-- В случае пустой строки рендер производится на экран.
|
||||
output_texture: str,
|
||||
-- Размер фрейма. Например: {640, 480}
|
||||
size: vec2
|
||||
) -> Element, Document
|
||||
|
||||
-- Возвращает id активного фрейма (не id элемента).
|
||||
gui.get_active_frame() -> str
|
||||
|
||||
-- Устанавливает активный фрейм, получающий пользовательский ввод.
|
||||
-- Пустая строка указывает null-фрейм, при котором захватывается курсор.
|
||||
gui.set_active_frame(
|
||||
-- id фрейма, созданного через gui.create_frame
|
||||
id: str,
|
||||
-- Функция-поставщик позиции курсора во фрейме.
|
||||
-- Используется для пользовательской проекции (например в 3D)
|
||||
[опционально] cursorLocator
|
||||
)
|
||||
```
|
||||
|
||||
## Разметка
|
||||
|
||||
```lua
|
||||
|
|
@ -109,4 +140,4 @@ gui.template(
|
|||
```lua
|
||||
-- Корневой UI документ.
|
||||
gui.root: Document
|
||||
```
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<panel size='400' color='0' interval='1' context='menu'>
|
||||
<button onclick='menu:reset()'>@Continue</button>
|
||||
<button onclick='gui.close_menu()'>@Continue</button>
|
||||
<button onclick='menu.page="content?mode=world"'>@Content</button>
|
||||
<button onclick='menu.page="settings"'>@Settings</button>
|
||||
<button onclick='app.close_world(true)'>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<container size='668,418' color='#0F1E2DB2' context='menu'>
|
||||
<container size='668,444' color='#0F1E2DB2' context='menu'>
|
||||
<panel pos='6' size='250' color='0' interval='1'>
|
||||
<button id='s_aud' onclick='sections:set("audio")'>@Audio</button>
|
||||
<button id='s_dsp' onclick='sections:set("display")'>@Display</button>
|
||||
|
|
|
|||
|
|
@ -46,4 +46,5 @@ function on_open()
|
|||
create_checkbox("graphics.advanced-render", "Advanced render", "graphics.advanced-render.tooltip")
|
||||
create_setting("graphics.ssao", "SSAO", 1, "", "graphics.ssao.tooltip")
|
||||
create_setting("graphics.shadows-quality", "Shadows quality", 1)
|
||||
create_setting("graphics.clouds-quality", "Clouds quality", 1)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
"translucent",
|
||||
"background",
|
||||
"skybox_gen",
|
||||
"shadows"
|
||||
"shadows",
|
||||
"clouds"
|
||||
],
|
||||
"post-effects": [
|
||||
"default",
|
||||
|
|
@ -48,7 +49,8 @@
|
|||
"gui/world",
|
||||
"gui/hud",
|
||||
"gui/entity",
|
||||
"gui/half_block"
|
||||
"gui/half_block",
|
||||
"misc/blank"
|
||||
],
|
||||
"fonts": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
{
|
||||
"clouds": 0.8
|
||||
"clouds": 1.0,
|
||||
"clouds_tint": [0.6, 0.7, 0.8],
|
||||
"sky_tint": [1.5, 1.5, 1.5],
|
||||
"min_sky_light": [0.05, 0.07, 0.1]
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@
|
|||
"fog_opacity": 0.98,
|
||||
"fog_dencity": 3.6,
|
||||
"fog_curve": 0.25,
|
||||
"clouds": 0.8
|
||||
"clouds": 0.9,
|
||||
"min_sky_light": [0.0, 0.0, 0.0]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,5 +19,8 @@
|
|||
"fog_opacity": 0.5,
|
||||
"fog_dencity": 1.7,
|
||||
"fog_curve": 0.5,
|
||||
"clouds": 0.9
|
||||
"clouds": 0.96,
|
||||
"sky_tint": [2.0, 2.0, 2.0],
|
||||
"clouds_tint": [0.6, 0.7, 0.8],
|
||||
"min_sky_light": [0.05, 0.07, 0.1]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,5 +11,8 @@
|
|||
"fog_opacity": 0.8,
|
||||
"fog_dencity": 2.5,
|
||||
"fog_curve": 0.5,
|
||||
"clouds": 0.5
|
||||
"clouds": 1.0,
|
||||
"sky_tint": [1.9, 1.9, 1.9],
|
||||
"clouds_tint": [0.7, 0.8, 0.9],
|
||||
"min_sky_light": [0.05, 0.07, 0.1]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,5 +19,7 @@
|
|||
"fog_dencity": 2.0,
|
||||
"fog_curve": 0.5,
|
||||
"clouds": 0.7,
|
||||
"thunder_rate": 0.1
|
||||
"thunder_rate": 0.1,
|
||||
"clouds_tint": [0.3, 0.4, 0.5],
|
||||
"min_sky_light": [0.0, 0.0, 0.0]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,6 +232,22 @@ _GUI_ROOT = Document.new("core:root")
|
|||
_MENU = _GUI_ROOT.menu
|
||||
menu = _MENU
|
||||
gui.root = _GUI_ROOT
|
||||
gui.main_frame_id = "core:main"
|
||||
|
||||
function gui.close_menu()
|
||||
if menu then
|
||||
menu:reset()
|
||||
end
|
||||
gui.set_active_frame("")
|
||||
end
|
||||
|
||||
local __gui_create_frame = gui.create_frame
|
||||
function gui.create_frame(id, output, size)
|
||||
__gui_create_frame(id, output, size)
|
||||
|
||||
local document = Document.new(id)
|
||||
return document.root, document
|
||||
end
|
||||
|
||||
do
|
||||
local status, err = pcall(function()
|
||||
|
|
@ -435,9 +451,12 @@ function __vc_on_hud_open()
|
|||
if menu.page ~= "" then
|
||||
if not menu:back() then
|
||||
menu:reset()
|
||||
gui.set_active_frame("")
|
||||
end
|
||||
elseif hud.is_inventory_open() then
|
||||
hud.close_inventory()
|
||||
elseif gui.get_active_frame() then
|
||||
gui.set_active_frame("")
|
||||
else
|
||||
hud.pause()
|
||||
end
|
||||
|
|
|
|||
31
res/shaders/clouds.glslf
Normal file
31
res/shaders/clouds.glslf
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
layout (location = 0) out vec4 f_color;
|
||||
layout (location = 1) out vec4 f_position;
|
||||
layout (location = 2) out vec4 f_normal;
|
||||
layout (location = 3) out vec4 f_emission;
|
||||
|
||||
#include <world_fragment_header>
|
||||
|
||||
in vec4 a_torchLight;
|
||||
|
||||
uniform sampler2D u_texture0;
|
||||
uniform vec3 u_sunDir;
|
||||
|
||||
// flags
|
||||
uniform bool u_alphaClip;
|
||||
uniform bool u_debugLights;
|
||||
uniform bool u_debugNormals;
|
||||
uniform vec4 u_tint;
|
||||
|
||||
void main() {
|
||||
f_color.rgb = max(a_torchLight.rgb, a_skyLight);
|
||||
f_color.a = 1.0;
|
||||
f_color *= u_tint;
|
||||
|
||||
#ifndef ADVANCED_RENDER
|
||||
vec3 fogColor = texture(u_skybox, a_dir).rgb;
|
||||
f_color = mix(f_color, vec4(fogColor, 1.0), a_fog);
|
||||
#endif
|
||||
f_position = vec4(a_position, 1.0);
|
||||
f_normal = vec4(a_normal, 1.0);
|
||||
f_emission = vec4(vec3(a_emission), 1.0);
|
||||
}
|
||||
49
res/shaders/clouds.glslv
Normal file
49
res/shaders/clouds.glslv
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include <commons>
|
||||
|
||||
layout (location = 0) in vec3 v_position;
|
||||
layout (location = 1) in vec2 v_texCoord;
|
||||
layout (location = 2) in vec4 v_light;
|
||||
layout (location = 3) in vec4 v_normal;
|
||||
|
||||
#include <world_vertex_header>
|
||||
#include <lighting>
|
||||
#include <fog>
|
||||
#include <sky>
|
||||
|
||||
uniform float u_dayTime;
|
||||
|
||||
out vec4 a_torchLight;
|
||||
|
||||
void main() {
|
||||
a_modelpos = u_model * vec4(v_position, 1.0f);
|
||||
a_modelpos.x += sin(u_timer * 0.05 + a_modelpos.z * 0.002) * 1.5;
|
||||
a_modelpos.z += sin(u_timer * 0.2 + a_modelpos.x * 0.002) * 1.5;
|
||||
a_modelpos.y += sin(u_timer * 0.1 + (a_modelpos.x + a_modelpos.z) * 0.001) * 2.0;
|
||||
|
||||
vec3 pos3d = a_modelpos.xyz - u_cameraPos;
|
||||
|
||||
a_realnormal = v_normal.xyz * 2.0 - 1.0;
|
||||
a_normal = calc_screen_normal(a_realnormal);
|
||||
|
||||
a_torchLight = vec4(calc_torch_light(
|
||||
v_light.rgb, a_realnormal, a_modelpos.xyz, u_torchlightColor, u_gamma
|
||||
), 1.0);
|
||||
a_texCoord = v_texCoord;
|
||||
|
||||
a_dir = a_modelpos.xyz - u_cameraPos;
|
||||
vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime, u_minSkyLight);
|
||||
a_skyLight = skyLightColor.rgb*v_light.a;
|
||||
|
||||
mat4 viewmodel = u_view * u_model;
|
||||
a_distance = length(viewmodel * vec4(pos3d, 0.0));
|
||||
|
||||
#ifndef ADVANCED_RENDER
|
||||
a_fog = calc_fog(length(viewmodel * vec4(pos3d * FOG_POS_SCALE, 0.0)) / 256.0);
|
||||
#endif
|
||||
|
||||
a_emission = v_normal.w;
|
||||
|
||||
vec4 viewmodelpos = u_view * a_modelpos;
|
||||
a_position = viewmodelpos.xyz;
|
||||
gl_Position = u_proj * viewmodelpos;
|
||||
}
|
||||
|
|
@ -13,6 +13,8 @@ layout (location = 4) in vec4 v_normal;
|
|||
|
||||
out vec4 a_color;
|
||||
|
||||
uniform float u_dayTime;
|
||||
|
||||
void main() {
|
||||
a_modelpos = u_model * vec4(v_position, 1.0);
|
||||
vec3 pos3d = a_modelpos.xyz - u_cameraPos;
|
||||
|
|
@ -26,7 +28,7 @@ void main() {
|
|||
a_texCoord = v_texCoord;
|
||||
|
||||
a_dir = a_modelpos.xyz - u_cameraPos;
|
||||
vec3 skyLightColor = pick_sky_color(u_skybox);
|
||||
vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime, u_minSkyLight);
|
||||
a_color.rgb = max(a_color.rgb, skyLightColor.rgb * v_light.a) * v_color;
|
||||
a_color.a = u_opacity;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
#define CURVATURE_FACTOR 0.002
|
||||
|
||||
// lighting
|
||||
#define SKY_LIGHT_MUL 2.9
|
||||
#define SKY_LIGHT_TINT (vec3(1.0, 0.95, 0.9) * 2.0)
|
||||
#define MIN_SKY_LIGHT vec3(0.2, 0.25, 0.33)
|
||||
#define SKY_LIGHT_MUL 2.5
|
||||
#define SKY_LIGHT_TINT (vec3(1.0, 0.75, 0.6) * 2.0)
|
||||
|
||||
// fog
|
||||
#define FOG_POS_SCALE vec3(1.0, 0.2, 1.0)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@
|
|||
|
||||
#include <constants>
|
||||
|
||||
vec3 pick_sky_color(samplerCube cubemap) {
|
||||
vec3 skyLightColor = texture(cubemap, vec3(0.4f, 0.05f, 0.4f)).rgb;
|
||||
vec3 pick_sky_color(samplerCube cubemap, float dayTime, vec3 minSkyLight) {
|
||||
float elevation = sin(dayTime * PI2) * 0.2;
|
||||
vec3 skyLightColor = texture(cubemap, vec3(0.0f, elevation, -0.4f)).rgb;
|
||||
skyLightColor *= SKY_LIGHT_TINT;
|
||||
skyLightColor = min(vec3(1.0f), skyLightColor * SKY_LIGHT_MUL);
|
||||
skyLightColor = max(MIN_SKY_LIGHT, skyLightColor);
|
||||
skyLightColor = max(minSkyLight, skyLightColor);
|
||||
return skyLightColor;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,5 +11,6 @@ uniform float u_timer;
|
|||
uniform samplerCube u_skybox;
|
||||
uniform vec3 u_torchlightColor;
|
||||
uniform float u_torchlightDistance;
|
||||
uniform vec3 u_minSkyLight;
|
||||
|
||||
#endif // GLSL_WORLD_UNIFORMS_
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ layout (location = 3) in vec4 v_normal;
|
|||
#include <fog>
|
||||
#include <sky>
|
||||
|
||||
uniform float u_dayTime;
|
||||
|
||||
out vec4 a_torchLight;
|
||||
|
||||
void main() {
|
||||
|
|
@ -25,7 +27,7 @@ void main() {
|
|||
a_texCoord = v_texCoord;
|
||||
|
||||
a_dir = a_modelpos.xyz - u_cameraPos;
|
||||
vec3 skyLightColor = pick_sky_color(u_skybox);
|
||||
vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime, u_minSkyLight);
|
||||
a_skyLight = skyLightColor.rgb*v_light.a;
|
||||
|
||||
mat4 viewmodel = u_view * u_model;
|
||||
|
|
|
|||
|
|
@ -241,6 +241,8 @@ uniform vec3 u_xaxis;
|
|||
uniform vec3 u_yaxis;
|
||||
uniform vec3 u_zaxis;
|
||||
uniform vec3 u_lightDir;
|
||||
uniform vec3 u_tint;
|
||||
uniform vec3 u_hightlight;
|
||||
uniform int u_quality;
|
||||
uniform float u_mie;
|
||||
uniform float u_fog;
|
||||
|
|
@ -295,5 +297,5 @@ void main() {
|
|||
col = 1.0 - exp(-col);
|
||||
col = min(col, vec3(1.0));
|
||||
// Output to screen
|
||||
f_color = vec4(col, 1.0);
|
||||
f_color = vec4(col * u_tint + u_hightlight, 1.0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ layout (location = 3) in vec4 v_normal;
|
|||
|
||||
out vec4 a_torchLight;
|
||||
|
||||
uniform float u_dayTime;
|
||||
|
||||
void main() {
|
||||
a_modelpos = u_model * vec4(v_position, 1.0f);
|
||||
vec3 pos3d = a_modelpos.xyz - u_cameraPos;
|
||||
|
|
@ -25,7 +27,7 @@ void main() {
|
|||
a_texCoord = v_texCoord;
|
||||
|
||||
a_dir = a_modelpos.xyz - u_cameraPos;
|
||||
vec3 skyLightColor = pick_sky_color(u_skybox);
|
||||
vec3 skyLightColor = pick_sky_color(u_skybox, u_dayTime, u_minSkyLight);
|
||||
a_skyLight = skyLightColor.rgb*v_light.a;
|
||||
|
||||
mat4 viewmodel = u_view * u_model;
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ settings.Windowed=Оконный
|
|||
settings.Borderless=Безрамочный
|
||||
settings.Microphone=Микрофон
|
||||
settings.microphone.None=Нет
|
||||
settings.Clouds quality=Качество облаков
|
||||
settings.Acoustic effects=Акустические эффекты
|
||||
|
||||
# Управление
|
||||
|
|
|
|||
BIN
res/textures/misc/blank.png
Normal file
BIN
res/textures/misc/blank.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 B |
|
|
@ -375,7 +375,7 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone, int maxWorkers) {
|
|||
auto pool =
|
||||
std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>(
|
||||
"assets-loader-pool",
|
||||
[=]() { return std::make_shared<LoaderWorker>(this); },
|
||||
[=]() { return std::make_unique<LoaderWorker>(this); },
|
||||
[this](const assetload::postfunc& func) { func(&assets); },
|
||||
maxWorkers
|
||||
);
|
||||
|
|
|
|||
|
|
@ -251,13 +251,13 @@ std::unique_ptr<ImageData> png::load_image(const ubyte* bytes, size_t size) {
|
|||
}
|
||||
png_read_image(pngPtr, rowPointers.get());
|
||||
|
||||
ImageFormat format = ImageFormat::rgba8888;
|
||||
ImageFormat format = ImageFormat::RGBA8888;
|
||||
switch (colorType) {
|
||||
case PNG_COLOR_TYPE_RGBA:
|
||||
format = ImageFormat::rgba8888;
|
||||
format = ImageFormat::RGBA8888;
|
||||
break;
|
||||
case PNG_COLOR_TYPE_RGB:
|
||||
format = ImageFormat::rgb888;
|
||||
format = ImageFormat::RGB888;
|
||||
break;
|
||||
default:
|
||||
png_destroy_read_struct(&pngPtr, &infoPtr, nullptr);
|
||||
|
|
@ -296,7 +296,7 @@ void png::write_image(const std::string& filename, const ImageData* image) {
|
|||
image->getWidth(),
|
||||
image->getHeight(),
|
||||
(const ubyte*)image->getData(),
|
||||
image->getFormat() == ImageFormat::rgba8888
|
||||
image->getFormat() == ImageFormat::RGBA8888
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -306,6 +306,6 @@ util::Buffer<ubyte> png::encode_image(const ImageData& image) {
|
|||
image.getWidth(),
|
||||
image.getHeight(),
|
||||
image.getData(),
|
||||
format == ImageFormat::rgba8888
|
||||
format == ImageFormat::RGBA8888
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,8 +52,6 @@ std::vector<std::string>& ContentControl::getBasePacks() {
|
|||
}
|
||||
|
||||
void ContentControl::resetContent(const std::vector<std::string>& nonReset) {
|
||||
paths.setCurrentWorldFolder("");
|
||||
|
||||
scripting::cleanup(nonReset);
|
||||
std::vector<PathsRoot> resRoots;
|
||||
{
|
||||
|
|
|
|||
|
|
@ -306,6 +306,9 @@ void Engine::startPauseLoop() {
|
|||
}
|
||||
|
||||
void Engine::renderFrame() {
|
||||
if (input->isCursorLocked() != (gui->getActiveFrame() == nullptr)) {
|
||||
input->toggleCursor();
|
||||
}
|
||||
screen->draw(time.getDelta());
|
||||
|
||||
DrawContext ctx(nullptr, *window, nullptr);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include "ServerMainloop.hpp"
|
||||
|
||||
#include "Engine.hpp"
|
||||
#include "EnginePaths.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "logic/LevelController.hpp"
|
||||
#include "interfaces/Process.hpp"
|
||||
|
|
@ -80,11 +79,10 @@ void ServerMainloop::run() {
|
|||
void ServerMainloop::setLevel(std::unique_ptr<Level> level) {
|
||||
if (level == nullptr) {
|
||||
controller->onWorldQuit();
|
||||
engine.getPaths().setCurrentWorldFolder("");
|
||||
controller = nullptr;
|
||||
} else {
|
||||
controller = std::make_unique<LevelController>(
|
||||
&engine, std::move(level), nullptr
|
||||
engine, std::move(level), nullptr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
UiDocument::UiDocument(
|
||||
std::string id,
|
||||
uidocscript script,
|
||||
UiDocScript script,
|
||||
const std::shared_ptr<gui::UINode>& root,
|
||||
scriptenv env
|
||||
) : id(std::move(id)), script(script), root(root), env(std::move(env)) {
|
||||
|
|
@ -48,7 +48,7 @@ std::shared_ptr<gui::UINode> UiDocument::get(const std::string& id) const {
|
|||
return found->second.lock();
|
||||
}
|
||||
|
||||
const uidocscript& UiDocument::getScript() const {
|
||||
const UiDocScript& UiDocument::getScript() const {
|
||||
return script;
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ std::unique_ptr<UiDocument> UiDocument::read(
|
|||
gui::UiXmlReader reader(gui, scriptenv(env));
|
||||
auto view = reader.readXML(file.string(), *xmldoc->getRoot());
|
||||
view->setId("root");
|
||||
uidocscript script {};
|
||||
UiDocScript script {};
|
||||
auto scriptFile = io::path(file.string()+".lua");
|
||||
if (io::is_regular_file(scriptFile)) {
|
||||
scripting::load_layout_script(
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace gui {
|
|||
class UINode;
|
||||
}
|
||||
|
||||
struct uidocscript {
|
||||
struct UiDocScript {
|
||||
bool onopen : 1;
|
||||
bool onprogress : 1;
|
||||
bool onclose : 1;
|
||||
|
|
@ -23,14 +23,14 @@ using UINodesMap = std::unordered_map<std::string, std::weak_ptr<gui::UINode>>;
|
|||
|
||||
class UiDocument {
|
||||
std::string id;
|
||||
uidocscript script;
|
||||
UiDocScript script;
|
||||
UINodesMap map;
|
||||
std::shared_ptr<gui::UINode> root;
|
||||
scriptenv env;
|
||||
public:
|
||||
UiDocument(
|
||||
std::string id,
|
||||
uidocscript script,
|
||||
UiDocScript script,
|
||||
const std::shared_ptr<gui::UINode> &root,
|
||||
scriptenv env
|
||||
);
|
||||
|
|
@ -42,7 +42,7 @@ public:
|
|||
const UINodesMap& getMap() const;
|
||||
std::shared_ptr<gui::UINode> getRoot() const;
|
||||
std::shared_ptr<gui::UINode> get(const std::string& id) const;
|
||||
const uidocscript& getScript() const;
|
||||
const UiDocScript& getScript() const;
|
||||
scriptenv getEnvironment() const;
|
||||
|
||||
static std::unique_ptr<UiDocument> read(
|
||||
|
|
|
|||
|
|
@ -1,9 +1,3 @@
|
|||
#include <bitset>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "audio/audio.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "content/Content.hpp"
|
||||
|
|
@ -37,6 +31,12 @@
|
|||
#include "world/Level.hpp"
|
||||
#include "world/World.hpp"
|
||||
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
using namespace gui;
|
||||
|
||||
static std::shared_ptr<Label> create_label(GUI& gui, wstringsupplier supplier) {
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
|||
frontend(frontend),
|
||||
player(player),
|
||||
debugImgWorldGen(std::make_unique<ImageData>(
|
||||
ImageFormat::rgba8888, WORLDGEN_IMG_SIZE, WORLDGEN_IMG_SIZE
|
||||
ImageFormat::RGBA8888, WORLDGEN_IMG_SIZE, WORLDGEN_IMG_SIZE
|
||||
)) {
|
||||
contentAccess = createContentAccess();
|
||||
contentAccess->setId("hud.content-access");
|
||||
|
|
@ -207,9 +207,6 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
|||
}
|
||||
|
||||
Hud::~Hud() {
|
||||
if (input.isCursorLocked()) {
|
||||
input.toggleCursor();
|
||||
}
|
||||
// removing all controlled ui
|
||||
for (auto& element : elements) {
|
||||
onRemove(element);
|
||||
|
|
@ -342,9 +339,6 @@ void Hud::update(bool visible) {
|
|||
if (!gui.isFocusCaught()) {
|
||||
processInput(visible);
|
||||
}
|
||||
if ((isMenuOpen || inventoryOpen) == input.isCursorLocked()) {
|
||||
input.toggleCursor();
|
||||
}
|
||||
|
||||
if (blockUI) {
|
||||
voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z);
|
||||
|
|
@ -395,6 +389,7 @@ void Hud::openInventory(bool playerInventory) {
|
|||
inventoryView->bind(inventory, &content);
|
||||
add(HudElement(HudElementMode::INVENTORY, inventoryDocument, inventoryView, false));
|
||||
}
|
||||
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||
}
|
||||
|
||||
std::shared_ptr<Inventory> Hud::openInventory(
|
||||
|
|
@ -548,6 +543,7 @@ void Hud::closeInventory() {
|
|||
}
|
||||
}
|
||||
cleanup();
|
||||
gui.setActiveFrame("");
|
||||
}
|
||||
|
||||
void Hud::add(const HudElement& element, const dv::value& argsArray) {
|
||||
|
|
@ -710,9 +706,11 @@ void Hud::setPause(bool pause) {
|
|||
|
||||
if (!pause && menu.hasOpenPage()) {
|
||||
menu.reset();
|
||||
gui.setActiveFrame("");
|
||||
}
|
||||
if (pause && !menu.hasOpenPage()) {
|
||||
menu.setPage("pause");
|
||||
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@
|
|||
#include <glm/glm.hpp>
|
||||
|
||||
class Camera;
|
||||
class Block;
|
||||
class Assets;
|
||||
class Player;
|
||||
class Engine;
|
||||
|
|
@ -25,7 +24,6 @@ namespace gui {
|
|||
class GUI;
|
||||
class Menu;
|
||||
class UINode;
|
||||
class Panel;
|
||||
class Container;
|
||||
class InventoryView;
|
||||
class SlotView;
|
||||
|
|
|
|||
|
|
@ -56,12 +56,13 @@ LevelScreen::LevelScreen(
|
|||
auto& assets = *engine.getAssets();
|
||||
auto menu = engine.getGUI().getMenu();
|
||||
menu->reset();
|
||||
gui.setActiveFrame("");
|
||||
|
||||
auto player = level->players->get(localPlayer);
|
||||
assert(player != nullptr);
|
||||
|
||||
controller =
|
||||
std::make_unique<LevelController>(&engine, std::move(levelPtr), player);
|
||||
std::make_unique<LevelController>(engine, std::move(levelPtr), player);
|
||||
playerController = std::make_unique<PlayerController>(
|
||||
settings, *level, *player, *controller->getBlocksController()
|
||||
);
|
||||
|
|
@ -112,11 +113,10 @@ LevelScreen::~LevelScreen() {
|
|||
saveDecorations();
|
||||
}
|
||||
scripting::on_frontend_close();
|
||||
// unblock all bindings
|
||||
input.getBindings().enableAll();
|
||||
playerController->getPlayer()->chunks->saveAndClear();
|
||||
controller->onWorldQuit();
|
||||
engine.getPaths().setCurrentWorldFolder("");
|
||||
|
||||
}
|
||||
|
||||
void LevelScreen::onOpen() {
|
||||
|
|
@ -184,7 +184,7 @@ void LevelScreen::saveWorldPreview() {
|
|||
static_cast<uint>(previewSize)}
|
||||
);
|
||||
|
||||
renderer->renderFrame(ctx, camera, false, true, 0.0f, *postProcessing);
|
||||
renderer->renderFrame(ctx, camera, false, *postProcessing);
|
||||
auto image = postProcessing->toImage();
|
||||
image->flipY();
|
||||
imageio::write("world:preview.png", image.get());
|
||||
|
|
@ -240,7 +240,7 @@ void LevelScreen::update(float delta) {
|
|||
|
||||
auto menu = gui.getMenu();
|
||||
bool inputLocked =
|
||||
menu->hasOpenPage() || hud->isInventoryOpen() || gui.isFocusCaught();
|
||||
gui.getActiveFrame() || hud->isInventoryOpen() || gui.isFocusCaught();
|
||||
bool paused = hud->isPause();
|
||||
if (!paused) {
|
||||
world.updateTimers(delta);
|
||||
|
|
@ -271,9 +271,11 @@ void LevelScreen::draw(float delta) {
|
|||
if (!hud->isPause()) {
|
||||
scripting::on_entities_render(engine.getTime().getDelta());
|
||||
}
|
||||
renderer->renderFrame(
|
||||
ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing
|
||||
);
|
||||
renderer->update(*camera, delta * !hud->isPause());
|
||||
renderer->renderFrame(ctx, *camera, hudVisible, *postProcessing);
|
||||
if (!hud->isPause()) {
|
||||
scripting::on_frontend_render();
|
||||
}
|
||||
|
||||
if (hudVisible) {
|
||||
hud->draw(ctx);
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ std::unique_ptr<Atlas> AtlasBuilder::build(uint extrusion, bool prepare, uint ma
|
|||
}
|
||||
}
|
||||
|
||||
auto canvas = std::make_unique<ImageData>(ImageFormat::rgba8888, width, height);
|
||||
auto canvas = std::make_unique<ImageData>(ImageFormat::RGBA8888, width, height);
|
||||
std::unordered_map<std::string, UVRegion> regions;
|
||||
std::vector<rectangle> rects = packer.getResult();
|
||||
for (uint i = 0; i < entries.size(); i++) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Batch2D::Batch2D(size_t capacity) : capacity(capacity), color(1.0f){
|
|||
const ubyte pixels[] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
ImageData image(ImageFormat::rgba8888, 1, 1, pixels);
|
||||
ImageData image(ImageFormat::RGBA8888, 1, 1, pixels);
|
||||
blank = Texture::from(&image);
|
||||
currentTexture = nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Batch3D::Batch3D(size_t capacity)
|
|||
const ubyte pixels[] = {
|
||||
255, 255, 255, 255,
|
||||
};
|
||||
ImageData image(ImageFormat::rgba8888, 1, 1, pixels);
|
||||
ImageData image(ImageFormat::RGBA8888, 1, 1, pixels);
|
||||
blank = Texture::from(&image);
|
||||
currentTexture = nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ Texture* Framebuffer::getTexture() const {
|
|||
return texture.get();
|
||||
}
|
||||
|
||||
std::shared_ptr<Texture> Framebuffer::getSharedTexture() const {
|
||||
return texture;
|
||||
}
|
||||
|
||||
uint Framebuffer::getWidth() const {
|
||||
return width;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class Framebuffer : public Bindable {
|
|||
uint width;
|
||||
uint height;
|
||||
uint format;
|
||||
std::unique_ptr<Texture> texture;
|
||||
std::shared_ptr<Texture> texture;
|
||||
public:
|
||||
Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture);
|
||||
Framebuffer(uint width, uint height, bool alpha=false);
|
||||
|
|
@ -33,6 +33,8 @@ public:
|
|||
/// @brief Get framebuffer color attachment
|
||||
Texture* getTexture() const;
|
||||
|
||||
std::shared_ptr<Texture> getSharedTexture() const;
|
||||
|
||||
/// @brief Get framebuffer width
|
||||
uint getWidth() const;
|
||||
/// @brief Get framebuffer height
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ std::unique_ptr<ImageData> GBuffer::toImage() const {
|
|||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGB, GL_UNSIGNED_BYTE, data.get());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return std::make_unique<ImageData>(
|
||||
ImageFormat::rgb888, width, height, std::move(data)
|
||||
ImageFormat::RGB888, width, height, std::move(data)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ ImageData::ImageData(ImageFormat format, uint width, uint height)
|
|||
: format(format), width(width), height(height) {
|
||||
size_t pixsize;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: pixsize = 3; break;
|
||||
case ImageFormat::rgba8888: pixsize = 4; break;
|
||||
case ImageFormat::RGB888: pixsize = 3; break;
|
||||
case ImageFormat::RGBA8888: pixsize = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("format is not supported");
|
||||
}
|
||||
|
|
@ -27,8 +27,8 @@ ImageData::ImageData(ImageFormat format, uint width, uint height, const ubyte* d
|
|||
: format(format), width(width), height(height) {
|
||||
size_t pixsize;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: pixsize = 3; break;
|
||||
case ImageFormat::rgba8888: pixsize = 4; break;
|
||||
case ImageFormat::RGB888: pixsize = 3; break;
|
||||
case ImageFormat::RGBA8888: pixsize = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("format is not supported");
|
||||
}
|
||||
|
|
@ -40,9 +40,9 @@ ImageData::~ImageData() = default;
|
|||
|
||||
void ImageData::flipX() {
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888:
|
||||
case ImageFormat::rgba8888: {
|
||||
uint size = (format == ImageFormat::rgba8888) ? 4 : 3;
|
||||
case ImageFormat::RGB888:
|
||||
case ImageFormat::RGBA8888: {
|
||||
uint size = (format == ImageFormat::RGBA8888) ? 4 : 3;
|
||||
for (uint y = 0; y < height; y++) {
|
||||
for (uint x = 0; x < width / 2; x++) {
|
||||
for (uint c = 0; c < size; c++) {
|
||||
|
|
@ -62,9 +62,9 @@ void ImageData::flipX() {
|
|||
|
||||
void ImageData::flipY() {
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888:
|
||||
case ImageFormat::rgba8888: {
|
||||
uint size = (format == ImageFormat::rgba8888) ? 4 : 3;
|
||||
case ImageFormat::RGB888:
|
||||
case ImageFormat::RGBA8888: {
|
||||
uint size = (format == ImageFormat::RGBA8888) ? 4 : 3;
|
||||
for (uint y = 0; y < height/2; y++) {
|
||||
for (uint x = 0; x < width; x++) {
|
||||
for (uint c = 0; c < size; c++) {
|
||||
|
|
@ -87,8 +87,8 @@ void ImageData::blit(const ImageData& image, int x, int y) {
|
|||
blitMatchingFormat(image, x, y);
|
||||
return;
|
||||
}
|
||||
if (format == ImageFormat::rgba8888 &&
|
||||
image.format == ImageFormat::rgb888) {
|
||||
if (format == ImageFormat::RGBA8888 &&
|
||||
image.format == ImageFormat::RGB888) {
|
||||
blitRGB_on_RGBA(image, x, y);
|
||||
return;
|
||||
}
|
||||
|
|
@ -188,10 +188,10 @@ static void draw_line(ImageData& image, int x1, int y1, int x2, int y2, const gl
|
|||
|
||||
void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color) {
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888:
|
||||
case ImageFormat::RGB888:
|
||||
draw_line<3>(*this, x1, y1, x2, y2, color);
|
||||
break;
|
||||
case ImageFormat::rgba8888:
|
||||
case ImageFormat::RGBA8888:
|
||||
draw_line<4>(*this, x1, y1, x2, y2, color);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -223,10 +223,10 @@ static void draw_rect(ImageData& image, int dstX, int dstY, int width, int heigh
|
|||
|
||||
void ImageData::drawRect(int x, int y, int width, int height, const glm::ivec4& color) {
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888:
|
||||
case ImageFormat::RGB888:
|
||||
draw_rect<3>(*this, x, y, width, height, color);
|
||||
break;
|
||||
case ImageFormat::rgba8888:
|
||||
case ImageFormat::RGBA8888:
|
||||
draw_rect<4>(*this, x, y, width, height, color);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -260,8 +260,8 @@ void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) {
|
|||
void ImageData::blitMatchingFormat(const ImageData& image, int x, int y) {
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
case ImageFormat::RGB888: comps = 3; break;
|
||||
case ImageFormat::RGBA8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
|
|
@ -295,8 +295,8 @@ void ImageData::blitMatchingFormat(const ImageData& image, int x, int y) {
|
|||
void ImageData::extrude(int x, int y, int w, int h) {
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
case ImageFormat::RGB888: comps = 3; break;
|
||||
case ImageFormat::RGBA8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
|
|
@ -431,8 +431,8 @@ static void check_matching(const ImageData& a, const ImageData& b) {
|
|||
void ImageData::mulColor(const glm::ivec4& color) {
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
case ImageFormat::RGB888: comps = 3; break;
|
||||
case ImageFormat::RGBA8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
|
|
@ -453,8 +453,8 @@ void ImageData::addColor(const ImageData& other, int multiplier) {
|
|||
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
case ImageFormat::RGB888: comps = 3; break;
|
||||
case ImageFormat::RGBA8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
|
|
@ -473,8 +473,8 @@ void ImageData::addColor(const ImageData& other, int multiplier) {
|
|||
void ImageData::extend(int newWidth, int newHeight) {
|
||||
size_t comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
case ImageFormat::RGB888: comps = 3; break;
|
||||
case ImageFormat::RGBA8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
|
|
@ -499,8 +499,8 @@ void ImageData::extend(int newWidth, int newHeight) {
|
|||
void ImageData::addColor(const glm::ivec4& color, int multiplier) {
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
case ImageFormat::RGB888: comps = 3; break;
|
||||
case ImageFormat::RGBA8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
|
|
@ -521,8 +521,8 @@ void ImageData::mulColor(const ImageData& other) {
|
|||
|
||||
uint comps;
|
||||
switch (format) {
|
||||
case ImageFormat::rgb888: comps = 3; break;
|
||||
case ImageFormat::rgba8888: comps = 4; break;
|
||||
case ImageFormat::RGB888: comps = 3; break;
|
||||
case ImageFormat::RGBA8888: comps = 4; break;
|
||||
default:
|
||||
throw std::runtime_error("only unsigned byte formats supported");
|
||||
}
|
||||
|
|
@ -541,7 +541,7 @@ void ImageData::mulColor(const ImageData& other) {
|
|||
|
||||
std::unique_ptr<ImageData> add_atlas_margins(ImageData* image, int grid_size) {
|
||||
// RGBA is only supported
|
||||
assert(image->getFormat() == ImageFormat::rgba8888);
|
||||
assert(image->getFormat() == ImageFormat::RGBA8888);
|
||||
assert(image->getWidth() == image->getHeight());
|
||||
|
||||
int srcwidth = image->getWidth();
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@
|
|||
#include <memory>
|
||||
|
||||
enum class ImageFormat {
|
||||
rgb888,
|
||||
rgba8888
|
||||
RGB888,
|
||||
RGBA8888
|
||||
};
|
||||
|
||||
class ImageData {
|
||||
|
|
@ -57,7 +57,7 @@ public:
|
|||
}
|
||||
|
||||
size_t getDataSize() const {
|
||||
size_t channels = 3 + (format == ImageFormat::rgba8888);
|
||||
size_t channels = 3 + (format == ImageFormat::RGBA8888);
|
||||
return width * height * channels;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
26
src/graphics/core/PaletteAtlasBuilder.cpp
Normal file
26
src/graphics/core/PaletteAtlasBuilder.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#include "PaletteAtlasBuilder.hpp"
|
||||
|
||||
#include "../core/Atlas.hpp"
|
||||
#include "../core/ImageData.hpp"
|
||||
|
||||
PaletteAtlasBuilder::PaletteAtlasBuilder() = default;
|
||||
|
||||
void PaletteAtlasBuilder::put(const std::string& name, const glm::vec4& color) {
|
||||
entries[name] = { glm::clamp(color, glm::vec4(0.0f), glm::vec4(1.0f)) };
|
||||
}
|
||||
|
||||
std::unique_ptr<Atlas> PaletteAtlasBuilder::build() const {
|
||||
AtlasBuilder builder;
|
||||
for (const auto& [name, entry] : entries) {
|
||||
ubyte data[4] {
|
||||
static_cast<ubyte>(entry.color.r * 255),
|
||||
static_cast<ubyte>(entry.color.g * 255),
|
||||
static_cast<ubyte>(entry.color.b * 255),
|
||||
static_cast<ubyte>(entry.color.a * 255),
|
||||
};
|
||||
builder.add(name, std::make_unique<ImageData>(
|
||||
ImageFormat::RGBA8888, 1, 1, data
|
||||
));
|
||||
}
|
||||
return builder.build(1, true);
|
||||
}
|
||||
23
src/graphics/core/PaletteAtlasBuilder.hpp
Normal file
23
src/graphics/core/PaletteAtlasBuilder.hpp
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
class Atlas;
|
||||
|
||||
class PaletteAtlasBuilder {
|
||||
public:
|
||||
struct Entry {
|
||||
glm::vec4 color;
|
||||
};
|
||||
|
||||
PaletteAtlasBuilder();
|
||||
|
||||
void put(const std::string& name, const glm::vec4& color);
|
||||
|
||||
std::unique_ptr<Atlas> build() const;
|
||||
private:
|
||||
std::map<std::string, Entry> entries;
|
||||
};
|
||||
|
|
@ -96,41 +96,56 @@ void Shadows::setQuality(int quality) {
|
|||
}
|
||||
|
||||
void Shadows::setup(Shader& shader, const Weather& weather) {
|
||||
if (shadows) {
|
||||
const auto& worldInfo = level.getWorld()->getInfo();
|
||||
float cloudsIntensity = glm::max(worldInfo.fog, 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
|
||||
));
|
||||
shader.uniform1i("u_screen", 0);
|
||||
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
|
||||
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
|
||||
shader.uniform3f("u_sunDir", shadowCamera.front);
|
||||
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
|
||||
shader.uniform1f("u_shadowsOpacity", shadowsOpacity); // TODO: make it configurable
|
||||
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
|
||||
shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0);
|
||||
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap());
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1);
|
||||
shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1);
|
||||
glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap());
|
||||
|
||||
glActiveTexture(TEXTURE_MAIN);
|
||||
if (!shadows) {
|
||||
return;
|
||||
}
|
||||
const auto& worldInfo = level.getWorld()->getInfo();
|
||||
float cloudsIntensity = glm::max(worldInfo.fog, 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
|
||||
));
|
||||
shader.uniform1i("u_screen", 0);
|
||||
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
|
||||
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
|
||||
shader.uniform3f("u_sunDir", shadowCamera.front);
|
||||
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
|
||||
shader.uniform1f("u_shadowsOpacity", shadowsOpacity); // TODO: make it configurable
|
||||
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
|
||||
shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0);
|
||||
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap());
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1);
|
||||
shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1);
|
||||
glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap());
|
||||
|
||||
glActiveTexture(TEXTURE_MAIN);
|
||||
}
|
||||
|
||||
void Shadows::refresh(const Camera& camera, const DrawContext& pctx, std::function<void(Camera&)> renderShadowPass) {
|
||||
void Shadows::refresh(
|
||||
const Camera& camera,
|
||||
const DrawContext& pctx,
|
||||
const std::function<void(Camera&)>& renderShadowPass
|
||||
) {
|
||||
static int frameid = 0;
|
||||
if (shadows) {
|
||||
if (frameid % 2 == 0) {
|
||||
generateShadowsMap(camera, pctx, *shadowMap, shadowCamera, 1.0f, renderShadowPass);
|
||||
} else {
|
||||
generateShadowsMap(camera, pctx, *wideShadowMap, wideShadowCamera, 3.0f, renderShadowPass);
|
||||
}
|
||||
if (!shadows) {
|
||||
return;
|
||||
}
|
||||
if (frameid % 2 == 0) {
|
||||
generateShadowsMap(
|
||||
camera, pctx, *shadowMap, shadowCamera, 1.0f, renderShadowPass
|
||||
);
|
||||
} else {
|
||||
generateShadowsMap(
|
||||
camera,
|
||||
pctx,
|
||||
*wideShadowMap,
|
||||
wideShadowCamera,
|
||||
3.0f,
|
||||
renderShadowPass
|
||||
);
|
||||
}
|
||||
frameid++;
|
||||
}
|
||||
|
|
@ -141,7 +156,7 @@ void Shadows::generateShadowsMap(
|
|||
ShadowMap& shadowMap,
|
||||
Camera& shadowCamera,
|
||||
float scale,
|
||||
std::function<void(Camera&)> renderShadowPass
|
||||
const std::function<void(Camera&)>& renderShadowPass
|
||||
) {
|
||||
auto world = level.getWorld();
|
||||
const auto& worldInfo = world->getInfo();
|
||||
|
|
@ -162,10 +177,7 @@ void Shadows::generateShadowsMap(
|
|||
shadowCamera.setAspectRatio(1.0f);
|
||||
|
||||
float t = worldInfo.daytime - 0.25f;
|
||||
if (t < 0.0f) {
|
||||
t += 1.0f;
|
||||
}
|
||||
t = fmod(t, 0.5f);
|
||||
t = glm::mod(t < 0.0f ? t + 1.0f : t, 0.5f);
|
||||
|
||||
float sunCycleStep = 1.0f / 500.0f;
|
||||
float sunAngle = glm::radians(
|
||||
|
|
@ -192,15 +204,13 @@ void Shadows::generateShadowsMap(
|
|||
|
||||
shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f));
|
||||
|
||||
{
|
||||
auto sctx = pctx.sub();
|
||||
sctx.setDepthTest(true);
|
||||
sctx.setCullFace(true);
|
||||
sctx.setViewport({resolution, resolution});
|
||||
shadowMap.bind();
|
||||
if (renderShadowPass) {
|
||||
renderShadowPass(shadowCamera);
|
||||
}
|
||||
shadowMap.unbind();
|
||||
auto sctx = pctx.sub();
|
||||
sctx.setDepthTest(true);
|
||||
sctx.setCullFace(true);
|
||||
sctx.setViewport({resolution, resolution});
|
||||
shadowMap.bind();
|
||||
if (renderShadowPass) {
|
||||
renderShadowPass(shadowCamera);
|
||||
}
|
||||
shadowMap.unbind();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class DrawContext;
|
|||
struct EngineSettings;
|
||||
class ShadowMap;
|
||||
|
||||
class Shadows {
|
||||
class Shadows final {
|
||||
public:
|
||||
Shadows(const Level& level);
|
||||
~Shadows();
|
||||
|
|
@ -24,7 +24,7 @@ public:
|
|||
void refresh(
|
||||
const Camera& camera,
|
||||
const DrawContext& pctx,
|
||||
std::function<void(Camera&)> renderShadowPass
|
||||
const std::function<void(Camera&)>& renderShadowPass
|
||||
);
|
||||
private:
|
||||
const Level& level;
|
||||
|
|
@ -41,6 +41,6 @@ private:
|
|||
ShadowMap& shadowMap,
|
||||
Camera& shadowCamera,
|
||||
float scale,
|
||||
std::function<void(Camera&)> renderShadowPass
|
||||
const std::function<void(Camera&)>& renderShadowPass
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ std::unique_ptr<ImageData> Texture::readData() {
|
|||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.get());
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
return std::make_unique<ImageData>(
|
||||
ImageFormat::rgba8888, width, height, std::move(data)
|
||||
ImageFormat::RGBA8888, width, height, std::move(data)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
namespace gl {
|
||||
inline GLenum to_glenum(ImageFormat imageFormat) {
|
||||
switch (imageFormat) {
|
||||
case ImageFormat::rgb888: return GL_RGB;
|
||||
case ImageFormat::rgba8888: return GL_RGBA;
|
||||
case ImageFormat::RGB888: return GL_RGB;
|
||||
case ImageFormat::RGBA8888: return GL_RGBA;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,21 +42,22 @@ void BlocksRenderer::vertex(
|
|||
const glm::vec3& normal,
|
||||
float emission
|
||||
) {
|
||||
vertexBuffer[vertexCount].position = coord;
|
||||
|
||||
vertexBuffer[vertexCount].uv = {u,v};
|
||||
|
||||
vertexBuffer[vertexCount].normal[0] = static_cast<uint8_t>(normal.r * 127 + 128);
|
||||
vertexBuffer[vertexCount].normal[1] = static_cast<uint8_t>(normal.g * 127 + 128);
|
||||
vertexBuffer[vertexCount].normal[2] = static_cast<uint8_t>(normal.b * 127 + 128);
|
||||
vertexBuffer[vertexCount].normal[3] = static_cast<uint8_t>(emission * 255);
|
||||
|
||||
vertexBuffer[vertexCount].color[0] = static_cast<uint8_t>(light.r * 255);
|
||||
vertexBuffer[vertexCount].color[1] = static_cast<uint8_t>(light.g * 255);
|
||||
vertexBuffer[vertexCount].color[2] = static_cast<uint8_t>(light.b * 255);
|
||||
vertexBuffer[vertexCount].color[3] = static_cast<uint8_t>(light.a * 255);
|
||||
|
||||
vertexCount++;
|
||||
vertexBuffer[vertexCount++] = {
|
||||
coord,
|
||||
{u, v},
|
||||
{
|
||||
static_cast<uint8_t>(light.r * 255),
|
||||
static_cast<uint8_t>(light.g * 255),
|
||||
static_cast<uint8_t>(light.b * 255),
|
||||
static_cast<uint8_t>(light.a * 255),
|
||||
}, {
|
||||
static_cast<uint8_t>(normal.x * 127 + 128),
|
||||
static_cast<uint8_t>(normal.y * 127 + 128),
|
||||
static_cast<uint8_t>(normal.z * 127 + 128),
|
||||
static_cast<uint8_t>(emission * 255)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void BlocksRenderer::index(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e, uint32_t f) {
|
||||
|
|
@ -440,28 +441,11 @@ void BlocksRenderer::blockCube(
|
|||
}
|
||||
}
|
||||
|
||||
bool BlocksRenderer::isOpenForLight(int x, int y, int z) const {
|
||||
blockid_t id = voxelsBuffer->pickBlockId(chunk->x * CHUNK_W + x,
|
||||
y,
|
||||
chunk->z * CHUNK_D + z);
|
||||
if (id == BLOCK_VOID) {
|
||||
return false;
|
||||
}
|
||||
const Block& block = *blockDefsCache[id];
|
||||
if (block.lightPassing) {
|
||||
return true;
|
||||
}
|
||||
return !id;
|
||||
}
|
||||
|
||||
glm::vec4 BlocksRenderer::pickLight(int x, int y, int z) const {
|
||||
if (isOpenForLight(x, y, z)) {
|
||||
light_t light = voxelsBuffer->pickLight(chunk->x * CHUNK_W + x, y,
|
||||
chunk->z * CHUNK_D + z);
|
||||
return Lightmap::extractNormalized(light);
|
||||
} else {
|
||||
return glm::vec4(0.0f);
|
||||
}
|
||||
light_t light = voxelsBuffer->pickLight(
|
||||
chunk->x * CHUNK_W + x, y, chunk->z * CHUNK_D + z
|
||||
);
|
||||
return light ? Lightmap::extractNormalized(light) : glm::vec4(0.0f);
|
||||
}
|
||||
|
||||
glm::vec4 BlocksRenderer::pickLight(const glm::ivec3& coord) const {
|
||||
|
|
@ -700,7 +684,9 @@ SortingMeshData BlocksRenderer::renderTranslucent(
|
|||
return sortingMesh;
|
||||
}
|
||||
|
||||
void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) {
|
||||
void BlocksRenderer::build(
|
||||
const Chunk* chunk, const VoxelsRenderVolume& volume
|
||||
) {
|
||||
this->chunk = chunk;
|
||||
this->voxelsBuffer = &volume;
|
||||
if (voxelsBuffer->pickBlockId(
|
||||
|
|
@ -713,16 +699,17 @@ void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) {
|
|||
|
||||
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;
|
||||
beginEnds[variant.drawGroup][0] = i + 1;
|
||||
}
|
||||
beginEnds[variant.drawGroup][1] = i;
|
||||
}
|
||||
|
|
@ -735,7 +722,11 @@ void BlocksRenderer::build(const Chunk* chunk, const VoxelsVolume& volume) {
|
|||
denseRender = false;
|
||||
densePass = false;
|
||||
|
||||
sortingMesh = renderTranslucent(voxels, beginEnds);
|
||||
if (hasTranslucent) {
|
||||
sortingMesh = renderTranslucent(voxels, beginEnds);
|
||||
} else {
|
||||
sortingMesh = {};
|
||||
}
|
||||
|
||||
overflow = false;
|
||||
vertexCount = 0;
|
||||
|
|
@ -781,7 +772,7 @@ ChunkMeshData BlocksRenderer::createMesh() {
|
|||
}
|
||||
|
||||
ChunkMesh BlocksRenderer::render(
|
||||
const Chunk* chunk, const VoxelsVolume& volume
|
||||
const Chunk* chunk, const VoxelsRenderVolume& volume
|
||||
) {
|
||||
build(chunk, volume);
|
||||
|
||||
|
|
@ -795,7 +786,7 @@ ChunkMesh BlocksRenderer::render(
|
|||
IndexBufferData {indexBuffer.get(), indexCount},
|
||||
IndexBufferData {denseIndexBuffer.get(), denseIndexCount},
|
||||
}
|
||||
), std::move(sortingMesh)};
|
||||
), std::move(sortingMesh), nullptr};
|
||||
}
|
||||
|
||||
size_t BlocksRenderer::getMemoryConsumption() const {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
#include "voxels/voxel.hpp"
|
||||
#include "typedefs.hpp"
|
||||
|
||||
#include "voxels/voxel.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
#include "voxels/VoxelsVolume.hpp"
|
||||
|
|
@ -12,16 +9,39 @@
|
|||
#include "commons.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
template<typename VertexStructure> class Mesh;
|
||||
class Content;
|
||||
class Block;
|
||||
class Chunk;
|
||||
class Chunks;
|
||||
class VoxelsVolume;
|
||||
class ContentGfxCache;
|
||||
struct UVRegion;
|
||||
|
||||
class BlocksRenderer {
|
||||
class BlocksRenderer final {
|
||||
public:
|
||||
BlocksRenderer(
|
||||
size_t capacity,
|
||||
const Content& content,
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
~BlocksRenderer();
|
||||
|
||||
void build(const Chunk* chunk, const VoxelsRenderVolume& volume);
|
||||
ChunkMesh render(
|
||||
const Chunk* chunk, const VoxelsRenderVolume& volume
|
||||
);
|
||||
ChunkMeshData createMesh();
|
||||
|
||||
size_t getMemoryConsumption() const;
|
||||
|
||||
bool isCancelled() const {
|
||||
return cancelled;
|
||||
}
|
||||
private:
|
||||
static const glm::vec3 SUN_VECTOR;
|
||||
const Content& content;
|
||||
std::unique_ptr<ChunkVertex[]> vertexBuffer;
|
||||
|
|
@ -37,7 +57,7 @@ class BlocksRenderer {
|
|||
bool densePass = false;
|
||||
bool denseRender = false;
|
||||
const Chunk* chunk = nullptr;
|
||||
const VoxelsVolume* voxelsBuffer = nullptr;
|
||||
const VoxelsRenderVolume* voxelsBuffer = nullptr;
|
||||
|
||||
const Block* const* blockDefsCache;
|
||||
const ContentGfxCache& cache;
|
||||
|
|
@ -122,11 +142,9 @@ class BlocksRenderer {
|
|||
bool ao
|
||||
);
|
||||
|
||||
bool isOpenForLight(int x, int y, int z) const;
|
||||
|
||||
// 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 {
|
||||
auto vox = voxelsBuffer->pickBlock(
|
||||
const auto& vox = voxelsBuffer->pickBlock(
|
||||
chunk->x * CHUNK_W + pos.x, pos.y, chunk->z * CHUNK_D + pos.z
|
||||
);
|
||||
if (vox.id == BLOCK_VOID) {
|
||||
|
|
@ -160,24 +178,4 @@ class BlocksRenderer {
|
|||
|
||||
void render(const voxel* voxels, const int beginEnds[256][2]);
|
||||
SortingMeshData renderTranslucent(const voxel* voxels, int beginEnds[256][2]);
|
||||
public:
|
||||
BlocksRenderer(
|
||||
size_t capacity,
|
||||
const Content& content,
|
||||
const ContentGfxCache& cache,
|
||||
const EngineSettings& settings
|
||||
);
|
||||
virtual ~BlocksRenderer();
|
||||
|
||||
void build(const Chunk* chunk, const VoxelsVolume& volume);
|
||||
ChunkMesh render(
|
||||
const Chunk* chunk, const VoxelsVolume& volume
|
||||
);
|
||||
ChunkMeshData createMesh();
|
||||
|
||||
size_t getMemoryConsumption() const;
|
||||
|
||||
bool isCancelled() const {
|
||||
return cancelled;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ static debug::Logger logger("chunks-render");
|
|||
|
||||
size_t ChunksRenderer::visibleChunks = 0;
|
||||
|
||||
static constexpr inline size_t MAX_CHUNKS_ENQUEUED_IN_FRAME = 4;
|
||||
|
||||
class RendererWorker : public util::Worker<RendererJob, RendererResult> {
|
||||
BlocksRenderer renderer;
|
||||
public:
|
||||
|
|
@ -51,11 +53,10 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
static util::ObjectsPool<VoxelsVolume> voxelsVolumesPool {};
|
||||
static inline const int VOXELS_BUFFER_PADDING = 2;
|
||||
static util::ObjectsPool<VoxelsRenderVolume> voxelsVolumesPool {};
|
||||
|
||||
ChunksRenderer::ChunksRenderer(
|
||||
const Level* level,
|
||||
const Level& level,
|
||||
const Chunks& chunks,
|
||||
const Assets& assets,
|
||||
const Frustum& frustum,
|
||||
|
|
@ -69,25 +70,27 @@ ChunksRenderer::ChunksRenderer(
|
|||
threadPool(
|
||||
"chunks-render-pool",
|
||||
[&]() {
|
||||
return std::make_shared<RendererWorker>(
|
||||
*level, cache, settings
|
||||
return std::make_unique<RendererWorker>(
|
||||
level, cache, settings
|
||||
);
|
||||
},
|
||||
[&](RendererResult& result) {
|
||||
if (!result.cancelled) {
|
||||
auto meshData = std::move(result.meshData);
|
||||
meshes[result.key] = ChunkMesh {
|
||||
std::make_unique<Mesh<ChunkVertex>>(meshData.mesh),
|
||||
std::move(meshData.sortingMesh)};
|
||||
}
|
||||
inwork.erase(result.key);
|
||||
[&](RendererResult&& result) {
|
||||
if (!result.cancelled) {
|
||||
auto meshData = std::move(result.meshData);
|
||||
auto chunk = std::make_unique<Mesh<ChunkVertex>>(meshData.mesh);
|
||||
meshes[result.key] = ChunkMesh {
|
||||
std::move(chunk),
|
||||
std::move(meshData.sortingMesh),
|
||||
nullptr};
|
||||
}
|
||||
inwork.erase(result.key);
|
||||
},
|
||||
settings.graphics.chunkMaxRenderers.get()
|
||||
) {
|
||||
threadPool.setStopOnFail(false);
|
||||
renderer = std::make_unique<BlocksRenderer>(
|
||||
settings.graphics.chunkMaxVertices.get(),
|
||||
level->content, cache, settings
|
||||
level.content, cache, settings
|
||||
);
|
||||
logger.info() << "created " << threadPool.getWorkersCount() << " workers";
|
||||
logger.info() << "memory consumption is "
|
||||
|
|
@ -103,14 +106,10 @@ ChunksRenderer::ChunksRenderer(
|
|||
|
||||
ChunksRenderer::~ChunksRenderer() = default;
|
||||
|
||||
std::shared_ptr<VoxelsVolume> ChunksRenderer::prepareVoxelsVolume(
|
||||
std::shared_ptr<VoxelsRenderVolume> ChunksRenderer::prepareVoxelsVolume(
|
||||
const Chunk& chunk
|
||||
) {
|
||||
auto voxelsBuffer = voxelsVolumesPool.create(
|
||||
CHUNK_W + VOXELS_BUFFER_PADDING * 2,
|
||||
CHUNK_H,
|
||||
CHUNK_D + VOXELS_BUFFER_PADDING * 2
|
||||
);
|
||||
auto voxelsBuffer = voxelsVolumesPool.create();
|
||||
voxelsBuffer->setPosition(
|
||||
chunk.x * CHUNK_W - VOXELS_BUFFER_PADDING, 0,
|
||||
chunk.z * CHUNK_D - VOXELS_BUFFER_PADDING
|
||||
|
|
@ -122,28 +121,27 @@ std::shared_ptr<VoxelsVolume> ChunksRenderer::prepareVoxelsVolume(
|
|||
}
|
||||
|
||||
const Mesh<ChunkVertex>* ChunksRenderer::render(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
|
||||
) {
|
||||
glm::ivec2 key(chunk->x, chunk->z);
|
||||
chunk->flags.modified = false;
|
||||
if (important) {
|
||||
ChunkMesh mesh {};
|
||||
auto voxelsBuffer = prepareVoxelsVolume(*chunk);
|
||||
|
||||
auto mesh = renderer->render(chunk.get(), *voxelsBuffer);
|
||||
meshes[key] =
|
||||
ChunkMesh {std::move(mesh.mesh), std::move(mesh.sortingMeshData)};
|
||||
mesh = renderer->render(chunk.get(), *voxelsBuffer);
|
||||
meshes[key] = std::move(mesh);
|
||||
return meshes[key].mesh.get();
|
||||
}
|
||||
if (inwork.find(key) != inwork.end()) {
|
||||
if (inwork.find(key) != inwork.end() ||
|
||||
((inwork.size() >= threadPool.getWorkersCount() ||
|
||||
enqueuedInFrame >= MAX_CHUNKS_ENQUEUED_IN_FRAME) &&
|
||||
lowPriority)) {
|
||||
return nullptr;
|
||||
}
|
||||
enqueuedInFrame++;
|
||||
auto voxelsBuffer = prepareVoxelsVolume(*chunk);
|
||||
inwork[key] = true;
|
||||
chunks.getVoxels(
|
||||
*voxelsBuffer, settings.graphics.backlight.get(), chunk->top + 1
|
||||
);
|
||||
|
||||
threadPool.enqueueJob({chunk, std::move(voxelsBuffer)});
|
||||
inwork[key] = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -161,20 +159,21 @@ void ChunksRenderer::clear() {
|
|||
}
|
||||
|
||||
const Mesh<ChunkVertex>* ChunksRenderer::getOrRender(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
|
||||
) {
|
||||
auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||
if (found == meshes.end()) {
|
||||
return render(chunk, important);
|
||||
return render(chunk, important, lowPriority);
|
||||
}
|
||||
if (chunk->flags.modified && chunk->flags.lighted) {
|
||||
render(chunk, important);
|
||||
render(chunk, important, lowPriority);
|
||||
}
|
||||
return found->second.mesh.get();
|
||||
}
|
||||
|
||||
void ChunksRenderer::update() {
|
||||
threadPool.update();
|
||||
threadPool.pullResults();
|
||||
enqueuedInFrame = 0;
|
||||
}
|
||||
|
||||
const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
|
||||
|
|
@ -200,7 +199,11 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
|
|||
(chunk->z + 0.5f) * CHUNK_D
|
||||
)
|
||||
);
|
||||
auto mesh = getOrRender(chunk, distance < CHUNK_W * 1.5f);
|
||||
auto mesh = getOrRender(
|
||||
chunk,
|
||||
distance < CHUNK_W * 1.5f * 10.0f,
|
||||
distance > CHUNK_W * settings.chunks.loadDistance.get() * 0.5
|
||||
);
|
||||
if (mesh == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -238,7 +241,7 @@ void ChunksRenderer::drawShadowsPass(
|
|||
continue;
|
||||
}
|
||||
glm::ivec2 pos {chunk->x, chunk->z};
|
||||
const auto& found = meshes.find({chunk->x, chunk->z});
|
||||
const auto& found = meshes.find(pos);
|
||||
if (found == meshes.end()) {
|
||||
continue;
|
||||
}
|
||||
|
|
@ -247,11 +250,11 @@ void ChunksRenderer::drawShadowsPass(
|
|||
pos.x * CHUNK_W + 0.5f, 0.5f, pos.y * CHUNK_D + 0.5f
|
||||
);
|
||||
|
||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
||||
glm::vec3 min(pos.x * CHUNK_W, chunk->bottom, pos.y * CHUNK_D);
|
||||
glm::vec3 max(
|
||||
chunk->x * CHUNK_W + CHUNK_W,
|
||||
pos.x * CHUNK_W + CHUNK_W,
|
||||
chunk->top,
|
||||
chunk->z * CHUNK_D + CHUNK_D
|
||||
pos.y * CHUNK_D + CHUNK_D
|
||||
);
|
||||
|
||||
if (!frustum.isBoxVisible(min, max)) {
|
||||
|
|
@ -305,17 +308,17 @@ void ChunksRenderer::drawChunks(
|
|||
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) {
|
||||
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),
|
||||
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
|
||||
visibleChunks++;
|
||||
if (mesh == nullptr) {
|
||||
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),
|
||||
(coord + glm::vec3(CHUNK_W * 0.5f, 0.0f, CHUNK_D * 0.5f))) < denseDistance2);
|
||||
visibleChunks++;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ class Chunks;
|
|||
class Frustum;
|
||||
class BlocksRenderer;
|
||||
class ContentGfxCache;
|
||||
class VoxelsVolume;
|
||||
struct EngineSettings;
|
||||
|
||||
struct ChunksSortEntry {
|
||||
|
|
@ -41,7 +40,7 @@ struct RendererResult {
|
|||
|
||||
struct RendererJob {
|
||||
std::shared_ptr<Chunk> chunk;
|
||||
std::shared_ptr<VoxelsVolume> volume;
|
||||
std::shared_ptr<VoxelsRenderVolume> volume;
|
||||
};
|
||||
|
||||
class ChunksRenderer {
|
||||
|
|
@ -58,10 +57,12 @@ class ChunksRenderer {
|
|||
const Mesh<ChunkVertex>* retrieveChunk(
|
||||
size_t index, const Camera& camera, bool culling
|
||||
);
|
||||
std::shared_ptr<VoxelsVolume> prepareVoxelsVolume(const Chunk& chunk);
|
||||
std::shared_ptr<VoxelsRenderVolume> prepareVoxelsVolume(const Chunk& chunk);
|
||||
|
||||
size_t enqueuedInFrame = 0;
|
||||
public:
|
||||
ChunksRenderer(
|
||||
const Level* level,
|
||||
const Level& level,
|
||||
const Chunks& chunks,
|
||||
const Assets& assets,
|
||||
const Frustum& frustum,
|
||||
|
|
@ -71,13 +72,13 @@ public:
|
|||
virtual ~ChunksRenderer();
|
||||
|
||||
const Mesh<ChunkVertex>* render(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
|
||||
);
|
||||
void unload(const Chunk* chunk);
|
||||
void clear();
|
||||
|
||||
const Mesh<ChunkVertex>* getOrRender(
|
||||
const std::shared_ptr<Chunk>& chunk, bool important
|
||||
const std::shared_ptr<Chunk>& chunk, bool important, bool lowPriority
|
||||
);
|
||||
|
||||
void drawShadowsPass(
|
||||
|
|
|
|||
337
src/graphics/render/CloudsRenderer.cpp
Executable file
337
src/graphics/render/CloudsRenderer.cpp
Executable file
|
|
@ -0,0 +1,337 @@
|
|||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include "CloudsRenderer.hpp"
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "coders/imageio.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "graphics/core/ImageData.hpp"
|
||||
#include "graphics/core/Mesh.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "lighting/Lightmap.hpp"
|
||||
#include "maths/FastNoiseLite.h"
|
||||
#include "maths/FrustumCulling.hpp"
|
||||
#include "maths/voxmaths.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "world/Weather.hpp"
|
||||
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
static debug::Logger logger("clouds-render");
|
||||
|
||||
static inline constexpr int MAP_SIZE = 512;
|
||||
static inline constexpr float CLOUD_VOXEL_SCALE = 8.0f;
|
||||
static inline constexpr float CLOUDS_SPEED = 4.0f;
|
||||
|
||||
class CloudsMap {
|
||||
public:
|
||||
glm::ivec3 size;
|
||||
const bool* voxels;
|
||||
|
||||
CloudsMap() = default;
|
||||
|
||||
CloudsMap(const glm::ivec3& size, const bool* voxels)
|
||||
: size(size), voxels(voxels) {
|
||||
}
|
||||
|
||||
bool isOpen(int x, int y, int z) const {
|
||||
if (x >= 0 && x < size.x && y >= 0 && y < size.y && z >= 0 && z < size.z) {
|
||||
return !voxels[vox_index(x, y, z, size.x, size.z)];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class VolumeRenderer final {
|
||||
public:
|
||||
VolumeRenderer(size_t capacity)
|
||||
: vertices(std::make_unique<ChunkVertex[]>(capacity)),
|
||||
indexBuffer(std::make_unique<uint32_t[]>(capacity * 6)),
|
||||
capacity(capacity) {
|
||||
}
|
||||
|
||||
~VolumeRenderer() = default;
|
||||
|
||||
void build(CloudsMap volumeMap) {
|
||||
this->map = std::move(volumeMap);
|
||||
overflow = false;
|
||||
offset = 0;
|
||||
indexOffset = 0;
|
||||
indexCount = 0;
|
||||
|
||||
int end = map.size.x * map.size.y * map.size.z;
|
||||
for (int i = 0; i < end; i++) {
|
||||
int x = i % map.size.x;
|
||||
int y = i / (map.size.z * map.size.x);
|
||||
int z = (i / map.size.x) % map.size.z;
|
||||
const auto& vox = map.voxels[i];
|
||||
if (!vox) {
|
||||
continue;
|
||||
}
|
||||
cube({x, y, z});
|
||||
if (overflow) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MeshData<ChunkVertex> createMesh() const {
|
||||
return MeshData(
|
||||
util::Buffer(vertices.get(), offset),
|
||||
std::vector<util::Buffer<uint32_t>> {
|
||||
util::Buffer(indexBuffer.get(), indexCount),
|
||||
},
|
||||
util::Buffer(
|
||||
ChunkVertex::ATTRIBUTES,
|
||||
sizeof(ChunkVertex::ATTRIBUTES) / sizeof(VertexAttribute)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ChunkVertex[]> vertices;
|
||||
std::unique_ptr<uint32_t[]> indexBuffer;
|
||||
size_t capacity;
|
||||
uint32_t indexOffset = 0;
|
||||
uint32_t indexCount = 0;
|
||||
uint32_t offset = 0;
|
||||
bool overflow = false;
|
||||
CloudsMap map {};
|
||||
|
||||
void vertex(
|
||||
const glm::vec3& coord,
|
||||
const glm::vec3& normal
|
||||
) {
|
||||
auto& vert = vertices[offset++];
|
||||
vert.position = coord;
|
||||
vert.uv = {};
|
||||
vert.normal = {
|
||||
static_cast<uint8_t>(normal.r * 127 + 128),
|
||||
static_cast<uint8_t>(normal.g * 127 + 128),
|
||||
static_cast<uint8_t>(normal.b * 127 + 128),
|
||||
255
|
||||
};
|
||||
vert.color = {
|
||||
0, 0, 0, static_cast<uint8_t>((coord.y / 8.0f * 0.25f + 0.75f) * 255)
|
||||
};
|
||||
}
|
||||
|
||||
void face(
|
||||
const glm::vec3& coord,
|
||||
const glm::vec3& X,
|
||||
const glm::vec3& Y,
|
||||
const glm::vec3& Z
|
||||
) {
|
||||
if (!isOpen(coord + Z)) {
|
||||
return;
|
||||
}
|
||||
if (offset + 4 >= capacity) {
|
||||
overflow = true;
|
||||
return;
|
||||
}
|
||||
|
||||
float s = 0.5f;
|
||||
vertex(coord + (-X - Y + Z) * s, Z);
|
||||
vertex(coord + ( X - Y + Z) * s, Z);
|
||||
vertex(coord + ( X + Y + Z) * s, Z);
|
||||
vertex(coord + (-X + Y + Z) * s, Z);
|
||||
|
||||
const uint32_t indices[] {0, 1, 2, 0, 2, 3};
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
indexBuffer[indexCount++] = indexOffset + indices[i];
|
||||
}
|
||||
indexOffset += 4;
|
||||
}
|
||||
|
||||
bool isOpen(const glm::ivec3& pos) const {
|
||||
return pos.y < 0 || pos.y >= map.size.y ||
|
||||
map.isOpen(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
void cube(const glm::ivec3& coord) {
|
||||
const glm::ivec3 X(1, 0, 0);
|
||||
const glm::ivec3 Y(0, 1, 0);
|
||||
const glm::ivec3 Z(0, 0, 1);
|
||||
|
||||
face(coord, X, Y, Z);
|
||||
face(coord, -X, Y, -Z);
|
||||
face(coord, X, -Z, Y);
|
||||
face(coord, X, Z, -Y);
|
||||
face(coord, -Z, Y, X);
|
||||
face(coord, Z, Y, -X);
|
||||
}
|
||||
};
|
||||
|
||||
static void generate_heightmap(
|
||||
float* heightmap, fnl_state& state, int w, int dd, int layerid
|
||||
) {
|
||||
float pi2 = glm::two_pi<float>();
|
||||
|
||||
for (int lz = 0; lz < dd; lz++) {
|
||||
for (int lx = 0; lx < w; lx++) {
|
||||
float x = glm::sin(lx / static_cast<float>(w) * pi2) * w / pi2;
|
||||
float y = -glm::cos(lx / static_cast<float>(w) * pi2) * w / pi2;
|
||||
float z = lz;
|
||||
float s = 1.5f;
|
||||
auto n = fnlGetNoise3D(&state, x * s * 0.7, y * s, z * s * 0.7);
|
||||
n += fnlGetNoise3D(&state, x * s + fnlGetNoise2D(&state, x * s * 4 + 2, z * s * 4) * 2.0f, y * s, z * 3.0f) * 0.5f;
|
||||
n += fnlGetNoise3D(&state, x * s * 2, y * s * 2, z * s * 2) * 0.25f;
|
||||
n += fnlGetNoise3D(&state, x * s * 4, y * s * 4, z * s * 4) * 0.125f * 2;
|
||||
n += fnlGetNoise3D(&state, x * s * 8, y * s * 8, z * s * 8) * 0.125f * 0.5f * 2;
|
||||
n += fnlGetNoise3D(&state, x * s * 16, y * s * 16, z * s * 16) * 0.125f * 0.25f * 3;
|
||||
n = glm::max(0.0f, n);
|
||||
n += -0.1f - layerid * 0.3f;
|
||||
|
||||
heightmap[lz * w + lx] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sample_voxels(
|
||||
bool* voxels,
|
||||
const float* heightmap,
|
||||
int height,
|
||||
int segmentSize,
|
||||
int segmentX,
|
||||
int segmentZ
|
||||
) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int z = 0; z < segmentSize; z++) {
|
||||
for (int x = 0; x < segmentSize; x++) {
|
||||
int gx = (segmentX * segmentSize) + x;
|
||||
int gz = (segmentZ * segmentSize) + z;
|
||||
|
||||
float n = heightmap[gz * MAP_SIZE + gx];
|
||||
if (gz < MAP_SIZE / 2) {
|
||||
float t = gz / static_cast<float>(MAP_SIZE / 2);
|
||||
n = n * t +
|
||||
heightmap[(MAP_SIZE + gz) * MAP_SIZE + gx] * (1.0f - t);
|
||||
}
|
||||
bool solid = y <= n * height && y >= (0.5f - n * 0.5f) * height;
|
||||
voxels[vox_index(x, y, z, segmentSize, segmentSize)] = solid;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloudsRenderer::CloudsRenderer() {
|
||||
VolumeRenderer volumeRenderer(1024 * 512);
|
||||
|
||||
const int diameter = 4;
|
||||
const int segmentSize = MAP_SIZE / diameter;
|
||||
|
||||
const int w = MAP_SIZE;
|
||||
const int h = 8;
|
||||
const int d = MAP_SIZE;
|
||||
const int dd = d * 1.5;
|
||||
auto heightmap = std::make_unique<float[]>(w * dd);
|
||||
|
||||
for (int layerid = 0; layerid < 2; layerid++) {
|
||||
auto& layer = layers[layerid];
|
||||
layer.diameter = diameter;
|
||||
layer.segmentSize = segmentSize;
|
||||
|
||||
fnl_state state = fnlCreateState();
|
||||
state.seed = 5265 + layerid * 3521;
|
||||
|
||||
generate_heightmap(heightmap.get(), state, w, dd, layerid);
|
||||
|
||||
bool voxels[segmentSize * h * segmentSize];
|
||||
for (int sz = 0; sz < diameter; sz++) {
|
||||
for (int sx = 0; sx < diameter; sx++) {
|
||||
sample_voxels(voxels, heightmap.get(), h, segmentSize, sx, sz);
|
||||
|
||||
CloudsMap map({segmentSize, h, segmentSize}, voxels);
|
||||
|
||||
volumeRenderer.build(map);
|
||||
layer.meshes.push_back(std::make_unique<Mesh<ChunkVertex>>(
|
||||
volumeRenderer.createMesh()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CloudsRenderer::~CloudsRenderer() = default;
|
||||
|
||||
void CloudsRenderer::draw(
|
||||
Layer& layer,
|
||||
Frustum& frustum,
|
||||
Shader& shader,
|
||||
const Camera& camera,
|
||||
float timer,
|
||||
int layerId
|
||||
) {
|
||||
float scale = CLOUD_VOXEL_SCALE;
|
||||
int totalDiameter = layer.segmentSize * scale;
|
||||
|
||||
int gcellX = floordiv(camera.position.x, totalDiameter);
|
||||
int gcellZ = floordiv(camera.position.z, totalDiameter);
|
||||
|
||||
float speed = CLOUDS_SPEED;
|
||||
float speedX = glm::sin(layerId * 0.3f + 0.4f) * speed / (layerId + 1);
|
||||
float speedZ = -glm::cos(layerId * 0.3f + 0.4f) * speed / (layerId + 1);
|
||||
|
||||
int radius = 4;
|
||||
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
for (int z = -radius; z <= radius; z++) {
|
||||
int lcellX = gcellX - floordiv(glm::floor(timer * speedX), totalDiameter);
|
||||
int lcellZ = gcellZ - floordiv(glm::floor(timer * speedZ), totalDiameter);
|
||||
glm::vec3 position(
|
||||
-128 * scale + (x + gcellX) * layer.segmentSize * scale +
|
||||
glm::mod(timer * speedX, static_cast<float>(totalDiameter)),
|
||||
250 + layerId * 200,
|
||||
-128 * scale + (z + gcellZ) * layer.segmentSize * scale +
|
||||
glm::mod(timer * speedZ, static_cast<float>(totalDiameter))
|
||||
);
|
||||
if (glm::distance2(
|
||||
glm::vec2(
|
||||
position.x + totalDiameter * 0.5f,
|
||||
position.z + totalDiameter * 0.5f
|
||||
),
|
||||
glm::vec2(camera.position.x, camera.position.z)
|
||||
) > 4e6) {
|
||||
continue;
|
||||
}
|
||||
if (!frustum.isBoxVisible(position, position + glm::vec3(layer.segmentSize * scale))) {
|
||||
continue;
|
||||
}
|
||||
auto matrix = glm::mat4(1.0f);
|
||||
matrix = glm::translate(matrix, position);
|
||||
matrix = glm::scale(matrix, glm::vec3(scale, scale, scale));
|
||||
shader.uniformMatrix("u_model", matrix);
|
||||
|
||||
int lx = (x + radius + lcellX) % layer.diameter;
|
||||
int lz = (z + radius + lcellZ) % layer.diameter;
|
||||
if (lx < 0) lx += layer.diameter;
|
||||
if (lz < 0) lz += layer.diameter;
|
||||
|
||||
layer.meshes[lz * layer.diameter + lx]->draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CloudsRenderer::draw(
|
||||
Shader& shader,
|
||||
const Weather& weather,
|
||||
float timer,
|
||||
float fogFactor,
|
||||
const Camera& camera,
|
||||
int quality
|
||||
) {
|
||||
Frustum frustum;
|
||||
frustum.update(camera.getProjView());
|
||||
|
||||
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);
|
||||
|
||||
for (int i = 0; i < std::min<int>(quality, layers.size()); i++) {
|
||||
draw(layers[i], frustum, shader, camera, timer, i);
|
||||
}
|
||||
}
|
||||
44
src/graphics/render/CloudsRenderer.hpp
Executable file
44
src/graphics/render/CloudsRenderer.hpp
Executable file
|
|
@ -0,0 +1,44 @@
|
|||
#pragma once
|
||||
|
||||
#include "commons.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class Camera;
|
||||
class Shader;
|
||||
struct Weather;
|
||||
class Frustum;
|
||||
|
||||
class CloudsRenderer final {
|
||||
public:
|
||||
CloudsRenderer();
|
||||
~CloudsRenderer();
|
||||
|
||||
void draw(
|
||||
Shader& shader,
|
||||
const Weather& weather,
|
||||
float timer,
|
||||
float fogFactor,
|
||||
const Camera& camera,
|
||||
int quality
|
||||
);
|
||||
private:
|
||||
struct Layer {
|
||||
int diameter;
|
||||
int segmentSize;
|
||||
std::vector<std::unique_ptr<Mesh<ChunkVertex>>> meshes;
|
||||
};
|
||||
|
||||
std::array<Layer, 2> layers;
|
||||
|
||||
void draw(
|
||||
Layer& layer,
|
||||
Frustum& frustum,
|
||||
Shader& shader,
|
||||
const Camera& camera,
|
||||
float timer,
|
||||
int layerId
|
||||
);
|
||||
};
|
||||
|
|
@ -21,9 +21,7 @@ HandsRenderer::HandsRenderer(
|
|||
skeleton(std::move(skeleton)) {
|
||||
}
|
||||
|
||||
void HandsRenderer::renderHands(
|
||||
const Camera& camera, float delta
|
||||
) {
|
||||
void HandsRenderer::render(const Camera& camera) {
|
||||
auto& skeleton = *this->skeleton;
|
||||
const auto& config = *skeleton.config;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ public:
|
|||
std::shared_ptr<rigging::Skeleton> skeleton
|
||||
);
|
||||
|
||||
void renderHands(const Camera& camera, float delta);
|
||||
void render(const Camera& camera);
|
||||
private:
|
||||
const Assets& assets;
|
||||
ModelBatch& modelBatch;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ MainBatch::MainBatch(size_t capacity)
|
|||
const ubyte pixels[] = {
|
||||
255, 255, 255, 255,
|
||||
};
|
||||
ImageData image(ImageFormat::rgba8888, 1, 1, pixels);
|
||||
ImageData image(ImageFormat::RGBA8888, 1, 1, pixels);
|
||||
blank = Texture::from(&image);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ ParticlesRenderer::ParticlesRenderer(
|
|||
const Assets& assets,
|
||||
const Level& level,
|
||||
const Chunks& chunks,
|
||||
const GraphicsSettings* settings
|
||||
const GraphicsSettings& settings
|
||||
)
|
||||
: chunks(chunks),
|
||||
assets(assets),
|
||||
settings(settings),
|
||||
batch(std::make_unique<MainBatch>(4096)) {
|
||||
batch(std::make_unique<MainBatch>(settings.particlesBatchVertices.get())) {
|
||||
}
|
||||
|
||||
ParticlesRenderer::~ParticlesRenderer() = default;
|
||||
|
|
@ -46,12 +46,7 @@ static inline void update_particle(
|
|||
particle.lifetime -= delta;
|
||||
}
|
||||
|
||||
void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
||||
const auto& right = camera.right;
|
||||
const auto& up = camera.up;
|
||||
|
||||
bool backlight = settings->backlight.get();
|
||||
|
||||
void ParticlesRenderer::updateParticles(float delta) {
|
||||
std::vector<const Texture*> unusedTextures;
|
||||
|
||||
for (auto& [texture, vec] : particles) {
|
||||
|
|
@ -59,8 +54,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
|||
unusedTextures.push_back(texture);
|
||||
continue;
|
||||
}
|
||||
batch->setTexture(texture);
|
||||
|
||||
visibleParticles += vec.size();
|
||||
|
||||
auto iter = vec.begin();
|
||||
|
|
@ -87,59 +80,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
|||
}
|
||||
}
|
||||
update_particle(particle, delta, chunks);
|
||||
|
||||
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||
0.001f * preset.sizeSpread;
|
||||
|
||||
glm::vec4 light(1, 1, 1, 0);
|
||||
if (preset.lighting) {
|
||||
light = MainBatch::sampleLight(
|
||||
particle.position,
|
||||
chunks,
|
||||
backlight
|
||||
);
|
||||
auto size = glm::max(glm::vec3(0.5f), preset.size * scale);
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int z = -1; z <= 1; z++) {
|
||||
light = glm::max(
|
||||
light,
|
||||
MainBatch::sampleLight(
|
||||
particle.position -
|
||||
size * glm::vec3(x, y, z),
|
||||
chunks,
|
||||
backlight
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
light *= 0.9f + (particle.random % 100) * 0.001f;
|
||||
}
|
||||
|
||||
glm::vec3 localRight = right;
|
||||
glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up;
|
||||
float angle = particle.angle;
|
||||
if (glm::abs(angle) >= 0.005f) {
|
||||
glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f);
|
||||
glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f);
|
||||
|
||||
localRight = right * rotatedRight.x + localUp * rotatedRight.y +
|
||||
camera.front * rotatedRight.z;
|
||||
localUp = right * rotatedUp.x + localUp * rotatedUp.y +
|
||||
camera.front * rotatedUp.z;
|
||||
}
|
||||
batch->quad(
|
||||
particle.position,
|
||||
localRight,
|
||||
localUp,
|
||||
-camera.front,
|
||||
preset.size * scale,
|
||||
light,
|
||||
glm::vec3(1.0f),
|
||||
particle.region,
|
||||
preset.lighting ? 0.0f : 1.0f
|
||||
);
|
||||
if (particle.lifetime <= 0.0f) {
|
||||
iter = vec.erase(iter);
|
||||
emitter.refCount--;
|
||||
|
|
@ -148,19 +88,85 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
|||
}
|
||||
}
|
||||
}
|
||||
batch->flush();
|
||||
|
||||
for (const auto& texture : unusedTextures) {
|
||||
particles.erase(texture);
|
||||
}
|
||||
}
|
||||
|
||||
void ParticlesRenderer::render(const Camera& camera, float delta) {
|
||||
batch->begin();
|
||||
|
||||
aliveEmitters = emitters.size();
|
||||
visibleParticles = 0;
|
||||
static inline glm::vec4 calc_lights(
|
||||
Particle& particle,
|
||||
ParticlesPreset& preset,
|
||||
bool backlight,
|
||||
float scale,
|
||||
const Chunks& chunks
|
||||
) {
|
||||
auto light = MainBatch::sampleLight(
|
||||
particle.position,
|
||||
chunks,
|
||||
backlight
|
||||
);
|
||||
auto size = glm::max(glm::vec3(0.5f), preset.size * scale);
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
for (int z = -1; z <= 1; z++) {
|
||||
light = glm::max(
|
||||
light,
|
||||
MainBatch::sampleLight(
|
||||
particle.position - size * glm::vec3(x, y, z),
|
||||
chunks,
|
||||
backlight
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
light *= 0.9f + (particle.random % 100) * 0.001f;
|
||||
return light;
|
||||
}
|
||||
|
||||
renderParticles(camera, delta);
|
||||
void ParticlesRenderer::renderParticle(
|
||||
Particle& particle, const Camera& camera, bool backlight
|
||||
) {
|
||||
const auto& right = camera.right;
|
||||
const auto& up = camera.up;
|
||||
auto& emitter = *particle.emitter;
|
||||
auto& preset = emitter.preset;
|
||||
float scale = 1.0f + ((particle.random ^ 2628172) % 1000) *
|
||||
0.001f * preset.sizeSpread;
|
||||
|
||||
glm::vec4 light(1, 1, 1, 0);
|
||||
if (preset.lighting) {
|
||||
light = calc_lights(particle, preset, backlight, scale, chunks);
|
||||
}
|
||||
|
||||
glm::vec3 localRight = right;
|
||||
glm::vec3 localUp = preset.globalUpVector ? glm::vec3(0, 1, 0) : up;
|
||||
float angle = particle.angle;
|
||||
if (glm::abs(angle) >= 0.005f) {
|
||||
glm::vec3 rotatedRight(glm::cos(angle), -glm::sin(angle), 0.0f);
|
||||
glm::vec3 rotatedUp(glm::sin(angle), glm::cos(angle), 0.0f);
|
||||
|
||||
localRight = right * rotatedRight.x + localUp * rotatedRight.y +
|
||||
camera.front * rotatedRight.z;
|
||||
localUp = right * rotatedUp.x + localUp * rotatedUp.y +
|
||||
camera.front * rotatedUp.z;
|
||||
}
|
||||
batch->quad(
|
||||
particle.position,
|
||||
localRight,
|
||||
localUp,
|
||||
-camera.front,
|
||||
preset.size * scale,
|
||||
light,
|
||||
glm::vec3(1.0f),
|
||||
particle.region,
|
||||
preset.lighting ? 0.0f : 1.0f
|
||||
);
|
||||
}
|
||||
|
||||
void ParticlesRenderer::update(const Camera& camera, float delta) {
|
||||
updateParticles(delta);
|
||||
|
||||
auto iter = emitters.begin();
|
||||
while (iter != emitters.end()) {
|
||||
|
|
@ -178,6 +184,33 @@ void ParticlesRenderer::render(const Camera& camera, float delta) {
|
|||
}
|
||||
}
|
||||
|
||||
void ParticlesRenderer::render(const Camera& camera) {
|
||||
aliveEmitters = emitters.size();
|
||||
visibleParticles = 0;
|
||||
|
||||
bool backlight = settings.backlight.get();
|
||||
|
||||
batch->begin();
|
||||
for (auto& [texture, vec] : particles) {
|
||||
batch->setTexture(texture);
|
||||
|
||||
auto iter = vec.begin();
|
||||
while (iter != vec.end()) {
|
||||
auto& particle = *iter;
|
||||
|
||||
renderParticle(particle, camera, backlight);
|
||||
|
||||
if (particle.lifetime <= 0.0f) {
|
||||
iter = vec.erase(iter);
|
||||
particle.emitter->refCount--;
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
batch->flush();
|
||||
}
|
||||
|
||||
Emitter* ParticlesRenderer::getEmitter(u64id_t id) const {
|
||||
const auto& found = emitters.find(id);
|
||||
if (found == emitters.end()) {
|
||||
|
|
|
|||
|
|
@ -18,24 +18,28 @@ struct GraphicsSettings;
|
|||
class ParticlesRenderer {
|
||||
const Chunks& chunks;
|
||||
const Assets& assets;
|
||||
const GraphicsSettings* settings;
|
||||
const GraphicsSettings& settings;
|
||||
std::unordered_map<const Texture*, std::vector<Particle>> particles;
|
||||
std::unique_ptr<MainBatch> batch;
|
||||
|
||||
std::unordered_map<u64id_t, std::unique_ptr<Emitter>> emitters;
|
||||
u64id_t nextEmitter = 1;
|
||||
|
||||
void renderParticles(const Camera& camera, float delta);
|
||||
void renderParticle(
|
||||
Particle& particle, const Camera& camera, bool backlight
|
||||
);
|
||||
void updateParticles(float delta);
|
||||
public:
|
||||
ParticlesRenderer(
|
||||
const Assets& assets,
|
||||
const Level& level,
|
||||
const Chunks& chunks,
|
||||
const GraphicsSettings* settings
|
||||
const GraphicsSettings& settings
|
||||
);
|
||||
~ParticlesRenderer();
|
||||
|
||||
void render(const Camera& camera, float delta);
|
||||
void update(const Camera& camera, float delta);
|
||||
void render(const Camera& camera);
|
||||
|
||||
u64id_t add(std::unique_ptr<Emitter> emitter);
|
||||
|
||||
|
|
|
|||
|
|
@ -90,11 +90,13 @@ static UVRegion calc_uv(
|
|||
return {u1, v1, u1 + m * scale, v1 + FACE_SIZE.y * scale};
|
||||
}
|
||||
|
||||
void PrecipitationRenderer::render(
|
||||
const Camera& camera, float delta, const WeatherPreset& weather
|
||||
) {
|
||||
void PrecipitationRenderer::update(float delta) {
|
||||
timer += delta;
|
||||
}
|
||||
|
||||
void PrecipitationRenderer::render(
|
||||
const Camera& camera, const WeatherPreset& weather
|
||||
) {
|
||||
const int radius = 6;
|
||||
const int depth = 12;
|
||||
|
||||
|
|
@ -149,7 +151,7 @@ void PrecipitationRenderer::render(
|
|||
glm::cross(glm::vec3(0, 1, 0), face.right),
|
||||
FACE_SIZE,
|
||||
light_at(chunks, pos.x, y, pos.z),
|
||||
glm::vec3(3.0f),
|
||||
glm::vec3(1.0f),
|
||||
calc_uv(pos, face.right, timer, weather)
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,5 +27,7 @@ public:
|
|||
|
||||
~PrecipitationRenderer();
|
||||
|
||||
void render(const Camera& camera, float delta, const WeatherPreset& weather);
|
||||
void update(float delta);
|
||||
|
||||
void render(const Camera& camera, const WeatherPreset& weather);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ Skybox::Skybox(uint size, Shader& shader)
|
|||
shader(shader),
|
||||
batch3d(std::make_unique<Batch3D>(4096))
|
||||
{
|
||||
auto cubemap = std::make_unique<Cubemap>(size, size, ImageFormat::rgb888);
|
||||
auto cubemap = std::make_unique<Cubemap>(size, size, ImageFormat::RGB888);
|
||||
|
||||
uint fboid;
|
||||
glGenFramebuffers(1, &fboid);
|
||||
|
|
@ -170,7 +170,7 @@ void Skybox::draw(
|
|||
drawStars(angle, opacity);
|
||||
}
|
||||
|
||||
void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality) {
|
||||
void Skybox::refresh(const DrawContext& pctx, float t, float mie, const glm::vec3& tint, const glm::vec3& hightlight, uint quality) {
|
||||
frameid++;
|
||||
float dayTime = t;
|
||||
DrawContext ctx = pctx.sub();
|
||||
|
|
@ -201,11 +201,13 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality)
|
|||
|
||||
shader.uniform1i("u_quality", quality);
|
||||
shader.uniform1f("u_mie", mie);
|
||||
shader.uniform3f("u_tint", tint);
|
||||
shader.uniform3f("u_hightlight", hightlight);
|
||||
shader.uniform1f("u_fog", mie - 1.0f);
|
||||
shader.uniform3f("u_lightDir", lightDir);
|
||||
shader.uniform1f("u_dayTime", dayTime);
|
||||
|
||||
if (glm::abs(mie-prevMie) + glm::abs(t-prevT) >= 0.01) {
|
||||
if (glm::abs(mie-prevMie) + glm::abs(t-prevT) + glm::abs(prevHighlight.r - hightlight.r) >= 0.01) {
|
||||
for (uint face = 0; face < 6; face++) {
|
||||
refreshFace(face, cubemap);
|
||||
}
|
||||
|
|
@ -215,6 +217,7 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, uint quality)
|
|||
}
|
||||
prevMie = mie;
|
||||
prevT = t;
|
||||
prevHighlight = hightlight;
|
||||
|
||||
cubemap->unbind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ class Skybox {
|
|||
float prevMie = -1.0f;
|
||||
float prevT = -1.0f;
|
||||
float sunAltitude = 45.0f;
|
||||
glm::vec3 prevHighlight {1.0f};
|
||||
glm::mat4 rotation;
|
||||
|
||||
void drawStars(float angle, float opacity);
|
||||
|
|
@ -68,7 +69,7 @@ public:
|
|||
float fog
|
||||
);
|
||||
|
||||
void refresh(const DrawContext& pctx, float t, float mie, uint quality);
|
||||
void refresh(const DrawContext& pctx, float t, float mie, const glm::vec3& tint, const glm::vec3& hightlight, uint quality);
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
bool isReady() const {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,5 @@
|
|||
#include "WorldRenderer.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "assets/assets_util.hpp"
|
||||
#include "content/Content.hpp"
|
||||
|
|
@ -18,7 +11,6 @@
|
|||
#include "items/ItemDef.hpp"
|
||||
#include "items/ItemStack.hpp"
|
||||
#include "logic/PlayerController.hpp"
|
||||
#include "logic/scripting/scripting_hud.hpp"
|
||||
#include "maths/FrustumCulling.hpp"
|
||||
#include "maths/voxmaths.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
|
|
@ -58,6 +50,13 @@
|
|||
#include "Skybox.hpp"
|
||||
#include "Emitter.hpp"
|
||||
#include "TextNote.hpp"
|
||||
#include "CloudsRenderer.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <memory>
|
||||
|
||||
using namespace advanced_pipeline;
|
||||
|
||||
|
|
@ -81,44 +80,44 @@ WorldRenderer::WorldRenderer(
|
|||
MODEL_BATCH_CAPACITY, assets, *player.chunks, engine.getSettings()
|
||||
)),
|
||||
chunksRenderer(std::make_unique<ChunksRenderer>(
|
||||
&level,
|
||||
level,
|
||||
*player.chunks,
|
||||
assets,
|
||||
*frustumCulling,
|
||||
frontend.getContentGfxCache(),
|
||||
engine.getSettings()
|
||||
)),
|
||||
precipitation(std::make_unique<PrecipitationRenderer>(
|
||||
assets, level, *player.chunks, &engine.getSettings().graphics
|
||||
)),
|
||||
particles(std::make_unique<ParticlesRenderer>(
|
||||
assets, level, *player.chunks, &engine.getSettings().graphics
|
||||
assets, level, *player.chunks, engine.getSettings().graphics
|
||||
)),
|
||||
texts(std::make_unique<TextsRenderer>(*batch3d, assets, *frustumCulling)),
|
||||
blockWraps(
|
||||
std::make_unique<BlockWrapsRenderer>(assets, level, *player.chunks)
|
||||
),
|
||||
precipitation(std::make_unique<PrecipitationRenderer>(
|
||||
assets, level, *player.chunks, &engine.getSettings().graphics
|
||||
)) {
|
||||
) {
|
||||
auto& settings = engine.getSettings();
|
||||
level.events->listen(
|
||||
LevelEventType::CHUNK_HIDDEN,
|
||||
[this](LevelEventType, Chunk* chunk) { chunksRenderer->unload(chunk); }
|
||||
);
|
||||
auto assets = engine.getAssets();
|
||||
skybox = std::make_unique<Skybox>(
|
||||
settings.graphics.skyboxResolution.get(),
|
||||
assets->require<Shader>("skybox_gen")
|
||||
assets.require<Shader>("skybox_gen")
|
||||
);
|
||||
|
||||
const auto& content = level.content;
|
||||
skeletons = std::make_unique<NamedSkeletons>();
|
||||
const auto& skeletonConfig = assets->require<rigging::SkeletonConfig>(
|
||||
const auto& skeletonConfig = assets.require<rigging::SkeletonConfig>(
|
||||
content.getDefaults()["hand-skeleton"].asString()
|
||||
);
|
||||
hands = std::make_unique<HandsRenderer>(
|
||||
*assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
|
||||
assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
|
||||
);
|
||||
shadowMapping = std::make_unique<Shadows>(level);
|
||||
debugLines = std::make_unique<DebugLinesRenderer>(level);
|
||||
cloudsRenderer = std::make_unique<CloudsRenderer>();
|
||||
}
|
||||
|
||||
WorldRenderer::~WorldRenderer() = default;
|
||||
|
|
@ -127,6 +126,7 @@ static void setup_weather(Shader& shader, const Weather& weather) {
|
|||
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
|
||||
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
|
||||
shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
|
||||
shader.uniform3f("u_minSkyLight", weather.minSkyLight());
|
||||
}
|
||||
|
||||
static void setup_camera(Shader& shader, const Camera& camera) {
|
||||
|
|
@ -179,8 +179,6 @@ void WorldRenderer::renderOpaque(
|
|||
const DrawContext& ctx,
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
float delta,
|
||||
bool pause,
|
||||
bool hudVisible
|
||||
) {
|
||||
texts->render(ctx, camera, settings, hudVisible, false);
|
||||
|
|
@ -203,15 +201,14 @@ void WorldRenderer::renderOpaque(
|
|||
assets,
|
||||
*modelBatch,
|
||||
culling ? frustumCulling.get() : nullptr,
|
||||
delta,
|
||||
pause,
|
||||
player.currentCamera.get() == player.fpCamera.get() ? player.getEntity()
|
||||
: 0
|
||||
);
|
||||
modelBatch->render();
|
||||
particles->render(camera, delta * !pause);
|
||||
particles->render(camera);
|
||||
|
||||
auto& shader = assets.require<Shader>("main");
|
||||
auto& cloudsShader = assets.require<Shader>("clouds");
|
||||
auto& linesShader = assets.require<Shader>("lines");
|
||||
|
||||
setupWorldShader(shader, camera, settings, fogFactor);
|
||||
|
|
@ -219,12 +216,16 @@ void WorldRenderer::renderOpaque(
|
|||
chunksRenderer->drawChunks(camera, shader);
|
||||
blockWraps->draw(ctx, player);
|
||||
|
||||
if (hudVisible) {
|
||||
renderLines(camera, linesShader, ctx);
|
||||
int cloudsQuality = settings.graphics.cloudsQuality.get();
|
||||
if (cloudsQuality > 0) {
|
||||
setupWorldShader(cloudsShader, camera, settings, fogFactor);
|
||||
cloudsRenderer->draw(
|
||||
cloudsShader, weather, timer, fogFactor, camera, cloudsQuality
|
||||
);
|
||||
}
|
||||
|
||||
if (!pause) {
|
||||
scripting::on_frontend_render();
|
||||
if (hudVisible) {
|
||||
renderLines(camera, linesShader, ctx);
|
||||
}
|
||||
skybox->unbind();
|
||||
}
|
||||
|
|
@ -275,45 +276,17 @@ void WorldRenderer::renderLines(
|
|||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::renderFrame(
|
||||
const DrawContext& pctx,
|
||||
Camera& camera,
|
||||
bool hudVisible,
|
||||
bool pause,
|
||||
float uiDelta,
|
||||
PostProcessing& postProcessing
|
||||
) {
|
||||
// TODO: REFACTOR WHOLE RENDER ENGINE
|
||||
void WorldRenderer::refreshSettings(Shader** shaders) {
|
||||
const auto& graphics = engine.getSettings().graphics;
|
||||
gbufferPipeline = graphics.advancedRender.get();
|
||||
|
||||
auto projView = camera.getProjView();
|
||||
|
||||
float delta = uiDelta * !pause;
|
||||
timer += delta;
|
||||
weather.update(delta);
|
||||
|
||||
auto world = level.getWorld();
|
||||
|
||||
const auto& vp = pctx.getViewport();
|
||||
camera.setAspectRatio(vp.x / static_cast<float>(vp.y));
|
||||
|
||||
auto& mainShader = assets.require<Shader>("main");
|
||||
auto& entityShader = assets.require<Shader>("entity");
|
||||
auto& translucentShader = assets.require<Shader>("translucent");
|
||||
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
|
||||
const auto& settings = engine.getSettings();
|
||||
|
||||
Shader* affectedShaders[] {
|
||||
&mainShader, &entityShader, &translucentShader, &deferredShader
|
||||
};
|
||||
|
||||
gbufferPipeline = settings.graphics.advancedRender.get();
|
||||
int shadowsQuality = settings.graphics.shadowsQuality.get() * gbufferPipeline;
|
||||
int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline;
|
||||
shadowMapping->setQuality(shadowsQuality);
|
||||
|
||||
|
||||
CompileTimeShaderSettings currentSettings {
|
||||
gbufferPipeline,
|
||||
shadowsQuality != 0,
|
||||
settings.graphics.ssao.get() && gbufferPipeline
|
||||
graphics.ssao.get() && gbufferPipeline
|
||||
};
|
||||
if (
|
||||
prevCTShaderSettings.advancedRender != currentSettings.advancedRender ||
|
||||
|
|
@ -325,11 +298,50 @@ void WorldRenderer::renderFrame(
|
|||
if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO");
|
||||
if (currentSettings.advancedRender) defines.emplace_back("ADVANCED_RENDER");
|
||||
|
||||
for (auto shader : affectedShaders) {
|
||||
shader->recompile(defines);
|
||||
for (size_t i = 0; shaders[i]; i++) {
|
||||
shaders[i]->recompile(defines);
|
||||
}
|
||||
prevCTShaderSettings = currentSettings;
|
||||
}
|
||||
}
|
||||
|
||||
void WorldRenderer::update(const Camera& camera, float delta) {
|
||||
timer += delta;
|
||||
weather.update(delta);
|
||||
precipitation->update(delta);
|
||||
particles->update(camera, delta);
|
||||
}
|
||||
|
||||
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<float>(vp.y));
|
||||
|
||||
auto& mainShader = assets.require<Shader>("main");
|
||||
auto& entityShader = assets.require<Shader>("entity");
|
||||
auto& cloudsShader = assets.require<Shader>("clouds");
|
||||
auto& translucentShader = assets.require<Shader>("translucent");
|
||||
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
|
||||
|
||||
const auto& settings = engine.getSettings();
|
||||
|
||||
Shader* affectedShaders[] {
|
||||
&mainShader,
|
||||
&entityShader,
|
||||
&cloudsShader,
|
||||
&translucentShader,
|
||||
&deferredShader,
|
||||
nullptr
|
||||
};
|
||||
|
||||
refreshSettings(affectedShaders);
|
||||
|
||||
const auto& worldInfo = world->getInfo();
|
||||
|
||||
|
|
@ -337,7 +349,15 @@ void WorldRenderer::renderFrame(
|
|||
clouds = glm::max(worldInfo.fog, clouds);
|
||||
float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f;
|
||||
|
||||
skybox->refresh(pctx, worldInfo.daytime, mie, 4);
|
||||
float random = rand() / static_cast<float>(RAND_MAX);
|
||||
skybox->refresh(
|
||||
pctx,
|
||||
worldInfo.daytime,
|
||||
mie,
|
||||
weather.skyTint(),
|
||||
weather.highlight * random,
|
||||
4
|
||||
);
|
||||
|
||||
chunksRenderer->update();
|
||||
|
||||
|
|
@ -356,7 +376,7 @@ void WorldRenderer::renderFrame(
|
|||
DrawContext ctx = wctx.sub();
|
||||
ctx.setDepthTest(true);
|
||||
ctx.setCullFace(true);
|
||||
renderOpaque(ctx, camera, settings, uiDelta, pause, hudVisible);
|
||||
renderOpaque(ctx, camera, settings, hudVisible);
|
||||
}
|
||||
texts->render(pctx, camera, settings, hudVisible, true);
|
||||
}
|
||||
|
|
@ -375,7 +395,7 @@ void WorldRenderer::renderFrame(
|
|||
if (gbufferPipeline) {
|
||||
postProcessing.bindDepthBuffer();
|
||||
} else {
|
||||
postProcessing.getFramebuffer()->bind();
|
||||
ctx.setFramebuffer(postProcessing.getFramebuffer());
|
||||
}
|
||||
|
||||
// Background sky plane
|
||||
|
|
@ -391,15 +411,14 @@ void WorldRenderer::renderFrame(
|
|||
linesShader.uniformMatrix("u_projview", projView);
|
||||
lineBatch->flush();
|
||||
|
||||
skybox->bind();
|
||||
// Translucent blocks
|
||||
{
|
||||
auto sctx = ctx.sub();
|
||||
sctx.setCullFace(true);
|
||||
skybox->bind();
|
||||
translucentShader.use();
|
||||
setupWorldShader(translucentShader, camera, settings, fogFactor);
|
||||
chunksRenderer->drawSortedMeshes(camera, translucentShader);
|
||||
skybox->unbind();
|
||||
}
|
||||
|
||||
// Weather effects
|
||||
|
|
@ -415,9 +434,10 @@ void WorldRenderer::renderFrame(
|
|||
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, pause ? 0.0f : delta, *weather);
|
||||
precipitation->render(camera, *weather);
|
||||
}
|
||||
}
|
||||
skybox->unbind();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
|
@ -434,7 +454,7 @@ void WorldRenderer::renderFrame(
|
|||
hudcam.setFov(0.9f);
|
||||
hudcam.position = {};
|
||||
|
||||
hands->renderHands(camera, delta);
|
||||
hands->render(camera);
|
||||
|
||||
display::clearDepth();
|
||||
setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);
|
||||
|
|
@ -454,43 +474,45 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
|||
int y = std::floor(player.currentCamera->position.y);
|
||||
int z = std::floor(player.currentCamera->position.z);
|
||||
auto block = player.chunks->get(x, y, z);
|
||||
if (block && block->id) {
|
||||
const auto& def = level.content.getIndices()->blocks.require(block->id);
|
||||
if (def.overlayTexture.empty()) {
|
||||
return;
|
||||
}
|
||||
auto textureRegion = util::get_texture_region(
|
||||
assets, def.overlayTexture, "blocks:notfound"
|
||||
);
|
||||
DrawContext ctx = wctx.sub();
|
||||
ctx.setDepthTest(false);
|
||||
ctx.setCullFace(false);
|
||||
|
||||
auto& shader = assets.require<Shader>("ui3d");
|
||||
shader.use();
|
||||
batch3d->begin();
|
||||
shader.uniformMatrix("u_projview", glm::mat4(1.0f));
|
||||
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||
auto light = player.chunks->getLight(x, y, z);
|
||||
float s = Lightmap::extract(light, 3) / 15.0f;
|
||||
glm::vec4 tint(
|
||||
glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s),
|
||||
glm::min(1.0f, Lightmap::extract(light, 1) / 15.0f + s),
|
||||
glm::min(1.0f, Lightmap::extract(light, 2) / 15.0f + s),
|
||||
1.0f
|
||||
);
|
||||
batch3d->texture(textureRegion.texture);
|
||||
batch3d->sprite(
|
||||
glm::vec3(),
|
||||
glm::vec3(0, 1, 0),
|
||||
glm::vec3(1, 0, 0),
|
||||
2,
|
||||
2,
|
||||
textureRegion.region,
|
||||
tint
|
||||
);
|
||||
batch3d->flush();
|
||||
|
||||
if (block == nullptr || block->id == BLOCK_AIR || block->id == BLOCK_VOID) {
|
||||
return;
|
||||
}
|
||||
const auto& def = level.content.getIndices()->blocks.require(block->id);
|
||||
if (def.overlayTexture.empty()) {
|
||||
return;
|
||||
}
|
||||
auto textureRegion = util::get_texture_region(
|
||||
assets, def.overlayTexture, "blocks:notfound"
|
||||
);
|
||||
DrawContext ctx = wctx.sub();
|
||||
ctx.setDepthTest(false);
|
||||
ctx.setCullFace(false);
|
||||
|
||||
auto& shader = assets.require<Shader>("ui3d");
|
||||
shader.use();
|
||||
batch3d->begin();
|
||||
shader.uniformMatrix("u_projview", glm::mat4(1.0f));
|
||||
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||
auto light = player.chunks->getLight(x, y, z);
|
||||
float s = Lightmap::extract(light, 3) / 15.0f;
|
||||
glm::vec4 tint(
|
||||
glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s),
|
||||
glm::min(1.0f, Lightmap::extract(light, 1) / 15.0f + s),
|
||||
glm::min(1.0f, Lightmap::extract(light, 2) / 15.0f + s),
|
||||
1.0f
|
||||
);
|
||||
batch3d->texture(textureRegion.texture);
|
||||
batch3d->sprite(
|
||||
glm::vec3(),
|
||||
glm::vec3(0, 1, 0),
|
||||
glm::vec3(1, 0, 0),
|
||||
2,
|
||||
2,
|
||||
textureRegion.region,
|
||||
tint
|
||||
);
|
||||
batch3d->flush();
|
||||
}
|
||||
|
||||
void WorldRenderer::clear() {
|
||||
|
|
|
|||
|
|
@ -1,40 +1,40 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "commons.hpp"
|
||||
#include "typedefs.hpp"
|
||||
|
||||
#include "presets/WeatherPreset.hpp"
|
||||
#include "world/Weather.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
|
||||
class Level;
|
||||
class Player;
|
||||
class Camera;
|
||||
class Batch3D;
|
||||
class LineBatch;
|
||||
class ChunksRenderer;
|
||||
class ParticlesRenderer;
|
||||
class BlockWrapsRenderer;
|
||||
class PrecipitationRenderer;
|
||||
class HandsRenderer;
|
||||
class NamedSkeletons;
|
||||
class TextsRenderer;
|
||||
class Shader;
|
||||
class Frustum;
|
||||
class Engine;
|
||||
class LevelFrontend;
|
||||
class Skybox;
|
||||
class PostProcessing;
|
||||
class DrawContext;
|
||||
class ModelBatch;
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
class Assets;
|
||||
class Shadows;
|
||||
class GBuffer;
|
||||
class Batch3D;
|
||||
class BlockWrapsRenderer;
|
||||
class ChunksRenderer;
|
||||
class DebugLinesRenderer;
|
||||
class DrawContext;
|
||||
class Engine;
|
||||
class Frustum;
|
||||
class HandsRenderer;
|
||||
class Level;
|
||||
class LevelFrontend;
|
||||
class LineBatch;
|
||||
class ModelBatch;
|
||||
class NamedSkeletons;
|
||||
class ParticlesRenderer;
|
||||
class Player;
|
||||
class PostProcessing;
|
||||
class PrecipitationRenderer;
|
||||
class Shader;
|
||||
class Shadows;
|
||||
class Skybox;
|
||||
class TextsRenderer;
|
||||
class CloudsRenderer;
|
||||
struct EngineSettings;
|
||||
|
||||
struct CompileTimeShaderSettings {
|
||||
|
|
@ -57,6 +57,8 @@ class WorldRenderer {
|
|||
std::unique_ptr<Skybox> skybox;
|
||||
std::unique_ptr<Shadows> shadowMapping;
|
||||
std::unique_ptr<DebugLinesRenderer> debugLines;
|
||||
std::unique_ptr<PrecipitationRenderer> precipitation;
|
||||
std::unique_ptr<CloudsRenderer> cloudsRenderer;
|
||||
Weather weather {};
|
||||
|
||||
float timer = 0.0f;
|
||||
|
|
@ -93,15 +95,14 @@ class WorldRenderer {
|
|||
const DrawContext& context,
|
||||
const Camera& camera,
|
||||
const EngineSettings& settings,
|
||||
float delta,
|
||||
bool pause,
|
||||
bool hudVisible
|
||||
);
|
||||
|
||||
void refreshSettings(Shader** shaders);
|
||||
public:
|
||||
std::unique_ptr<ParticlesRenderer> particles;
|
||||
std::unique_ptr<TextsRenderer> texts;
|
||||
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
||||
std::unique_ptr<PrecipitationRenderer> precipitation;
|
||||
std::unique_ptr<NamedSkeletons> skeletons;
|
||||
|
||||
static bool showChunkBorders;
|
||||
|
|
@ -110,12 +111,12 @@ public:
|
|||
WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
|
||||
~WorldRenderer();
|
||||
|
||||
void update(const Camera& camera, float delta);
|
||||
|
||||
void renderFrame(
|
||||
const DrawContext& context,
|
||||
Camera& camera,
|
||||
bool hudVisible,
|
||||
bool pause,
|
||||
float delta,
|
||||
PostProcessing& postProcessing
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "constants.hpp"
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
/// @brief Chunk mesh vertex format
|
||||
struct ChunkVertex {
|
||||
glm::vec3 position;
|
||||
|
|
@ -49,5 +50,14 @@ struct ChunkMeshData {
|
|||
struct ChunkMesh {
|
||||
std::unique_ptr<Mesh<ChunkVertex>> mesh;
|
||||
SortingMeshData sortingMeshData;
|
||||
std::unique_ptr<Mesh<ChunkVertex> > sortedMesh = nullptr;
|
||||
std::unique_ptr<Mesh<ChunkVertex> > sortedMesh;
|
||||
};
|
||||
|
||||
inline constexpr int VOXELS_BUFFER_PADDING = 2;
|
||||
|
||||
template<int, int, int> class StaticVoxelsVolume;
|
||||
|
||||
using VoxelsRenderVolume = StaticVoxelsVolume<
|
||||
CHUNK_W + VOXELS_BUFFER_PADDING * 2,
|
||||
CHUNK_H,
|
||||
CHUNK_D + VOXELS_BUFFER_PADDING * 2>;
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
#include "GUI.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "elements/Label.hpp"
|
||||
#include "elements/Menu.hpp"
|
||||
#include "elements/Panel.hpp"
|
||||
#include "elements/Frame.hpp"
|
||||
#include "elements/UINode.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "frontend/UiDocument.hpp"
|
||||
|
|
@ -26,13 +27,15 @@
|
|||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
static debug::Logger logger("gui");
|
||||
|
||||
using namespace gui;
|
||||
|
||||
GUI::GUI(Engine& engine)
|
||||
: engine(engine),
|
||||
input(engine.getInput()),
|
||||
batch2D(std::make_unique<Batch2D>(1024)),
|
||||
container(std::make_shared<Container>(*this, glm::vec2(1000))) {
|
||||
container(std::make_shared<Frame>(*this, CORE_MAIN, "")) {
|
||||
container->setId("root");
|
||||
uicamera =
|
||||
std::make_unique<Camera>(glm::vec3(), engine.getWindow().getSize().y);
|
||||
|
|
@ -59,10 +62,12 @@ GUI::GUI(Engine& engine)
|
|||
|
||||
rootDocument = std::make_unique<UiDocument>(
|
||||
"core:root",
|
||||
uidocscript {},
|
||||
UiDocScript {},
|
||||
std::dynamic_pointer_cast<gui::UINode>(container),
|
||||
nullptr
|
||||
);
|
||||
addFrame(container);
|
||||
activeFrame = container;
|
||||
}
|
||||
|
||||
GUI::~GUI() = default;
|
||||
|
|
@ -117,12 +122,19 @@ void GUI::updateTooltip(float delta) {
|
|||
}
|
||||
|
||||
/// @brief Mouse related input and logic handling
|
||||
void GUI::actMouse(float delta, const CursorState& cursor) {
|
||||
void GUI::actMouse(Frame& frame, float delta, const CursorState& cursor) {
|
||||
float mouseDelta = glm::length(cursor.delta);
|
||||
doubleClicked = false;
|
||||
doubleClickTimer += delta + mouseDelta * 0.1f;
|
||||
|
||||
auto hover = container->getAt(cursor.pos);
|
||||
auto cursorPos = cursor.pos;
|
||||
if (cursorLocator) {
|
||||
cursorPos = cursorLocator();
|
||||
if (activeFrame) {
|
||||
cursorPos.y += activeFrame->getPos().y;
|
||||
}
|
||||
}
|
||||
auto hover = frame.getAt(cursorPos);
|
||||
if (this->hover && this->hover != hover) {
|
||||
this->hover->setMouseEnter(false);
|
||||
}
|
||||
|
|
@ -157,13 +169,13 @@ void GUI::actMouse(float delta, const CursorState& cursor) {
|
|||
}
|
||||
|
||||
for (auto it = mouseOver.begin(); it != mouseOver.end(); ) {
|
||||
auto node = it->lock();
|
||||
if (node) {
|
||||
if (node->isInside(cursor.pos)) {
|
||||
auto mouseOverNode = it->lock();
|
||||
if (mouseOverNode) {
|
||||
if (mouseOverNode->isInside(cursorPos)) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
node->setMouseOver(false);
|
||||
mouseOverNode->setMouseOver(false);
|
||||
}
|
||||
it = mouseOver.erase(it);
|
||||
}
|
||||
|
|
@ -172,10 +184,10 @@ void GUI::actMouse(float delta, const CursorState& cursor) {
|
|||
if (pressed == nullptr && this->hover) {
|
||||
pressed = hover;
|
||||
if (doubleClickTimer < doubleClickDelay) {
|
||||
pressed->doubleClick(cursor.pos.x, cursor.pos.y);
|
||||
pressed->doubleClick(cursorPos.x, cursorPos.y);
|
||||
doubleClicked = true;
|
||||
} else {
|
||||
pressed->click(cursor.pos.x, cursor.pos.y);
|
||||
pressed->click(cursorPos.x, cursorPos.y);
|
||||
}
|
||||
doubleClickTimer = 0.0f;
|
||||
if (focus && focus != pressed) {
|
||||
|
|
@ -192,7 +204,7 @@ void GUI::actMouse(float delta, const CursorState& cursor) {
|
|||
focus = nullptr;
|
||||
}
|
||||
} else if (!input.clicked(Mousecode::BUTTON_1) && pressed) {
|
||||
pressed->mouseRelease(cursor.pos.x, cursor.pos.y);
|
||||
pressed->mouseRelease(cursorPos.x, cursorPos.y);
|
||||
pressed = nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -232,14 +244,16 @@ void GUI::actFocused() {
|
|||
|
||||
void GUI::act(float delta, const glm::uvec2& vp) {
|
||||
container->setSize(vp);
|
||||
container->act(delta);
|
||||
for (auto& pair : frames) {
|
||||
pair.second->act(delta);
|
||||
}
|
||||
auto prevfocus = focus;
|
||||
|
||||
updateTooltip(delta);
|
||||
|
||||
const auto& cursor = input.getCursor();
|
||||
if (!cursor.locked) {
|
||||
actMouse(delta, cursor);
|
||||
if (!cursor.locked && activeFrame) {
|
||||
actMouse(*activeFrame, delta, cursor);
|
||||
} else {
|
||||
if (hover) {
|
||||
hover->setMouseEnter(false);
|
||||
|
|
@ -262,7 +276,7 @@ void GUI::postAct() {
|
|||
}
|
||||
}
|
||||
|
||||
void GUI::draw(const DrawContext& pctx, const Assets& assets) {
|
||||
void GUI::draw(const DrawContext& pctx, Assets& assets) {
|
||||
auto ctx = pctx.sub(batch2D.get());
|
||||
|
||||
auto& viewport = ctx.getViewport();
|
||||
|
|
@ -284,7 +298,10 @@ void GUI::draw(const DrawContext& pctx, const Assets& assets) {
|
|||
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
||||
|
||||
batch2D->begin();
|
||||
container->draw(ctx, assets);
|
||||
for (auto& [outputTexture, frame] : frames) {
|
||||
frame->updateOutput(assets);
|
||||
frame->draw(ctx, assets);
|
||||
}
|
||||
|
||||
if (hover) {
|
||||
engine.getWindow().setCursor(hover->getCursor());
|
||||
|
|
@ -337,6 +354,30 @@ void GUI::add(std::shared_ptr<UINode> node) {
|
|||
container->add(std::move(node));
|
||||
}
|
||||
|
||||
void GUI::addFrame(std::shared_ptr<Frame> frame) {
|
||||
const auto& id = frame->getFrameId();
|
||||
frames[id] = std::move(frame);
|
||||
}
|
||||
|
||||
void GUI::setActiveFrame(const std::string& id, vec2supplier cursorLocator) {
|
||||
this->cursorLocator = std::move(cursorLocator);
|
||||
if (id.empty()) {
|
||||
activeFrame = nullptr;
|
||||
return;
|
||||
}
|
||||
const auto& found = frames.find(id);
|
||||
if (found == frames.end()) {
|
||||
logger.error() << "attempted to make non-existing frame '" << id
|
||||
<< "' as active";
|
||||
return;
|
||||
}
|
||||
activeFrame = found->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<gui::Frame> GUI::getActiveFrame() const {
|
||||
return activeFrame;
|
||||
}
|
||||
|
||||
void GUI::remove(UINode* node) noexcept {
|
||||
container->remove(node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,13 +61,14 @@ namespace gui {
|
|||
class UINode;
|
||||
class Container;
|
||||
class Menu;
|
||||
class Frame;
|
||||
|
||||
/// @brief The main UI controller
|
||||
class GUI {
|
||||
Engine& engine;
|
||||
Input& input;
|
||||
std::unique_ptr<Batch2D> batch2D;
|
||||
std::shared_ptr<Container> container;
|
||||
std::shared_ptr<Frame> container;
|
||||
std::shared_ptr<UINode> hover;
|
||||
std::shared_ptr<UINode> pressed;
|
||||
std::shared_ptr<UINode> focus;
|
||||
|
|
@ -75,11 +76,14 @@ namespace gui {
|
|||
std::shared_ptr<UiDocument> rootDocument;
|
||||
std::unique_ptr<FontStylesScheme> syntaxColorScheme;
|
||||
std::unordered_map<std::string, std::shared_ptr<UINode>> storage;
|
||||
std::shared_ptr<Frame> activeFrame;
|
||||
|
||||
std::unique_ptr<Camera> uicamera;
|
||||
std::shared_ptr<Menu> menu;
|
||||
std::queue<runnable> postRunnables;
|
||||
std::vector<std::weak_ptr<UINode>> mouseOver;
|
||||
std::unordered_map<std::string, std::shared_ptr<Frame>> frames;
|
||||
vec2supplier cursorLocator;
|
||||
|
||||
float tooltipTimer = 0.0f;
|
||||
float doubleClickTimer = 0.0f;
|
||||
|
|
@ -87,11 +91,12 @@ namespace gui {
|
|||
bool doubleClicked = false;
|
||||
bool debug = false;
|
||||
|
||||
void actMouse(float delta, const CursorState& cursor);
|
||||
void actMouse(Frame& frame, float delta, const CursorState& cursor);
|
||||
void actFocused();
|
||||
void updateTooltip(float delta);
|
||||
void resetTooltip();
|
||||
public:
|
||||
static inline std::string CORE_MAIN = "core:main";
|
||||
static constexpr int CONTEXT_MENU_ZINDEX = 999;
|
||||
|
||||
GUI(Engine& engine);
|
||||
|
|
@ -115,7 +120,7 @@ namespace gui {
|
|||
/// @brief Draw all visible elements on main container
|
||||
/// @param pctx parent graphics context
|
||||
/// @param assets active assets storage
|
||||
void draw(const DrawContext& pctx, const Assets& assets);
|
||||
void draw(const DrawContext& pctx, Assets& assets);
|
||||
|
||||
void postAct();
|
||||
|
||||
|
|
@ -123,6 +128,14 @@ namespace gui {
|
|||
/// @param node UI element
|
||||
void add(std::shared_ptr<UINode> node);
|
||||
|
||||
void addFrame(std::shared_ptr<Frame> frame);
|
||||
|
||||
void setActiveFrame(
|
||||
const std::string& id, vec2supplier cursorLocator = nullptr
|
||||
);
|
||||
|
||||
std::shared_ptr<Frame> getActiveFrame() const;
|
||||
|
||||
/// @brief Remove node from the main container
|
||||
void remove(UINode* node) noexcept;
|
||||
|
||||
|
|
|
|||
64
src/graphics/ui/elements/Frame.cpp
Normal file
64
src/graphics/ui/elements/Frame.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#include "Frame.hpp"
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "graphics/core/Batch2D.hpp"
|
||||
#include "graphics/core/Framebuffer.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "frontend/UiDocument.hpp"
|
||||
#include "window/Window.hpp"
|
||||
#include "../GUI.hpp"
|
||||
|
||||
using namespace gui;
|
||||
|
||||
static inline constexpr int MAX_TEXTURE_SIZE = 2048;
|
||||
|
||||
gui::Frame::Frame(GUI& gui, std::string id, std::string outputTexture)
|
||||
: Container(gui, {}),
|
||||
fbo(nullptr),
|
||||
outputTexture(std::move(outputTexture)) {
|
||||
frameId = std::move(id);
|
||||
}
|
||||
|
||||
gui::Frame::~Frame() = default;
|
||||
|
||||
void gui::Frame::draw(const DrawContext& pctx, const Assets& assets) {
|
||||
if (outputTexture.empty()) {
|
||||
Container::draw(pctx, assets);
|
||||
return;
|
||||
}
|
||||
if (fbo == nullptr) {
|
||||
return;
|
||||
}
|
||||
glm::ivec2 size = getSize();
|
||||
if (size.x <= 0 || size.y <= 0 ||
|
||||
size.x > MAX_TEXTURE_SIZE || size.y > MAX_TEXTURE_SIZE) {
|
||||
return;
|
||||
}
|
||||
// ui uses flipped camera with matrix based on main viewport
|
||||
setPos({0, pctx.getViewport().y - size.y});
|
||||
|
||||
auto ctx = pctx.sub();
|
||||
ctx.setFramebuffer(fbo.get());
|
||||
display::clear();
|
||||
Container::draw(ctx, assets);
|
||||
ctx.getBatch2D()->flush();
|
||||
}
|
||||
|
||||
void gui::Frame::updateOutput(Assets& assets) {
|
||||
if (fbo && (fbo->getWidth() != size.x || fbo->getHeight() != size.y)) {
|
||||
fbo->resize(size.x, size.y);
|
||||
assets.store(fbo->getSharedTexture(), outputTexture);
|
||||
} else if (fbo == nullptr) {
|
||||
fbo = std::make_unique<Framebuffer>(size.x, size.y, true);
|
||||
assets.store(fbo->getSharedTexture(), outputTexture);
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& gui::Frame::getOutputTexture() const {
|
||||
return outputTexture;
|
||||
}
|
||||
|
||||
const std::string& gui::Frame::getFrameId() const {
|
||||
return frameId;
|
||||
}
|
||||
26
src/graphics/ui/elements/Frame.hpp
Normal file
26
src/graphics/ui/elements/Frame.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include "Container.hpp"
|
||||
|
||||
class Framebuffer;
|
||||
class UiDocument;
|
||||
|
||||
namespace gui {
|
||||
class Frame final : public Container {
|
||||
public:
|
||||
Frame(GUI& gui, std::string id, std::string outputTexture);
|
||||
virtual ~Frame();
|
||||
|
||||
void draw(const DrawContext& pctx, const Assets& assets) override;
|
||||
|
||||
void updateOutput(Assets& assets);
|
||||
|
||||
const std::string& getOutputTexture() const;
|
||||
|
||||
const std::string& getFrameId() const;
|
||||
private:
|
||||
std::string frameId;
|
||||
std::unique_ptr<Framebuffer> fbo;
|
||||
std::string outputTexture;
|
||||
};
|
||||
}
|
||||
|
|
@ -78,6 +78,7 @@ void guiutil::alert(
|
|||
}));
|
||||
menu.addPage("<alert>", panel, true);
|
||||
menu.setPage("<alert>");
|
||||
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||
}
|
||||
|
||||
void guiutil::confirm(
|
||||
|
|
@ -155,6 +156,7 @@ void guiutil::confirm(
|
|||
panel->refresh();
|
||||
menu->addPage("<confirm>", container, true);
|
||||
menu->setPage("<confirm>");
|
||||
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||
}
|
||||
|
||||
void guiutil::confirm_with_memo(
|
||||
|
|
@ -201,4 +203,5 @@ void guiutil::confirm_with_memo(
|
|||
panel->refresh();
|
||||
menu->addPage("<confirm>", panel, true);
|
||||
menu->setPage("<confirm>");
|
||||
gui.setActiveFrame(GUI::CORE_MAIN);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -652,7 +652,7 @@ static std::shared_ptr<UINode> read_canvas(
|
|||
size = element.attr("size").asVec2();
|
||||
}
|
||||
auto image =
|
||||
std::make_shared<Canvas>(reader.getGUI(), ImageFormat::rgba8888, size);
|
||||
std::make_shared<Canvas>(reader.getGUI(), ImageFormat::RGBA8888, size);
|
||||
read_uinode(reader, element, *image);
|
||||
return image;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,11 +77,13 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
|
|||
builder.add("chunk-max-vertices", &settings.graphics.chunkMaxVertices);
|
||||
builder.add("chunk-max-vertices-dense", &settings.graphics.chunkMaxVerticesDense);
|
||||
builder.add("chunk-max-renderers", &settings.graphics.chunkMaxRenderers);
|
||||
builder.add("particles-batch-vertices", &settings.graphics.particlesBatchVertices);
|
||||
builder.add("advanced-render", &settings.graphics.advancedRender);
|
||||
builder.add("ssao", &settings.graphics.ssao);
|
||||
builder.add("shadows-quality", &settings.graphics.shadowsQuality);
|
||||
builder.add("dense-render-distance", &settings.graphics.denseRenderDistance);
|
||||
builder.add("soft-lighting", &settings.graphics.softLighting);
|
||||
builder.add("clouds-quality", &settings.graphics.cloudsQuality);
|
||||
|
||||
builder.addSection("ui");
|
||||
builder.add("language", &settings.ui.language);
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ void BlocksController::onBlockInteraction(
|
|||
}
|
||||
|
||||
void BlocksController::listenBlockInteraction(
|
||||
const on_block_interaction& callback
|
||||
const OnBlockInteraction& callback
|
||||
) {
|
||||
blockInteractionCallbacks.push_back(callback);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class ContentIndices;
|
|||
enum class BlockInteraction { step, destruction, placing };
|
||||
|
||||
/// @brief Player argument is nullable
|
||||
using on_block_interaction = std::function<
|
||||
using OnBlockInteraction = std::function<
|
||||
void(Player*, const glm::ivec3&, const Block&, BlockInteraction)>;
|
||||
|
||||
/// BlocksController manages block updates and data (inventories, metadata)
|
||||
|
|
@ -32,7 +32,7 @@ class BlocksController {
|
|||
util::Clock blocksTickClock;
|
||||
util::Clock worldTickClock;
|
||||
FastRandom random {};
|
||||
std::vector<on_block_interaction> blockInteractionCallbacks;
|
||||
std::vector<OnBlockInteraction> blockInteractionCallbacks;
|
||||
uint64_t randomTickId = 0;
|
||||
public:
|
||||
BlocksController(const Level& level, Lighting* lighting);
|
||||
|
|
@ -61,5 +61,5 @@ public:
|
|||
);
|
||||
|
||||
/// @brief Add block interaction callback
|
||||
void listenBlockInteraction(const on_block_interaction& callback);
|
||||
void listenBlockInteraction(const OnBlockInteraction& callback);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -133,9 +133,8 @@ static void check_world(const EnginePaths& paths, const io::path& folder) {
|
|||
}
|
||||
|
||||
static const Content* load_world_content(Engine& engine, const io::path& folder) {
|
||||
auto& paths = engine.getPaths();
|
||||
const auto& paths = engine.getPaths();
|
||||
auto& contentControl = engine.getContentControl();
|
||||
paths.setCurrentWorldFolder(folder);
|
||||
|
||||
check_world(paths, folder);
|
||||
call(engine, [&contentControl]() {
|
||||
|
|
@ -221,9 +220,10 @@ static void confirm(
|
|||
}
|
||||
|
||||
void EngineController::openWorld(const std::string& name, bool confirmConvert) {
|
||||
const auto& paths = engine.getPaths();
|
||||
auto& paths = engine.getPaths();
|
||||
auto& debugSettings = engine.getSettings().debug;
|
||||
auto folder = paths.getWorldsFolder() / name;
|
||||
paths.setCurrentWorldFolder(folder);
|
||||
|
||||
auto content = load_world_content(engine, folder);
|
||||
auto worldFiles = std::make_shared<WorldFiles>(folder, debugSettings);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "debug/Logger.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "engine/EnginePaths.hpp"
|
||||
#include "world/files/WorldFiles.hpp"
|
||||
#include "maths/voxmaths.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
|
|
@ -22,9 +23,10 @@
|
|||
static debug::Logger logger("level-control");
|
||||
|
||||
LevelController::LevelController(
|
||||
Engine* engine, std::unique_ptr<Level> levelPtr, Player* clientPlayer
|
||||
Engine& engine, std::unique_ptr<Level> levelPtr, Player* clientPlayer
|
||||
)
|
||||
: settings(engine->getSettings()),
|
||||
: engine(engine),
|
||||
settings(engine.getSettings()),
|
||||
level(std::move(levelPtr)),
|
||||
chunks(std::make_unique<ChunksController>(*level)),
|
||||
playerTickClock(20, 3) {
|
||||
|
|
@ -147,6 +149,7 @@ void LevelController::saveWorld() {
|
|||
|
||||
void LevelController::onWorldQuit() {
|
||||
scripting::on_world_quit();
|
||||
engine.getPaths().setCurrentWorldFolder("");
|
||||
}
|
||||
|
||||
Level* LevelController::getLevel() {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ struct EngineSettings;
|
|||
|
||||
/// @brief LevelController manages other controllers
|
||||
class LevelController {
|
||||
Engine& engine;
|
||||
EngineSettings& settings;
|
||||
std::unique_ptr<Level> level;
|
||||
// Sub-controllers
|
||||
|
|
@ -24,7 +25,7 @@ class LevelController {
|
|||
public:
|
||||
CallbacksSet<> preQuitCallbacks;
|
||||
|
||||
LevelController(Engine* engine, std::unique_ptr<Level> level, Player* clientPlayer);
|
||||
LevelController(Engine& engine, std::unique_ptr<Level> level, Player* clientPlayer);
|
||||
|
||||
/// @param delta time elapsed since the last update
|
||||
/// @param pause is world and player simulation paused
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
|
||||
#include "BlocksController.hpp"
|
||||
#include "content/Content.hpp"
|
||||
|
|
@ -28,17 +29,19 @@
|
|||
#include "window/input.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
const float INTERACTION_RELOAD = 0.160f;
|
||||
const float STEPS_SPEED = 2.2f;
|
||||
const float CAM_SHAKE_OFFSET = 0.0075f;
|
||||
const float CAM_SHAKE_OFFSET_Y = 0.031f;
|
||||
const float CAM_SHAKE_SPEED = STEPS_SPEED;
|
||||
const float CAM_SHAKE_DELTA_K = 10.0f;
|
||||
const float ZOOM_SPEED = 16.0f;
|
||||
const float CROUCH_ZOOM = 0.9f;
|
||||
const float RUN_ZOOM = 1.1f;
|
||||
const float C_ZOOM = 0.1f;
|
||||
const float CROUCH_SHIFT_Y = -0.2f;
|
||||
namespace {
|
||||
const float INTERACTION_RELOAD = 0.160f;
|
||||
const float STEPS_SPEED = 2.2f;
|
||||
const float CAM_SHAKE_OFFSET = 0.0075f;
|
||||
const float CAM_SHAKE_OFFSET_Y = 0.031f;
|
||||
const float CAM_SHAKE_SPEED = STEPS_SPEED;
|
||||
const float CAM_SHAKE_DELTA_K = 10.0f;
|
||||
const float ZOOM_SPEED = 16.0f;
|
||||
const float CROUCH_ZOOM = 0.9f;
|
||||
const float RUN_ZOOM = 1.1f;
|
||||
const float C_ZOOM = 0.1f;
|
||||
const float CROUCH_SHIFT_Y = -0.2f;
|
||||
}
|
||||
|
||||
CameraControl::CameraControl(Player& player, const CameraSettings& settings)
|
||||
: player(player),
|
||||
|
|
@ -249,17 +252,18 @@ void PlayerController::onFootstep(const Hitbox& hitbox) {
|
|||
}
|
||||
|
||||
void PlayerController::updateFootsteps(float delta) {
|
||||
const float pi = glm::pi<float>();
|
||||
auto hitbox = player.getHitbox();
|
||||
if (hitbox && hitbox->grounded) {
|
||||
const glm::vec3& vel = hitbox->velocity;
|
||||
float f = glm::length(glm::vec2(vel.x, vel.z));
|
||||
stepsTimer += delta * f * STEPS_SPEED;
|
||||
if (stepsTimer >= M_PI) {
|
||||
stepsTimer = fmod(stepsTimer, M_PI);
|
||||
if (stepsTimer >= pi) {
|
||||
stepsTimer = fmod(stepsTimer, pi);
|
||||
onFootstep(*hitbox);
|
||||
}
|
||||
} else {
|
||||
stepsTimer = M_PI;
|
||||
stepsTimer = pi;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ static rigging::Skeleton* get_skeleton(lua::State* L) {
|
|||
|
||||
static int l_get_model(lua::State* L) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
if (skeleton->config == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto& rigConfig = *skeleton->config;
|
||||
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
|
||||
const auto& modelOverride = skeleton->modelOverrides[index];
|
||||
|
|
@ -96,6 +99,9 @@ static int l_set_texture(lua::State* L) {
|
|||
|
||||
static int l_index(lua::State* L) {
|
||||
if (auto skeleton = get_skeleton(L)) {
|
||||
if (skeleton->config == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
if (auto bone = skeleton->config->find(lua::require_string(L, 2))) {
|
||||
return lua::pushinteger(L, bone->getIndex());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include "graphics/ui/elements/TrackBar.hpp"
|
||||
#include "graphics/ui/elements/InlineFrame.hpp"
|
||||
#include "graphics/ui/elements/ModelViewer.hpp"
|
||||
#include "graphics/ui/elements/Frame.hpp"
|
||||
#include "graphics/ui/gui_util.hpp"
|
||||
#include "graphics/ui/markdown.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
|
|
@ -1149,6 +1150,62 @@ static int l_set_syntax_styles(lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int l_create_frame(lua::State* L) {
|
||||
if (engine->isHeadless()) {
|
||||
return 0;
|
||||
}
|
||||
std::string id = lua::require_string(L, 1);
|
||||
std::string outputTexture = lua::require_string(L, 2);
|
||||
auto size = lua::tovec2(L, 3);
|
||||
|
||||
auto& gui = engine->getGUI();
|
||||
auto frame = std::make_shared<gui::Frame>(gui, id, outputTexture);
|
||||
frame->setSize(std::move(size));
|
||||
auto& assets = *engine->getAssets();
|
||||
auto document = std::make_shared<UiDocument>(
|
||||
id, UiDocScript {}, frame, nullptr
|
||||
);
|
||||
assets.store(document, id);
|
||||
gui.addFrame(std::move(frame));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_active_frame(lua::State* L) {
|
||||
if (engine->isHeadless()) {
|
||||
return 0;
|
||||
}
|
||||
std::string id = lua::require_string(L, 1);
|
||||
vec2supplier cursorLocator = nullptr;
|
||||
if (lua::isfunction(L, 2)) {
|
||||
auto lambda = lua::create_lambda(L);
|
||||
cursorLocator = [lambda]() -> glm::vec2 {
|
||||
auto table = lambda({});
|
||||
if (!table.isList()) {
|
||||
throw std::runtime_error("invalid value returned from locator");
|
||||
}
|
||||
glm::vec2 pos {};
|
||||
table.at(0).get(pos.x);
|
||||
table.at(1).get(pos.y);
|
||||
return pos;
|
||||
};
|
||||
}
|
||||
auto& gui = engine->getGUI();
|
||||
gui.setActiveFrame(id, std::move(cursorLocator));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_active_frame(lua::State* L) {
|
||||
if (engine->isHeadless()) {
|
||||
return 0;
|
||||
}
|
||||
auto& gui = engine->getGUI();
|
||||
auto frame = gui.getActiveFrame();
|
||||
if (frame == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
return lua::pushstring(L, frame->getId());
|
||||
}
|
||||
|
||||
const luaL_Reg guilib[] = {
|
||||
{"get_viewport", lua::wrap<l_gui_getviewport>},
|
||||
{"getattr", lua::wrap<l_gui_getattr>},
|
||||
|
|
@ -1162,6 +1219,9 @@ const luaL_Reg guilib[] = {
|
|||
{"alert", lua::wrap<l_gui_alert>},
|
||||
{"load_document", lua::wrap<l_gui_load_document>},
|
||||
{"set_syntax_styles", lua::wrap<l_set_syntax_styles>},
|
||||
{"create_frame", lua::wrap<l_create_frame>},
|
||||
{"set_active_frame", lua::wrap<l_set_active_frame>},
|
||||
{"get_active_frame", lua::wrap<l_get_active_frame>},
|
||||
{"__reindex", lua::wrap<l_gui_reindex>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ static int l_meta_meta_call(lua::State* L) {
|
|||
return newuserdata<LuaCanvas>(
|
||||
L,
|
||||
nullptr,
|
||||
std::make_shared<ImageData>(ImageFormat::rgba8888, size.x, size.y)
|
||||
std::make_shared<ImageData>(ImageFormat::RGBA8888, size.x, size.y)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ static int l_dump(lua::State* L) {
|
|||
io::path file = require_string(L, 2);
|
||||
uint w = heightmap->getWidth();
|
||||
uint h = heightmap->getHeight();
|
||||
ImageData image(ImageFormat::rgb888, w, h);
|
||||
ImageData image(ImageFormat::RGB888, w, h);
|
||||
auto heights = heightmap->getValues();
|
||||
auto raster = image.getData();
|
||||
for (uint y = 0; y < h; y++) {
|
||||
|
|
|
|||
|
|
@ -768,7 +768,7 @@ void scripting::load_layout_script(
|
|||
const std::string& prefix,
|
||||
const io::path& file,
|
||||
const std::string& fileName,
|
||||
uidocscript& script
|
||||
UiDocScript& script
|
||||
) {
|
||||
int env = *senv;
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ struct ItemFuncsSet;
|
|||
struct ItemFuncNamesCache;
|
||||
struct WorldFuncsSet;
|
||||
struct UserComponent;
|
||||
struct uidocscript;
|
||||
struct UiDocScript;
|
||||
class BlocksController;
|
||||
class LevelController;
|
||||
class Entity;
|
||||
|
|
@ -231,7 +231,7 @@ namespace scripting {
|
|||
const std::string& prefix,
|
||||
const io::path& file,
|
||||
const std::string& fileName,
|
||||
uidocscript& script
|
||||
UiDocScript& script
|
||||
);
|
||||
|
||||
/// @brief Finalize lua state. Using scripting after will lead to Lua panic
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
|
|||
if (map.has(COMP_TRANSFORM)) {
|
||||
transform.deserialize(map[COMP_TRANSFORM]);
|
||||
}
|
||||
if (skeleton == nullptr) {
|
||||
if (skeleton == nullptr || skeleton->config == nullptr) {
|
||||
return;
|
||||
}
|
||||
std::string skeletonName = skeleton->config->getName();
|
||||
|
|
@ -341,6 +341,16 @@ void Entities::update(float delta) {
|
|||
}
|
||||
updatePhysics(delta);
|
||||
scripting::on_entities_physics_update(delta);
|
||||
|
||||
auto view = registry->view<Transform, rigging::Skeleton>();
|
||||
for (auto [entity, transform, skeleton] : view.each()) {
|
||||
if (transform.dirty) {
|
||||
transform.refresh();
|
||||
}
|
||||
if (skeleton.interpolation.isEnabled()) {
|
||||
skeleton.interpolation.updateTimer(delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void debug_render_skeleton(
|
||||
|
|
@ -397,6 +407,9 @@ void Entities::renderDebug(
|
|||
ctx.setLineWidth(2);
|
||||
for (auto [entity, transform, skeleton] : view.each()) {
|
||||
auto config = skeleton.config;
|
||||
if (config == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const auto& pos = transform.pos;
|
||||
const auto& size = transform.size;
|
||||
if (frustum && !frustum->isBoxVisible(pos - size, pos + size)) {
|
||||
|
|
@ -412,8 +425,6 @@ void Entities::render(
|
|||
const Assets& assets,
|
||||
ModelBatch& batch,
|
||||
const Frustum* frustum,
|
||||
float delta,
|
||||
bool pause,
|
||||
entityid_t fpsEntity
|
||||
) {
|
||||
auto view = registry->view<EntityId, Transform, rigging::Skeleton>();
|
||||
|
|
@ -421,12 +432,6 @@ void Entities::render(
|
|||
if (eid.uid == fpsEntity) {
|
||||
continue;
|
||||
}
|
||||
if (transform.dirty) {
|
||||
transform.refresh();
|
||||
}
|
||||
if (skeleton.interpolation.isEnabled()) {
|
||||
skeleton.interpolation.updateTimer(delta);
|
||||
}
|
||||
const auto& pos = transform.pos;
|
||||
const auto& size = transform.size;
|
||||
if (frustum && !frustum->isBoxVisible(pos - size, pos + size)) {
|
||||
|
|
@ -434,9 +439,11 @@ void Entities::render(
|
|||
}
|
||||
|
||||
const auto* rigConfig = skeleton.config;
|
||||
rigConfig->render(
|
||||
assets, batch, skeleton, transform.rot, pos, size
|
||||
);
|
||||
if (rigConfig) {
|
||||
rigConfig->render(
|
||||
assets, batch, skeleton, transform.rot, pos, size
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,8 +69,6 @@ public:
|
|||
const Assets& assets,
|
||||
ModelBatch& batch,
|
||||
const Frustum* frustum,
|
||||
float delta,
|
||||
bool pause,
|
||||
entityid_t fpsEntity
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ dv::value Entity::serialize() const {
|
|||
root[COMP_RIGIDBODY] =
|
||||
rigidbody.serialize(def.save.body.velocity, def.save.body.settings);
|
||||
|
||||
if (skeleton != nullptr) {
|
||||
if (skeleton != nullptr && skeleton->config != nullptr) {
|
||||
if (skeleton->config->getName() != def.skeletonName) {
|
||||
root["skeleton-name"] = skeleton->config->getName();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ dv::value WeatherPreset::serialize() const {
|
|||
root["fog_opacity"] = fogOpacity;
|
||||
root["fog_dencity"] = fogDencity;
|
||||
root["fog_curve"] = fogCurve;
|
||||
root["sky_tint"] = dv::to_value(skyTint);
|
||||
root["clouds_tint"] = dv::to_value(cloudsTint);
|
||||
root["min_sky_light"] = dv::to_value(minSkyLight);
|
||||
root["clouds"] = clouds;
|
||||
root["thunder_rate"] = thunderRate;
|
||||
|
||||
|
|
@ -51,6 +54,9 @@ void WeatherPreset::deserialize(const dv::value& src) {
|
|||
src.at("fog_opacity").get(fogOpacity);
|
||||
src.at("fog_dencity").get(fogDencity);
|
||||
src.at("fog_curve").get(fogCurve);
|
||||
dv::get_vec(src, "sky_tint", skyTint);
|
||||
dv::get_vec(src, "clouds_tint", cloudsTint);
|
||||
dv::get_vec(src, "min_sky_light", minSkyLight);
|
||||
src.at("clouds").get(clouds);
|
||||
src.at("thunder_rate").get(thunderRate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,15 @@ struct WeatherPreset : Serializable {
|
|||
/// @brief Weather effects intensity
|
||||
float intensity = 0.0f;
|
||||
|
||||
/// @brief Skybox color multiplier
|
||||
glm::vec3 skyTint {1.0f, 1.0f, 1.0f};
|
||||
|
||||
/// @brief Clouds color multiplier
|
||||
glm::vec3 cloudsTint {1.0f, 1.0f, 1.0f};
|
||||
|
||||
/// @brief Minimal sky-emitted lights (moon light)
|
||||
glm::vec3 minSkyLight {0.2f, 0.25f, 0.33f};
|
||||
|
||||
dv::value serialize() const override;
|
||||
void deserialize(const dv::value& src) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -81,6 +81,8 @@ struct GraphicsSettings {
|
|||
IntegerSetting chunkMaxVerticesDense {800'000, 0, 8'000'000};
|
||||
/// @brief Limit of chunk renderers count
|
||||
IntegerSetting chunkMaxRenderers {6, -4, 32};
|
||||
/// @brief Particles renderer vertices buffer capacity
|
||||
IntegerSetting particlesBatchVertices {4'096, 0, 1'000'000};
|
||||
/// @brief Advanced render pipeline
|
||||
FlagSetting advancedRender {true};
|
||||
/// @brief Screen space ambient occlusion quality
|
||||
|
|
@ -91,6 +93,8 @@ struct GraphicsSettings {
|
|||
IntegerSetting denseRenderDistance {56, 0, 10'000};
|
||||
/// @brief Soft lighting for blocks
|
||||
FlagSetting softLighting {true};
|
||||
/// @brief Clouds quality level
|
||||
IntegerSetting cloudsQuality {2, 0, 2};
|
||||
};
|
||||
|
||||
struct PathfindingSettings {
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ namespace util {
|
|||
size_t countTotal() const {
|
||||
return objects.size();
|
||||
}
|
||||
|
||||
size_t countFree() const {
|
||||
return freeObjects.size();
|
||||
}
|
||||
private:
|
||||
std::vector<std::unique_ptr<void, AlignedDeleter>> objects;
|
||||
std::queue<void*> freeObjects;
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ namespace util {
|
|||
std::condition_variable jobsMutexCondition;
|
||||
std::mutex jobsMutex;
|
||||
std::vector<std::unique_lock<std::mutex>> workersBlocked;
|
||||
consumer<R&> resultConsumer;
|
||||
consumer<R&&> resultConsumer;
|
||||
consumer<T&> onJobFailed = nullptr;
|
||||
runnable onComplete = nullptr;
|
||||
std::atomic<int> busyWorkers = 0;
|
||||
|
|
@ -54,7 +54,7 @@ namespace util {
|
|||
bool standaloneResults = true;
|
||||
bool stopOnFail = true;
|
||||
|
||||
void threadLoop(int index, std::shared_ptr<Worker<T, R>> worker) {
|
||||
void threadLoop(int index, std::unique_ptr<Worker<T, R>> worker) {
|
||||
std::condition_variable variable;
|
||||
std::mutex mutex;
|
||||
bool locked = false;
|
||||
|
|
@ -78,7 +78,7 @@ namespace util {
|
|||
{
|
||||
std::lock_guard<std::mutex> lock(resultsMutex);
|
||||
results.push(ThreadPoolResult<T, R> {
|
||||
job, variable, index, locked, result});
|
||||
job, variable, index, locked, std::move(result)});
|
||||
if (!standaloneResults) {
|
||||
locked = true;
|
||||
}
|
||||
|
|
@ -117,8 +117,8 @@ namespace util {
|
|||
/// unlimited, -2 is half of auto count, -4 is quarter.
|
||||
ThreadPool(
|
||||
std::string name,
|
||||
supplier<std::shared_ptr<Worker<T, R>>> workersSupplier,
|
||||
consumer<R&> resultConsumer,
|
||||
supplier<std::unique_ptr<Worker<T, R>>> workersSupplier,
|
||||
consumer<R&&> resultConsumer,
|
||||
int maxWorkers=UNLIMITED
|
||||
)
|
||||
: logger(std::move(name)), resultConsumer(resultConsumer) {
|
||||
|
|
@ -184,22 +184,28 @@ namespace util {
|
|||
}
|
||||
|
||||
void update() override {
|
||||
pullResults();
|
||||
}
|
||||
|
||||
size_t pullResults(size_t maxResults = -1) {
|
||||
if (!working) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (failed) {
|
||||
throw std::runtime_error("some job failed");
|
||||
}
|
||||
|
||||
bool complete = false;
|
||||
size_t resultsProcessed = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(resultsMutex);
|
||||
while (!results.empty()) {
|
||||
ThreadPoolResult<T, R> entry = results.front();
|
||||
while (!results.empty() && resultsProcessed < maxResults) {
|
||||
ThreadPoolResult<T, R> entry = std::move(results.front());
|
||||
results.pop();
|
||||
|
||||
++resultsProcessed;
|
||||
try {
|
||||
resultConsumer(entry.entry);
|
||||
resultConsumer(std::move(entry.entry));
|
||||
} catch (std::exception& err) {
|
||||
logger.error() << err.what();
|
||||
if (onJobFailed) {
|
||||
|
|
@ -219,7 +225,7 @@ namespace util {
|
|||
}
|
||||
}
|
||||
|
||||
if (onComplete && busyWorkers == 0) {
|
||||
if (onComplete && busyWorkers == 0 && results.empty()) {
|
||||
std::lock_guard<std::mutex> jobsLock(jobsMutex);
|
||||
if (jobs.empty()) {
|
||||
onComplete();
|
||||
|
|
@ -249,9 +255,10 @@ namespace util {
|
|||
if (complete) {
|
||||
terminate();
|
||||
}
|
||||
return resultsProcessed;
|
||||
}
|
||||
|
||||
void enqueueJob(T job) {
|
||||
void enqueueJob(T&& job) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(jobsMutex);
|
||||
jobs.push(std::move(job));
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
using std::chrono::duration_cast;
|
||||
using std::chrono::high_resolution_clock;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::nanoseconds;
|
||||
|
||||
timeutil::Timer::Timer() {
|
||||
start = high_resolution_clock::now();
|
||||
|
|
@ -14,6 +15,11 @@ int64_t timeutil::Timer::stop() {
|
|||
.count();
|
||||
}
|
||||
|
||||
int64_t timeutil::Timer::stopNs() {
|
||||
return duration_cast<nanoseconds>(high_resolution_clock::now() - start)
|
||||
.count();
|
||||
}
|
||||
|
||||
timeutil::ScopeLogTimer::ScopeLogTimer(long long id) : scopeid_(id) {
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace timeutil {
|
|||
public:
|
||||
Timer();
|
||||
int64_t stop();
|
||||
int64_t stopNs();
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ VC_ENUM_METADATA(BlockModelType)
|
|||
{"custom", BlockModelType::CUSTOM},
|
||||
VC_ENUM_END
|
||||
|
||||
enum class CullingMode {
|
||||
enum class CullingMode : uint8_t {
|
||||
DEFAULT,
|
||||
OPTIONAL,
|
||||
DISABLED,
|
||||
|
|
|
|||
|
|
@ -336,26 +336,103 @@ bool Chunks::putChunk(const std::shared_ptr<Chunk>& chunk) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// reduce nesting on next modification
|
||||
// 25.06.2024: not now
|
||||
// 11.11.2024: not now
|
||||
// 12.12.2025: not now
|
||||
void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const {
|
||||
voxel* voxels = volume.getVoxels();
|
||||
light_t* lights = volume.getLights();
|
||||
int x = volume.getX();
|
||||
int y = volume.getY();
|
||||
int z = volume.getZ();
|
||||
static void fill_with_void(
|
||||
voxel* voxels,
|
||||
light_t* lights,
|
||||
const glm::ivec3& pos,
|
||||
const glm::ivec3& size,
|
||||
int cx,
|
||||
int cz
|
||||
) {
|
||||
for (int ly = pos.y; ly < pos.y + size.y; ly++) {
|
||||
for (int lz = std::max(pos.z, cz * CHUNK_D);
|
||||
lz < std::min(pos.z + size.z, (cz + 1) * CHUNK_D);
|
||||
lz++) {
|
||||
for (int lx = std::max(pos.x, cx * CHUNK_W);
|
||||
lx < std::min(pos.x + size.x, (cx + 1) * CHUNK_W);
|
||||
lx++) {
|
||||
uint idx = vox_index(
|
||||
lx - pos.x, ly - pos.y, lz - pos.z, size.x, size.z
|
||||
);
|
||||
voxels[idx].id = BLOCK_VOID;
|
||||
lights[idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int w = volume.getW();
|
||||
int h = std::min<int>(volume.getH(), top);
|
||||
int d = volume.getD();
|
||||
static inline light_t apply_backlight(light_t light) {
|
||||
return Lightmap::combine(
|
||||
std::min(15, Lightmap::extract(light, 0) + 1),
|
||||
std::min(15, Lightmap::extract(light, 1) + 1),
|
||||
std::min(15, Lightmap::extract(light, 2) + 1),
|
||||
std::min(15, static_cast<int>(Lightmap::extract(light, 3)))
|
||||
);
|
||||
}
|
||||
|
||||
int scx = floordiv<CHUNK_W>(x);
|
||||
int scz = floordiv<CHUNK_D>(z);
|
||||
// ugly
|
||||
static inline void sample_chunk(
|
||||
const decltype(ContentIndices::blocks)& defs,
|
||||
const Chunk& chunk,
|
||||
voxel* voxels,
|
||||
light_t* lights,
|
||||
const glm::ivec3& pos,
|
||||
const glm::ivec3& size,
|
||||
int cx,
|
||||
int cz,
|
||||
bool backlight
|
||||
) {
|
||||
const auto cvoxels = chunk.voxels;
|
||||
const auto clights = chunk.lightmap ? chunk.lightmap->getLights() : nullptr;
|
||||
for (int ly = pos.y; ly < pos.y + size.y; ly++) {
|
||||
for (int lz = std::max(pos.z, cz * CHUNK_D);
|
||||
lz < std::min(pos.z + size.z, (cz + 1) * CHUNK_D);
|
||||
lz++) {
|
||||
for (int lx = std::max(pos.x, cx * CHUNK_W);
|
||||
lx < std::min(pos.x + size.x, (cx + 1) * CHUNK_W);
|
||||
lx++) {
|
||||
uint vidx = vox_index(
|
||||
lx - pos.x, ly - pos.y, lz - pos.z, size.x, size.z
|
||||
);
|
||||
uint cidx = vox_index(
|
||||
lx - cx * CHUNK_W,
|
||||
ly,
|
||||
lz - cz * CHUNK_D,
|
||||
CHUNK_W,
|
||||
CHUNK_D
|
||||
);
|
||||
auto& vox = voxels[vidx];
|
||||
vox = cvoxels[cidx];
|
||||
light_t light = clights ? clights[cidx]
|
||||
: Lightmap::SUN_LIGHT_ONLY;
|
||||
// todo: move to the BlocksRenderer
|
||||
if (backlight) {
|
||||
const auto block = defs.get(vox.id);
|
||||
if (block && block->lightPassing) {
|
||||
light = apply_backlight(light);
|
||||
}
|
||||
}
|
||||
lights[vidx] = light;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ecx = floordiv<CHUNK_W>(x + w);
|
||||
int ecz = floordiv<CHUNK_D>(z + d);
|
||||
void Chunks::getVoxels(
|
||||
voxel* voxels,
|
||||
light_t* lights,
|
||||
const glm::ivec3& pos,
|
||||
const glm::ivec3& size,
|
||||
bool backlight,
|
||||
int top
|
||||
) const {
|
||||
int h = std::min<int>(size.y, top);
|
||||
|
||||
int scx = floordiv<CHUNK_W>(pos.x);
|
||||
int scz = floordiv<CHUNK_D>(pos.z);
|
||||
|
||||
int ecx = floordiv<CHUNK_W>(pos.x + size.x);
|
||||
int ecz = floordiv<CHUNK_D>(pos.z + size.z);
|
||||
|
||||
int cw = ecx - scx + 1;
|
||||
int cd = ecz - scz + 1;
|
||||
|
|
@ -365,67 +442,37 @@ void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const {
|
|||
for (int cx = scx; cx < scx + cw; cx++) {
|
||||
const auto chunk = getChunk(cx, cz);
|
||||
if (chunk == nullptr) {
|
||||
// no chunk loaded -> filling with BLOCK_VOID
|
||||
for (int ly = y; ly < y + h; ly++) {
|
||||
for (int lz = std::max(z, cz * CHUNK_D);
|
||||
lz < std::min(z + d, (cz + 1) * CHUNK_D);
|
||||
lz++) {
|
||||
for (int lx = std::max(x, cx * CHUNK_W);
|
||||
lx < std::min(x + w, (cx + 1) * CHUNK_W);
|
||||
lx++) {
|
||||
uint idx = vox_index(lx - x, ly - y, lz - z, w, d);
|
||||
voxels[idx].id = BLOCK_VOID;
|
||||
lights[idx] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const voxel* cvoxels = chunk->voxels;
|
||||
const light_t* clights =
|
||||
chunk->lightmap ? chunk->lightmap->getLights() : nullptr;
|
||||
for (int ly = y; ly < y + h; ly++) {
|
||||
for (int lz = std::max(z, cz * CHUNK_D);
|
||||
lz < std::min(z + d, (cz + 1) * CHUNK_D);
|
||||
lz++) {
|
||||
for (int lx = std::max(x, cx * CHUNK_W);
|
||||
lx < std::min(x + w, (cx + 1) * CHUNK_W);
|
||||
lx++) {
|
||||
uint vidx = vox_index(lx - x, ly - y, lz - z, w, d);
|
||||
uint cidx = vox_index(
|
||||
lx - cx * CHUNK_W,
|
||||
ly,
|
||||
lz - cz * CHUNK_D,
|
||||
CHUNK_W,
|
||||
CHUNK_D
|
||||
);
|
||||
voxels[vidx] = cvoxels[cidx];
|
||||
light_t light = clights ? clights[cidx]
|
||||
: Lightmap::SUN_LIGHT_ONLY;
|
||||
if (backlight) {
|
||||
const auto block =
|
||||
indices.blocks.get(voxels[vidx].id);
|
||||
if (block && block->lightPassing) {
|
||||
light = Lightmap::combine(
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 0) + 1),
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 1) + 1),
|
||||
std::min(15,
|
||||
Lightmap::extract(light, 2) + 1),
|
||||
std::min(15,
|
||||
static_cast<int>(Lightmap::extract(light, 3)))
|
||||
);
|
||||
}
|
||||
}
|
||||
lights[vidx] = light;
|
||||
}
|
||||
}
|
||||
}
|
||||
fill_with_void(
|
||||
voxels, lights, pos, {size.x, h, size.z}, cx, cz
|
||||
);
|
||||
continue;
|
||||
}
|
||||
sample_chunk(
|
||||
indices.blocks,
|
||||
*chunk,
|
||||
voxels,
|
||||
lights,
|
||||
pos,
|
||||
{size.x, h, size.z},
|
||||
cx,
|
||||
cz,
|
||||
backlight
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Chunks::getVoxels(VoxelsVolume& volume, bool backlight, int top) const {
|
||||
getVoxels(
|
||||
volume.getVoxels(),
|
||||
volume.getLights(),
|
||||
{volume.getX(), volume.getY(), volume.getZ()},
|
||||
{volume.getW(), volume.getH(), volume.getD()},
|
||||
backlight,
|
||||
top
|
||||
);
|
||||
}
|
||||
|
||||
void Chunks::saveAndClear() {
|
||||
areaMap.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ class LevelEvents;
|
|||
class Block;
|
||||
class VoxelsVolume;
|
||||
|
||||
template <int w, int h, int d> class StaticVoxelsVolume;
|
||||
|
||||
/// Player-centred chunks matrix
|
||||
class Chunks {
|
||||
LevelEvents* events;
|
||||
|
|
@ -129,7 +131,34 @@ public:
|
|||
bool isReplaceableBlock(int32_t x, int32_t y, int32_t z);
|
||||
bool isObstacleBlock(int32_t x, int32_t y, int32_t z);
|
||||
|
||||
void getVoxels(VoxelsVolume& volume, bool backlight = false, int top = CHUNK_H) const;
|
||||
void getVoxels(
|
||||
VoxelsVolume& volume, bool backlight = false, int top = CHUNK_H
|
||||
) const;
|
||||
|
||||
template <int w, int h, int d>
|
||||
void getVoxels(
|
||||
StaticVoxelsVolume<w, h, d>& volume,
|
||||
bool backlight = false,
|
||||
int top = CHUNK_H
|
||||
) const {
|
||||
getVoxels(
|
||||
volume.getVoxels(),
|
||||
volume.getLights(),
|
||||
{volume.getX(), volume.getY(), volume.getZ()},
|
||||
{w, h, d},
|
||||
backlight,
|
||||
top
|
||||
);
|
||||
}
|
||||
|
||||
void getVoxels(
|
||||
voxel* voxels,
|
||||
light_t* lights,
|
||||
const glm::ivec3& pos,
|
||||
const glm::ivec3& size,
|
||||
bool backlight,
|
||||
int top
|
||||
) const;
|
||||
|
||||
void setCenter(int32_t x, int32_t z);
|
||||
void resize(uint32_t newW, uint32_t newD);
|
||||
|
|
|
|||
|
|
@ -1,28 +1,55 @@
|
|||
#include "VoxelsVolume.hpp"
|
||||
|
||||
VoxelsVolume::VoxelsVolume(int x, int y, int z, int w, int h, int d)
|
||||
: x(x),
|
||||
y(y),
|
||||
z(z),
|
||||
w(w),
|
||||
h(h),
|
||||
d(d),
|
||||
voxels(std::make_unique<voxel[]>(w * h * d)),
|
||||
lights(std::make_unique<light_t[]>(w * h * d)) {
|
||||
for (int i = 0; i < w * h * d; i++) {
|
||||
voxels[i].id = BLOCK_VOID;
|
||||
}
|
||||
}
|
||||
|
||||
VoxelsVolume::VoxelsVolume(int w, int h, int d)
|
||||
: VoxelsVolume(0, 0, 0, w, h, d) {
|
||||
}
|
||||
|
||||
VoxelsVolume::~VoxelsVolume() {
|
||||
}
|
||||
|
||||
void VoxelsVolume::setPosition(int x, int y, int z) {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
this->z = z;
|
||||
}
|
||||
#include "VoxelsVolume.hpp"
|
||||
|
||||
#include "constants.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void VoxelsVolume::compressInto(VoxelsVolume& dst, const Content& content) const {
|
||||
assert(dst.w < w && dst.h < h && dst.d < d);
|
||||
assert(w % dst.w == 0 && h % dst.h == 0 && d % dst.d == 0);
|
||||
|
||||
int stepW = w / dst.w;
|
||||
int stepH = h / dst.h;
|
||||
int stepD = d / dst.d;
|
||||
int height = dst.h;
|
||||
int depth = dst.d;
|
||||
int width = dst.w;
|
||||
|
||||
auto dstVoxels = dst.getVoxels();
|
||||
auto dstLights = dst.getLights();
|
||||
|
||||
const auto& blockDefs = content.getIndices()->blocks;
|
||||
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int z = 0; z < depth; z++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
voxel selectedVoxel {BLOCK_AIR, {}};
|
||||
light_t light = 0;
|
||||
for (int ly = 0; ly < stepH; ly++) {
|
||||
for (int lz = 0; lz < stepD; lz++) {
|
||||
for (int lx = 0; lx < stepW; lx++) {
|
||||
size_t srcIndex = vox_index(
|
||||
x * stepW + lx, y * stepH + ly, z * stepD, w, d
|
||||
);
|
||||
auto vox = voxels[srcIndex];
|
||||
if (vox.id == BLOCK_VOID) {
|
||||
continue;
|
||||
}
|
||||
const auto& def = blockDefs.require(vox.id);
|
||||
if (def.rt.solid) {
|
||||
selectedVoxel = std::move(vox);
|
||||
} else if (light == 0) {
|
||||
light = lights[srcIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t dstIndex = vox_index(x, y, z, dst.w, dst.d);
|
||||
dstLights[dstIndex] = light;
|
||||
dstVoxels[dstIndex] = std::move(selectedVoxel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,27 @@
|
|||
#include "typedefs.hpp"
|
||||
#include "voxel.hpp"
|
||||
|
||||
class VoxelsVolume {
|
||||
int x, y, z;
|
||||
int w, h, d;
|
||||
std::unique_ptr<voxel[]> voxels;
|
||||
std::unique_ptr<light_t[]> lights;
|
||||
public:
|
||||
VoxelsVolume(int w, int h, int d);
|
||||
VoxelsVolume(int x, int y, int z, int w, int h, int d);
|
||||
virtual ~VoxelsVolume();
|
||||
#include <cstring>
|
||||
|
||||
void setPosition(int x, int y, int z);
|
||||
class Content;
|
||||
|
||||
class VoxelsVolume {
|
||||
public:
|
||||
VoxelsVolume(int w, int h, int d)
|
||||
: VoxelsVolume(0, 0, 0, w, h, d) {}
|
||||
|
||||
VoxelsVolume(int x, int y, int z, int w, int h, int d)
|
||||
: x(x), y(y), z(z), w(w), h(h), d(d),
|
||||
voxels(std::make_unique<voxel[]>(w * h * d)),
|
||||
lights(std::make_unique<light_t[]>(w * h * d)) {
|
||||
std::memset(voxels.get(), 0xFF, sizeof(voxel) * w * h * d);
|
||||
}
|
||||
|
||||
void setPosition(int x, int y, int z) {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
this->z = z;
|
||||
}
|
||||
|
||||
int getX() const {
|
||||
return x;
|
||||
|
|
@ -40,11 +50,19 @@ public:
|
|||
return d;
|
||||
}
|
||||
|
||||
voxel* getVoxels() const {
|
||||
voxel* getVoxels() {
|
||||
return voxels.get();
|
||||
}
|
||||
|
||||
light_t* getLights() const {
|
||||
const voxel* getVoxels() const {
|
||||
return voxels.get();
|
||||
}
|
||||
|
||||
light_t* getLights() {
|
||||
return lights.get();
|
||||
}
|
||||
|
||||
const light_t* getLights() const {
|
||||
return lights.get();
|
||||
}
|
||||
|
||||
|
|
@ -72,4 +90,92 @@ public:
|
|||
}
|
||||
return lights[vox_index(bx - x, by - y, bz - z, w, d)];
|
||||
}
|
||||
|
||||
void compressInto(VoxelsVolume& dst, const Content& content) const;
|
||||
private:
|
||||
int x, y, z;
|
||||
int w, h, d;
|
||||
std::unique_ptr<voxel[]> voxels;
|
||||
std::unique_ptr<light_t[]> lights;
|
||||
};
|
||||
|
||||
|
||||
template <int w, int h, int d>
|
||||
class StaticVoxelsVolume {
|
||||
public:
|
||||
static inline constexpr size_t size = w * h * d;
|
||||
static inline constexpr int width = w;
|
||||
static inline constexpr int height = h;
|
||||
static inline constexpr int depth = d;
|
||||
|
||||
StaticVoxelsVolume()
|
||||
: StaticVoxelsVolume(0, 0, 0) {}
|
||||
|
||||
StaticVoxelsVolume(int x, int y, int z)
|
||||
: x(x), y(y), z(z) {
|
||||
std::memset(voxels, 0xFF, sizeof(voxel) * size);
|
||||
}
|
||||
|
||||
void setPosition(int x, int y, int z) {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
this->z = z;
|
||||
}
|
||||
|
||||
int getX() const {
|
||||
return x;
|
||||
}
|
||||
|
||||
int getY() const {
|
||||
return y;
|
||||
}
|
||||
|
||||
int getZ() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
voxel* getVoxels() {
|
||||
return voxels;
|
||||
}
|
||||
|
||||
light_t* getLights() {
|
||||
return lights;
|
||||
}
|
||||
|
||||
inline blockid_t pickBlockId(uint bx, uint by, uint bz) const {
|
||||
bx -= x;
|
||||
by -= y;
|
||||
bz -= z;
|
||||
if (bx >= w || by >= h || bz >= d) {
|
||||
return BLOCK_VOID;
|
||||
}
|
||||
return voxels[vox_index(bx, by, bz, w, d)].id;
|
||||
}
|
||||
|
||||
|
||||
inline const voxel& pickBlock(uint bx, uint by, uint bz) const {
|
||||
bx -= x;
|
||||
by -= y;
|
||||
bz -= z;
|
||||
if (bx >= w || by >= h || bz >= d) {
|
||||
static voxel voidVoxel {BLOCK_VOID, {}};
|
||||
return voidVoxel;
|
||||
}
|
||||
return voxels[vox_index(bx, by, bz, w, d)];
|
||||
}
|
||||
|
||||
inline light_t pickLight(uint bx, uint by, uint bz) const {
|
||||
bx -= x;
|
||||
by -= y;
|
||||
bz -= z;
|
||||
if (bx >= w || by >= h || bz >= d) {
|
||||
return 0;
|
||||
}
|
||||
return lights[vox_index(bx, by, bz, w, d)];
|
||||
}
|
||||
private:
|
||||
int x, y, z;
|
||||
voxel voxels[size];
|
||||
light_t lights[size];
|
||||
};
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue