Skip to content

Commit

Permalink
gltfpack: Introduce artificial seam edges on UV flips
Browse files Browse the repository at this point in the history
When two triangles with reverse UV direction meet, an edge between them
needs to be a seam edge. This makes sure that UV does not degrade by
preventing the collapses that go across different UV directions.

Ideally this should be implemented inside meshopt library at some point,
but it requires a separate function and some way to make possibly-manifold
vertices "seam" which may run into issues with the seam handling logic.

So for now, pre-process the mesh by splitting vertices in gltfpack, and
remap triangles with negative UV area to use the newly created vertices.
The number of these vertices is generally low.
  • Loading branch information
zeux committed Nov 17, 2024
1 parent a64a212 commit 021adda
Showing 1 changed file with 68 additions and 0 deletions.
68 changes: 68 additions & 0 deletions gltf/mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,71 @@ static void simplifyAttributes(std::vector<float>& attrs, float* attrw, size_t s
}
}

static void simplifyUvSplit(Mesh& mesh)
{
assert(mesh.type == cgltf_primitive_type_triangles);
assert(!mesh.indices.empty());

const Stream* uv = getStream(mesh, cgltf_attribute_type_texcoord);
if (!uv)
return;

size_t vertex_count = uv->data.size();

std::vector<unsigned char> uvsign(mesh.indices.size() / 3);
std::vector<unsigned char> flipseam(vertex_count);

for (size_t i = 0; i < mesh.indices.size(); i += 3)
{
unsigned int a = mesh.indices[i + 0];
unsigned int b = mesh.indices[i + 1];
unsigned int c = mesh.indices[i + 2];

const Attr& va = uv->data[a];
const Attr& vb = uv->data[b];
const Attr& vc = uv->data[c];

float uvarea = (vb.f[0] - va.f[0]) * (vc.f[1] - va.f[1]) - (vc.f[0] - va.f[0]) * (vb.f[1] - va.f[1]);
unsigned char flag = uvarea > 0 ? 1 : (uvarea < 0 ? 2 : 0);

uvsign[i / 3] = flag;
flipseam[a] |= flag;
flipseam[b] |= flag;
flipseam[c] |= flag;
}

std::vector<unsigned int> split(vertex_count);
size_t splits = 0;

for (size_t i = 0; i < vertex_count; ++i)
{
if (flipseam[i] == 3)
{
split[i] = unsigned(vertex_count + splits);
splits++;

for (size_t k = 0; k < mesh.streams.size(); ++k)
mesh.streams[k].data.push_back(mesh.streams[k].data[i]);
}
}

for (size_t i = 0; i < mesh.indices.size(); i += 3)
{
unsigned int a = mesh.indices[i + 0];
unsigned int b = mesh.indices[i + 1];
unsigned int c = mesh.indices[i + 2];

unsigned char sign = uvsign[i / 3];

if (flipseam[a] == 3 && sign == 2)
mesh.indices[i + 0] = split[a];
if (flipseam[b] == 3 && sign == 2)
mesh.indices[i + 1] = split[b];
if (flipseam[c] == 3 && sign == 2)
mesh.indices[i + 2] = split[c];
}
}

static void simplifyMesh(Mesh& mesh, float threshold, float error, bool attributes, bool aggressive, bool lock_borders, bool debug = false)
{
enum
Expand All @@ -778,6 +843,9 @@ static void simplifyMesh(Mesh& mesh, float threshold, float error, bool attribut
if (!positions)
return;

if (attributes)
simplifyUvSplit(mesh);

size_t vertex_count = mesh.streams[0].data.size();

size_t target_index_count = size_t(double(size_t(mesh.indices.size() / 3)) * threshold) * 3;
Expand Down

0 comments on commit 021adda

Please sign in to comment.