mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-11 22:11:51 +00:00
Merge pull request #659 from eliotbyte/feature/block-placement
Feature/block placement
This commit is contained in:
commit
626a08b9d2
10 changed files with 748 additions and 428 deletions
|
|
@ -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` — для туннелей/линий.
|
||||||
|
|
||||||
|
|
||||||
### Расстановка малых структур
|
### Расстановка малых структур
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,62 @@
|
||||||
const glm::vec3 BlocksRenderer::SUN_VECTOR(0.528265, 0.833149, -0.163704);
|
const glm::vec3 BlocksRenderer::SUN_VECTOR(0.528265, 0.833149, -0.163704);
|
||||||
const float DIRECTIONAL_LIGHT_FACTOR = 0.3f;
|
const float DIRECTIONAL_LIGHT_FACTOR = 0.3f;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static constexpr float K_CHUNK_CENTER_BIAS = 0.5f;
|
||||||
|
static constexpr float K_AO_NORMAL_PUSH = 0.75f;
|
||||||
|
static constexpr float K_FACE_OFFSET_EPS = 1e-3f;
|
||||||
|
|
||||||
|
static inline void expand_aabb_point(AABB& aabb, bool& init, const glm::vec3& p) {
|
||||||
|
if (!init) { aabb.a = aabb.b = p; init = true; } else { aabb.addPoint(p); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float apply_directional_factor(float d) {
|
||||||
|
return (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void expand_aabb_4(
|
||||||
|
AABB& aabb, bool& init,
|
||||||
|
const glm::vec3& p0, const glm::vec3& p1,
|
||||||
|
const glm::vec3& p2, const glm::vec3& p3
|
||||||
|
) {
|
||||||
|
expand_aabb_point(aabb, init, p0);
|
||||||
|
expand_aabb_point(aabb, init, p1);
|
||||||
|
expand_aabb_point(aabb, init, p2);
|
||||||
|
expand_aabb_point(aabb, init, p3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void expand_aabb_4_if_needed(
|
||||||
|
AABB& aabb, bool& init, bool densePass,
|
||||||
|
const glm::vec3& p0, const glm::vec3& p1,
|
||||||
|
const glm::vec3& p2, const glm::vec3& p3
|
||||||
|
) {
|
||||||
|
if (!densePass) {
|
||||||
|
expand_aabb_4(aabb, init, p0, p1, p2, p3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void compute_face_points(
|
||||||
|
const glm::vec3& coord,
|
||||||
|
const glm::vec3& X, const glm::vec3& Y, const glm::vec3& Z,
|
||||||
|
float bias,
|
||||||
|
glm::vec3& p0, glm::vec3& p1, glm::vec3& p2, glm::vec3& p3
|
||||||
|
) {
|
||||||
|
float s = bias;
|
||||||
|
p0 = coord + (-X - Y + Z) * s;
|
||||||
|
p1 = coord + ( X - Y + Z) * s;
|
||||||
|
p2 = coord + ( X + Y + Z) * s;
|
||||||
|
p3 = coord + (-X + Y + Z) * s;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void fill_texfaces(
|
||||||
|
const ContentGfxCache& cache,
|
||||||
|
blockid_t id, uint8_t variantId, bool densePass,
|
||||||
|
UVRegion (&out)[6]
|
||||||
|
) {
|
||||||
|
for (int f = 0; f < 6; ++f) out[f] = cache.getRegion(id, variantId, f, densePass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BlocksRenderer::BlocksRenderer(
|
BlocksRenderer::BlocksRenderer(
|
||||||
size_t capacity,
|
size_t capacity,
|
||||||
const Content& content,
|
const Content& content,
|
||||||
|
|
@ -91,23 +147,30 @@ void BlocksRenderer::face(
|
||||||
auto X = axisX * w;
|
auto X = axisX * w;
|
||||||
auto Y = axisY * h;
|
auto Y = axisY * h;
|
||||||
auto Z = axisZ * d;
|
auto Z = axisZ * d;
|
||||||
float s = 0.5f;
|
glm::vec3 p0, p1, p2, p3;
|
||||||
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, lights[0] * tint, axisZ, 0);
|
compute_face_points(coord, X, Y, Z, K_CHUNK_CENTER_BIAS, p0, p1, p2, p3);
|
||||||
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, lights[1] * tint, axisZ, 0);
|
vertex(p0, region.u1, region.v1, lights[0] * tint, axisZ, 0);
|
||||||
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, lights[2] * tint, axisZ, 0);
|
vertex(p1, region.u2, region.v1, lights[1] * tint, axisZ, 0);
|
||||||
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, lights[3] * tint, axisZ, 0);
|
vertex(p2, region.u2, region.v2, lights[2] * tint, axisZ, 0);
|
||||||
|
vertex(p3, region.u1, region.v2, lights[3] * tint, axisZ, 0);
|
||||||
index(0, 1, 3, 1, 2, 3);
|
index(0, 1, 3, 1, 2, 3);
|
||||||
|
|
||||||
|
// Expand local opaque AABB while vertices are still in chunk-local space
|
||||||
|
expand_aabb_4_if_needed(localAabb, localAabbInit, densePass, p0, p1, p2, p3);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlocksRenderer::vertexAO(
|
void BlocksRenderer::vertexAO(
|
||||||
const glm::vec3& coord,
|
const glm::vec3& coord,
|
||||||
float u, float v,
|
float u, float v,
|
||||||
const glm::vec4& tint,
|
const glm::vec4& tint,
|
||||||
|
float normalHalfLen,
|
||||||
const glm::vec3& axisX,
|
const glm::vec3& axisX,
|
||||||
const glm::vec3& axisY,
|
const glm::vec3& axisY,
|
||||||
const glm::vec3& axisZ
|
const glm::vec3& axisZ
|
||||||
) {
|
) {
|
||||||
auto pos = coord+axisZ*0.5f+(axisX+axisY)*0.5f;
|
// Sample AO in world-voxel grid with a slightly longer reach along the normal
|
||||||
|
// to avoid sampling the same chunk voxel when faces reside over neighbor chunk.
|
||||||
|
auto pos = coord + axisZ * normalHalfLen + (axisX + axisY) * 0.5f;
|
||||||
auto light = pickSoftLight(
|
auto light = pickSoftLight(
|
||||||
glm::ivec3(std::round(pos.x), std::round(pos.y), std::round(pos.z)),
|
glm::ivec3(std::round(pos.x), std::round(pos.y), std::round(pos.z)),
|
||||||
axisX,
|
axisX,
|
||||||
|
|
@ -129,27 +192,33 @@ void BlocksRenderer::faceAO(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float s = 0.5f;
|
|
||||||
if (lights) {
|
if (lights) {
|
||||||
float d = glm::dot(glm::normalize(Z), SUN_VECTOR);
|
const auto nZ = glm::normalize(Z);
|
||||||
d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR;
|
float d = apply_directional_factor(glm::dot(nZ, SUN_VECTOR));
|
||||||
|
|
||||||
auto axisX = glm::normalize(X);
|
auto axisX = glm::normalize(X);
|
||||||
auto axisY = glm::normalize(Y);
|
auto axisY = glm::normalize(Y);
|
||||||
auto axisZ = glm::normalize(Z);
|
auto axisZ = nZ;
|
||||||
|
|
||||||
glm::vec4 tint(d);
|
glm::vec4 tint(d);
|
||||||
vertexAO(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisX, axisY, axisZ);
|
const float nh = K_AO_NORMAL_PUSH; // push AO sample a bit farther along normal
|
||||||
vertexAO(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisX, axisY, axisZ);
|
glm::vec3 p0, p1, p2, p3;
|
||||||
vertexAO(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisX, axisY, axisZ);
|
compute_face_points(coord, X, Y, Z, K_CHUNK_CENTER_BIAS, p0, p1, p2, p3);
|
||||||
vertexAO(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisX, axisY, axisZ);
|
vertexAO(p0, region.u1, region.v1, tint, nh, axisX, axisY, axisZ);
|
||||||
|
vertexAO(p1, region.u2, region.v1, tint, nh, axisX, axisY, axisZ);
|
||||||
|
vertexAO(p2, region.u2, region.v2, tint, nh, axisX, axisY, axisZ);
|
||||||
|
vertexAO(p3, region.u1, region.v2, tint, nh, axisX, axisY, axisZ);
|
||||||
|
expand_aabb_4_if_needed(localAabb, localAabbInit, densePass, p0, p1, p2, p3);
|
||||||
} else {
|
} else {
|
||||||
auto axisZ = glm::normalize(Z);
|
auto axisZ = glm::normalize(Z);
|
||||||
glm::vec4 tint(1.0f);
|
glm::vec4 tint(1.0f);
|
||||||
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, axisZ, 1);
|
glm::vec3 p0, p1, p2, p3;
|
||||||
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, axisZ, 1);
|
compute_face_points(coord, X, Y, Z, K_CHUNK_CENTER_BIAS, p0, p1, p2, p3);
|
||||||
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, axisZ, 1);
|
vertex(p0, region.u1, region.v1, tint, axisZ, 1);
|
||||||
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, axisZ, 1);
|
vertex(p1, region.u2, region.v1, tint, axisZ, 1);
|
||||||
|
vertex(p2, region.u2, region.v2, tint, axisZ, 1);
|
||||||
|
vertex(p3, region.u1, region.v2, tint, axisZ, 1);
|
||||||
|
expand_aabb_4_if_needed(localAabb, localAabbInit, densePass, p0, p1, p2, p3);
|
||||||
}
|
}
|
||||||
index(0, 1, 2, 0, 2, 3);
|
index(0, 1, 2, 0, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
@ -168,16 +237,19 @@ void BlocksRenderer::face(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float s = 0.5f;
|
const auto nZ = glm::normalize(Z);
|
||||||
if (lights) {
|
if (lights) {
|
||||||
float d = glm::dot(glm::normalize(Z), SUN_VECTOR);
|
float d = apply_directional_factor(glm::dot(nZ, SUN_VECTOR));
|
||||||
d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR;
|
|
||||||
tint *= d;
|
tint *= d;
|
||||||
}
|
}
|
||||||
vertex(coord + (-X - Y + Z) * s, region.u1, region.v1, tint, Z, lights ? 0 : 1);
|
const auto nZ2 = lights ? nZ : Z;
|
||||||
vertex(coord + ( X - Y + Z) * s, region.u2, region.v1, tint, Z, lights ? 0 : 1);
|
glm::vec3 p0, p1, p2, p3;
|
||||||
vertex(coord + ( X + Y + Z) * s, region.u2, region.v2, tint, Z, lights ? 0 : 1);
|
compute_face_points(coord, X, Y, Z, K_CHUNK_CENTER_BIAS, p0, p1, p2, p3);
|
||||||
vertex(coord + (-X + Y + Z) * s, region.u1, region.v2, tint, Z, lights ? 0 : 1);
|
vertex(p0, region.u1, region.v1, tint, nZ2, lights ? 0 : 1);
|
||||||
|
vertex(p1, region.u2, region.v1, tint, nZ2, lights ? 0 : 1);
|
||||||
|
vertex(p2, region.u2, region.v2, tint, nZ2, lights ? 0 : 1);
|
||||||
|
vertex(p3, region.u1, region.v2, tint, nZ2, lights ? 0 : 1);
|
||||||
|
expand_aabb_4_if_needed(localAabb, localAabbInit, densePass, p0, p1, p2, p3);
|
||||||
index(0, 1, 2, 0, 2, 3);
|
index(0, 1, 2, 0, 2, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -333,8 +405,7 @@ void BlocksRenderer::blockCustomModel(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
float d = glm::dot(n, SUN_VECTOR);
|
float d = apply_directional_factor(glm::dot(n, SUN_VECTOR));
|
||||||
d = (1.0f - DIRECTIONAL_LIGHT_FACTOR) + d * DIRECTIONAL_LIGHT_FACTOR;
|
|
||||||
glm::vec3 t = glm::cross(r, n);
|
glm::vec3 t = glm::cross(r, n);
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
|
|
@ -347,14 +418,18 @@ void BlocksRenderer::blockCustomModel(
|
||||||
vcoord.z * Z + r * 0.5f + t * 0.5f + n * 0.5f;
|
vcoord.z * Z + r * 0.5f + t * 0.5f + n * 0.5f;
|
||||||
aoColor = pickSoftLight(p.x, p.y, p.z, glm::ivec3(r), glm::ivec3(t));
|
aoColor = pickSoftLight(p.x, p.y, p.z, glm::ivec3(r), glm::ivec3(t));
|
||||||
}
|
}
|
||||||
|
auto pLocal = coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z;
|
||||||
this->vertex(
|
this->vertex(
|
||||||
coord + vcoord.x * X + vcoord.y * Y + vcoord.z * Z,
|
pLocal,
|
||||||
vertex.uv.x,
|
vertex.uv.x,
|
||||||
vertex.uv.y,
|
vertex.uv.y,
|
||||||
mesh.shading ? (glm::vec4(d, d, d, d) * aoColor) : glm::vec4(1, 1, 1, d),
|
mesh.shading ? (glm::vec4(d, d, d, d) * aoColor) : glm::vec4(1, 1, 1, d),
|
||||||
n,
|
n,
|
||||||
mesh.shading ? 0.0f : 1.0
|
mesh.shading ? 0.0f : 1.0
|
||||||
);
|
);
|
||||||
|
if (!densePass) {
|
||||||
|
expand_aabb_point(localAabb, localAabbInit, pLocal);
|
||||||
|
}
|
||||||
indexBuffer[indexCount++] = vertexOffset++;
|
indexBuffer[indexCount++] = vertexOffset++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -502,14 +577,8 @@ void BlocksRenderer::render(
|
||||||
if (def.translucent) {
|
if (def.translucent) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const UVRegion texfaces[6] {
|
UVRegion texfaces[6];
|
||||||
cache.getRegion(id, variantId, 0, densePass),
|
fill_texfaces(cache, id, variantId, densePass, texfaces);
|
||||||
cache.getRegion(id, variantId, 1, densePass),
|
|
||||||
cache.getRegion(id, variantId, 2, densePass),
|
|
||||||
cache.getRegion(id, variantId, 3, densePass),
|
|
||||||
cache.getRegion(id, variantId, 4, densePass),
|
|
||||||
cache.getRegion(id, variantId, 5, densePass)
|
|
||||||
};
|
|
||||||
int x = i % CHUNK_W;
|
int x = i % CHUNK_W;
|
||||||
int y = i / (CHUNK_D * CHUNK_W);
|
int y = i / (CHUNK_D * CHUNK_W);
|
||||||
int z = (i / CHUNK_D) % CHUNK_W;
|
int z = (i / CHUNK_D) % CHUNK_W;
|
||||||
|
|
@ -581,14 +650,8 @@ SortingMeshData BlocksRenderer::renderTranslucent(
|
||||||
if (!def.translucent) {
|
if (!def.translucent) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const UVRegion texfaces[6] {
|
UVRegion texfaces[6];
|
||||||
cache.getRegion(id, variantId, 0, densePass),
|
fill_texfaces(cache, id, variantId, densePass, texfaces);
|
||||||
cache.getRegion(id, variantId, 1, densePass),
|
|
||||||
cache.getRegion(id, variantId, 2, densePass),
|
|
||||||
cache.getRegion(id, variantId, 3, densePass),
|
|
||||||
cache.getRegion(id, variantId, 4, densePass),
|
|
||||||
cache.getRegion(id, variantId, 5, densePass)
|
|
||||||
};
|
|
||||||
int x = i % CHUNK_W;
|
int x = i % CHUNK_W;
|
||||||
int y = i / (CHUNK_D * CHUNK_W);
|
int y = i / (CHUNK_D * CHUNK_W);
|
||||||
int z = (i / CHUNK_D) % CHUNK_W;
|
int z = (i / CHUNK_D) % CHUNK_W;
|
||||||
|
|
@ -654,6 +717,9 @@ SortingMeshData BlocksRenderer::renderTranslucent(
|
||||||
aabb.addPoint(vertex.position);
|
aabb.addPoint(vertex.position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// also widen overall local AABB for translucent geometry
|
||||||
|
expand_aabb_point(localAabb, localAabbInit, vertex.position);
|
||||||
|
|
||||||
vertex.position.x += chunk->x * CHUNK_W + 0.5f;
|
vertex.position.x += chunk->x * CHUNK_W + 0.5f;
|
||||||
vertex.position.y += 0.5f;
|
vertex.position.y += 0.5f;
|
||||||
vertex.position.z += chunk->z * CHUNK_D + 0.5f;
|
vertex.position.z += chunk->z * CHUNK_D + 0.5f;
|
||||||
|
|
@ -689,6 +755,9 @@ SortingMeshData BlocksRenderer::renderTranslucent(
|
||||||
|
|
||||||
void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
|
void BlocksRenderer::build(const Chunk* chunk, const Chunks* chunks) {
|
||||||
this->chunk = chunk;
|
this->chunk = chunk;
|
||||||
|
// reset local AABB accumulation
|
||||||
|
localAabbInit = false;
|
||||||
|
localAabb = AABB{glm::vec3(0.0f), glm::vec3(0.0f)};
|
||||||
voxelsBuffer->setPosition(
|
voxelsBuffer->setPosition(
|
||||||
chunk->x * CHUNK_W - voxelBufferPadding, 0,
|
chunk->x * CHUNK_W - voxelBufferPadding, 0,
|
||||||
chunk->z * CHUNK_D - voxelBufferPadding);
|
chunk->z * CHUNK_D - voxelBufferPadding);
|
||||||
|
|
@ -766,7 +835,8 @@ ChunkMeshData BlocksRenderer::createMesh() {
|
||||||
ChunkVertex::ATTRIBUTES, sizeof(ChunkVertex::ATTRIBUTES) / sizeof(VertexAttribute)
|
ChunkVertex::ATTRIBUTES, sizeof(ChunkVertex::ATTRIBUTES) / sizeof(VertexAttribute)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
std::move(sortingMesh)
|
std::move(sortingMesh),
|
||||||
|
localAabbInit ? localAabb : AABB{glm::vec3(0.0f), glm::vec3(0.0f)}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "maths/util.hpp"
|
#include "maths/util.hpp"
|
||||||
#include "commons.hpp"
|
#include "commons.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
#include "maths/aabb.hpp"
|
||||||
|
|
||||||
template<typename VertexStructure> class Mesh;
|
template<typename VertexStructure> class Mesh;
|
||||||
class Content;
|
class Content;
|
||||||
|
|
@ -32,7 +33,8 @@ class BlocksRenderer {
|
||||||
size_t indexCount;
|
size_t indexCount;
|
||||||
size_t denseIndexCount;
|
size_t denseIndexCount;
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
int voxelBufferPadding = 2;
|
// Increased to better cover cross-chunk sampling for extended blocks (e.g., 3x3x3)
|
||||||
|
int voxelBufferPadding = 4;
|
||||||
bool overflow = false;
|
bool overflow = false;
|
||||||
bool cancelled = false;
|
bool cancelled = false;
|
||||||
bool densePass = false;
|
bool densePass = false;
|
||||||
|
|
@ -47,6 +49,9 @@ class BlocksRenderer {
|
||||||
util::PseudoRandom randomizer;
|
util::PseudoRandom randomizer;
|
||||||
|
|
||||||
SortingMeshData sortingMesh;
|
SortingMeshData sortingMesh;
|
||||||
|
// Accumulated local-space AABB over opaque build pass
|
||||||
|
AABB localAabb {glm::vec3(0.0f), glm::vec3(0.0f)};
|
||||||
|
bool localAabbInit = false;
|
||||||
|
|
||||||
void vertex(
|
void vertex(
|
||||||
const glm::vec3& coord,
|
const glm::vec3& coord,
|
||||||
|
|
@ -61,6 +66,7 @@ class BlocksRenderer {
|
||||||
void vertexAO(
|
void vertexAO(
|
||||||
const glm::vec3& coord, float u, float v,
|
const glm::vec3& coord, float u, float v,
|
||||||
const glm::vec4& brightness,
|
const glm::vec4& brightness,
|
||||||
|
float normalHalfLen,
|
||||||
const glm::vec3& axisX,
|
const glm::vec3& axisX,
|
||||||
const glm::vec3& axisY,
|
const glm::vec3& axisY,
|
||||||
const glm::vec3& axisZ
|
const glm::vec3& axisZ
|
||||||
|
|
@ -170,6 +176,7 @@ public:
|
||||||
ChunkMesh render(const Chunk* chunk, const Chunks* chunks);
|
ChunkMesh render(const Chunk* chunk, const Chunks* chunks);
|
||||||
ChunkMeshData createMesh();
|
ChunkMeshData createMesh();
|
||||||
VoxelsVolume* getVoxelsBuffer() const;
|
VoxelsVolume* getVoxelsBuffer() const;
|
||||||
|
inline const AABB& getLocalAabb() const { return localAabb; }
|
||||||
|
|
||||||
size_t getMemoryConsumption() const;
|
size_t getMemoryConsumption() const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,65 @@
|
||||||
#include "maths/FrustumCulling.hpp"
|
#include "maths/FrustumCulling.hpp"
|
||||||
#include "util/listutil.hpp"
|
#include "util/listutil.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
static debug::Logger logger("chunks-render");
|
static debug::Logger logger("chunks-render");
|
||||||
|
|
||||||
size_t ChunksRenderer::visibleChunks = 0;
|
size_t ChunksRenderer::visibleChunks = 0;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct CullingBounds { glm::vec3 min; glm::vec3 max; };
|
||||||
|
static constexpr float K_CHUNK_CENTER_BIAS = 0.5f;
|
||||||
|
// Minimal thickness to avoid culling flicker for geometry that forms 2D sheets
|
||||||
|
static constexpr float K_AABB_MIN_EXTENT = 1e-2f;
|
||||||
|
|
||||||
|
static inline bool has_volume(const AABB& aabb) {
|
||||||
|
auto s = aabb.size();
|
||||||
|
return s.x > 0.0f || s.y > 0.0f || s.z > 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline CullingBounds compute_chunk_culling_bounds(
|
||||||
|
const Chunk& chunk,
|
||||||
|
const std::unordered_map<glm::ivec2, ChunkMesh>& meshes
|
||||||
|
) {
|
||||||
|
glm::vec3 min(chunk.x * CHUNK_W, chunk.bottom, chunk.z * CHUNK_D);
|
||||||
|
glm::vec3 max(
|
||||||
|
chunk.x * CHUNK_W + CHUNK_W,
|
||||||
|
chunk.top,
|
||||||
|
chunk.z * CHUNK_D + CHUNK_D
|
||||||
|
);
|
||||||
|
auto it = meshes.find({chunk.x, chunk.z});
|
||||||
|
if (it != meshes.end()) {
|
||||||
|
const auto& aabb = it->second.localAabb;
|
||||||
|
if (has_volume(aabb)) {
|
||||||
|
// Convert to world coords (same 0.5 bias as draw model matrix)
|
||||||
|
min = glm::vec3(chunk.x * CHUNK_W + aabb.min().x + K_CHUNK_CENTER_BIAS,
|
||||||
|
aabb.min().y + K_CHUNK_CENTER_BIAS,
|
||||||
|
chunk.z * CHUNK_D + aabb.min().z + K_CHUNK_CENTER_BIAS);
|
||||||
|
max = glm::vec3(chunk.x * CHUNK_W + aabb.max().x + K_CHUNK_CENTER_BIAS,
|
||||||
|
aabb.max().y + K_CHUNK_CENTER_BIAS,
|
||||||
|
chunk.z * CHUNK_D + aabb.max().z + K_CHUNK_CENTER_BIAS);
|
||||||
|
|
||||||
|
// Clamp vertically to chunk vertical span to keep bounds tight and valid
|
||||||
|
min.y = (std::max)(static_cast<float>(chunk.bottom), min.y);
|
||||||
|
max.y = (std::min)(static_cast<float>(chunk.top), max.y);
|
||||||
|
|
||||||
|
// Ensure non-degenerate extents to avoid view-dependent flicker
|
||||||
|
glm::vec3 size = max - min;
|
||||||
|
auto inflate_axis = [&](int axis) {
|
||||||
|
float c = (min[axis] + max[axis]) * 0.5f;
|
||||||
|
min[axis] = c - K_AABB_MIN_EXTENT * 0.5f;
|
||||||
|
max[axis] = c + K_AABB_MIN_EXTENT * 0.5f;
|
||||||
|
};
|
||||||
|
if (size.x < K_AABB_MIN_EXTENT) inflate_axis(0);
|
||||||
|
if (size.y < K_AABB_MIN_EXTENT) inflate_axis(1);
|
||||||
|
if (size.z < K_AABB_MIN_EXTENT) inflate_axis(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {min, max};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
|
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
|
||||||
const Chunks& chunks;
|
const Chunks& chunks;
|
||||||
BlocksRenderer renderer;
|
BlocksRenderer renderer;
|
||||||
|
|
@ -76,6 +130,7 @@ ChunksRenderer::ChunksRenderer(
|
||||||
meshes[result.key] = ChunkMesh {
|
meshes[result.key] = ChunkMesh {
|
||||||
std::make_unique<Mesh<ChunkVertex>>(meshData.mesh),
|
std::make_unique<Mesh<ChunkVertex>>(meshData.mesh),
|
||||||
std::move(meshData.sortingMesh)};
|
std::move(meshData.sortingMesh)};
|
||||||
|
meshes[result.key].localAabb = meshData.localAabb;
|
||||||
}
|
}
|
||||||
inwork.erase(result.key);
|
inwork.erase(result.key);
|
||||||
},
|
},
|
||||||
|
|
@ -103,6 +158,8 @@ const Mesh<ChunkVertex>* ChunksRenderer::render(
|
||||||
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
||||||
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
||||||
};
|
};
|
||||||
|
// propagate local aabb from immediate path too
|
||||||
|
meshes[glm::ivec2(chunk->x, chunk->z)].localAabb = renderer->getLocalAabb();
|
||||||
return meshes[glm::ivec2(chunk->x, chunk->z)].mesh.get();
|
return meshes[glm::ivec2(chunk->x, chunk->z)].mesh.get();
|
||||||
}
|
}
|
||||||
glm::ivec2 key(chunk->x, chunk->z);
|
glm::ivec2 key(chunk->x, chunk->z);
|
||||||
|
|
@ -175,14 +232,8 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
|
||||||
chunk->updateHeights();
|
chunk->updateHeights();
|
||||||
}
|
}
|
||||||
if (culling) {
|
if (culling) {
|
||||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
||||||
glm::vec3 max(
|
if (!frustum.isBoxVisible(bounds.min, bounds.max)) return nullptr;
|
||||||
chunk->x * CHUNK_W + CHUNK_W,
|
|
||||||
chunk->top,
|
|
||||||
chunk->z * CHUNK_D + CHUNK_D
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!frustum.isBoxVisible(min, max)) return nullptr;
|
|
||||||
}
|
}
|
||||||
return mesh;
|
return mesh;
|
||||||
}
|
}
|
||||||
|
|
@ -211,24 +262,18 @@ void ChunksRenderer::drawShadowsPass(
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 coord(
|
glm::vec3 coord(
|
||||||
pos.x * CHUNK_W + 0.5f, 0.5f, pos.y * CHUNK_D + 0.5f
|
pos.x * CHUNK_W + K_CHUNK_CENTER_BIAS, K_CHUNK_CENTER_BIAS, pos.y * CHUNK_D + K_CHUNK_CENTER_BIAS
|
||||||
);
|
);
|
||||||
|
|
||||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
||||||
glm::vec3 max(
|
if (!frustum.isBoxVisible(bounds.min, bounds.max)) {
|
||||||
chunk->x * CHUNK_W + CHUNK_W,
|
|
||||||
chunk->top,
|
|
||||||
chunk->z * CHUNK_D + CHUNK_D
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!frustum.isBoxVisible(min, max)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||||
shader.uniformMatrix("u_model", model);
|
shader.uniformMatrix("u_model", model);
|
||||||
found->second.mesh->draw(GL_TRIANGLES,
|
found->second.mesh->draw(GL_TRIANGLES,
|
||||||
glm::distance2(playerCamera.position * glm::vec3(1, 0, 1),
|
glm::distance2(playerCamera.position * glm::vec3(1, 0, 1),
|
||||||
(min + max) * 0.5f * glm::vec3(1, 0, 1)) < denseDistance2);
|
(bounds.min + bounds.max) * 0.5f * glm::vec3(1, 0, 1)) < denseDistance2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -275,7 +320,7 @@ void ChunksRenderer::drawChunks(
|
||||||
|
|
||||||
if (mesh) {
|
if (mesh) {
|
||||||
glm::vec3 coord(
|
glm::vec3 coord(
|
||||||
chunk->x * CHUNK_W + 0.5f, 0.5f, chunk->z * CHUNK_D + 0.5f
|
chunk->x * CHUNK_W + K_CHUNK_CENTER_BIAS, K_CHUNK_CENTER_BIAS, chunk->z * CHUNK_D + K_CHUNK_CENTER_BIAS
|
||||||
);
|
);
|
||||||
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
glm::mat4 model = glm::translate(glm::mat4(1.0f), coord);
|
||||||
shader.uniformMatrix("u_model", model);
|
shader.uniformMatrix("u_model", model);
|
||||||
|
|
@ -305,7 +350,7 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
||||||
static int frameid = 0;
|
static int frameid = 0;
|
||||||
frameid++;
|
frameid++;
|
||||||
|
|
||||||
bool culling = settings.graphics.frustumCulling.get();
|
const bool culling = settings.graphics.frustumCulling.get();
|
||||||
const auto& chunks = this->chunks.getChunks();
|
const auto& chunks = this->chunks.getChunks();
|
||||||
const auto& cameraPos = camera.position;
|
const auto& cameraPos = camera.position;
|
||||||
const auto& atlas = assets.require<Atlas>("blocks");
|
const auto& atlas = assets.require<Atlas>("blocks");
|
||||||
|
|
@ -315,52 +360,67 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
||||||
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
shader.uniformMatrix("u_model", glm::mat4(1.0f));
|
||||||
shader.uniform1i("u_alphaClip", false);
|
shader.uniform1i("u_alphaClip", false);
|
||||||
|
|
||||||
|
struct VisibleChunkTrans {
|
||||||
|
glm::ivec2 key;
|
||||||
|
const std::shared_ptr<Chunk>* chunkPtr;
|
||||||
|
long long nearestDist2;
|
||||||
|
};
|
||||||
|
std::vector<VisibleChunkTrans> order;
|
||||||
|
order.reserve(indices.size());
|
||||||
|
|
||||||
|
// Build per-chunk nearest distance for translucent entries
|
||||||
for (const auto& index : indices) {
|
for (const auto& index : indices) {
|
||||||
const auto& chunk = chunks[index.index];
|
const auto& chunk = chunks[index.index];
|
||||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto& found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
const auto found = meshes.find(glm::ivec2(chunk->x, chunk->z));
|
||||||
if (found == meshes.end() || found->second.sortingMeshData.entries.empty()) {
|
if (found == meshes.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& entries = found->second.sortingMeshData.entries;
|
||||||
|
if (entries.empty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (culling) {
|
if (culling) {
|
||||||
glm::vec3 min(chunk->x * CHUNK_W, chunk->bottom, chunk->z * CHUNK_D);
|
const auto bounds = compute_chunk_culling_bounds(*chunk, meshes);
|
||||||
glm::vec3 max(
|
if (!frustum.isBoxVisible(bounds.min, bounds.max)) continue;
|
||||||
chunk->x * CHUNK_W + CHUNK_W,
|
|
||||||
chunk->top,
|
|
||||||
chunk->z * CHUNK_D + CHUNK_D
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!frustum.isBoxVisible(min, max)) continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& chunkEntries = found->second.sortingMeshData.entries;
|
long long nearest = LLONG_MAX;
|
||||||
|
for (const auto& e : entries) {
|
||||||
|
long long d2 = static_cast<long long>(glm::distance2(e.position, cameraPos));
|
||||||
|
if (d2 < nearest) nearest = d2;
|
||||||
|
}
|
||||||
|
order.push_back(VisibleChunkTrans{glm::ivec2(chunk->x, chunk->z), &chunks[index.index], nearest});
|
||||||
|
}
|
||||||
|
|
||||||
if (chunkEntries.size() == 1) {
|
// Sort chunks by nearest translucent distance back-to-front (far to near)
|
||||||
auto& entry = chunkEntries.at(0);
|
std::sort(order.begin(), order.end(), [](const VisibleChunkTrans& a, const VisibleChunkTrans& b) {
|
||||||
if (found->second.sortedMesh == nullptr) {
|
return a.nearestDist2 > b.nearestDist2;
|
||||||
found->second.sortedMesh = std::make_unique<Mesh<ChunkVertex>>(
|
});
|
||||||
entry.vertexData.data(), entry.vertexData.size()
|
|
||||||
|
// Draw per-chunk sorted mesh (keeps GPU buffers and avoids per-frame repack)
|
||||||
|
for (const auto& item : order) {
|
||||||
|
const auto& chunk = *item.chunkPtr;
|
||||||
|
const auto found = meshes.find(item.key);
|
||||||
|
if (found == meshes.end()) continue;
|
||||||
|
auto& chunkEntries = found->second.sortingMeshData.entries;
|
||||||
|
if (chunkEntries.empty()) continue;
|
||||||
|
|
||||||
|
// Keep per-chunk internal order up-to-date occasionally
|
||||||
|
if (found->second.sortedMesh == nullptr || (frameid + chunk->x) % sortInterval == 0) {
|
||||||
|
for (auto& entry : chunkEntries) {
|
||||||
|
entry.distance = static_cast<long long>(
|
||||||
|
glm::distance2(entry.position, cameraPos)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
found->second.sortedMesh->draw();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (auto& entry : chunkEntries) {
|
|
||||||
entry.distance = static_cast<long long>(
|
|
||||||
glm::distance2(entry.position, cameraPos)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (found->second.sortedMesh == nullptr ||
|
|
||||||
(frameid + chunk->x) % sortInterval == 0) {
|
|
||||||
std::sort(chunkEntries.begin(), chunkEntries.end());
|
std::sort(chunkEntries.begin(), chunkEntries.end());
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
for (const auto& entry : chunkEntries) {
|
for (const auto& entry : chunkEntries) {
|
||||||
size += entry.vertexData.size();
|
size += entry.vertexData.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
static util::Buffer<ChunkVertex> buffer;
|
static util::Buffer<ChunkVertex> buffer;
|
||||||
if (buffer.size() < size) {
|
if (buffer.size() < size) {
|
||||||
buffer = util::Buffer<ChunkVertex>(size);
|
buffer = util::Buffer<ChunkVertex>(size);
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "graphics/core/MeshData.hpp"
|
#include "graphics/core/MeshData.hpp"
|
||||||
#include "util/Buffer.hpp"
|
#include "util/Buffer.hpp"
|
||||||
|
#include "maths/aabb.hpp"
|
||||||
|
|
||||||
/// @brief Chunk mesh vertex format
|
/// @brief Chunk mesh vertex format
|
||||||
struct ChunkVertex {
|
struct ChunkVertex {
|
||||||
|
|
@ -44,10 +45,12 @@ struct SortingMeshData {
|
||||||
struct ChunkMeshData {
|
struct ChunkMeshData {
|
||||||
MeshData<ChunkVertex> mesh;
|
MeshData<ChunkVertex> mesh;
|
||||||
SortingMeshData sortingMesh;
|
SortingMeshData sortingMesh;
|
||||||
|
AABB localAabb; // mesh-space (chunk-local) bounds for precise culling
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChunkMesh {
|
struct ChunkMesh {
|
||||||
std::unique_ptr<Mesh<ChunkVertex>> mesh;
|
std::unique_ptr<Mesh<ChunkVertex>> mesh;
|
||||||
SortingMeshData sortingMeshData;
|
SortingMeshData sortingMeshData;
|
||||||
std::unique_ptr<Mesh<ChunkVertex> > sortedMesh = nullptr;
|
std::unique_ptr<Mesh<ChunkVertex> > sortedMesh = nullptr;
|
||||||
|
AABB localAabb; // mesh-space (chunk-local) bounds for precise culling
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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…
Add table
Add a link
Reference in a new issue