@@ -5,7 +5,9 @@
|
||||
"sounds": [
|
||||
"blocks/door_open",
|
||||
"blocks/door_close",
|
||||
"events/pickup"
|
||||
"events/pickup",
|
||||
"ambient/rain",
|
||||
"ambient/thunder"
|
||||
],
|
||||
"models": [
|
||||
"drop-item"
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
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"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"clouds": 0.8
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"fog_opacity": 0.98,
|
||||
"fog_dencity": 3.6,
|
||||
"fog_curve": 0.25,
|
||||
"clouds": 0.8
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+29
-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;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 344 KiB |
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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+1
-2
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user