feat: 'lines' (cave-like structures/tunnels) (WIP)
This commit is contained in:
parent
8813d280dc
commit
116cbd61db
23
res/generators/default.files/script.lua
Normal file
23
res/generators/default.files/script.lua
Normal file
@ -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
|
||||||
@ -83,31 +83,38 @@ public:
|
|||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<StructurePlacement> placeStructures(
|
void perform_line(lua::State* L, PrototypePlacements& placements) {
|
||||||
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed,
|
rawgeti(L, 2);
|
||||||
const std::shared_ptr<Heightmap>& heightmap, uint chunkHeight
|
blockid_t block = touinteger(L, -1);
|
||||||
) override {
|
pop(L);
|
||||||
std::vector<StructurePlacement> placements;
|
|
||||||
|
|
||||||
stackguard _(L);
|
rawgeti(L, 3);
|
||||||
pushenv(L, *env);
|
glm::ivec3 a = tovec3(L, -1);
|
||||||
if (getfield(L, "place_structures")) {
|
pop(L);
|
||||||
pushivec_stack(L, offset);
|
|
||||||
pushivec_stack(L, size);
|
|
||||||
pushinteger(L, seed);
|
|
||||||
newuserdata<LuaHeightmap>(L, heightmap);
|
|
||||||
pushinteger(L, chunkHeight);
|
|
||||||
if (call_nothrow(L, 7, 1)) {
|
|
||||||
int len = objlen(L, -1);
|
|
||||||
for (int i = 1; i <= len; i++) {
|
|
||||||
rawgeti(L, i);
|
|
||||||
|
|
||||||
|
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);
|
rawgeti(L, 1);
|
||||||
int structIndex = 0;
|
int structIndex = 0;
|
||||||
if (isstring(L, -1)) {
|
if (isstring(L, -1)) {
|
||||||
const auto& found = def.structuresIndices.find(
|
const char* name = require_string(L, -1);
|
||||||
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()) {
|
if (found != def.structuresIndices.end()) {
|
||||||
structIndex = found->second;
|
structIndex = found->second;
|
||||||
}
|
}
|
||||||
@ -124,9 +131,31 @@ public:
|
|||||||
int rotation = tointeger(L, -1) & 0b11;
|
int rotation = tointeger(L, -1) & 0b11;
|
||||||
pop(L);
|
pop(L);
|
||||||
|
|
||||||
pop(L);
|
placements.structs.emplace_back(structIndex, pos, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
placements.emplace_back(structIndex, pos, rotation);
|
PrototypePlacements placeStructures(
|
||||||
|
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed,
|
||||||
|
const std::shared_ptr<Heightmap>& heightmap, uint chunkHeight
|
||||||
|
) override {
|
||||||
|
PrototypePlacements placements {};
|
||||||
|
|
||||||
|
stackguard _(L);
|
||||||
|
pushenv(L, *env);
|
||||||
|
if (getfield(L, "place_structures")) {
|
||||||
|
pushivec_stack(L, offset);
|
||||||
|
pushivec_stack(L, size);
|
||||||
|
pushinteger(L, seed);
|
||||||
|
newuserdata<LuaHeightmap>(L, heightmap);
|
||||||
|
pushinteger(L, chunkHeight);
|
||||||
|
if (call_nothrow(L, 7, 1)) {
|
||||||
|
int len = objlen(L, -1);
|
||||||
|
for (int i = 1; i <= len; i++) {
|
||||||
|
rawgeti(L, i);
|
||||||
|
|
||||||
|
perform_placement(L, placements);
|
||||||
|
|
||||||
|
pop(L);
|
||||||
}
|
}
|
||||||
pop(L);
|
pop(L);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,13 @@
|
|||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
namespace util {
|
namespace util {
|
||||||
|
constexpr inline float EPSILON = 1e-6f;
|
||||||
|
|
||||||
class PseudoRandom {
|
class PseudoRandom {
|
||||||
unsigned short seed;
|
unsigned short seed;
|
||||||
public:
|
public:
|
||||||
@ -63,4 +69,21 @@ namespace util {
|
|||||||
rand();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -118,6 +118,11 @@ struct Biome {
|
|||||||
BlocksLayers seaLayers;
|
BlocksLayers seaLayers;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PrototypePlacements {
|
||||||
|
std::vector<StructurePlacement> structs {};
|
||||||
|
std::vector<LinePlacement> lines {};
|
||||||
|
};
|
||||||
|
|
||||||
/// @brief Generator behaviour and settings interface
|
/// @brief Generator behaviour and settings interface
|
||||||
class GeneratorScript {
|
class GeneratorScript {
|
||||||
public:
|
public:
|
||||||
@ -156,8 +161,8 @@ public:
|
|||||||
/// @param seed world seed
|
/// @param seed world seed
|
||||||
/// @param heightmap area heightmap
|
/// @param heightmap area heightmap
|
||||||
/// @param chunkHeight chunk height to use as heights multiplier
|
/// @param chunkHeight chunk height to use as heights multiplier
|
||||||
/// @return structure placements
|
/// @return structure & line placements
|
||||||
virtual std::vector<StructurePlacement> placeStructures(
|
virtual PrototypePlacements placeStructures(
|
||||||
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed,
|
const glm::ivec2& offset, const glm::ivec2& size, uint64_t seed,
|
||||||
const std::shared_ptr<Heightmap>& heightmap, uint chunkHeight) = 0;
|
const std::shared_ptr<Heightmap>& heightmap, uint chunkHeight) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -14,3 +14,14 @@ struct StructurePlacement {
|
|||||||
rotation(rotation) {
|
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) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
#include "util/timeutil.hpp"
|
#include "util/timeutil.hpp"
|
||||||
#include "util/listutil.hpp"
|
#include "util/listutil.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
|
#include "maths/util.hpp"
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
static debug::Logger logger("world-generator");
|
static debug::Logger logger("world-generator");
|
||||||
@ -138,7 +139,7 @@ inline AABB gen_chunk_aabb(int chunkX, int chunkZ) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WorldGenerator::placeStructure(
|
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
|
int chunkX, int chunkZ
|
||||||
) {
|
) {
|
||||||
auto& structure = *def.structures[structureId]->fragments[rotation];
|
auto& structure = *def.structures[structureId]->fragments[rotation];
|
||||||
@ -157,8 +158,7 @@ void WorldGenerator::placeStructure(
|
|||||||
if (chunkAABB.intersect(aabb)) {
|
if (chunkAABB.intersect(aabb)) {
|
||||||
otherPrototype.structures.emplace_back(
|
otherPrototype.structures.emplace_back(
|
||||||
structureId,
|
structureId,
|
||||||
offset -
|
offset - glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D),
|
||||||
glm::ivec3(lcx * CHUNK_W, 0, lcz * CHUNK_D),
|
|
||||||
rotation
|
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(
|
void WorldGenerator::generateStructures(
|
||||||
ChunkPrototype& prototype, int chunkX, int chunkZ
|
ChunkPrototype& prototype, int chunkX, int chunkZ
|
||||||
) {
|
) {
|
||||||
@ -175,10 +202,12 @@ void WorldGenerator::generateStructures(
|
|||||||
const auto& biomes = prototype.biomes;
|
const auto& biomes = prototype.biomes;
|
||||||
const auto& heightmap = prototype.heightmap;
|
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,
|
{chunkX * CHUNK_W, chunkZ * CHUNK_D}, {CHUNK_W, CHUNK_D}, seed,
|
||||||
heightmap, CHUNK_H
|
heightmap, CHUNK_H
|
||||||
));
|
);
|
||||||
|
util::concat(prototype.structures, placements.structs);
|
||||||
|
|
||||||
for (const auto& placement : prototype.structures) {
|
for (const auto& placement : prototype.structures) {
|
||||||
const auto& offset = placement.position;
|
const auto& offset = placement.position;
|
||||||
if (placement.structure < 0 || placement.structure >= def.structures.size()) {
|
if (placement.structure < 0 || placement.structure >= def.structures.size()) {
|
||||||
@ -188,6 +217,9 @@ void WorldGenerator::generateStructures(
|
|||||||
placeStructure(
|
placeStructure(
|
||||||
offset, placement.structure, placement.rotation, chunkX, chunkZ);
|
offset, placement.structure, placement.rotation, chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
|
for (const auto& line : placements.lines) {
|
||||||
|
placeLine(line);
|
||||||
|
}
|
||||||
|
|
||||||
util::PseudoRandom structsRand;
|
util::PseudoRandom structsRand;
|
||||||
structsRand.setSeed(chunkX, chunkZ);
|
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 {
|
WorldGenDebugInfo WorldGenerator::createDebugInfo() const {
|
||||||
|
|||||||
@ -32,6 +32,8 @@ struct ChunkPrototype {
|
|||||||
std::shared_ptr<Heightmap> heightmap;
|
std::shared_ptr<Heightmap> heightmap;
|
||||||
|
|
||||||
std::vector<StructurePlacement> structures;
|
std::vector<StructurePlacement> structures;
|
||||||
|
|
||||||
|
std::vector<LinePlacement> lines;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WorldGenDebugInfo {
|
struct WorldGenDebugInfo {
|
||||||
@ -69,9 +71,11 @@ class WorldGenerator {
|
|||||||
void generateHeightmap(ChunkPrototype& prototype, int x, int z);
|
void generateHeightmap(ChunkPrototype& prototype, int x, int z);
|
||||||
|
|
||||||
void placeStructure(
|
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
|
int chunkX, int chunkZ
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void placeLine(const LinePlacement& line);
|
||||||
public:
|
public:
|
||||||
WorldGenerator(
|
WorldGenerator(
|
||||||
const GeneratorDef& def,
|
const GeneratorDef& def,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user