diff --git a/res/generators/default.files/script.lua b/res/generators/default.files/script.lua new file mode 100644 index 00000000..3af69134 --- /dev/null +++ b/res/generators/default.files/script.lua @@ -0,0 +1,23 @@ +-- TODO: delete this file after caves complete implementation + +function generate_heightmap(x, y, w, h, seed, s) + local map = Heightmap(w, h) + map:add(0.25) + return map +end + + +function place_structures(x, z, w, d, seed, hmap, chunk_height) + local placements = {} + do + local sy = math.random() * (chunk_height / 4) + local ey = math.random() * (chunk_height / 4) + local sx = x + math.random() * 20 - 10 + local ex = x + math.random() * 20 - 10 + local sz = z + math.random() * 20 - 10 + local ez = z + math.random() * 20 - 10 + table.insert(placements, + {":line", 0, {sx - 10, sy, sz - 10}, {ex + 10, ey, ez + 10}, 2}) + end + return placements +end diff --git a/src/logic/scripting/scripting_world_generation.cpp b/src/logic/scripting/scripting_world_generation.cpp index c7655084..7a872e18 100644 --- a/src/logic/scripting/scripting_world_generation.cpp +++ b/src/logic/scripting/scripting_world_generation.cpp @@ -83,11 +83,62 @@ public: return maps; } - std::vector placeStructures( + void perform_line(lua::State* L, PrototypePlacements& placements) { + rawgeti(L, 2); + blockid_t block = touinteger(L, -1); + pop(L); + + rawgeti(L, 3); + glm::ivec3 a = tovec3(L, -1); + pop(L); + + rawgeti(L, 4); + glm::ivec3 b = tovec3(L, -1); + pop(L); + + rawgeti(L, 5); + int radius = touinteger(L, -1); + pop(L); + + placements.lines.emplace_back(block, a, b, radius); + } + + void perform_placement(lua::State* L, PrototypePlacements& placements) { + rawgeti(L, 1); + int structIndex = 0; + if (isstring(L, -1)) { + const char* name = require_string(L, -1); + if (!std::strcmp(name, ":line")) { + pop(L); + + perform_line(L, placements); + return; + } + const auto& found = def.structuresIndices.find(name); + if (found != def.structuresIndices.end()) { + structIndex = found->second; + } + } else { + structIndex = tointeger(L, -1); + } + pop(L); + + rawgeti(L, 2); + glm::ivec3 pos = tovec3(L, -1); + pop(L); + + rawgeti(L, 3); + int rotation = tointeger(L, -1) & 0b11; + pop(L); + + placements.structs.emplace_back(structIndex, pos, rotation); + } + + PrototypePlacements placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight ) override { - std::vector placements; + PrototypePlacements placements {}; stackguard _(L); pushenv(L, *env); @@ -102,31 +153,9 @@ public: for (int i = 1; i <= len; i++) { rawgeti(L, i); - rawgeti(L, 1); - int structIndex = 0; - if (isstring(L, -1)) { - const auto& found = def.structuresIndices.find( - require_string(L, -1) - ); - if (found != def.structuresIndices.end()) { - structIndex = found->second; - } - } else { - structIndex = tointeger(L, -1); - } - pop(L); - - rawgeti(L, 2); - glm::ivec3 pos = tovec3(L, -1); - pop(L); - - rawgeti(L, 3); - int rotation = tointeger(L, -1) & 0b11; - pop(L); + perform_placement(L, placements); pop(L); - - placements.emplace_back(structIndex, pos, rotation); } pop(L); } diff --git a/src/maths/util.hpp b/src/maths/util.hpp index 432fe0bb..ddedb855 100644 --- a/src/maths/util.hpp +++ b/src/maths/util.hpp @@ -4,7 +4,13 @@ #include +#define GLM_ENABLE_EXPERIMENTAL +#include +#include + namespace util { + constexpr inline float EPSILON = 1e-6f; + class PseudoRandom { unsigned short seed; public: @@ -63,4 +69,21 @@ namespace util { rand(); } }; + + /// @brief Find nearest point on segment to given + /// @param a segment point A + /// @param b segment point B + /// @param point given point (may be anywhere) + /// @return nearest point on the segment to given point + inline glm::vec3 closest_point_on_segment( + glm::vec3 a, glm::vec3 b, const glm::vec3& point + ) { + auto vec = b - a; + float da = glm::distance2(point, a); + float db = glm::distance2(point, b); + float len = glm::length2(vec); + float t = (((da - db) / len) * 0.5f + 0.5f); + t = std::min(1.0f, std::max(0.0f, t)); + return a + vec * t; + } } diff --git a/src/world/generator/GeneratorDef.hpp b/src/world/generator/GeneratorDef.hpp index 630f7400..8dec4fbb 100644 --- a/src/world/generator/GeneratorDef.hpp +++ b/src/world/generator/GeneratorDef.hpp @@ -118,6 +118,11 @@ struct Biome { BlocksLayers seaLayers; }; +struct PrototypePlacements { + std::vector structs {}; + std::vector lines {}; +}; + /// @brief Generator behaviour and settings interface class GeneratorScript { public: @@ -156,8 +161,8 @@ public: /// @param seed world seed /// @param heightmap area heightmap /// @param chunkHeight chunk height to use as heights multiplier - /// @return structure placements - virtual std::vector placeStructures( + /// @return structure & line placements + virtual PrototypePlacements placeStructures( const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed, const std::shared_ptr& heightmap, uint chunkHeight) = 0; }; diff --git a/src/world/generator/StructurePlacement.hpp b/src/world/generator/StructurePlacement.hpp index ba688ddb..58f90c78 100644 --- a/src/world/generator/StructurePlacement.hpp +++ b/src/world/generator/StructurePlacement.hpp @@ -14,3 +14,14 @@ struct StructurePlacement { rotation(rotation) { } }; + +struct LinePlacement { + blockid_t block; + glm::ivec3 a; + glm::ivec3 b; + int radius; + + LinePlacement(blockid_t block, glm::ivec3 a, glm::ivec3 b, int radius) + : block(block), a(std::move(a)), b(std::move(b)), radius(radius) { + } +}; diff --git a/src/world/generator/WorldGenerator.cpp b/src/world/generator/WorldGenerator.cpp index e31f3cb0..cb981c01 100644 --- a/src/world/generator/WorldGenerator.cpp +++ b/src/world/generator/WorldGenerator.cpp @@ -11,6 +11,7 @@ #include "util/timeutil.hpp" #include "util/listutil.hpp" #include "maths/voxmaths.hpp" +#include "maths/util.hpp" #include "debug/Logger.hpp" static debug::Logger logger("world-generator"); @@ -138,7 +139,7 @@ inline AABB gen_chunk_aabb(int chunkX, int chunkZ) { } void WorldGenerator::placeStructure( - const glm::ivec3 offset, size_t structureId, uint8_t rotation, + const glm::ivec3& offset, size_t structureId, uint8_t rotation, int chunkX, int chunkZ ) { auto& structure = *def.structures[structureId]->fragments[rotation]; @@ -157,8 +158,7 @@ void WorldGenerator::placeStructure( if (chunkAABB.intersect(aabb)) { otherPrototype.structures.emplace_back( structureId, - offset - - glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), + offset - glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D), rotation ); } @@ -166,6 +166,33 @@ void WorldGenerator::placeStructure( } } +void WorldGenerator::placeLine(const LinePlacement& line) { + AABB aabb(line.a, line.b); + aabb.fix(); + aabb.a -= line.radius; + aabb.b += line.radius; + int cxa = floordiv(aabb.a.x, CHUNK_W); + int cza = floordiv(aabb.a.z, CHUNK_D); + int cxb = floordiv(aabb.b.x, CHUNK_W); + int czb = floordiv(aabb.b.z, CHUNK_D); + for (int cz = cza; cz <= czb; cz++) { + for (int cx = cxa; cx <= cxb; cx++) { + auto& otherPrototype = requirePrototype(cx, cz); + auto chunkAABB = gen_chunk_aabb(cx, cz); + chunkAABB.a -= line.radius; + chunkAABB.b += line.radius; + auto found = util::closest_point_on_segment(line.a, line.b, { + cx * CHUNK_W + CHUNK_W / 2, + 0, + cz * CHUNK_D + CHUNK_D / 2 + }); + if (chunkAABB.contains(found)) { + otherPrototype.lines.push_back(line); + } + } + } +} + void WorldGenerator::generateStructures( ChunkPrototype& prototype, int chunkX, int chunkZ ) { @@ -175,10 +202,12 @@ void WorldGenerator::generateStructures( const auto& biomes = prototype.biomes; const auto& heightmap = prototype.heightmap; - util::concat(prototype.structures, def.script->placeStructures( + auto placements = def.script->placeStructures( {chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed, heightmap, CHUNK_H - )); + ); + util::concat(prototype.structures, placements.structs); + for (const auto& placement : prototype.structures) { const auto& offset = placement.position; if (placement.structure < 0 || placement.structure >= def.structures.size()) { @@ -188,6 +217,9 @@ void WorldGenerator::generateStructures( placeStructure( offset, placement.structure, placement.rotation, chunkX, chunkZ); } + for (const auto& line : placements.lines) { + placeLine(line); + } util::PseudoRandom structsRand; structsRand.setSeed(chunkX, chunkZ); @@ -358,6 +390,22 @@ void WorldGenerator::generate(voxel* voxels, int chunkX, int chunkZ) { } } } + for (const auto& line : prototype.lines) { + int minY = std::max(0, std::min(line.a.y-line.radius, line.b.y-line.radius)); + int maxY = std::min(CHUNK_H, std::max(line.a.y+line.radius, line.b.y+line.radius)); + for (int y = minY; y < maxY; y++) { + for (int z = 0; z < CHUNK_D; z++) { + for (int x = 0; x < CHUNK_W; x++) { + int gx = x + chunkX * CHUNK_W; + int gz = z + chunkZ * CHUNK_D; + + if (glm::distance2(util::closest_point_on_segment(line.a, line.b, {gx, y, gz}), {gx, y, gz}) <= line.radius*line.radius) { + voxels[vox_index(x, y, z)] = {line.block, {}}; + } + } + } + } + } } WorldGenDebugInfo WorldGenerator::createDebugInfo() const { diff --git a/src/world/generator/WorldGenerator.hpp b/src/world/generator/WorldGenerator.hpp index 7490ef4d..a503c312 100644 --- a/src/world/generator/WorldGenerator.hpp +++ b/src/world/generator/WorldGenerator.hpp @@ -32,6 +32,8 @@ struct ChunkPrototype { std::shared_ptr heightmap; std::vector structures; + + std::vector lines; }; struct WorldGenDebugInfo { @@ -69,9 +71,11 @@ class WorldGenerator { void generateHeightmap(ChunkPrototype& prototype, int x, int z); void placeStructure( - const glm::ivec3 offset, size_t structure, uint8_t rotation, + const glm::ivec3& offset, size_t structure, uint8_t rotation, int chunkX, int chunkZ ); + + void placeLine(const LinePlacement& line); public: WorldGenerator( const GeneratorDef& def,