commit
1f9b619392
@ -5,7 +5,9 @@
|
||||
"sounds": [
|
||||
"blocks/door_open",
|
||||
"blocks/door_close",
|
||||
"events/pickup"
|
||||
"events/pickup",
|
||||
"ambient/rain",
|
||||
"ambient/thunder"
|
||||
],
|
||||
"models": [
|
||||
"drop-item"
|
||||
|
||||
BIN
res/content/base/sounds/ambient/rain_0.ogg
Normal file
BIN
res/content/base/sounds/ambient/rain_0.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/ambient/rain_1.ogg
Normal file
BIN
res/content/base/sounds/ambient/rain_1.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/ambient/rain_2.ogg
Normal file
BIN
res/content/base/sounds/ambient/rain_2.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/ambient/rain_3.ogg
Normal file
BIN
res/content/base/sounds/ambient/rain_3.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/ambient/rain_4.ogg
Normal file
BIN
res/content/base/sounds/ambient/rain_4.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/ambient/thunder_0.ogg
Normal file
BIN
res/content/base/sounds/ambient/thunder_0.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/ambient/thunder_1.ogg
Normal file
BIN
res/content/base/sounds/ambient/thunder_1.ogg
Normal file
Binary file not shown.
BIN
res/content/base/sounds/ambient/thunder_2.ogg
Normal file
BIN
res/content/base/sounds/ambient/thunder_2.ogg
Normal file
Binary file not shown.
BIN
res/content/base/textures/particles/rain_splash_0.png
Normal file
BIN
res/content/base/textures/particles/rain_splash_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
BIN
res/content/base/textures/particles/rain_splash_1.png
Normal file
BIN
res/content/base/textures/particles/rain_splash_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
BIN
res/content/base/textures/particles/rain_splash_2.png
Normal file
BIN
res/content/base/textures/particles/rain_splash_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
@ -20,6 +20,8 @@
|
||||
"gui/refresh",
|
||||
"gui/folder_icon",
|
||||
"gui/settings_icon",
|
||||
"misc/rain",
|
||||
"misc/snow",
|
||||
"gui/check_mark",
|
||||
"gui/left_arrow",
|
||||
"gui/right_arrow"
|
||||
|
||||
1
res/presets/weather/clear.json
Normal file
1
res/presets/weather/clear.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
||||
4
res/presets/weather/cloudy.json
Normal file
4
res/presets/weather/cloudy.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"clouds": 0.8
|
||||
}
|
||||
|
||||
6
res/presets/weather/fog.json
Normal file
6
res/presets/weather/fog.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"fog_opacity": 0.98,
|
||||
"fog_dencity": 3.6,
|
||||
"fog_curve": 0.25,
|
||||
"clouds": 0.8
|
||||
}
|
||||
23
res/presets/weather/rain.json
Normal file
23
res/presets/weather/rain.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"fall": {
|
||||
"vspeed": 2.0,
|
||||
"texture": "misc/rain",
|
||||
"noise": "ambient/rain",
|
||||
"splash": {
|
||||
"lifetime": 0.2,
|
||||
"spawn_interval": 0.0,
|
||||
"size": [0.2, 0.2, 0.2],
|
||||
"frames": [
|
||||
"particles:rain_splash_0",
|
||||
"particles:rain_splash_1",
|
||||
"particles:rain_splash_2"
|
||||
]
|
||||
},
|
||||
"min_opacity": 0.8,
|
||||
"max_intensity": 0.5
|
||||
},
|
||||
"fog_opacity": 0.5,
|
||||
"fog_dencity": 1.7,
|
||||
"fog_curve": 0.5,
|
||||
"clouds": 0.9
|
||||
}
|
||||
15
res/presets/weather/snow.json
Normal file
15
res/presets/weather/snow.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"fall": {
|
||||
"vspeed": 0.75,
|
||||
"hspeed": 0.5,
|
||||
"texture": "misc/snow",
|
||||
"scale": 0.35,
|
||||
"opaque": true,
|
||||
"min_opacity": 0.8,
|
||||
"max_opacity": 2.0
|
||||
},
|
||||
"fog_opacity": 0.8,
|
||||
"fog_dencity": 2.5,
|
||||
"fog_curve": 0.5,
|
||||
"clouds": 0.5
|
||||
}
|
||||
23
res/presets/weather/thunder.json
Normal file
23
res/presets/weather/thunder.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"fall": {
|
||||
"vspeed": 2.0,
|
||||
"texture": "misc/rain",
|
||||
"noise": "ambient/rain",
|
||||
"splash": {
|
||||
"lifetime": 0.2,
|
||||
"spawn_interval": 0.0,
|
||||
"size": [0.2, 0.2, 0.2],
|
||||
"frames": [
|
||||
"particles:rain_splash_0",
|
||||
"particles:rain_splash_1",
|
||||
"particles:rain_splash_2"
|
||||
]
|
||||
},
|
||||
"min_opacity": 0.8
|
||||
},
|
||||
"fog_opacity": 0.8,
|
||||
"fog_dencity": 2.0,
|
||||
"fog_curve": 0.5,
|
||||
"clouds": 0.7,
|
||||
"thunder_rate": 0.1
|
||||
}
|
||||
@ -264,6 +264,33 @@ console.add_command(
|
||||
end
|
||||
)
|
||||
|
||||
console.add_command(
|
||||
"weather.set name:str time:num=1",
|
||||
"Change weather",
|
||||
function (args, kwargs)
|
||||
local filename = file.find("presets/weather/"..args[1]..".json")
|
||||
if not filename then
|
||||
return "weather preset not found"
|
||||
end
|
||||
local preset = json.parse(file.read(filename))
|
||||
gfx.weather.change(preset, args[2], args[1])
|
||||
return "weather set to "..filename.." preset ("..tostring(args[2]).." s)"
|
||||
end
|
||||
)
|
||||
|
||||
console.add_command(
|
||||
"weather",
|
||||
"Display current weather preset name",
|
||||
function (args, kwargs)
|
||||
local name = gfx.weather.get_current()
|
||||
if name == "" then
|
||||
return "unnamed " .. json.tostring(gfx.weather.get_current_data(), true)
|
||||
else
|
||||
return name
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
console.cheats = {
|
||||
"blocks.fill",
|
||||
"tp",
|
||||
@ -271,5 +298,6 @@ console.cheats = {
|
||||
"time.set",
|
||||
"time.daycycle",
|
||||
"entity.despawn",
|
||||
"player.respawn"
|
||||
"player.respawn",
|
||||
"weather.set",
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
in vec4 a_color;
|
||||
in vec2 a_texCoord;
|
||||
in float a_distance;
|
||||
in vec3 a_dir;
|
||||
in float a_fog;
|
||||
out vec4 f_color;
|
||||
|
||||
uniform sampler2D u_texture0;
|
||||
@ -14,12 +14,10 @@ uniform bool u_alphaClip;
|
||||
void main() {
|
||||
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
|
||||
vec4 tex_color = texture(u_texture0, a_texCoord);
|
||||
float depth = (a_distance/256.0);
|
||||
float alpha = a_color.a * tex_color.a;
|
||||
// anyway it's any alpha-test alternative required
|
||||
if (alpha < (u_alphaClip ? 0.5f : 0.2f))
|
||||
if (alpha < (u_alphaClip ? 0.5f : 0.15f))
|
||||
discard;
|
||||
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
||||
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
||||
f_color = mix(a_color * tex_color, vec4(fogColor,1.0), a_fog);
|
||||
f_color.a = alpha;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ layout (location = 3) in float v_light;
|
||||
|
||||
out vec4 a_color;
|
||||
out vec2 a_texCoord;
|
||||
out float a_distance;
|
||||
out float a_fog;
|
||||
out vec3 a_dir;
|
||||
|
||||
uniform mat4 u_model;
|
||||
@ -15,6 +15,12 @@ uniform mat4 u_proj;
|
||||
uniform mat4 u_view;
|
||||
uniform vec3 u_cameraPos;
|
||||
uniform float u_gamma;
|
||||
uniform float u_opacity;
|
||||
uniform float u_fogFactor;
|
||||
uniform float u_fogCurve;
|
||||
uniform float u_weatherFogOpacity;
|
||||
uniform float u_weatherFogDencity;
|
||||
uniform float u_weatherFogCurve;
|
||||
uniform samplerCube u_cubemap;
|
||||
|
||||
uniform vec3 u_torchlightColor;
|
||||
@ -36,6 +42,11 @@ void main() {
|
||||
a_dir = modelpos.xyz - u_cameraPos;
|
||||
vec3 skyLightColor = pick_sky_color(u_cubemap);
|
||||
a_color.rgb = max(a_color.rgb, skyLightColor.rgb*decomp_light.a) * v_color;
|
||||
a_distance = length(u_view * u_model * vec4(pos3d * FOG_POS_SCALE, 0.0));
|
||||
a_color.a = u_opacity;
|
||||
|
||||
float dist = length(u_view * u_model * vec4(pos3d * FOG_POS_SCALE, 0.0));
|
||||
float depth = (dist / 256.0);
|
||||
a_fog = min(1.0, max(pow(depth * u_fogFactor, u_fogCurve),
|
||||
min(pow(depth * u_weatherFogDencity, u_weatherFogCurve), u_weatherFogOpacity)));
|
||||
gl_Position = u_proj * u_view * modelpos;
|
||||
}
|
||||
|
||||
@ -1,20 +1,16 @@
|
||||
in vec4 a_color;
|
||||
in vec2 a_texCoord;
|
||||
in float a_distance;
|
||||
in float a_fog;
|
||||
in vec3 a_dir;
|
||||
out vec4 f_color;
|
||||
|
||||
uniform sampler2D u_texture0;
|
||||
uniform samplerCube u_cubemap;
|
||||
uniform vec3 u_fogColor;
|
||||
uniform float u_fogFactor;
|
||||
uniform float u_fogCurve;
|
||||
uniform bool u_alphaClip;
|
||||
|
||||
void main() {
|
||||
vec3 fogColor = texture(u_cubemap, a_dir).rgb;
|
||||
vec4 tex_color = texture(u_texture0, a_texCoord);
|
||||
float depth = (a_distance/256.0);
|
||||
float alpha = a_color.a * tex_color.a;
|
||||
if (u_alphaClip) {
|
||||
if (alpha < 0.2f)
|
||||
@ -24,7 +20,6 @@ void main() {
|
||||
if (alpha < 0.002f)
|
||||
discard;
|
||||
}
|
||||
f_color = mix(a_color * tex_color, vec4(fogColor,1.0),
|
||||
min(1.0, pow(depth*u_fogFactor, u_fogCurve)));
|
||||
f_color = mix(a_color * tex_color, vec4(fogColor,1.0), a_fog);
|
||||
f_color.a = alpha;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ layout (location = 2) in float v_light;
|
||||
out vec4 a_color;
|
||||
out vec2 a_texCoord;
|
||||
out float a_distance;
|
||||
out float a_fog;
|
||||
out vec3 a_dir;
|
||||
|
||||
uniform mat4 u_model;
|
||||
@ -14,6 +15,12 @@ uniform mat4 u_proj;
|
||||
uniform mat4 u_view;
|
||||
uniform vec3 u_cameraPos;
|
||||
uniform float u_gamma;
|
||||
uniform float u_fogFactor;
|
||||
uniform float u_fogCurve;
|
||||
uniform float u_weatherFogOpacity;
|
||||
uniform float u_weatherFogDencity;
|
||||
uniform float u_weatherFogCurve;
|
||||
uniform float u_timer;
|
||||
uniform samplerCube u_cubemap;
|
||||
|
||||
uniform vec3 u_torchlightColor;
|
||||
@ -35,6 +42,10 @@ void main() {
|
||||
a_dir = modelpos.xyz - u_cameraPos;
|
||||
vec3 skyLightColor = pick_sky_color(u_cubemap);
|
||||
a_color.rgb = max(a_color.rgb, skyLightColor.rgb*decomp_light.a);
|
||||
|
||||
a_distance = length(u_view * u_model * vec4(pos3d * FOG_POS_SCALE, 0.0));
|
||||
float depth = (a_distance / 256.0);
|
||||
a_fog = min(1.0, max(pow(depth * u_fogFactor, u_fogCurve),
|
||||
min(pow(depth * u_weatherFogDencity, u_weatherFogCurve), u_weatherFogOpacity)));
|
||||
gl_Position = u_proj * u_view * modelpos;
|
||||
}
|
||||
|
||||
BIN
res/textures/misc/rain.png
Normal file
BIN
res/textures/misc/rain.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 344 KiB |
BIN
res/textures/misc/snow.png
Normal file
BIN
res/textures/misc/snow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@ -36,10 +36,14 @@
|
||||
|
||||
static debug::Logger logger("level-screen");
|
||||
|
||||
inline const io::path CLIENT_FILE = "world:client/environment.json";
|
||||
|
||||
LevelScreen::LevelScreen(
|
||||
Engine& engine, std::unique_ptr<Level> levelPtr, int64_t localPlayer
|
||||
)
|
||||
: Screen(engine), postProcessing(std::make_unique<PostProcessing>()) {
|
||||
: Screen(engine),
|
||||
world(*levelPtr->getWorld()),
|
||||
postProcessing(std::make_unique<PostProcessing>()) {
|
||||
Level* level = levelPtr.get();
|
||||
|
||||
auto& settings = engine.getSettings();
|
||||
@ -62,22 +66,22 @@ LevelScreen::LevelScreen(
|
||||
frontend = std::make_unique<LevelFrontend>(
|
||||
player, controller.get(), assets, settings
|
||||
);
|
||||
worldRenderer = std::make_unique<WorldRenderer>(
|
||||
renderer = std::make_unique<WorldRenderer>(
|
||||
engine, *frontend, *player
|
||||
);
|
||||
hud = std::make_unique<Hud>(engine, *frontend, *player);
|
||||
|
||||
decorator = std::make_unique<Decorator>(
|
||||
engine, *controller, *worldRenderer, assets, *player
|
||||
engine, *controller, *renderer, assets, *player
|
||||
);
|
||||
|
||||
keepAlive(settings.graphics.backlight.observe([=](bool) {
|
||||
player->chunks->saveAndClear();
|
||||
worldRenderer->clear();
|
||||
renderer->clear();
|
||||
}));
|
||||
keepAlive(settings.graphics.denseRender.observe([=](bool) {
|
||||
player->chunks->saveAndClear();
|
||||
worldRenderer->clear();
|
||||
renderer->clear();
|
||||
frontend->getContentGfxCache().refresh();
|
||||
}));
|
||||
keepAlive(settings.camera.fov.observe([=](double value) {
|
||||
@ -85,22 +89,35 @@ LevelScreen::LevelScreen(
|
||||
}));
|
||||
keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){
|
||||
player->chunks->saveAndClear();
|
||||
worldRenderer->clear();
|
||||
renderer->clear();
|
||||
return false;
|
||||
}));
|
||||
|
||||
animator = std::make_unique<TextureAnimator>();
|
||||
animator->addAnimations(assets.getAnimations());
|
||||
|
||||
loadDecorations();
|
||||
initializeContent();
|
||||
}
|
||||
|
||||
LevelScreen::~LevelScreen() {
|
||||
if (!controller->getLevel()->getWorld()->isNameless()) {
|
||||
saveDecorations();
|
||||
saveWorldPreview();
|
||||
}
|
||||
scripting::on_frontend_close();
|
||||
// unblock all bindings
|
||||
Events::enableBindings();
|
||||
controller->onWorldQuit();
|
||||
engine.getPaths().setCurrentWorldFolder("");
|
||||
}
|
||||
|
||||
void LevelScreen::initializeContent() {
|
||||
auto& content = controller->getLevel()->content;
|
||||
for (auto& entry : content.getPacks()) {
|
||||
initializePack(entry.second.get());
|
||||
}
|
||||
scripting::on_frontend_init(hud.get(), worldRenderer.get());
|
||||
scripting::on_frontend_init(hud.get(), renderer.get());
|
||||
}
|
||||
|
||||
void LevelScreen::initializePack(ContentPackRuntime* pack) {
|
||||
@ -116,15 +133,22 @@ void LevelScreen::initializePack(ContentPackRuntime* pack) {
|
||||
}
|
||||
}
|
||||
|
||||
LevelScreen::~LevelScreen() {
|
||||
if (!controller->getLevel()->getWorld()->isNameless()) {
|
||||
saveWorldPreview();
|
||||
void LevelScreen::loadDecorations() {
|
||||
if (!io::exists(CLIENT_FILE)) {
|
||||
return;
|
||||
}
|
||||
scripting::on_frontend_close();
|
||||
// unblock all bindings
|
||||
Events::enableBindings();
|
||||
controller->onWorldQuit();
|
||||
engine.getPaths().setCurrentWorldFolder("");
|
||||
auto data = io::read_object(CLIENT_FILE);
|
||||
if (data.has("weather")) {
|
||||
renderer->getWeather().deserialize(data["weather"]);
|
||||
}
|
||||
}
|
||||
|
||||
void LevelScreen::saveDecorations() {
|
||||
io::create_directory("world:client");
|
||||
|
||||
auto data = dv::object();
|
||||
data["weather"] = renderer->getWeather().serialize();
|
||||
io::write_json(CLIENT_FILE, data, true);
|
||||
}
|
||||
|
||||
void LevelScreen::saveWorldPreview() {
|
||||
@ -144,7 +168,7 @@ void LevelScreen::saveWorldPreview() {
|
||||
Viewport viewport(previewSize * 1.5, previewSize);
|
||||
DrawContext ctx(&pctx, viewport, batch.get());
|
||||
|
||||
worldRenderer->draw(ctx, camera, false, true, 0.0f, postProcessing.get());
|
||||
renderer->draw(ctx, camera, false, true, 0.0f, *postProcessing);
|
||||
auto image = postProcessing->toImage();
|
||||
image->flipY();
|
||||
imageio::write("world:preview.png", image.get());
|
||||
@ -164,26 +188,15 @@ void LevelScreen::updateHotkeys() {
|
||||
if (Events::jpressed(keycode::F3)) {
|
||||
debug = !debug;
|
||||
hud->setDebug(debug);
|
||||
worldRenderer->setDebug(debug);
|
||||
renderer->setDebug(debug);
|
||||
}
|
||||
}
|
||||
|
||||
void LevelScreen::update(float delta) {
|
||||
gui::GUI* gui = engine.getGUI();
|
||||
auto menu = gui->getMenu();
|
||||
|
||||
bool inputLocked = menu->hasOpenPage() ||
|
||||
hud->isInventoryOpen() ||
|
||||
gui->isFocusCaught();
|
||||
if (!gui->isFocusCaught()) {
|
||||
updateHotkeys();
|
||||
}
|
||||
|
||||
auto level = controller->getLevel();
|
||||
void LevelScreen::updateAudio() {
|
||||
auto player = playerController->getPlayer();
|
||||
auto camera = player->currentCamera;
|
||||
|
||||
bool paused = hud->isPause();
|
||||
|
||||
audio::get_channel("regular")->setPaused(paused);
|
||||
audio::get_channel("ambient")->setPaused(paused);
|
||||
glm::vec3 velocity {};
|
||||
@ -196,20 +209,34 @@ void LevelScreen::update(float delta) {
|
||||
camera->dir,
|
||||
glm::vec3(0, 1, 0)
|
||||
);
|
||||
const auto& settings = engine.getSettings();
|
||||
}
|
||||
|
||||
if (!hud->isPause()) {
|
||||
level->getWorld()->updateTimers(delta);
|
||||
animator->update(delta);
|
||||
void LevelScreen::update(float delta) {
|
||||
auto& gui = *engine.getGUI();
|
||||
|
||||
if (!gui.isFocusCaught()) {
|
||||
updateHotkeys();
|
||||
}
|
||||
if (!hud->isPause()) {
|
||||
updateAudio();
|
||||
|
||||
auto menu = gui.getMenu();
|
||||
bool inputLocked =
|
||||
menu->hasOpenPage() || hud->isInventoryOpen() || gui.isFocusCaught();
|
||||
bool paused = hud->isPause();
|
||||
if (!paused) {
|
||||
world.updateTimers(delta);
|
||||
animator->update(delta);
|
||||
playerController->update(delta, !inputLocked);
|
||||
}
|
||||
controller->update(glm::min(delta, 0.2f), hud->isPause());
|
||||
playerController->postUpdate(delta, !inputLocked, hud->isPause());
|
||||
controller->update(glm::min(delta, 0.2f), paused);
|
||||
playerController->postUpdate(delta, !inputLocked, paused);
|
||||
|
||||
hud->update(hudVisible);
|
||||
decorator->update(delta, *camera);
|
||||
|
||||
const auto& weather = renderer->getWeather();
|
||||
const auto& player = *playerController->getPlayer();
|
||||
const auto& camera = *player.currentCamera;
|
||||
decorator->update(paused ? 0.0f : delta, camera, weather);
|
||||
}
|
||||
|
||||
void LevelScreen::draw(float delta) {
|
||||
@ -221,8 +248,8 @@ void LevelScreen::draw(float delta) {
|
||||
if (!hud->isPause()) {
|
||||
scripting::on_entities_render(engine.getTime().getDelta());
|
||||
}
|
||||
worldRenderer->draw(
|
||||
ctx, *camera, hudVisible, hud->isPause(), delta, postProcessing.get()
|
||||
renderer->draw(
|
||||
ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing
|
||||
);
|
||||
|
||||
if (hudVisible) {
|
||||
@ -236,8 +263,3 @@ void LevelScreen::onEngineShutdown() {
|
||||
}
|
||||
controller->saveWorld();
|
||||
}
|
||||
|
||||
LevelController* LevelScreen::getLevelController() const {
|
||||
return controller.get();
|
||||
}
|
||||
|
||||
|
||||
@ -15,12 +15,14 @@ class PostProcessing;
|
||||
class ContentPackRuntime;
|
||||
class Decorator;
|
||||
class Level;
|
||||
class World;
|
||||
|
||||
class LevelScreen : public Screen {
|
||||
World& world;
|
||||
std::unique_ptr<LevelFrontend> frontend;
|
||||
std::unique_ptr<LevelController> controller;
|
||||
std::unique_ptr<PlayerController> playerController;
|
||||
std::unique_ptr<WorldRenderer> worldRenderer;
|
||||
std::unique_ptr<WorldRenderer> renderer;
|
||||
std::unique_ptr<TextureAnimator> animator;
|
||||
std::unique_ptr<PostProcessing> postProcessing;
|
||||
std::unique_ptr<Decorator> decorator;
|
||||
@ -33,6 +35,10 @@ class LevelScreen : public Screen {
|
||||
void updateHotkeys();
|
||||
void initializeContent();
|
||||
void initializePack(ContentPackRuntime* pack);
|
||||
|
||||
void loadDecorations();
|
||||
void saveDecorations();
|
||||
void updateAudio();
|
||||
public:
|
||||
LevelScreen(
|
||||
Engine& engine, std::unique_ptr<Level> level, int64_t localPlayer
|
||||
@ -43,6 +49,4 @@ public:
|
||||
void draw(float delta) override;
|
||||
|
||||
void onEngineShutdown() override;
|
||||
|
||||
LevelController* getLevelController() const;
|
||||
};
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include "assets/assets_util.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
#include "voxels/Block.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
@ -17,6 +18,8 @@
|
||||
#include "util/stringutil.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "audio/audio.hpp"
|
||||
#include "maths/util.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@ -80,8 +83,75 @@ void Decorator::addParticles(const Block& def, const glm::ivec3& pos) {
|
||||
}
|
||||
}
|
||||
|
||||
void Decorator::updateRandom(
|
||||
float delta,
|
||||
const glm::ivec3& areaCenter,
|
||||
const WeatherPreset& weather
|
||||
) {
|
||||
util::PseudoRandom random(rand());
|
||||
|
||||
const auto& chunks = *player.chunks;
|
||||
const auto& indices = *level.content.getIndices();
|
||||
const auto& rainSplash = weather.fall.splash;
|
||||
|
||||
auto pos = areaCenter + glm::ivec3(
|
||||
random.rand32() % 12,
|
||||
random.rand32() % 12,
|
||||
random.rand32() % 12
|
||||
);
|
||||
auto vox = chunks.get(pos);
|
||||
auto chunk = chunks.getChunkByVoxel(pos);
|
||||
if (vox == nullptr || chunk == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& def = indices.blocks.require(vox->id);
|
||||
auto dst2 = util::distance2(pos, areaCenter);
|
||||
if (!def.obstacle || dst2 >= 256 || weather.fall.noise.empty()) {
|
||||
return;
|
||||
}
|
||||
for (int y = pos.y + 1; y < chunk->top; y++) {
|
||||
if (indices.blocks.require(chunks.get(pos.x, y, pos.z)->id).obstacle) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
float intensity = weather.intensity * weather.fall.maxIntensity;
|
||||
if (rainSplash.has_value() && dst2 < 128 &&
|
||||
random.randFloat() < glm::pow(intensity, 2.0f)) {
|
||||
auto treg = util::get_texture_region(
|
||||
assets, "particles:rain_splash_0", ""
|
||||
);
|
||||
renderer.particles->add(std::make_unique<Emitter>(
|
||||
level,
|
||||
glm::vec3 {
|
||||
pos.x + random.randFloat(),
|
||||
pos.y + 1.1,
|
||||
pos.z + random.randFloat()},
|
||||
*rainSplash,
|
||||
treg.texture,
|
||||
treg.region,
|
||||
2
|
||||
));
|
||||
}
|
||||
if (random.rand() % 200 < 3 && pos.y < areaCenter.y + 1) {
|
||||
auto sound = assets.get<audio::Sound>(weather.fall.noise);
|
||||
audio::play(
|
||||
sound,
|
||||
pos,
|
||||
false,
|
||||
intensity * intensity,
|
||||
1.0f,
|
||||
false,
|
||||
audio::PRIORITY_LOW,
|
||||
audio::get_channel_index("ambient")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void Decorator::update(
|
||||
float delta, const glm::ivec3& areaStart, const glm::ivec3& areaCenter
|
||||
float delta,
|
||||
const glm::ivec3& areaStart,
|
||||
const glm::ivec3& areaCenter
|
||||
) {
|
||||
int index = currentIndex;
|
||||
currentIndex = (currentIndex + BIG_PRIME) % UPDATE_BLOCKS;
|
||||
@ -93,7 +163,8 @@ void Decorator::update(
|
||||
int lz = (index / UPDATE_AREA_DIAMETER) % UPDATE_AREA_DIAMETER;
|
||||
int ly = (index / UPDATE_AREA_DIAMETER / UPDATE_AREA_DIAMETER);
|
||||
|
||||
auto pos = areaStart + glm::ivec3(lx, ly, lz);
|
||||
glm::ivec3 offset {lx, ly, lz};
|
||||
auto pos = areaStart + offset;
|
||||
|
||||
if (auto vox = chunks.get(pos)) {
|
||||
const auto& def = indices.blocks.require(vox->id);
|
||||
@ -103,11 +174,7 @@ void Decorator::update(
|
||||
}
|
||||
}
|
||||
|
||||
void Decorator::update(float delta, const Camera& camera) {
|
||||
glm::ivec3 pos = camera.position;
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
update(delta, pos - glm::ivec3(UPDATE_AREA_DIAMETER / 2), pos);
|
||||
}
|
||||
void Decorator::updateBlockEmitters(const Camera& camera) {
|
||||
const auto& chunks = *player.chunks;
|
||||
const auto& indices = *level.content.getIndices();
|
||||
auto iter = blockEmitters.begin();
|
||||
@ -139,7 +206,9 @@ void Decorator::update(float delta, const Camera& camera) {
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void Decorator::updateTextNotes() {
|
||||
for (const auto& [id, player] : *level.players) {
|
||||
if (id == this->player.getId() ||
|
||||
playerTexts.find(id) != playerTexts.end()) {
|
||||
@ -169,3 +238,47 @@ void Decorator::update(float delta, const Camera& camera) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Decorator::updateRandomSounds(float delta, const Weather& weather) {
|
||||
thunderTimer += delta;
|
||||
util::PseudoRandom random(rand());
|
||||
if (thunderTimer >= 1.0f) {
|
||||
thunderTimer = 0.0f;
|
||||
if (random.randFloat() < weather.thunderRate()) {
|
||||
audio::play(
|
||||
assets.get<audio::Sound>("ambient/thunder"),
|
||||
glm::vec3(),
|
||||
false,
|
||||
1.0f,
|
||||
1.0f + random.randFloat() - 0.5f,
|
||||
false,
|
||||
audio::PRIORITY_NORMAL,
|
||||
audio::get_channel_index("ambient")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Decorator::update(
|
||||
float delta,
|
||||
const Camera& camera,
|
||||
const Weather& weather
|
||||
) {
|
||||
updateRandomSounds(delta, weather);
|
||||
|
||||
glm::ivec3 pos = camera.position;
|
||||
for (int i = 0; i < ITERATIONS; i++) {
|
||||
update(delta, pos - glm::ivec3(UPDATE_AREA_DIAMETER / 2), pos);
|
||||
}
|
||||
int randIters = std::min(50'000, static_cast<int>(delta * 24'000));
|
||||
for (int i = 0; i < randIters; i++) {
|
||||
if (weather.a.intensity > 1.e-3f) {
|
||||
updateRandom(delta, pos, weather.a);
|
||||
}
|
||||
if (weather.b.intensity > 1.e-3f) {
|
||||
updateRandom(delta, pos, weather.b);
|
||||
}
|
||||
}
|
||||
updateBlockEmitters(camera);
|
||||
updateTextNotes();
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ class Block;
|
||||
class Engine;
|
||||
class LevelController;
|
||||
class WorldRenderer;
|
||||
class Weather;
|
||||
struct WeatherPreset;
|
||||
|
||||
class Decorator {
|
||||
Engine& engine;
|
||||
@ -29,10 +31,23 @@ class Decorator {
|
||||
std::unordered_map<int64_t, u64id_t> playerTexts;
|
||||
int currentIndex = 0;
|
||||
NotePreset playerNamePreset {};
|
||||
float thunderTimer = 0.0f;
|
||||
|
||||
void update(
|
||||
float delta, const glm::ivec3& areaStart, const glm::ivec3& areaCenter
|
||||
float delta,
|
||||
const glm::ivec3& areaStart,
|
||||
const glm::ivec3& areaCenter
|
||||
);
|
||||
|
||||
/// @brief Updates weather effects, blocks ambient sounds, etc..
|
||||
void updateRandom(
|
||||
float delta,
|
||||
const glm::ivec3& areaCenter,
|
||||
const WeatherPreset& weather
|
||||
);
|
||||
void updateRandomSounds(float delta, const Weather& weather);
|
||||
void updateBlockEmitters(const Camera& camera);
|
||||
void updateTextNotes();
|
||||
void addParticles(const Block& def, const glm::ivec3& pos);
|
||||
public:
|
||||
Decorator(
|
||||
@ -43,5 +58,9 @@ public:
|
||||
Player& player
|
||||
);
|
||||
|
||||
void update(float delta, const Camera& camera);
|
||||
void update(
|
||||
float delta,
|
||||
const Camera& camera,
|
||||
const Weather& weather
|
||||
);
|
||||
};
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
#include "assets/assets_util.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/render/MainBatch.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "MainBatch.hpp"
|
||||
#include "settings.hpp"
|
||||
|
||||
size_t ParticlesRenderer::visibleParticles = 0;
|
||||
@ -179,24 +179,6 @@ void ParticlesRenderer::render(const Camera& camera, float delta) {
|
||||
}
|
||||
}
|
||||
|
||||
void ParticlesRenderer::gc() {
|
||||
std::set<Emitter*> usedEmitters;
|
||||
for (const auto& [_, vec] : particles) {
|
||||
for (const auto& particle : vec) {
|
||||
usedEmitters.insert(particle.emitter);
|
||||
}
|
||||
}
|
||||
auto iter = emitters.begin();
|
||||
while (iter != emitters.end()) {
|
||||
auto emitter = iter->second.get();
|
||||
if (usedEmitters.find(emitter) == usedEmitters.end()) {
|
||||
iter = emitters.erase(iter);
|
||||
} else {
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Emitter* ParticlesRenderer::getEmitter(u64id_t id) const {
|
||||
const auto& found = emitters.find(id);
|
||||
if (found == emitters.end()) {
|
||||
|
||||
@ -40,12 +40,6 @@ public:
|
||||
|
||||
u64id_t add(std::unique_ptr<Emitter> emitter);
|
||||
|
||||
/// @brief Perform garbage collection (remove extra dead emitters).
|
||||
/// @note Emitters are deleting without GC when there's no particles with same
|
||||
/// texture left.
|
||||
/// @note Currently unused
|
||||
void gc();
|
||||
|
||||
/// @brief Get emitter by UID
|
||||
/// @return Emitter or nullptr
|
||||
Emitter* getEmitter(u64id_t id) const;
|
||||
|
||||
160
src/graphics/render/PrecipitationRenderer.cpp
Normal file
160
src/graphics/render/PrecipitationRenderer.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
#include "PrecipitationRenderer.hpp"
|
||||
|
||||
#include "MainBatch.hpp"
|
||||
#include "assets/Assets.hpp"
|
||||
#include "assets/assets_util.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "lighting/Lightmap.hpp"
|
||||
#include "maths/util.hpp"
|
||||
#include "maths/voxmaths.hpp"
|
||||
#include "util/CentredMatrix.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "presets/WeatherPreset.hpp"
|
||||
#include "voxels/Chunk.hpp"
|
||||
#include "voxels/Chunks.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
PrecipitationRenderer::PrecipitationRenderer(
|
||||
const Assets& assets,
|
||||
const Level& level,
|
||||
const Chunks& chunks,
|
||||
const GraphicsSettings* settings
|
||||
)
|
||||
: batch(std::make_unique<MainBatch>(4096)),
|
||||
level(level),
|
||||
chunks(chunks),
|
||||
assets(assets),
|
||||
settings(settings) {
|
||||
}
|
||||
|
||||
PrecipitationRenderer::~PrecipitationRenderer() = default;
|
||||
|
||||
int PrecipitationRenderer::getHeightAt(int x, int z) {
|
||||
int y = CHUNK_H - 1;
|
||||
int cx = floordiv<CHUNK_W>(x);
|
||||
int cz = floordiv<CHUNK_D>(z);
|
||||
auto chunk = chunks.getChunk(cx, cz);
|
||||
if (chunk == nullptr) {
|
||||
return y;
|
||||
}
|
||||
y = chunk->top;
|
||||
x -= cx * CHUNK_W;
|
||||
z -= cz * CHUNK_D;
|
||||
while (y > 0) {
|
||||
const auto& vox = chunk->voxels[vox_index(x, y, z)];
|
||||
if (vox.id == 0) {
|
||||
y--;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
static inline glm::vec4 light_at(const Chunks& chunks, int x, int y, int z) {
|
||||
light_t lightval = chunks.getLight(x, y, z);
|
||||
return glm::vec4(
|
||||
Lightmap::extract(lightval, 0) / 15.f,
|
||||
Lightmap::extract(lightval, 1) / 15.f,
|
||||
Lightmap::extract(lightval, 2) / 15.f,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
/// @brief 'Random' vertical texture coord offset to randomize rain layers
|
||||
static constexpr float K = 21.41149;
|
||||
/// @brief Precipitation face size
|
||||
static constexpr glm::ivec2 FACE_SIZE {1, 16};
|
||||
|
||||
static UVRegion calc_uv(
|
||||
const glm::vec3& pos,
|
||||
const glm::vec3& right,
|
||||
float timer,
|
||||
const WeatherPreset& weather
|
||||
) {
|
||||
static util::PseudoRandom random(0);
|
||||
|
||||
float scale = weather.fall.scale;
|
||||
|
||||
float m = glm::sign(right.x + right.z);
|
||||
int ux = pos.x;
|
||||
int uz = pos.z;
|
||||
if (glm::abs(right.x) < glm::abs(right.z)) {
|
||||
std::swap(ux, uz);
|
||||
}
|
||||
random.setSeed(uz);
|
||||
float hspeed = (random.randFloat() * 2.0f - 1.0f) * weather.fall.hspeed;
|
||||
float u1 = ux * scale + timer * hspeed * -m;
|
||||
float v1 = timer * weather.fall.vspeed + pos.y * scale + uz * K;
|
||||
|
||||
return {u1, v1, u1 + m * scale, v1 + FACE_SIZE.y * scale};
|
||||
}
|
||||
|
||||
void PrecipitationRenderer::render(
|
||||
const Camera& camera, float delta, const WeatherPreset& weather
|
||||
) {
|
||||
timer += delta;
|
||||
|
||||
const int radius = 6;
|
||||
const int depth = 12;
|
||||
|
||||
int x = glm::floor(camera.position.x);
|
||||
int y = glm::floor(camera.position.y);
|
||||
int z = glm::floor(camera.position.z);
|
||||
|
||||
util::CentredMatrix<int, (depth + 1) * 2> heights;
|
||||
heights.setCenter(x, z);
|
||||
for (int z = heights.beginY(); z < heights.endY(); z++) {
|
||||
for (int x = heights.beginX(); x < heights.endX(); x++) {
|
||||
heights.at(x, z) = getHeightAt(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
batch->begin();
|
||||
auto& texture = assets.require<Texture>(weather.fall.texture);
|
||||
texture.setMipMapping(false);
|
||||
batch->setTexture(&texture, {});
|
||||
|
||||
const struct {
|
||||
glm::vec3 right;
|
||||
glm::vec3 front;
|
||||
} faces[] {
|
||||
{{-1, 0, 0}, {0, 0, 1}},
|
||||
{{1, 0, 0}, {0, 0, -1}},
|
||||
{{0, 0, -1}, {-1, 0, 0}},
|
||||
{{0, 0, 1}, {1, 0, 0}},
|
||||
};
|
||||
|
||||
bool cutBack = glm::dot(camera.up, glm::vec3(0, 1, 0)) > 0.35f * camera.getFov();
|
||||
for (const auto& face : faces) {
|
||||
if (glm::dot(camera.right, face.right) < 0.0f && cutBack) {
|
||||
continue;
|
||||
}
|
||||
for (int lx = -radius; lx <= radius; lx++) {
|
||||
for (int lz = depth; lz > 0; lz--) {
|
||||
// Position calculations
|
||||
glm::vec3 pos = face.right * static_cast<float>(lx) +
|
||||
face.front * static_cast<float>(lz);
|
||||
pos += glm::vec3(x, 0, z);
|
||||
pos.y =
|
||||
glm::max(y - FACE_SIZE.y / 2, heights.at(pos.x, pos.z)) +
|
||||
FACE_SIZE.y / 2 + 1;
|
||||
pos += glm::vec3(0.5f, 0.0f, 0.5f);
|
||||
|
||||
// Draw
|
||||
batch->quad(
|
||||
pos,
|
||||
face.right,
|
||||
{0, 1, 0},
|
||||
FACE_SIZE,
|
||||
light_at(chunks, pos.x, y, pos.z),
|
||||
glm::vec3(1.0f),
|
||||
calc_uv(pos, face.right, timer, weather)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
batch->flush();
|
||||
}
|
||||
33
src/graphics/render/PrecipitationRenderer.hpp
Normal file
33
src/graphics/render/PrecipitationRenderer.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "typedefs.hpp"
|
||||
|
||||
class Level;
|
||||
class Assets;
|
||||
class Chunks;
|
||||
class Camera;
|
||||
class MainBatch;
|
||||
struct GraphicsSettings;
|
||||
struct WeatherPreset;
|
||||
|
||||
class PrecipitationRenderer {
|
||||
std::unique_ptr<MainBatch> batch;
|
||||
const Level& level;
|
||||
const Chunks& chunks;
|
||||
const Assets& assets;
|
||||
const GraphicsSettings* settings;
|
||||
float timer = 0.0f;
|
||||
|
||||
int getHeightAt(int x, int z);
|
||||
public:
|
||||
PrecipitationRenderer(
|
||||
const Assets& assets,
|
||||
const Level& level,
|
||||
const Chunks& chunks,
|
||||
const GraphicsSettings* settings
|
||||
);
|
||||
|
||||
~PrecipitationRenderer();
|
||||
|
||||
void render(const Camera& camera, float delta, const WeatherPreset& weather);
|
||||
};
|
||||
@ -44,6 +44,7 @@
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "BlockWrapsRenderer.hpp"
|
||||
#include "ParticlesRenderer.hpp"
|
||||
#include "PrecipitationRenderer.hpp"
|
||||
#include "TextsRenderer.hpp"
|
||||
#include "ChunksRenderer.hpp"
|
||||
#include "GuidesRenderer.hpp"
|
||||
@ -86,7 +87,10 @@ WorldRenderer::WorldRenderer(
|
||||
)),
|
||||
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,
|
||||
@ -115,6 +119,9 @@ void WorldRenderer::setupWorldShader(
|
||||
shader.uniform1f("u_gamma", settings.graphics.gamma.get());
|
||||
shader.uniform1f("u_fogFactor", fogFactor);
|
||||
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
|
||||
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
|
||||
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
|
||||
shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
|
||||
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
|
||||
shader.uniform2f("u_lightDir", skybox->getLightDir());
|
||||
shader.uniform3f("u_cameraPos", camera.position);
|
||||
@ -160,6 +167,7 @@ void WorldRenderer::renderLevel(
|
||||
}
|
||||
|
||||
entityShader.uniform1i("u_alphaClip", true);
|
||||
entityShader.uniform1f("u_opacity", 1.0f);
|
||||
level.entities->render(
|
||||
assets,
|
||||
*modelBatch,
|
||||
@ -188,6 +196,21 @@ void WorldRenderer::renderLevel(
|
||||
scripting::on_frontend_render();
|
||||
}
|
||||
|
||||
setupWorldShader(entityShader, camera, settings, fogFactor);
|
||||
|
||||
std::array<const WeatherPreset*, 2> weatherInstances {&weather.a, &weather.b};
|
||||
for (const auto& weather : weatherInstances) {
|
||||
float maxIntensity = weather->fall.maxIntensity;
|
||||
float zero = weather->fall.minOpacity;
|
||||
float one = weather->fall.maxOpacity;
|
||||
float t = (weather->intensity * (one - zero)) * maxIntensity + zero;
|
||||
entityShader.uniform1i("u_alphaClip", weather->fall.opaque);
|
||||
entityShader.uniform1f("u_opacity", weather->fall.opaque ? t * t : t);
|
||||
if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) {
|
||||
precipitation->render(camera, pause ? 0.0f : delta, *weather);
|
||||
}
|
||||
}
|
||||
|
||||
skybox->unbind();
|
||||
}
|
||||
|
||||
@ -306,36 +329,46 @@ void WorldRenderer::draw(
|
||||
Camera& camera,
|
||||
bool hudVisible,
|
||||
bool pause,
|
||||
float delta,
|
||||
PostProcessing* postProcessing
|
||||
float uiDelta,
|
||||
PostProcessing& postProcessing
|
||||
) {
|
||||
timer += delta * !pause;
|
||||
float delta = uiDelta * !pause;
|
||||
timer += delta;
|
||||
weather.update(delta);
|
||||
|
||||
auto world = level.getWorld();
|
||||
|
||||
const Viewport& vp = pctx.getViewport();
|
||||
camera.aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
|
||||
|
||||
const auto& settings = engine.getSettings();
|
||||
const auto& worldInfo = world->getInfo();
|
||||
|
||||
float sqrtT = glm::sqrt(weather.t);
|
||||
float clouds = weather.b.clouds * sqrtT +
|
||||
weather.a.clouds * (1.0f - sqrtT);
|
||||
clouds = glm::max(worldInfo.fog, clouds);
|
||||
float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f;
|
||||
|
||||
skybox->refresh(pctx, worldInfo.daytime, 1.0f + worldInfo.fog * 2.0f, 4);
|
||||
skybox->refresh(pctx, worldInfo.daytime, mie, 4);
|
||||
|
||||
const auto& assets = *engine.getAssets();
|
||||
auto& linesShader = assets.require<Shader>("lines");
|
||||
|
||||
/* World render scope with diegetic HUD included */ {
|
||||
DrawContext wctx = pctx.sub();
|
||||
postProcessing->use(wctx);
|
||||
postProcessing.use(wctx);
|
||||
|
||||
Window::clearDepth();
|
||||
|
||||
// Drawing background sky plane
|
||||
skybox->draw(pctx, camera, assets, worldInfo.daytime, worldInfo.fog);
|
||||
|
||||
skybox->draw(pctx, camera, assets, worldInfo.daytime, clouds);
|
||||
|
||||
/* Actually world render with depth buffer on */ {
|
||||
DrawContext ctx = wctx.sub();
|
||||
ctx.setDepthTest(true);
|
||||
ctx.setCullFace(true);
|
||||
renderLevel(ctx, camera, settings, delta, pause, hudVisible);
|
||||
renderLevel(ctx, camera, settings, uiDelta, pause, hudVisible);
|
||||
// Debug lines
|
||||
if (hudVisible) {
|
||||
if (debug) {
|
||||
@ -344,7 +377,7 @@ void WorldRenderer::draw(
|
||||
);
|
||||
}
|
||||
if (player.currentCamera == player.fpCamera) {
|
||||
renderHands(camera, delta * !pause);
|
||||
renderHands(camera, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -355,12 +388,12 @@ void WorldRenderer::draw(
|
||||
renderBlockOverlay(wctx);
|
||||
}
|
||||
|
||||
// Rendering fullscreen quad with
|
||||
// Rendering fullscreen quad
|
||||
auto screenShader = assets.get<Shader>("screen");
|
||||
screenShader->use();
|
||||
screenShader->uniform1f("u_timer", timer);
|
||||
screenShader->uniform1f("u_dayTime", worldInfo.daytime);
|
||||
postProcessing->render(pctx, screenShader);
|
||||
postProcessing.render(pctx, screenShader);
|
||||
}
|
||||
|
||||
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||
@ -414,3 +447,7 @@ void WorldRenderer::clear() {
|
||||
void WorldRenderer::setDebug(bool flag) {
|
||||
debug = flag;
|
||||
}
|
||||
|
||||
Weather& WorldRenderer::getWeather() {
|
||||
return weather;
|
||||
}
|
||||
|
||||
@ -10,6 +10,9 @@
|
||||
|
||||
#include "typedefs.hpp"
|
||||
|
||||
#include "presets/WeatherPreset.hpp"
|
||||
#include "world/Weather.hpp"
|
||||
|
||||
class Level;
|
||||
class Player;
|
||||
class Camera;
|
||||
@ -18,6 +21,7 @@ class LineBatch;
|
||||
class ChunksRenderer;
|
||||
class ParticlesRenderer;
|
||||
class BlockWrapsRenderer;
|
||||
class PrecipitationRenderer;
|
||||
class GuidesRenderer;
|
||||
class TextsRenderer;
|
||||
class Shader;
|
||||
@ -43,6 +47,7 @@ class WorldRenderer {
|
||||
std::unique_ptr<GuidesRenderer> guides;
|
||||
std::unique_ptr<Skybox> skybox;
|
||||
std::unique_ptr<ModelBatch> modelBatch;
|
||||
Weather weather {};
|
||||
|
||||
float timer = 0.0f;
|
||||
bool debug = false;
|
||||
@ -71,6 +76,7 @@ public:
|
||||
std::unique_ptr<TextsRenderer> texts;
|
||||
std::unique_ptr<ParticlesRenderer> particles;
|
||||
std::unique_ptr<BlockWrapsRenderer> blockWraps;
|
||||
std::unique_ptr<PrecipitationRenderer> precipitation;
|
||||
|
||||
static bool showChunkBorders;
|
||||
static bool showEntitiesDebug;
|
||||
@ -84,7 +90,7 @@ public:
|
||||
bool hudVisible,
|
||||
bool pause,
|
||||
float delta,
|
||||
PostProcessing* postProcessing
|
||||
PostProcessing& postProcessing
|
||||
);
|
||||
|
||||
/// @brief Render level without diegetic interface
|
||||
@ -103,4 +109,6 @@ public:
|
||||
void clear();
|
||||
|
||||
void setDebug(bool flag);
|
||||
|
||||
Weather& getWeather();
|
||||
};
|
||||
|
||||
@ -9,4 +9,10 @@ public:
|
||||
virtual ~Serializable() {}
|
||||
virtual dv::value serialize() const = 0;
|
||||
virtual void deserialize(const dv::value& src) = 0;
|
||||
|
||||
void deserializeOpt(const dv::optionalvalue& opt) {
|
||||
if (opt.ptr) {
|
||||
deserialize(*opt.ptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -46,6 +46,7 @@ extern const luaL_Reg utf8lib[];
|
||||
extern const luaL_Reg vec2lib[]; // vecn.cpp
|
||||
extern const luaL_Reg vec3lib[]; // vecn.cpp
|
||||
extern const luaL_Reg vec4lib[]; // vecn.cpp
|
||||
extern const luaL_Reg weatherlib[];
|
||||
extern const luaL_Reg worldlib[];
|
||||
|
||||
// Components
|
||||
|
||||
68
src/logic/scripting/lua/libs/libweather.cpp
Normal file
68
src/logic/scripting/lua/libs/libweather.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "libhud.hpp"
|
||||
|
||||
#include "world/Level.hpp"
|
||||
#include "world/World.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static Weather& require_weather() {
|
||||
if (level == nullptr) {
|
||||
throw std::runtime_error("world is not open");
|
||||
}
|
||||
return renderer->getWeather();
|
||||
}
|
||||
|
||||
static int l_change(lua::State* L) {
|
||||
WeatherPreset preset {};
|
||||
preset.deserialize(lua::tovalue(L, 1));
|
||||
float time = lua::tonumber(L, 2);
|
||||
std::string name;
|
||||
if (lua::isstring(L, 3)) {
|
||||
name = lua::tostring(L, 3);
|
||||
}
|
||||
auto& weather = require_weather();
|
||||
weather.change(std::move(preset), time, std::move(name));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_get_current(lua::State* L) {
|
||||
const auto& weather = require_weather();
|
||||
if (weather.t > 0.5f) {
|
||||
return lua::pushstring(L, weather.nameB);
|
||||
} else {
|
||||
return lua::pushstring(L, weather.nameA);
|
||||
}
|
||||
}
|
||||
|
||||
static int l_get_fall_intensity(lua::State* L) {
|
||||
auto& weather = require_weather();
|
||||
const auto& a = weather.a;
|
||||
const auto& b = weather.b;
|
||||
float t = weather.t;
|
||||
return lua::pushnumber(L,
|
||||
(a.fall.texture.empty() ? 0.0f : (1.0f - t)) +
|
||||
(b.fall.texture.empty() ? 0.0f : t)
|
||||
);
|
||||
}
|
||||
|
||||
static int l_get_current_data(lua::State* L) {
|
||||
auto& weather = require_weather();
|
||||
if (weather.t > 0.5f) {
|
||||
return lua::pushvalue(L, weather.b.serialize());
|
||||
} else {
|
||||
return lua::pushvalue(L, weather.a.serialize());
|
||||
}
|
||||
}
|
||||
|
||||
static int l_is_transition(lua::State* L) {
|
||||
return lua::pushboolean(L, require_weather().t < 1.0f);
|
||||
}
|
||||
|
||||
const luaL_Reg weatherlib[] = {
|
||||
{"change", lua::wrap<l_change>},
|
||||
{"get_current", lua::wrap<l_get_current>},
|
||||
{"get_current_data", lua::wrap<l_get_current_data>},
|
||||
{"get_fall_intensity", lua::wrap<l_get_fall_intensity>},
|
||||
{"is_transition", lua::wrap<l_is_transition>},
|
||||
{NULL, NULL}
|
||||
};
|
||||
@ -35,6 +35,7 @@ void scripting::on_frontend_init(Hud* hud, WorldRenderer* renderer) {
|
||||
lua::openlib(L, "hud", hudlib);
|
||||
lua::openlib(L, "gfx", "blockwraps", blockwrapslib);
|
||||
lua::openlib(L, "gfx", "particles", particleslib);
|
||||
lua::openlib(L, "gfx", "weather", weatherlib);
|
||||
lua::openlib(L, "gfx", "text3d", text3dlib);
|
||||
|
||||
load_script("hud_classes.lua");
|
||||
|
||||
@ -20,6 +20,8 @@ namespace util {
|
||||
class PseudoRandom {
|
||||
unsigned short seed;
|
||||
public:
|
||||
PseudoRandom(unsigned short seed) : seed(seed) {}
|
||||
|
||||
PseudoRandom() {
|
||||
seed = static_cast<unsigned short>(time(0));
|
||||
}
|
||||
|
||||
56
src/presets/WeatherPreset.cpp
Normal file
56
src/presets/WeatherPreset.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include "WeatherPreset.hpp"
|
||||
|
||||
#include "data/dv_util.hpp"
|
||||
|
||||
dv::value WeatherPreset::serialize() const {
|
||||
auto root = dv::object();
|
||||
|
||||
auto froot = dv::object();
|
||||
froot["texture"] = fall.texture;
|
||||
froot["vspeed"] = fall.vspeed;
|
||||
froot["hspeed"] = fall.hspeed;
|
||||
froot["scale"] = fall.scale;
|
||||
froot["noise"] = fall.noise;
|
||||
froot["min_opacity"] = fall.minOpacity;
|
||||
froot["max_opacity"] = fall.maxOpacity;
|
||||
froot["max_intensity"] = fall.maxIntensity;
|
||||
froot["opaque"] = fall.opaque;
|
||||
if (fall.splash) {
|
||||
froot["splash"] = fall.splash->serialize();
|
||||
}
|
||||
root["fall"] = froot;
|
||||
|
||||
root["fog_opacity"] = fogOpacity;
|
||||
root["fog_dencity"] = fogDencity;
|
||||
root["fog_curve"] = fogCurve;
|
||||
root["clouds"] = clouds;
|
||||
root["thunder_rate"] = thunderRate;
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
void WeatherPreset::deserialize(const dv::value& src) {
|
||||
if (src.has("fall")) {
|
||||
const auto& froot = src["fall"];
|
||||
froot.at("texture").get(fall.texture);
|
||||
froot.at("vspeed").get(fall.vspeed);
|
||||
froot.at("hspeed").get(fall.hspeed);
|
||||
froot.at("scale").get(fall.scale);
|
||||
froot.at("noise").get(fall.noise);
|
||||
froot.at("min_opacity").get(fall.minOpacity);
|
||||
froot.at("max_opacity").get(fall.maxOpacity);
|
||||
froot.at("max_intensity").get(fall.maxIntensity);
|
||||
froot.at("opaque").get(fall.opaque);
|
||||
|
||||
if (froot.has("splash")) {
|
||||
const auto& sroot = froot["splash"];
|
||||
fall.splash = ParticlesPreset {};
|
||||
fall.splash->deserialize(sroot);
|
||||
}
|
||||
}
|
||||
src.at("fog_opacity").get(fogOpacity);
|
||||
src.at("fog_dencity").get(fogDencity);
|
||||
src.at("fog_curve").get(fogCurve);
|
||||
src.at("clouds").get(clouds);
|
||||
src.at("thunder_rate").get(thunderRate);
|
||||
}
|
||||
65
src/presets/WeatherPreset.hpp
Normal file
65
src/presets/WeatherPreset.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "interfaces/Serializable.hpp"
|
||||
|
||||
#include "ParticlesPreset.hpp"
|
||||
|
||||
struct WeatherPreset : Serializable {
|
||||
struct {
|
||||
/// @brief Precipitation texture
|
||||
std::string texture;
|
||||
|
||||
/// @brief Fall sound
|
||||
std::string noise;
|
||||
|
||||
/// @brief Vertical speed
|
||||
float vspeed = 1.0f;
|
||||
|
||||
/// @brief Max horizontal speed
|
||||
float hspeed = 0.1f;
|
||||
|
||||
/// @brief UV scaling
|
||||
float scale = 0.1f;
|
||||
|
||||
/// @brief Fall opacity interpreted as zero.
|
||||
/// @example if 0.8 then opacity range is 0.8-max for 0.0-1.0 intensity
|
||||
float minOpacity = 0.0f;
|
||||
|
||||
/// @brief Fall opacity interpreted as one.
|
||||
/// @example if 0.8 then opacity range is min-0.8 for 0.0-1.0 intensity
|
||||
float maxOpacity = 1.0f;
|
||||
|
||||
/// @brief Max fall intencity
|
||||
/// (influences opacity, noise volume and splashes frequency)
|
||||
float maxIntensity = 1.0f;
|
||||
|
||||
/// @brief Clip texture by alpha channel
|
||||
bool opaque = false;
|
||||
|
||||
/// @brief Fall splash
|
||||
std::optional<ParticlesPreset> splash;
|
||||
} fall {};
|
||||
|
||||
/// @brief Max weather fog opacity
|
||||
float fogOpacity = 0.0f;
|
||||
|
||||
/// @brief Weather fog depth multiplier
|
||||
float fogDencity = 1.0f;
|
||||
|
||||
/// @brief Weather fog curve
|
||||
float fogCurve = 1.0f;
|
||||
|
||||
/// @brief Clouds opacity
|
||||
float clouds = 0.0f;
|
||||
|
||||
/// @brief Thunder rate in range 0.0-1.0 (1.0 is 100% - every second)
|
||||
float thunderRate = 0.0f;
|
||||
|
||||
/// @brief Weather effects intensity
|
||||
float intensity = 1.0f;
|
||||
|
||||
dv::value serialize() const override;
|
||||
void deserialize(const dv::value& src) override;
|
||||
};
|
||||
55
src/util/CentredMatrix.hpp
Normal file
55
src/util/CentredMatrix.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace util {
|
||||
template<typename T, int diameter, typename CoordT = int>
|
||||
class CentredMatrix {
|
||||
public:
|
||||
static constexpr CoordT radius = diameter / 2;
|
||||
|
||||
CentredMatrix() {}
|
||||
|
||||
void setCenter(CoordT x, CoordT y) {
|
||||
centerX = x;
|
||||
centerY = y;
|
||||
}
|
||||
|
||||
T& at(CoordT x, CoordT y) {
|
||||
x -= centerX - (diameter - radius);
|
||||
y -= centerY - (diameter - radius);
|
||||
if (x < 0 || y < 0 || x >= diameter || y >= diameter) {
|
||||
throw std::invalid_argument("position is out if matrix");
|
||||
}
|
||||
return arr.at(y * diameter + x);
|
||||
}
|
||||
|
||||
auto begin() {
|
||||
return arr.begin();
|
||||
}
|
||||
|
||||
auto end() {
|
||||
return arr.end();
|
||||
}
|
||||
|
||||
CoordT beginX() const {
|
||||
return centerX - (diameter - radius);
|
||||
}
|
||||
|
||||
CoordT beginY() const {
|
||||
return centerY - (diameter - radius);
|
||||
}
|
||||
|
||||
CoordT endX() const {
|
||||
return centerX + radius;
|
||||
}
|
||||
|
||||
CoordT endY() const {
|
||||
return centerY + radius;
|
||||
}
|
||||
private:
|
||||
std::array<T, diameter * diameter> arr;
|
||||
CoordT centerX = 0, centerY = 0;
|
||||
};
|
||||
}
|
||||
67
src/world/Weather.hpp
Normal file
67
src/world/Weather.hpp
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "presets/WeatherPreset.hpp"
|
||||
|
||||
struct Weather : Serializable {
|
||||
WeatherPreset a {};
|
||||
WeatherPreset b {};
|
||||
std::string nameA;
|
||||
std::string nameB;
|
||||
float t = 1.0f;
|
||||
float speed = 0.0f;
|
||||
|
||||
void update(float delta) {
|
||||
t += delta * speed;
|
||||
t = std::min(t, 1.0f);
|
||||
b.intensity = t;
|
||||
a.intensity = 1.0f - t;
|
||||
}
|
||||
|
||||
void change(WeatherPreset preset, float time, std::string name="") {
|
||||
std::swap(a, b);
|
||||
std::swap(nameA, nameB);
|
||||
b = std::move(preset);
|
||||
t = 0.0f;
|
||||
speed = 1.0f / std::max(time, 1.e-5f);
|
||||
nameB = std::move(name);
|
||||
update(0.0f);
|
||||
}
|
||||
|
||||
float fogOpacity() const {
|
||||
return b.fogOpacity * t + a.fogOpacity * (1.0f - t);
|
||||
}
|
||||
|
||||
float fogDencity() const {
|
||||
return b.fogDencity * t + a.fogDencity * (1.0f - t);
|
||||
}
|
||||
|
||||
float fogCurve() const {
|
||||
return b.fogCurve * t + a.fogCurve * (1.0f - t);
|
||||
}
|
||||
|
||||
float thunderRate() const {
|
||||
return b.thunderRate * t + a.thunderRate * (1.0f - t);
|
||||
}
|
||||
|
||||
dv::value serialize() const override {
|
||||
return dv::object({
|
||||
{"a", a.serialize()},
|
||||
{"b", b.serialize()},
|
||||
{"name-a", nameA},
|
||||
{"name-b", nameB},
|
||||
{"t", t},
|
||||
{"speed", speed},
|
||||
});
|
||||
}
|
||||
|
||||
void deserialize(const dv::value& src) override {
|
||||
a.deserializeOpt(src.at("a"));
|
||||
b.deserializeOpt(src.at("b"));
|
||||
src.at("name-a").get(nameA);
|
||||
src.at("name-b").get(nameB);
|
||||
src.at("t").get(t);
|
||||
src.at("speed").get(speed);
|
||||
}
|
||||
};
|
||||
@ -231,8 +231,8 @@ dv::value WorldInfo::serialize() const {
|
||||
timeobj["day-time-speed"] = daytimeSpeed;
|
||||
timeobj["total-time"] = totalTime;
|
||||
|
||||
auto& weatherobj = root.object("weather");
|
||||
weatherobj["fog"] = fog;
|
||||
root["weather"] = dv::object();
|
||||
root["weather"]["fog"] = fog;
|
||||
|
||||
root["next-inventory-id"] = nextInventoryId;
|
||||
root["next-entity-id"] = nextEntityId;
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "io/fwd.hpp"
|
||||
#include "content/ContentPack.hpp"
|
||||
#include "interfaces/Serializable.hpp"
|
||||
#include "io/fwd.hpp"
|
||||
#include "typedefs.hpp"
|
||||
#include "util/timeutil.hpp"
|
||||
|
||||
@ -33,7 +33,6 @@ struct WorldInfo : public Serializable {
|
||||
/// 0.5 - is noon
|
||||
float daytime = timeutil::time_value(10, 00, 00);
|
||||
|
||||
// looking bad
|
||||
float daytimeSpeed = 1.0f;
|
||||
|
||||
/// @brief total time passed in the world (not depending on daytimeSpeed)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user