game: fix missing and spurious faces with smooth voxels

This commit is contained in:
Crypto City 2023-07-27 09:15:33 +00:00
parent e0ad81a097
commit 719ad3f515

View File

@ -105,6 +105,14 @@ static uint16_t get_material_for_block(const std::shared_ptr<Flag> &flag, uint8_
return variant;
}
static uint8_t get_item_for_block(const std::shared_ptr<Flag> &flag, uint8_t block)
{
const uint16_t variant = get_material_for_block(flag, block);
if (variant == cc::BLOCK_VARIANT_NONE)
return 0;
return cc::get_block_variant(variant).item;
}
CityMeshResources::CityMeshResources(ResourceCache *cache)
{
ownershipMaterial = cache->GetResource<Material>("Materials/Ownership.xml");
@ -1423,7 +1431,7 @@ std::vector<::Tile> CityMeshSection::MakeFlagLOD(const std::shared_ptr<Flag> &fl
return lod;
}
static void GenerateNormals(std::vector<float> &vertex_data, const std::vector<uint32_t> &index_data, int32_t ox, int32_t oy, uint16_t base_height, float material_hscale, float material_vscale, float lod_scale)
static void GenerateUVs(std::vector<float> &vertex_data, const std::vector<uint32_t> &index_data, int32_t ox, int32_t oy, uint16_t base_height, float material_hscale, float material_vscale, float lod_scale)
{
for (size_t i = 0; i < index_data.size(); i += 3)
{
@ -1439,10 +1447,6 @@ static void GenerateNormals(std::vector<float> &vertex_data, const std::vector<u
Vector3& n1 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * i1 + 3]));
Vector3& n2 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * i2 + 3]));
const Vector3 edge1 = v0 - v1;
const Vector3 edge2 = v0 - v2;
n0 = n1 = n2 = edge1.CrossProduct(edge2).Normalized();
Vector2& uv0 = *(reinterpret_cast<Vector2*>(&vertex_data[8 * i0 + 6]));
Vector2& uv1 = *(reinterpret_cast<Vector2*>(&vertex_data[8 * i1 + 6]));
Vector2& uv2 = *(reinterpret_cast<Vector2*>(&vertex_data[8 * i2 + 6]));
@ -1632,10 +1636,6 @@ void CityMeshSection::GenerateSurfaceNets(const std::shared_ptr<Flag> &flag, Sha
v[i] = x[i] + 0.5f + (s * v[i] - 0.5f) * smoothness;
}
const uint16_t material = flat_data[block_idx] & 0xff;
const uint16_t mv = (flat_data[block_idx] >> 8) & 3;
std::vector<uint32_t> &idata = index_data[material][mv];
//Add vertex to buffer, store pointer to vertex index in buffer
buffer[m] = vertex_data.size() / 8;
@ -1671,62 +1671,70 @@ void CityMeshSection::GenerateSurfaceNets(const std::shared_ptr<Flag> &flag, Sha
//Otherwise, look up adjacent edges in buffer
const int32_t du = R[iu] , dv = R[iv];
if (1)
{
// without interpolation, we're going to keep flat shading,
// so we duplicate all vertices
// also need to do this for UVs
const uint32_t base = vertex_data.size() / 8;
vertex_data.resize(vertex_data.size() + 8 * 4);
memcpy(vertex_data.data() + base * 8, vertex_data.data() + buffer[m-du-dv] * 8, 8 * sizeof(float));
memcpy(vertex_data.data() + (base + 1) * 8, vertex_data.data() + buffer[m-du] * 8, 8 * sizeof(float));
memcpy(vertex_data.data() + (base + 2) * 8, vertex_data.data() + buffer[m-dv] * 8, 8 * sizeof(float));
memcpy(vertex_data.data() + (base + 3) * 8, vertex_data.data() + buffer[m] * 8, 8 * sizeof(float));
// without interpolation, we're going to keep flat shading,
// so we duplicate all vertices
// also need to do this for UVs
const uint32_t base = vertex_data.size() / 8;
vertex_data.resize(vertex_data.size() + 8 * 4);
memcpy(vertex_data.data() + base * 8, vertex_data.data() + buffer[m-du-dv] * 8, 8 * sizeof(float));
memcpy(vertex_data.data() + (base + 1) * 8, vertex_data.data() + buffer[m-du] * 8, 8 * sizeof(float));
memcpy(vertex_data.data() + (base + 2) * 8, vertex_data.data() + buffer[m-dv] * 8, 8 * sizeof(float));
memcpy(vertex_data.data() + (base + 3) * 8, vertex_data.data() + buffer[m] * 8, 8 * sizeof(float));
if (!(mask & 1))
{
idata.push_back(base);
idata.push_back(base + 1);
idata.push_back(base + 3);
idata.push_back(base + 2);
idata.push_back(base);
idata.push_back(base + 3);
}
else
{
idata.push_back(base);
idata.push_back(base + 2);
idata.push_back(base + 3);
idata.push_back(base + 1);
idata.push_back(base);
idata.push_back(base + 3);
}
Vector3& v0 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * base]));
Vector3& v1 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * (base+1)]));
Vector3& v2 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * (base+2)]));
Vector3& v3 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * (base+3)]));
Vector3& n0 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * base + 3]));
Vector3& n1 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * (base+1) + 3]));
Vector3& n2 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * (base+2) + 3]));
Vector3& n3 = *(reinterpret_cast<Vector3*>(&vertex_data[8 * (base+3) + 3]));
const Vector3 edge1 = v0 - v1;
const Vector3 edge2 = v0 - v2;
const Vector3 &normal = edge1.CrossProduct(edge2).Normalized();
uint32_t indices[6];
if (!(mask & 1))
{
indices[0] = base;
indices[1] = base + 1;
indices[2] = base + 3;
indices[3] = base + 2;
indices[4] = base;
indices[5] = base + 3;
n0 = n1 = n2 = n3 = normal;
}
else
{
//Remember to flip orientation depending on the sign of the corner.
if (!(mask & 1))
{
idata.push_back(buffer[m-du-dv]);
idata.push_back(buffer[m-du]);
idata.push_back(buffer[m]);
idata.push_back(buffer[m-dv]);
idata.push_back(buffer[m-du-dv]);
idata.push_back(buffer[m]);
}
else
{
idata.push_back(buffer[m-du-dv]);
idata.push_back(buffer[m-dv]);
idata.push_back(buffer[m]);
idata.push_back(buffer[m-du]);
idata.push_back(buffer[m-du-dv]);
idata.push_back(buffer[m]);
}
indices[0] = base;
indices[1] = base + 2;
indices[2] = base + 3;
indices[3] = base + 1;
indices[4] = base;
indices[5] = base + 3;
n0 = n1 = n2 = n3 = -normal;
}
const Vector3 bar = (v0 + v1 + v2 + v3) / 4.0f + Vector3(0.5f, 0.5f, 0.5f) - n0 * 0.1f - Vector3(dx, h, dy);
const uint32_t bar_x = (uint32_t)bar.x_;
const uint32_t bar_y = (uint32_t)bar.z_;
const uint32_t bar_z = (uint32_t)bar.y_;
block_idx = bar_x + bar_y * x_len + bar_z * x_len * y_len;
const uint8_t type = flat_data[block_idx] & 0xff;
const uint16_t material = get_material_for_block(flag, type);
const uint16_t mv = (flat_data[block_idx] >> 8) & 3;
std::vector<uint32_t> &idata = index_data[material][mv];
if (!(flat_data[block_idx] & 0x8000))
printf("NOT GOOD: flag %u, pos %d %d %d\n", flag->id, x[0], x[1], x[2]);
const uint16_t skip = flat_data[block_idx] & 0x4000;
if (skip)
idata.resize(idata.size() - 6);
if (!skip)
for (int i = 0; i < 6; ++i)
idata.push_back(indices[i]);
}
}
}
@ -1760,7 +1768,7 @@ void CityMeshSection::GenerateSurfaceNets(const std::shared_ptr<Flag> &flag, Sha
const uint32_t material_vscale = resources.materials[i].vscale;
#if 1
const uint32_t base_height = std::max<uint32_t>(flag->base_height, -lod_base_depth) + lod_base_depth;
GenerateNormals(vertex_data, idata, (int32_t)(flag->x0 - ox), (int32_t)(flag->y0 - oy), base_height, material_hscale, material_vscale, lod_scale);
GenerateUVs(vertex_data, idata, (int32_t)(flag->x0 - ox), (int32_t)(flag->y0 - oy), base_height, material_hscale, material_vscale, lod_scale);
#else
GenerateSmoothNormals(vertex_data, idata, material_hscale, material_vscale, lod_scale, dx + 0.5f, dy + 0.5f, ox, oy);
#endif
@ -2053,12 +2061,12 @@ void CityMeshSection::RebuildFlags(const C &flags, const Selection &selection, b
++n_total;
const uint8_t type = tiles[tile_idx].get_type(h);
const uint16_t material_type = get_material_for_block(flag, type);
bool hidden = type == 0;
bool hidden = material_type == 0;
bool bottom_hidden = false, top_hidden = false, left_hidden = false, right_hidden = false, front_hidden = false, back_hidden = false;
if (!hidden)
{
#define OPAQUE(material) ((fully_opaque_block[material] & fully_opaque) == fully_opaque)
const uint8_t fully_opaque = fully_opaque_block[type];
#define OPAQUE(type) ((fully_opaque_block[get_item_for_block(flag, type)] & fully_opaque) == fully_opaque)
const uint8_t fully_opaque = fully_opaque_block[get_item_for_block(flag, type)];
bottom_hidden = h == 0 || OPAQUE(tiles[tile_idx].get_type(h-1));
top_hidden = h+1 < th && OPAQUE(tiles[tile_idx].get_type(h+1));
left_hidden = x > firstx && tiles[tile_idx-1].get_height() > h && OPAQUE(tiles[tile_idx-1].get_type(h));
@ -2072,8 +2080,6 @@ void CityMeshSection::RebuildFlags(const C &flags, const Selection &selection, b
if (!hidden && !(left_hidden && right_hidden && front_hidden && back_hidden) && cc::get_block_variant(material_type).item == ITEM_WATER)
++n_flowing_water[flag->id];
const bool has_smoothness = check_smoothness(tiles, x, y, h, tile_idx, firstx, firsty, lastx, lasty, tile_width);
auto &blocks = (*node.blocks);
if (IS_NEW(blocks[h]))
@ -2092,6 +2098,8 @@ void CityMeshSection::RebuildFlags(const C &flags, const Selection &selection, b
continue;
}
const bool has_smoothness = check_smoothness(tiles, x, y, h, tile_idx, firstx, firsty, lastx, lasty, tile_width);
#ifdef ENABLE_RENDER_CUBE_MODELS
if (0 && flagRenderMode == RenderCubeModels)
{
@ -2492,15 +2500,16 @@ void CityMeshSection::RebuildFlags(const C &flags, const Selection &selection, b
if (flagRenderMode == RenderSurfaceNets || flagRenderMode == RenderMixedSurfaceNets)
{
const uint32_t x_len = (fx1 - fx0) / (1 << shift) + 1 + 2;
const uint32_t y_len = (fy1 - fy0) / (1 << shift) + 1 + 2;
const uint32_t x_len = (fx1 - fx0 + 1 + (1 << shift) - 1) / (1 << shift) + 2;
const uint32_t y_len = (fy1 - fy0 + 1 + (1 << shift) - 1) / (1 << shift) + 2;
const uint32_t z_len = max_height + 1 + 2;
// data:
// 0-7: 8 bits: material
// 8-9: 2 bits: material version (normal/selected/new/burning)
// 10-12: 3 bits: smoothness
// 13-14: 2 bits: free
// 13: 1 bit: free
// 14: 1 bit: skip (already drawn)
// 15: 1 bit: present
std::vector<uint16_t> sndata;
sndata.resize(x_len * y_len * z_len, 0);
@ -2565,8 +2574,7 @@ void CityMeshSection::RebuildFlags(const C &flags, const Selection &selection, b
for (uint32_t h = 0; h < th; ++h)
{
const uint8_t type = tiles[tile_idx].get_type(h);
const uint16_t material_type = get_material_for_block(flag, type);
if (material_type == 0)
if (type == 0)
continue;
const bool has_smoothness = check_smoothness(tiles, x, y, h, tile_idx, firstx, firsty, lastx, lasty, tile_width);
@ -2581,7 +2589,7 @@ void CityMeshSection::RebuildFlags(const C &flags, const Selection &selection, b
x, y, h, snx, sny, snz, x_len, y_len, z_len, offset, sndata.size(), fx0, fy0, fx1, fy1, shift);
const uint16_t smoothness = tiles[tile_idx].get_smoothness(h);
const uint16_t skip = has_smoothness ? 0 : 1;
sndata[offset] = 0x8000 | (skip << 14) | (smoothness << 10) | (mv << 8) | material_type;
sndata[offset] = 0x8000 | (skip << 14) | (smoothness << 10) | (mv << 8) | type;
}
}
}