diff --git a/res/content/base/blocks/torch.json b/res/content/base/blocks/torch.json index 133f9739..47f02790 100644 --- a/res/content/base/blocks/torch.json +++ b/res/content/base/blocks/torch.json @@ -15,5 +15,21 @@ "obstacle": false, "grounded": true, "rotation": "pipe", - "material": "base:wood" + "material": "base:wood", + "particles": { + "lifetime": 2.0, + "spawn_interval": 0.3, + "acceleration": [0, 0, 0], + "velocity": [0, 0.3, 0], + "explosion": [0, 0, 0], + "size": [0.2, 0.2, 0.2], + "spawn_shape": "ball", + "spawn_spread": [0.05, 0.05, 0.05], + "lighting": false, + "frames": [ + "particles:fire_0", + "particles:smoke_0", + "particles:smoke_1" + ] + } } diff --git a/res/content/base/scripts/torch.lua b/res/content/base/scripts/torch.lua deleted file mode 100644 index 7a1d2848..00000000 --- a/res/content/base/scripts/torch.lua +++ /dev/null @@ -1,18 +0,0 @@ -function on_placed(x, y, z) - particles.emit({x + 0.5, y + 0.4, z + 0.5}, -1, { - lifetime=2.0, - spawn_interval=0.3, - acceleration={0, 0, 0}, - velocity={0, 0.3, 0}, - explosion={0, 0, 0}, - size={0.2, 0.2, 0.2}, - spawn_shape="ball", - spawn_spread={0.05, 0.05, 0.05}, - lighting=false, - frames={ - "particles:fire_0", - "particles:smoke_0", - "particles:smoke_1" - } - }) -end diff --git a/src/content/ContentLoader.cpp b/src/content/ContentLoader.cpp index 7bad427e..0e4ef3cf 100644 --- a/src/content/ContentLoader.cpp +++ b/src/content/ContentLoader.cpp @@ -22,6 +22,7 @@ #include "voxels/Block.hpp" #include "data/dv_util.hpp" #include "data/StructLayout.hpp" +#include "presets/ParticlesPreset.hpp" namespace fs = std::filesystem; using namespace data; @@ -335,6 +336,11 @@ void ContentLoader::loadBlock( perform_user_block_fields(def.name, *def.dataStruct); } + if (root.has("particles")) { + def.particles = std::make_unique(); + def.particles->deserialize(root["particles"]); + } + if (def.tickInterval == 0) { def.tickInterval = 1; } diff --git a/src/frontend/screens/LevelScreen.cpp b/src/frontend/screens/LevelScreen.cpp index b8bb59e6..ad8f35c6 100644 --- a/src/frontend/screens/LevelScreen.cpp +++ b/src/frontend/screens/LevelScreen.cpp @@ -14,6 +14,7 @@ #include "graphics/core/PostProcessing.hpp" #include "graphics/core/Viewport.hpp" #include "graphics/render/WorldRenderer.hpp" +#include "graphics/render/Decorator.hpp" #include "graphics/ui/elements/Menu.hpp" #include "graphics/ui/GUI.hpp" #include "logic/LevelController.hpp" @@ -29,20 +30,25 @@ static debug::Logger logger("level-screen"); -LevelScreen::LevelScreen(Engine* engine, std::unique_ptr level) +LevelScreen::LevelScreen(Engine* engine, std::unique_ptr levelPtr) : Screen(engine), postProcessing(std::make_unique()) { + Level* level = levelPtr.get(); + auto& settings = engine->getSettings(); auto assets = engine->getAssets(); auto menu = engine->getGUI()->getMenu(); menu->reset(); - controller = std::make_unique(engine, std::move(level)); + controller = std::make_unique(engine, std::move(levelPtr)); frontend = std::make_unique(controller->getPlayer(), controller.get(), assets); worldRenderer = std::make_unique(engine, frontend.get(), controller->getPlayer()); hud = std::make_unique(engine, frontend.get(), controller->getPlayer()); - + + decorator = + std::make_unique(*level, *worldRenderer->particles, *assets); + keepAlive(settings.graphics.backlight.observe([=](bool) { controller->getLevel()->chunks->saveAndClear(); worldRenderer->clear(); @@ -156,6 +162,7 @@ void LevelScreen::update(float delta) { } controller->update(glm::min(delta, 0.2f), !inputLocked, hud->isPause()); hud->update(hudVisible); + decorator->update(delta, *camera); } void LevelScreen::draw(float delta) { diff --git a/src/frontend/screens/LevelScreen.hpp b/src/frontend/screens/LevelScreen.hpp index d2fe1a58..db0f04ff 100644 --- a/src/frontend/screens/LevelScreen.hpp +++ b/src/frontend/screens/LevelScreen.hpp @@ -12,6 +12,7 @@ class WorldRenderer; class TextureAnimator; class PostProcessing; class ContentPackRuntime; +class Decorator; class Level; class LevelScreen : public Screen { @@ -20,6 +21,7 @@ class LevelScreen : public Screen { std::unique_ptr worldRenderer; std::unique_ptr animator; std::unique_ptr postProcessing; + std::unique_ptr decorator; std::unique_ptr hud; void saveWorldPreview(); diff --git a/src/graphics/render/Decorator.cpp b/src/graphics/render/Decorator.cpp new file mode 100644 index 00000000..51eca386 --- /dev/null +++ b/src/graphics/render/Decorator.cpp @@ -0,0 +1,103 @@ +#include "Decorator.hpp" + +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include "ParticlesRenderer.hpp" +#include "assets/assets_util.hpp" +#include "content/Content.hpp" +#include "voxels/Chunks.hpp" +#include "voxels/Block.hpp" +#include "world/Level.hpp" +#include "window/Camera.hpp" + +/// @brief Not greather than 64 for this BIG_PRIME value +inline constexpr int UPDATE_AREA_DIAMETER = 32; +/// @brief Number of blocks in the volume +inline constexpr int UPDATE_BLOCKS = + UPDATE_AREA_DIAMETER * UPDATE_AREA_DIAMETER * UPDATE_AREA_DIAMETER; +/// @brief Number of update iterations +inline constexpr int ITERATIONS = 512; +/// @brief Big prime number used for pseudo-random 3d array iteration +inline constexpr int BIG_PRIME = 666667; + +Decorator::Decorator( + const Level& level, ParticlesRenderer& particles, const Assets& assets +) + : level(level), particles(particles), assets(assets) { +} + +void Decorator::update( + float delta, const glm::ivec3& areaStart, const glm::ivec3& areaCenter +) { + int index = currentIndex; + currentIndex = (currentIndex + BIG_PRIME) % UPDATE_BLOCKS; + + const auto& chunks = *level.chunks; + const auto& indices = *level.content->getIndices(); + + int lx = index % UPDATE_AREA_DIAMETER; + 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); + + if (auto vox = chunks.get(pos)) { + const auto& def = indices.blocks.require(vox->id); + if (def.particles) { + const auto& found = blockEmitters.find(pos); + if (found == blockEmitters.end()) { + auto treg = util::get_texture_region( + assets, def.particles->texture, "" + ); + blockEmitters[pos] = particles.add(std::make_unique( + level, + glm::vec3{pos.x + 0.5, pos.y + 0.5, pos.z + 0.5}, + *def.particles, + treg.texture, + treg.region, + -1 + )); + } + } + } +} + +void Decorator::update(float delta, const Camera& camera) { + glm::ivec3 pos = camera.position; + pos -= glm::ivec3(UPDATE_AREA_DIAMETER / 2); + for (int i = 0; i < ITERATIONS; i++) { + update(delta, pos, camera.position); + } + const auto& chunks = *level.chunks; + const auto& indices = *level.content->getIndices(); + auto iter = blockEmitters.begin(); + while (iter != blockEmitters.end()) { + auto emitter = particles.getEmitter(iter->second); + if (emitter == nullptr) { + iter = blockEmitters.erase(iter); + continue; + } + + bool remove = false; + if (auto vox = chunks.get(iter->first)) { + const auto& def = indices.blocks.require(vox->id); + if (def.particles == nullptr) { + remove = true; + } + } else { + iter = blockEmitters.erase(iter); + continue; + } + if (util::distance2(iter->first, glm::ivec3(camera.position)) > + UPDATE_AREA_DIAMETER * UPDATE_AREA_DIAMETER) { + remove = true; + } + if (remove) { + emitter->stop(); + iter = blockEmitters.erase(iter); + continue; + } + iter++; + } +} diff --git a/src/graphics/render/Decorator.hpp b/src/graphics/render/Decorator.hpp new file mode 100644 index 00000000..83d8a661 --- /dev/null +++ b/src/graphics/render/Decorator.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include + +class Level; +class Chunks; +class Camera; +class Assets; +class ParticlesRenderer; + +class Decorator { + const Level& level; + const Assets& assets; + ParticlesRenderer& particles; + std::unordered_map blockEmitters; + int currentIndex = 0; + + void update( + float delta, const glm::ivec3& areaStart, const glm::ivec3& areaCenter + ); +public: + Decorator( + const Level& level, ParticlesRenderer& particles, const Assets& assets + ); + + void update(float delta, const Camera& camera); +}; diff --git a/src/presets/ParticlesPreset.hpp b/src/presets/ParticlesPreset.hpp index fcaddb3f..b3fa4be5 100644 --- a/src/presets/ParticlesPreset.hpp +++ b/src/presets/ParticlesPreset.hpp @@ -25,7 +25,7 @@ struct ParticlesPreset : public Serializable { /// @brief Apply lighting bool lighting = true; /// @brief Max distance of actually spawning particles. - float maxDistance = 32.0f; + float maxDistance = 16.0f; /// @brief Particles spawn interval float spawnInterval = 0.1f; /// @brief Particle life time diff --git a/src/voxels/Block.cpp b/src/voxels/Block.cpp index 963a0928..d94f6c82 100644 --- a/src/voxels/Block.cpp +++ b/src/voxels/Block.cpp @@ -5,6 +5,7 @@ #include "core_defs.hpp" #include "data/StructLayout.hpp" +#include "presets/ParticlesPreset.hpp" #include "util/stringutil.hpp" std::string to_string(BlockModel model) { @@ -142,6 +143,7 @@ void Block::cloneTo(Block& dst) { dst.inventorySize = inventorySize; dst.tickInterval = tickInterval; dst.overlayTexture = overlayTexture; + dst.particles = std::make_unique(*particles); } static std::set> RESERVED_BLOCK_FIELDS { diff --git a/src/voxels/Block.hpp b/src/voxels/Block.hpp index c2ff6ce2..d812e48f 100644 --- a/src/voxels/Block.hpp +++ b/src/voxels/Block.hpp @@ -10,6 +10,8 @@ #include "maths/aabb.hpp" #include "typedefs.hpp" +struct ParticlesPreset; + namespace data { class StructLayout; } @@ -199,6 +201,8 @@ public: std::unique_ptr dataStruct; + std::unique_ptr particles; + /// @brief Runtime indices (content indexing results) struct { /// @brief block runtime integer id diff --git a/src/voxels/Chunks.hpp b/src/voxels/Chunks.hpp index 98979423..67746199 100644 --- a/src/voxels/Chunks.hpp +++ b/src/voxels/Chunks.hpp @@ -60,6 +60,10 @@ public: return get(pos.x, pos.y, pos.z); } + inline const voxel* get(glm::ivec3 pos) const { + return get(pos.x, pos.y, pos.z); + } + light_t getLight(int32_t x, int32_t y, int32_t z) const; ubyte getLight(int32_t x, int32_t y, int32_t z, int channel) const; void set(int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state);