mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-23 05:02:48 +00:00
feat(ugc): the fast hidden-face method back, next to LU Toolbox's
UgcHsr::Options::method picks how hidden faces are found: toolbox (LU Toolbox's paths, the default, unchanged) or fast, the test the UGC server used before "hidden faces removed as LU Toolbox's Remove Hidden Faces decides them", restored unchanged as UgcRender::VisibleFromAround: the opaque mesh rendered from 42 directions around the model (Options::fastResolution pixels square, 1024 as before) and the triangles that show in none removed. It is much faster, but it also removes faces seen only by bounced light (interiors, recesses), which LU Toolbox keeps. Nothing sets it yet. Check: the UGC tests (the fast method removes a box seen only through a chimney, which the paths keep; its files are the same every time). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -80,9 +80,110 @@ namespace {
|
||||
|
||||
float PixelSize() const { return 2.0f * radius / resolution; }
|
||||
};
|
||||
|
||||
void Bounds(const UgcModel::Model& model, glm::vec3& center, float& radius) {
|
||||
glm::vec3 min{}, max{};
|
||||
if (!model.Bounds(min, max)) {
|
||||
center = glm::vec3(0.0f);
|
||||
radius = 1.0f;
|
||||
return;
|
||||
}
|
||||
center = (min + max) * 0.5f;
|
||||
radius = std::max(glm::length(max - min) * 0.5f, 0.01f);
|
||||
}
|
||||
}
|
||||
|
||||
namespace UgcRender {
|
||||
std::vector<glm::vec3> SphereDirections() {
|
||||
const float t = (1.0f + std::sqrt(5.0f)) / 2.0f;
|
||||
const std::vector<glm::vec3> corners = {
|
||||
{ -1, t, 0 }, { 1, t, 0 }, { -1, -t, 0 }, { 1, -t, 0 },
|
||||
{ 0, -1, t }, { 0, 1, t }, { 0, -1, -t }, { 0, 1, -t },
|
||||
{ t, 0, -1 }, { t, 0, 1 }, { -t, 0, -1 }, { -t, 0, 1 },
|
||||
};
|
||||
std::vector<glm::vec3> directions;
|
||||
for (const auto& corner : corners) directions.push_back(glm::normalize(corner));
|
||||
// Edge centres: the pairs of corners that are neighbours (the shortest distance apart)
|
||||
const float edge = glm::length(corners[0] - corners[1]);
|
||||
for (size_t i = 0; i < corners.size(); i++) {
|
||||
for (size_t j = i + 1; j < corners.size(); j++) {
|
||||
if (std::abs(glm::length(corners[i] - corners[j]) - edge) < 1e-3f) directions.push_back(glm::normalize(corners[i] + corners[j]));
|
||||
}
|
||||
}
|
||||
return directions;
|
||||
}
|
||||
|
||||
std::vector<bool> VisibleFromAround(const UgcModel::Model& model, int resolution, bool groundPlane) {
|
||||
const auto& opaque = model.opaque;
|
||||
const size_t triangles = opaque.TriangleCount();
|
||||
std::vector<bool> visible(triangles, false);
|
||||
if (opaque.Empty()) return visible;
|
||||
|
||||
glm::vec3 center{};
|
||||
float radius{};
|
||||
Bounds(model, center, radius);
|
||||
radius *= 1.02f;
|
||||
resolution = std::clamp(resolution, 64, 4096);
|
||||
const size_t pixels = static_cast<size_t>(resolution) * resolution;
|
||||
std::vector<float> depth(pixels);
|
||||
std::vector<uint32_t> ids(pixels);
|
||||
std::vector<glm::vec3> screen(opaque.positions.size());
|
||||
|
||||
std::vector<bool> facing(triangles, true);
|
||||
std::vector<glm::vec3> faceNormals(triangles, glm::vec3(0.0f));
|
||||
for (size_t t = 0; t < triangles; t++) {
|
||||
if (opaque.normals.size() != opaque.positions.size()) break;
|
||||
faceNormals[t] = glm::normalize(opaque.normals[opaque.indices[t * 3]] + opaque.normals[opaque.indices[t * 3 + 1]] + opaque.normals[opaque.indices[t * 3 + 2]] + glm::vec3(1e-6f));
|
||||
}
|
||||
// Without normals nothing is culled
|
||||
if (opaque.normals.size() != opaque.positions.size()) std::fill(faceNormals.begin(), faceNormals.end(), glm::vec3(0.0f));
|
||||
|
||||
for (const auto& direction : SphereDirections()) {
|
||||
// A ground plane under the model hides everything from below
|
||||
if (groundPlane && direction.y < -0.05f) continue;
|
||||
UgcThrottle::Checkpoint();
|
||||
const OrthoView view(center, radius, direction, resolution);
|
||||
const float bias = view.PixelSize();
|
||||
std::fill(depth.begin(), depth.end(), INF);
|
||||
std::fill(ids.begin(), ids.end(), 0);
|
||||
for (size_t v = 0; v < opaque.positions.size(); v++) screen[v] = view.Project(opaque.positions[v]);
|
||||
// Faces turned away can't be seen from here (they're seen from the directions they face); their vertex
|
||||
// normals say which way they face, which doesn't depend on the files' winding
|
||||
for (size_t t = 0; t < triangles; t++) facing[t] = glm::dot(faceNormals[t], direction) > -0.1f;
|
||||
for (size_t t = 0; t < triangles; t++) {
|
||||
if (!facing[t]) continue;
|
||||
if ((t & 0x3FFF) == 0) UgcThrottle::Checkpoint();
|
||||
const auto id = static_cast<uint32_t>(t + 1);
|
||||
Rasterize(resolution, resolution, screen[opaque.indices[t * 3]], screen[opaque.indices[t * 3 + 1]], screen[opaque.indices[t * 3 + 2]],
|
||||
[&](int x, int y, float z, float, float, float) {
|
||||
auto& stored = depth[static_cast<size_t>(y) * resolution + x];
|
||||
if (z < stored) {
|
||||
stored = z;
|
||||
ids[static_cast<size_t>(y) * resolution + x] = id;
|
||||
}
|
||||
});
|
||||
}
|
||||
for (const auto id : ids) {
|
||||
if (id != 0) visible[id - 1] = true;
|
||||
}
|
||||
|
||||
// Triangles too small or thin to cover a pixel centre: kept when their centre isn't behind what was drawn.
|
||||
// Bigger ones that show would have covered one.
|
||||
const float smallArea = 2.0f; // pixels
|
||||
for (size_t t = 0; t < triangles; t++) {
|
||||
if (visible[t] || !facing[t]) continue;
|
||||
const auto& a = screen[opaque.indices[t * 3]];
|
||||
const auto& b = screen[opaque.indices[t * 3 + 1]];
|
||||
const auto& c = screen[opaque.indices[t * 3 + 2]];
|
||||
if (std::abs(Edge(a, b, c.x, c.y)) * 0.5f > smallArea) continue;
|
||||
const auto centre = (a + b + c) / 3.0f;
|
||||
const int x = static_cast<int>(centre.x), y = static_cast<int>(centre.y);
|
||||
if (x < 0 || y < 0 || x >= resolution || y >= resolution || centre.z <= depth[static_cast<size_t>(y) * resolution + x] + bias) visible[t] = true;
|
||||
}
|
||||
}
|
||||
return visible;
|
||||
}
|
||||
|
||||
std::vector<float> AmbientOcclusion(const UgcModel::Mesh& mesh, const UgcModel::Mesh& occluders, float distance, int samples, UgcRays::eBackend rays) {
|
||||
std::vector<float> ao(mesh.positions.size(), 1.0f);
|
||||
if (occluders.Empty() || samples <= 0 || distance <= 0.0f || mesh.normals.size() != mesh.positions.size()) return ao;
|
||||
|
||||
Reference in New Issue
Block a user