Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

simplify: Rework point simplification to use reservoirs #618

Merged
merged 3 commits into from
Oct 12, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 59 additions & 27 deletions src/simplifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,7 @@ static const size_t kMaxAttributes = 16;

struct Quadric
{
// a00*x^2 + a11*y^2 + a22*z^2 + 2*(a10*xy + a20*xz + a21*yz) + b0*x + b1*y + b2*z + c
float a00, a11, a22;
float a10, a20, a21;
float b0, b1, b2, c;
Expand All @@ -453,9 +454,16 @@ struct Quadric

struct QuadricGrad
{
// gx*x + gy*y + gz*z + gw
float gx, gy, gz, gw;
};

struct Reservoir
{
float x, y, z;
float w;
};

struct Collapse
{
unsigned int v0;
Expand Down Expand Up @@ -596,22 +604,6 @@ static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, flo
Q.w = w;
}

static void quadricFromPoint(Quadric& Q, float x, float y, float z, float w)
{
// we need to encode (x - X) ^ 2 + (y - Y)^2 + (z - Z)^2 into the quadric
Q.a00 = w;
Q.a11 = w;
Q.a22 = w;
Q.a10 = 0.f;
Q.a20 = 0.f;
Q.a21 = 0.f;
Q.b0 = -2.f * x * w;
Q.b1 = -2.f * y * w;
Q.b2 = -2.f * z * w;
Q.c = (x * x + y * y + z * z) * w;
Q.w = w;
}

static void quadricFromTriangle(Quadric& Q, const Vector3& p0, const Vector3& p1, const Vector3& p2, float weight)
{
Vector3 p10 = {p1.x - p0.x, p1.y - p0.y, p1.z - p0.z};
Expand Down Expand Up @@ -1330,17 +1322,31 @@ static void fillCellQuadrics(Quadric* cell_quadrics, const unsigned int* indices
}
}

static void fillCellQuadrics(Quadric* cell_quadrics, const Vector3* vertex_positions, size_t vertex_count, const unsigned int* vertex_cells)
static void fillCellReservoirs(Reservoir* cell_reservoirs, size_t cell_count, const Vector3* vertex_positions, size_t vertex_count, const unsigned int* vertex_cells)
{
for (size_t i = 0; i < vertex_count; ++i)
{
unsigned int c = vertex_cells[i];
unsigned int cell = vertex_cells[i];
const Vector3& v = vertex_positions[i];
Reservoir& r = cell_reservoirs[cell];

Quadric Q;
quadricFromPoint(Q, v.x, v.y, v.z, 1.f);
float w = 1.f;

quadricAdd(cell_quadrics[c], Q);
r.x += v.x * w;
r.y += v.y * w;
r.z += v.z * w;
r.w += w;
}

for (size_t i = 0; i < cell_count; ++i)
{
Reservoir& r = cell_reservoirs[i];

float iw = r.w == 0.f ? 0.f : 1.f / r.w;

r.x *= iw;
r.y *= iw;
r.z *= iw;
}
}

Expand All @@ -1361,6 +1367,26 @@ static void fillCellRemap(unsigned int* cell_remap, float* cell_errors, size_t c
}
}

static void fillCellRemap(unsigned int* cell_remap, float* cell_errors, size_t cell_count, const unsigned int* vertex_cells, const Reservoir* cell_reservoirs, const Vector3* vertex_positions, size_t vertex_count)
{
memset(cell_remap, -1, cell_count * sizeof(unsigned int));

for (size_t i = 0; i < vertex_count; ++i)
{
unsigned int cell = vertex_cells[i];
const Vector3& v = vertex_positions[i];
const Reservoir& r = cell_reservoirs[cell];

float error = (v.x - r.x) * (v.x - r.x) + (v.y - r.y) * (v.y - r.y) + (v.z - r.z) * (v.z - r.z);

if (cell_remap[cell] == ~0u || cell_errors[cell] > error)
{
cell_remap[cell] = unsigned(i);
cell_errors[cell] = error;
}
}
}

static size_t filterTriangles(unsigned int* destination, unsigned int* tritable, size_t tritable_size, const unsigned int* indices, size_t index_count, const unsigned int* vertex_cells, const unsigned int* cell_remap)
{
TriangleHasher hasher = {destination};
Expand Down Expand Up @@ -1818,24 +1844,30 @@ size_t meshopt_simplifyPoints(unsigned int* destination, const float* vertex_pos
computeVertexIds(vertex_ids, vertex_positions, vertex_count, min_grid);
size_t cell_count = fillVertexCells(table, table_size, vertex_cells, vertex_ids, vertex_count);

// build a quadric for each target cell
Quadric* cell_quadrics = allocator.allocate<Quadric>(cell_count);
memset(cell_quadrics, 0, cell_count * sizeof(Quadric));
// accumulate points into a reservoir for each target cell
Reservoir* cell_reservoirs = allocator.allocate<Reservoir>(cell_count);
memset(cell_reservoirs, 0, cell_count * sizeof(Reservoir));

fillCellQuadrics(cell_quadrics, vertex_positions, vertex_count, vertex_cells);
fillCellReservoirs(cell_reservoirs, cell_count, vertex_positions, vertex_count, vertex_cells);

// for each target cell, find the vertex with the minimal error
unsigned int* cell_remap = allocator.allocate<unsigned int>(cell_count);
float* cell_errors = allocator.allocate<float>(cell_count);

fillCellRemap(cell_remap, cell_errors, cell_count, vertex_cells, cell_quadrics, vertex_positions, vertex_count);
fillCellRemap(cell_remap, cell_errors, cell_count, vertex_cells, cell_reservoirs, vertex_positions, vertex_count);

// copy results to the output
assert(cell_count <= target_vertex_count);
memcpy(destination, cell_remap, sizeof(unsigned int) * cell_count);

#if TRACE
printf("result: %d cells\n", int(cell_count));
// compute error
float result_error = 0.f;

for (size_t i = 0; i < cell_count; ++i)
result_error = result_error < cell_errors[i] ? cell_errors[i] : result_error;

printf("result: %d cells, %e error\n", int(cell_count), sqrtf(result_error));
#endif

return cell_count;
Expand Down