Merge pull request #701 from MihailRis/split-the-pr
extract Feature/block placement from #659
This commit is contained in:
commit
8de9295fcc
@ -427,6 +427,22 @@ Where:
|
|||||||
- point_a, point_b - vec3, vec3 positions of the start and end of the tunnel.
|
- point_a, point_b - vec3, vec3 positions of the start and end of the tunnel.
|
||||||
- radius - radius of the tunnel in blocks
|
- radius - radius of the tunnel in blocks
|
||||||
|
|
||||||
|
Single block:
|
||||||
|
```lua
|
||||||
|
{":block", block_id, position, [rotation], [priority]}
|
||||||
|
```
|
||||||
|
|
||||||
|
Where:
|
||||||
|
- block_id: numeric runtime id of the block to place.
|
||||||
|
- position: vec3 world position in blocks, relative to the current chunk start.
|
||||||
|
- rotation: 0–3, rotation around the Y axis. Default: 0. For extended blocks (size > 1), all segments use this rotation.
|
||||||
|
- priority: integer order. Higher values are placed later and overwrite lower‑priority placements.
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- `:block` automatically expands extended blocks into all their segments and replaces any voxels occupying those cells.
|
||||||
|
- Placement is chunk‑border safe: the engine distributes the placement to all affected chunk prototypes based on the block’s size/AABB.
|
||||||
|
- Use `:block` for single blocks; use `:line` for tunnels or continuous lines.
|
||||||
|
|
||||||
### Small structures placement
|
### Small structures placement
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|||||||
@ -430,6 +430,22 @@ end
|
|||||||
- точка_а, точка_б - vec3, vec3 позиции начала и конца тоннеля.
|
- точка_а, точка_б - vec3, vec3 позиции начала и конца тоннеля.
|
||||||
- радиус - радиус тоннеля в блоках
|
- радиус - радиус тоннеля в блоках
|
||||||
|
|
||||||
|
Одиночный блок:
|
||||||
|
```lua
|
||||||
|
{":block", id_блока, позиция, [поворот], [приоритет]}
|
||||||
|
```
|
||||||
|
|
||||||
|
Где:
|
||||||
|
- id_блока: числовой runtime‑id блока, который нужно поставить.
|
||||||
|
- позиция: vec3 позиция в блоках относительно начала текущего чанка.
|
||||||
|
- поворот: 0–3, поворот вокруг оси Y. По умолчанию: 0. Для расширенных блоков (размер > 1) этот поворот применяется ко всем сегментам.
|
||||||
|
- приоритет: целое число. Бóльший приоритет ставится позже и перезаписывает более низкий.
|
||||||
|
|
||||||
|
Примечания:
|
||||||
|
- `:block` автоматически раскладывает расширенные блоки на сегменты и заменяет любые блоки в занимаемых ячейках.
|
||||||
|
- Размещение корректно работает на границах чанков: движок сам разносит плейсмент по затрагиваемым прототипам на основе размера/AABB блока.
|
||||||
|
- `:block` используйте для точечных блоков; `:line` — для туннелей/линий.
|
||||||
|
|
||||||
|
|
||||||
### Расстановка малых структур
|
### Расстановка малых структур
|
||||||
|
|
||||||
|
|||||||
@ -152,6 +152,32 @@ public:
|
|||||||
placements.emplace_back(priority, LinePlacement {block, a, b, radius});
|
placements.emplace_back(priority, LinePlacement {block, a, b, radius});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void perform_block(lua::State* L, std::vector<Placement>& placements) {
|
||||||
|
rawgeti(L, 2);
|
||||||
|
blockid_t block = touinteger(L, -1);
|
||||||
|
pop(L);
|
||||||
|
|
||||||
|
rawgeti(L, 3);
|
||||||
|
glm::ivec3 pos = tovec3(L, -1);
|
||||||
|
pop(L);
|
||||||
|
|
||||||
|
uint8_t rotation = 0;
|
||||||
|
if (objlen(L, -1) >= 4) {
|
||||||
|
rawgeti(L, 4);
|
||||||
|
rotation = tointeger(L, -1) & 0b11;
|
||||||
|
pop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
int priority = 0;
|
||||||
|
if (objlen(L, -1) >= 5) {
|
||||||
|
rawgeti(L, 5);
|
||||||
|
priority = tointeger(L, -1);
|
||||||
|
pop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
placements.emplace_back(priority, BlockPlacement {block, pos, rotation});
|
||||||
|
}
|
||||||
|
|
||||||
void perform_placement(lua::State* L, std::vector<Placement>& placements) {
|
void perform_placement(lua::State* L, std::vector<Placement>& placements) {
|
||||||
rawgeti(L, 1);
|
rawgeti(L, 1);
|
||||||
int structIndex = 0;
|
int structIndex = 0;
|
||||||
@ -162,6 +188,11 @@ public:
|
|||||||
|
|
||||||
perform_line(L, placements);
|
perform_line(L, placements);
|
||||||
return;
|
return;
|
||||||
|
} else if (!std::strcmp(name, ":block")) {
|
||||||
|
pop(L);
|
||||||
|
|
||||||
|
perform_block(L, placements);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
const auto& found = def.structuresIndices.find(name);
|
const auto& found = def.structuresIndices.find(name);
|
||||||
if (found != def.structuresIndices.end()) {
|
if (found != def.structuresIndices.end()) {
|
||||||
|
|||||||
@ -27,12 +27,23 @@ struct LinePlacement {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BlockPlacement {
|
||||||
|
blockid_t block;
|
||||||
|
glm::ivec3 position;
|
||||||
|
uint8_t rotation;
|
||||||
|
bool mirror;
|
||||||
|
|
||||||
|
BlockPlacement(blockid_t block, glm::ivec3 position, uint8_t rotation, bool mirror=false)
|
||||||
|
: block(block), position(std::move(position)), rotation(rotation), mirror(mirror) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct Placement {
|
struct Placement {
|
||||||
int priority;
|
int priority;
|
||||||
std::variant<StructurePlacement, LinePlacement> placement;
|
std::variant<StructurePlacement, LinePlacement, BlockPlacement> placement;
|
||||||
|
|
||||||
Placement(
|
Placement(
|
||||||
int priority,
|
int priority,
|
||||||
std::variant<StructurePlacement, LinePlacement> placement
|
std::variant<StructurePlacement, LinePlacement, BlockPlacement> placement
|
||||||
) : priority(priority), placement(std::move(placement)) {}
|
) : priority(priority), placement(std::move(placement)) {}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -204,6 +204,47 @@ void WorldGenerator::placeLine(const LinePlacement& line, int priority) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldGenerator::placeBlock(const BlockPlacement& block, int priority) {
|
||||||
|
// Compute world-space AABB of the extended block to distribute to prototypes
|
||||||
|
const auto& indices = content.getIndices()->blocks;
|
||||||
|
const auto& def = indices.require(block.block);
|
||||||
|
const auto& rot = def.rotations.variants[block.rotation & 0b11];
|
||||||
|
|
||||||
|
glm::ivec3 minp = block.position;
|
||||||
|
glm::ivec3 maxp = block.position;
|
||||||
|
const auto size = def.size;
|
||||||
|
for (int sy = 0; sy < size.y; sy++) {
|
||||||
|
for (int sz = 0; sz < size.z; sz++) {
|
||||||
|
for (int sx = 0; sx < size.x; sx++) {
|
||||||
|
glm::ivec3 p = block.position;
|
||||||
|
p += rot.axes[0] * sx;
|
||||||
|
p += rot.axes[1] * sy;
|
||||||
|
p += rot.axes[2] * sz;
|
||||||
|
minp = glm::min(minp, p);
|
||||||
|
maxp = glm::max(maxp, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// inclusive-exclusive for max; expand by 1 to compute chunk coverage
|
||||||
|
maxp += glm::ivec3(1, 1, 1);
|
||||||
|
AABB aabb(minp, maxp);
|
||||||
|
int cxa = floordiv<CHUNK_W>(aabb.a.x);
|
||||||
|
int cza = floordiv<CHUNK_D>(aabb.a.z);
|
||||||
|
int cxb = floordiv<CHUNK_W>(aabb.b.x);
|
||||||
|
int czb = floordiv<CHUNK_D>(aabb.b.z);
|
||||||
|
for (int cz = cza; cz <= czb; cz++) {
|
||||||
|
for (int cx = cxa; cx <= cxb; cx++) {
|
||||||
|
const auto& found = prototypes.find({cx, cz});
|
||||||
|
if (found != prototypes.end()) {
|
||||||
|
// position becomes relative to prototype chunk
|
||||||
|
glm::ivec3 rel = block.position - glm::ivec3(cx * CHUNK_W, 0, cz * CHUNK_D);
|
||||||
|
bool owner = (cx == floordiv<CHUNK_W>(block.position.x)) && (cz == floordiv<CHUNK_D>(block.position.z));
|
||||||
|
found->second->placements.emplace_back(priority, BlockPlacement{block.block, rel, block.rotation, !owner});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void WorldGenerator::placeStructures(
|
void WorldGenerator::placeStructures(
|
||||||
const std::vector<Placement>& placements,
|
const std::vector<Placement>& placements,
|
||||||
ChunkPrototype& prototype,
|
ChunkPrototype& prototype,
|
||||||
@ -217,9 +258,11 @@ void WorldGenerator::placeStructures(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
placeStructure(*sp, placement.priority, chunkX, chunkZ);
|
placeStructure(*sp, placement.priority, chunkX, chunkZ);
|
||||||
|
} else if (auto lp = std::get_if<LinePlacement>(&placement.placement)) {
|
||||||
|
placeLine(*lp, placement.priority);
|
||||||
} else {
|
} else {
|
||||||
const auto& line = std::get<LinePlacement>(placement.placement);
|
const auto& bp = std::get<BlockPlacement>(placement.placement);
|
||||||
placeLine(line, placement.priority);
|
placeBlock(bp, placement.priority);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -482,9 +525,10 @@ void WorldGenerator::generatePlacements(
|
|||||||
for (const auto& placement : placements) {
|
for (const auto& placement : placements) {
|
||||||
if (auto structure = std::get_if<StructurePlacement>(&placement.placement)) {
|
if (auto structure = std::get_if<StructurePlacement>(&placement.placement)) {
|
||||||
generateStructure(prototype, *structure, voxels, chunkX, chunkZ);
|
generateStructure(prototype, *structure, voxels, chunkX, chunkZ);
|
||||||
} else {
|
} else if (auto line = std::get_if<LinePlacement>(&placement.placement)) {
|
||||||
const auto& line = std::get<LinePlacement>(placement.placement);
|
generateLine(prototype, *line, voxels, chunkX, chunkZ);
|
||||||
generateLine(prototype, line, voxels, chunkX, chunkZ);
|
} else if (auto block = std::get_if<BlockPlacement>(&placement.placement)) {
|
||||||
|
generateBlock(prototype, *block, voxels, chunkX, chunkZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -591,6 +635,61 @@ void WorldGenerator::generateLine(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldGenerator::generateBlock(
|
||||||
|
const ChunkPrototype& prototype,
|
||||||
|
const BlockPlacement& placement,
|
||||||
|
voxel* voxels,
|
||||||
|
int chunkX, int chunkZ
|
||||||
|
) {
|
||||||
|
const auto& indices = content.getIndices()->blocks;
|
||||||
|
const auto& def = indices.require(placement.block);
|
||||||
|
|
||||||
|
glm::ivec3 origin = placement.position; // relative; may be outside
|
||||||
|
int rotIndex = 0;
|
||||||
|
if (def.rotatable && def.rotations.variantsCount) {
|
||||||
|
rotIndex = placement.rotation % def.rotations.variantsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write origin only for owner chunk (mirror==false) and if inside bounds
|
||||||
|
if (!placement.mirror &&
|
||||||
|
origin.x >= 0 && origin.x < CHUNK_W &&
|
||||||
|
origin.y >= 0 && origin.y < CHUNK_H &&
|
||||||
|
origin.z >= 0 && origin.z < CHUNK_D) {
|
||||||
|
auto& vox = voxels[vox_index(origin.x, origin.y, origin.z)];
|
||||||
|
vox.id = placement.block;
|
||||||
|
vox.state = {};
|
||||||
|
vox.state.rotation = rotIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// expand extended blocks
|
||||||
|
if (def.rt.extended) {
|
||||||
|
const auto& rot = def.rotations.variants[rotIndex];
|
||||||
|
const auto size = def.size;
|
||||||
|
for (int sy = 0; sy < size.y; sy++) {
|
||||||
|
for (int sz = 0; sz < size.z; sz++) {
|
||||||
|
for (int sx = 0; sx < size.x; sx++) {
|
||||||
|
if ((sx | sy | sz) == 0) continue;
|
||||||
|
glm::ivec3 pos = origin;
|
||||||
|
pos += rot.axes[0] * sx;
|
||||||
|
pos += rot.axes[1] * sy;
|
||||||
|
pos += rot.axes[2] * sz;
|
||||||
|
if (pos.x < 0 || pos.x >= CHUNK_W ||
|
||||||
|
pos.y < 0 || pos.y >= CHUNK_H ||
|
||||||
|
pos.z < 0 || pos.z >= CHUNK_D) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
struct voxel seg;
|
||||||
|
seg.id = placement.block;
|
||||||
|
seg.state = {};
|
||||||
|
seg.state.rotation = rotIndex;
|
||||||
|
seg.state.segment = ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2));
|
||||||
|
voxels[vox_index(pos.x, pos.y, pos.z)] = seg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
WorldGenDebugInfo WorldGenerator::createDebugInfo() const {
|
WorldGenDebugInfo WorldGenerator::createDebugInfo() const {
|
||||||
const auto& area = surroundMap.getArea();
|
const auto& area = surroundMap.getArea();
|
||||||
const auto& levels = area.getBuffer();
|
const auto& levels = area.getBuffer();
|
||||||
|
|||||||
@ -79,6 +79,7 @@ class WorldGenerator {
|
|||||||
);
|
);
|
||||||
|
|
||||||
void placeLine(const LinePlacement& line, int priority);
|
void placeLine(const LinePlacement& line, int priority);
|
||||||
|
void placeBlock(const BlockPlacement& block, int priority);
|
||||||
|
|
||||||
void generatePlacements(
|
void generatePlacements(
|
||||||
const ChunkPrototype& prototype, voxel* voxels, int x, int z
|
const ChunkPrototype& prototype, voxel* voxels, int x, int z
|
||||||
@ -89,6 +90,12 @@ class WorldGenerator {
|
|||||||
voxel* voxels,
|
voxel* voxels,
|
||||||
int x, int z
|
int x, int z
|
||||||
);
|
);
|
||||||
|
void generateBlock(
|
||||||
|
const ChunkPrototype& prototype,
|
||||||
|
const BlockPlacement& placement,
|
||||||
|
voxel* voxels,
|
||||||
|
int x, int z
|
||||||
|
);
|
||||||
void generateStructure(
|
void generateStructure(
|
||||||
const ChunkPrototype& prototype,
|
const ChunkPrototype& prototype,
|
||||||
const StructurePlacement& placement,
|
const StructurePlacement& placement,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user