Update meshoptimizer to version v0.21

This commit is contained in:
Wojtek Figat
2024-08-08 15:30:47 +02:00
parent ca62a6c4bf
commit 1c24f5d3ce
11 changed files with 281 additions and 80 deletions

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2016-2023 Arseny Kapoulkine Copyright (c) 2016-2024 Arseny Kapoulkine
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
#include "meshoptimizer.h" #include "meshoptimizer.h"
void meshopt_setAllocator(void* (MESHOPTIMIZER_ALLOC_CALLCONV *allocate)(size_t), void (MESHOPTIMIZER_ALLOC_CALLCONV *deallocate)(void*)) void meshopt_setAllocator(void*(MESHOPTIMIZER_ALLOC_CALLCONV* allocate)(size_t), void(MESHOPTIMIZER_ALLOC_CALLCONV* deallocate)(void*))
{ {
meshopt_Allocator::Storage::allocate = allocate; meshopt_Allocator::Storage::allocate = allocate;
meshopt_Allocator::Storage::deallocate = deallocate; meshopt_Allocator::Storage::deallocate = deallocate;

View File

@@ -441,7 +441,7 @@ static size_t kdtreeBuild(size_t offset, KDNode* nodes, size_t node_count, const
} }
// split axis is one where the variance is largest // split axis is one where the variance is largest
unsigned int axis = vars[0] >= vars[1] && vars[0] >= vars[2] ? 0 : vars[1] >= vars[2] ? 1 : 2; unsigned int axis = (vars[0] >= vars[1] && vars[0] >= vars[2]) ? 0 : (vars[1] >= vars[2] ? 1 : 2);
float split = mean[axis]; float split = mean[axis];
size_t middle = kdtreePartition(indices, count, points, stride, axis, split); size_t middle = kdtreePartition(indices, count, points, stride, axis, split);
@@ -882,3 +882,93 @@ meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices
return meshopt_computeClusterBounds(indices, triangle_count * 3, vertex_positions, vertex_count, vertex_positions_stride); return meshopt_computeClusterBounds(indices, triangle_count * 3, vertex_positions, vertex_count, vertex_positions_stride);
} }
void meshopt_optimizeMeshlet(unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t triangle_count, size_t vertex_count)
{
using namespace meshopt;
assert(triangle_count <= kMeshletMaxTriangles);
assert(vertex_count <= kMeshletMaxVertices);
unsigned char* indices = meshlet_triangles;
unsigned int* vertices = meshlet_vertices;
// cache tracks vertex timestamps (corresponding to triangle index! all 3 vertices are added at the same time and never removed)
unsigned char cache[kMeshletMaxVertices];
memset(cache, 0, vertex_count);
// note that we start from a value that means all vertices aren't in cache
unsigned char cache_last = 128;
const unsigned char cache_cutoff = 3; // 3 triangles = ~5..9 vertices depending on reuse
for (size_t i = 0; i < triangle_count; ++i)
{
int next = -1;
int next_match = -1;
for (size_t j = i; j < triangle_count; ++j)
{
unsigned char a = indices[j * 3 + 0], b = indices[j * 3 + 1], c = indices[j * 3 + 2];
assert(a < vertex_count && b < vertex_count && c < vertex_count);
// score each triangle by how many vertices are in cache
// note: the distance is computed using unsigned 8-bit values, so cache timestamp overflow is handled gracefully
int aok = (unsigned char)(cache_last - cache[a]) < cache_cutoff;
int bok = (unsigned char)(cache_last - cache[b]) < cache_cutoff;
int cok = (unsigned char)(cache_last - cache[c]) < cache_cutoff;
if (aok + bok + cok > next_match)
{
next = (int)j;
next_match = aok + bok + cok;
// note that we could end up with all 3 vertices in the cache, but 2 is enough for ~strip traversal
if (next_match >= 2)
break;
}
}
assert(next >= 0);
unsigned char a = indices[next * 3 + 0], b = indices[next * 3 + 1], c = indices[next * 3 + 2];
// shift triangles before the next one forward so that we always keep an ordered partition
// note: this could have swapped triangles [i] and [next] but that distorts the order and may skew the output sequence
memmove(indices + (i + 1) * 3, indices + i * 3, (next - i) * 3 * sizeof(unsigned char));
indices[i * 3 + 0] = a;
indices[i * 3 + 1] = b;
indices[i * 3 + 2] = c;
// cache timestamp is the same between all vertices of each triangle to reduce overflow
cache_last++;
cache[a] = cache_last;
cache[b] = cache_last;
cache[c] = cache_last;
}
// reorder meshlet vertices for access locality assuming index buffer is scanned sequentially
unsigned int order[kMeshletMaxVertices];
unsigned char remap[kMeshletMaxVertices];
memset(remap, -1, vertex_count);
size_t vertex_offset = 0;
for (size_t i = 0; i < triangle_count * 3; ++i)
{
unsigned char& r = remap[indices[i]];
if (r == 0xff)
{
r = (unsigned char)(vertex_offset);
order[vertex_offset] = vertices[indices[i]];
vertex_offset++;
}
indices[i] = r;
}
assert(vertex_offset <= vertex_count);
memcpy(vertices, order, vertex_offset * sizeof(unsigned int));
}

View File

@@ -33,7 +33,7 @@ static int rotateTriangle(unsigned int a, unsigned int b, unsigned int c, unsign
{ {
(void)a; (void)a;
return (b == next) ? 1 : (c == next) ? 2 : 0; return (b == next) ? 1 : (c == next ? 2 : 0);
} }
static int getEdgeFifo(EdgeFifo fifo, unsigned int a, unsigned int b, unsigned int c, size_t offset) static int getEdgeFifo(EdgeFifo fifo, unsigned int a, unsigned int b, unsigned int c, size_t offset)
@@ -217,7 +217,7 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
int fe = fer >> 2; int fe = fer >> 2;
int fc = getVertexFifo(vertexfifo, c, vertexfifooffset); int fc = getVertexFifo(vertexfifo, c, vertexfifooffset);
int fec = (fc >= 1 && fc < fecmax) ? fc : (c == next) ? (next++, 0) : 15; int fec = (fc >= 1 && fc < fecmax) ? fc : (c == next ? (next++, 0) : 15);
if (fec == 15 && version >= 1) if (fec == 15 && version >= 1)
{ {
@@ -267,8 +267,8 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
// after rotation, a is almost always equal to next, so we don't waste bits on FIFO encoding for a // after rotation, a is almost always equal to next, so we don't waste bits on FIFO encoding for a
int fea = (a == next) ? (next++, 0) : 15; int fea = (a == next) ? (next++, 0) : 15;
int feb = (fb >= 0 && fb < 14) ? (fb + 1) : (b == next) ? (next++, 0) : 15; int feb = (fb >= 0 && fb < 14) ? fb + 1 : (b == next ? (next++, 0) : 15);
int fec = (fc >= 0 && fc < 14) ? (fc + 1) : (c == next) ? (next++, 0) : 15; int fec = (fc >= 0 && fc < 14) ? fc + 1 : (c == next ? (next++, 0) : 15);
// we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise // we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise
unsigned char codeaux = (unsigned char)((feb << 4) | fec); unsigned char codeaux = (unsigned char)((feb << 4) | fec);

View File

@@ -1,7 +1,7 @@
/** /**
* meshoptimizer - version 0.20 * meshoptimizer - version 0.21
* *
* Copyright (C) 2016-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Copyright (C) 2016-2024, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at https://github.com/zeux/meshoptimizer * Report bugs and download new versions at https://github.com/zeux/meshoptimizer
* *
* This library is distributed under the MIT License. See notice at the end of this file. * This library is distributed under the MIT License. See notice at the end of this file.
@@ -12,7 +12,7 @@
#include <stddef.h> #include <stddef.h>
/* Version macro; major * 1000 + minor * 10 + patch */ /* Version macro; major * 1000 + minor * 10 + patch */
#define MESHOPTIMIZER_VERSION 200 /* 0.20 */ #define MESHOPTIMIZER_VERSION 210 /* 0.21 */
/* If no API is defined, assume default */ /* If no API is defined, assume default */
#ifndef MESHOPTIMIZER_API #ifndef MESHOPTIMIZER_API
@@ -311,12 +311,12 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_decodeFilterExp(void* buffer, size_t cou
*/ */
enum meshopt_EncodeExpMode enum meshopt_EncodeExpMode
{ {
/* When encoding exponents, use separate values for each component (maximum quality) */ /* When encoding exponents, use separate values for each component (maximum quality) */
meshopt_EncodeExpSeparate, meshopt_EncodeExpSeparate,
/* When encoding exponents, use shared value for all components of each vector (better compression) */ /* When encoding exponents, use shared value for all components of each vector (better compression) */
meshopt_EncodeExpSharedVector, meshopt_EncodeExpSharedVector,
/* When encoding exponents, use shared value for each component of all vectors (best compression) */ /* When encoding exponents, use shared value for each component of all vectors (best compression) */
meshopt_EncodeExpSharedComponent, meshopt_EncodeExpSharedComponent,
}; };
MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data); MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data);
@@ -328,8 +328,12 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_
*/ */
enum enum
{ {
/* Do not move vertices that are located on the topological border (vertices on triangle edges that don't have a paired triangle). Useful for simplifying portions of the larger mesh. */ /* Do not move vertices that are located on the topological border (vertices on triangle edges that don't have a paired triangle). Useful for simplifying portions of the larger mesh. */
meshopt_SimplifyLockBorder = 1 << 0, meshopt_SimplifyLockBorder = 1 << 0,
/* Improve simplification performance assuming input indices are a sparse subset of the mesh. Note that error becomes relative to subset extents. */
meshopt_SimplifySparse = 1 << 1,
/* Treat error limit and resulting error as absolute instead of relative to mesh extents. */
meshopt_SimplifyErrorAbsolute = 1 << 2,
}; };
/** /**
@@ -357,9 +361,10 @@ MESHOPTIMIZER_API size_t meshopt_simplify(unsigned int* destination, const unsig
* vertex_attributes should have attribute_count floats for each vertex * vertex_attributes should have attribute_count floats for each vertex
* attribute_weights should have attribute_count floats in total; the weights determine relative priority of attributes between each other and wrt position. The recommended weight range is [1e-3..1e-1], assuming attribute data is in [0..1] range. * attribute_weights should have attribute_count floats in total; the weights determine relative priority of attributes between each other and wrt position. The recommended weight range is [1e-3..1e-1], assuming attribute data is in [0..1] range.
* attribute_count must be <= 16 * attribute_count must be <= 16
* vertex_lock can be NULL; when it's not NULL, it should have a value for each vertex; 1 denotes vertices that can't be moved
* TODO target_error/result_error currently use combined distance+attribute error; this may change in the future * TODO target_error/result_error currently use combined distance+attribute error; this may change in the future
*/ */
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float* result_error); MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* result_error);
/** /**
* Experimental: Mesh simplifier (sloppy) * Experimental: Mesh simplifier (sloppy)
@@ -486,13 +491,23 @@ struct meshopt_Meshlet
* meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices * meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices
* meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3 * meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3
* vertex_positions should have float3 position in the first 12 bytes of each vertex * vertex_positions should have float3 position in the first 12 bytes of each vertex
* max_vertices and max_triangles must not exceed implementation limits (max_vertices <= 255 - not 256!, max_triangles <= 512) * max_vertices and max_triangles must not exceed implementation limits (max_vertices <= 255 - not 256!, max_triangles <= 512; max_triangles must be divisible by 4)
* cone_weight should be set to 0 when cone culling is not used, and a value between 0 and 1 otherwise to balance between cluster size and cone culling efficiency * cone_weight should be set to 0 when cone culling is not used, and a value between 0 and 1 otherwise to balance between cluster size and cone culling efficiency
*/ */
MESHOPTIMIZER_API size_t meshopt_buildMeshlets(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight); MESHOPTIMIZER_API size_t meshopt_buildMeshlets(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight);
MESHOPTIMIZER_API size_t meshopt_buildMeshletsScan(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles); MESHOPTIMIZER_API size_t meshopt_buildMeshletsScan(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles);
MESHOPTIMIZER_API size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles); MESHOPTIMIZER_API size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles);
/**
* Experimental: Meshlet optimizer
* Reorders meshlet vertices and triangles to maximize locality to improve rasterizer throughput
*
* meshlet_triangles and meshlet_vertices must refer to meshlet triangle and vertex index data; when buildMeshlets* is used, these
* need to be computed from meshlet's vertex_offset and triangle_offset
* triangle_count and vertex_count must not exceed implementation limits (vertex_count <= 255 - not 256!, triangle_count <= 512)
*/
MESHOPTIMIZER_EXPERIMENTAL void meshopt_optimizeMeshlet(unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t triangle_count, size_t vertex_count);
struct meshopt_Bounds struct meshopt_Bounds
{ {
/* bounding sphere, useful for frustum and occlusion culling */ /* bounding sphere, useful for frustum and occlusion culling */
@@ -649,7 +664,7 @@ inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const
template <typename T> template <typename T>
inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = NULL); inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = NULL);
template <typename T> template <typename T>
inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = NULL); inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = NULL);
template <typename T> template <typename T>
inline size_t meshopt_simplifySloppy(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error = NULL); inline size_t meshopt_simplifySloppy(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error = NULL);
template <typename T> template <typename T>
@@ -956,12 +971,12 @@ inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_co
} }
template <typename T> template <typename T>
inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float* result_error) inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* result_error)
{ {
meshopt_IndexAdapter<T> in(NULL, indices, index_count); meshopt_IndexAdapter<T> in(NULL, indices, index_count);
meshopt_IndexAdapter<T> out(destination, NULL, index_count); meshopt_IndexAdapter<T> out(destination, NULL, index_count);
return meshopt_simplifyWithAttributes(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, vertex_attributes, vertex_attributes_stride, attribute_weights, attribute_count, target_index_count, target_error, options, result_error); return meshopt_simplifyWithAttributes(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, vertex_attributes, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, target_index_count, target_error, options, result_error);
} }
template <typename T> template <typename T>
@@ -1050,7 +1065,7 @@ inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_
#endif #endif
/** /**
* Copyright (c) 2016-2023 Arseny Kapoulkine * Copyright (c) 2016-2024 Arseny Kapoulkine
* *
* Permission is hereby granted, free of charge, to any person * Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation * obtaining a copy of this software and associated documentation

View File

@@ -53,11 +53,10 @@ static void rasterize(OverdrawBuffer* buffer, float v1x, float v1y, float v1z, f
// flip backfacing triangles to simplify rasterization logic // flip backfacing triangles to simplify rasterization logic
if (sign) if (sign)
{ {
// flipping v2 & v3 preserves depth gradients since they're based on v1 // flipping v2 & v3 preserves depth gradients since they're based on v1; only v1z is used below
float t; float t;
t = v2x, v2x = v3x, v3x = t; t = v2x, v2x = v3x, v3x = t;
t = v2y, v2y = v3y, v3y = t; t = v2y, v2y = v3y, v3y = t;
t = v2z, v2z = v3z, v3z = t;
// flip depth since we rasterize backfacing triangles to second buffer with reverse Z; only v1z is used below // flip depth since we rasterize backfacing triangles to second buffer with reverse Z; only v1z is used below
v1z = kViewport - v1z; v1z = kViewport - v1z;

View File

@@ -3,9 +3,15 @@
#include <assert.h> #include <assert.h>
union FloatBits
{
float f;
unsigned int ui;
};
unsigned short meshopt_quantizeHalf(float v) unsigned short meshopt_quantizeHalf(float v)
{ {
union { float f; unsigned int ui; } u = {v}; FloatBits u = {v};
unsigned int ui = u.ui; unsigned int ui = u.ui;
int s = (ui >> 16) & 0x8000; int s = (ui >> 16) & 0x8000;
@@ -30,7 +36,7 @@ float meshopt_quantizeFloat(float v, int N)
{ {
assert(N >= 0 && N <= 23); assert(N >= 0 && N <= 23);
union { float f; unsigned int ui; } u = {v}; FloatBits u = {v};
unsigned int ui = u.ui; unsigned int ui = u.ui;
const int mask = (1 << (23 - N)) - 1; const int mask = (1 << (23 - N)) - 1;
@@ -64,7 +70,7 @@ float meshopt_dequantizeHalf(unsigned short h)
// 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255 // 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255
r += (em >= (31 << 10)) ? (112 << 23) : 0; r += (em >= (31 << 10)) ? (112 << 23) : 0;
union { float f; unsigned int ui; } u; FloatBits u;
u.ui = s | r; u.ui = s | r;
return u.f; return u.f;
} }

View File

@@ -111,10 +111,12 @@ struct PositionHasher
{ {
const float* vertex_positions; const float* vertex_positions;
size_t vertex_stride_float; size_t vertex_stride_float;
const unsigned int* sparse_remap;
size_t hash(unsigned int index) const size_t hash(unsigned int index) const
{ {
const unsigned int* key = reinterpret_cast<const unsigned int*>(vertex_positions + index * vertex_stride_float); unsigned int ri = sparse_remap ? sparse_remap[index] : index;
const unsigned int* key = reinterpret_cast<const unsigned int*>(vertex_positions + ri * vertex_stride_float);
// scramble bits to make sure that integer coordinates have entropy in lower bits // scramble bits to make sure that integer coordinates have entropy in lower bits
unsigned int x = key[0] ^ (key[0] >> 17); unsigned int x = key[0] ^ (key[0] >> 17);
@@ -127,7 +129,25 @@ struct PositionHasher
bool equal(unsigned int lhs, unsigned int rhs) const bool equal(unsigned int lhs, unsigned int rhs) const
{ {
return memcmp(vertex_positions + lhs * vertex_stride_float, vertex_positions + rhs * vertex_stride_float, sizeof(float) * 3) == 0; unsigned int li = sparse_remap ? sparse_remap[lhs] : lhs;
unsigned int ri = sparse_remap ? sparse_remap[rhs] : rhs;
return memcmp(vertex_positions + li * vertex_stride_float, vertex_positions + ri * vertex_stride_float, sizeof(float) * 3) == 0;
}
};
struct RemapHasher
{
unsigned int* remap;
size_t hash(unsigned int id) const
{
return id * 0x5bd1e995;
}
bool equal(unsigned int lhs, unsigned int rhs) const
{
return remap[lhs] == rhs;
} }
}; };
@@ -167,9 +187,9 @@ static T* hashLookup2(T* table, size_t buckets, const Hash& hash, const T& key,
return NULL; return NULL;
} }
static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, meshopt_Allocator& allocator) static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const unsigned int* sparse_remap, meshopt_Allocator& allocator)
{ {
PositionHasher hasher = {vertex_positions_data, vertex_positions_stride / sizeof(float)}; PositionHasher hasher = {vertex_positions_data, vertex_positions_stride / sizeof(float), sparse_remap};
size_t table_size = hashBuckets2(vertex_count); size_t table_size = hashBuckets2(vertex_count);
unsigned int* table = allocator.allocate<unsigned int>(table_size); unsigned int* table = allocator.allocate<unsigned int>(table_size);
@@ -205,6 +225,57 @@ static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const f
allocator.deallocate(table); allocator.deallocate(table);
} }
static unsigned int* buildSparseRemap(unsigned int* indices, size_t index_count, size_t vertex_count, size_t* out_vertex_count, meshopt_Allocator& allocator)
{
// use a bit set to compute the precise number of unique vertices
unsigned char* filter = allocator.allocate<unsigned char>((vertex_count + 7) / 8);
memset(filter, 0, (vertex_count + 7) / 8);
size_t unique = 0;
for (size_t i = 0; i < index_count; ++i)
{
unsigned int index = indices[i];
assert(index < vertex_count);
unique += (filter[index / 8] & (1 << (index % 8))) == 0;
filter[index / 8] |= 1 << (index % 8);
}
unsigned int* remap = allocator.allocate<unsigned int>(unique);
size_t offset = 0;
// temporary map dense => sparse; we allocate it last so that we can deallocate it
size_t revremap_size = hashBuckets2(unique);
unsigned int* revremap = allocator.allocate<unsigned int>(revremap_size);
memset(revremap, -1, revremap_size * sizeof(unsigned int));
// fill remap, using revremap as a helper, and rewrite indices in the same pass
RemapHasher hasher = {remap};
for (size_t i = 0; i < index_count; ++i)
{
unsigned int index = indices[i];
unsigned int* entry = hashLookup2(revremap, revremap_size, hasher, index, ~0u);
if (*entry == ~0u)
{
remap[offset] = index;
*entry = unsigned(offset);
offset++;
}
indices[i] = *entry;
}
allocator.deallocate(revremap);
assert(offset == unique);
*out_vertex_count = unique;
return remap;
}
enum VertexKind enum VertexKind
{ {
Kind_Manifold, // not on an attribute seam, not on any boundary Kind_Manifold, // not on an attribute seam, not on any boundary
@@ -252,7 +323,7 @@ static bool hasEdge(const EdgeAdjacency& adjacency, unsigned int a, unsigned int
return false; return false;
} }
static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned int* loopback, size_t vertex_count, const EdgeAdjacency& adjacency, const unsigned int* remap, const unsigned int* wedge, unsigned int options) static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned int* loopback, size_t vertex_count, const EdgeAdjacency& adjacency, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_lock, const unsigned int* sparse_remap, unsigned int options)
{ {
memset(loop, -1, vertex_count * sizeof(unsigned int)); memset(loop, -1, vertex_count * sizeof(unsigned int));
memset(loopback, -1, vertex_count * sizeof(unsigned int)); memset(loopback, -1, vertex_count * sizeof(unsigned int));
@@ -298,7 +369,12 @@ static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned
{ {
if (remap[i] == i) if (remap[i] == i)
{ {
if (wedge[i] == i) if (vertex_lock && vertex_lock[sparse_remap ? sparse_remap[i] : i])
{
// vertex is explicitly locked
result[i] = Kind_Locked;
}
else if (wedge[i] == i)
{ {
// no attribute seam, need to check if it's manifold // no attribute seam, need to check if it's manifold
unsigned int openi = openinc[i], openo = openout[i]; unsigned int openi = openinc[i], openo = openout[i];
@@ -378,7 +454,7 @@ struct Vector3
float x, y, z; float x, y, z;
}; };
static float rescalePositions(Vector3* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride) static float rescalePositions(Vector3* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const unsigned int* sparse_remap = NULL)
{ {
size_t vertex_stride_float = vertex_positions_stride / sizeof(float); size_t vertex_stride_float = vertex_positions_stride / sizeof(float);
@@ -387,7 +463,8 @@ static float rescalePositions(Vector3* result, const float* vertex_positions_dat
for (size_t i = 0; i < vertex_count; ++i) for (size_t i = 0; i < vertex_count; ++i)
{ {
const float* v = vertex_positions_data + i * vertex_stride_float; unsigned int ri = sparse_remap ? sparse_remap[i] : unsigned(i);
const float* v = vertex_positions_data + ri * vertex_stride_float;
if (result) if (result)
{ {
@@ -426,15 +503,17 @@ static float rescalePositions(Vector3* result, const float* vertex_positions_dat
return extent; return extent;
} }
static void rescaleAttributes(float* result, const float* vertex_attributes_data, size_t vertex_count, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count) static void rescaleAttributes(float* result, const float* vertex_attributes_data, size_t vertex_count, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned int* sparse_remap)
{ {
size_t vertex_attributes_stride_float = vertex_attributes_stride / sizeof(float); size_t vertex_attributes_stride_float = vertex_attributes_stride / sizeof(float);
for (size_t i = 0; i < vertex_count; ++i) for (size_t i = 0; i < vertex_count; ++i)
{ {
unsigned int ri = sparse_remap ? sparse_remap[i] : unsigned(i);
for (size_t k = 0; k < attribute_count; ++k) for (size_t k = 0; k < attribute_count; ++k)
{ {
float a = vertex_attributes_data[i * vertex_attributes_stride_float + k]; float a = vertex_attributes_data[ri * vertex_attributes_stride_float + k];
result[i * attribute_count + k] = a * attribute_weights[k]; result[i * attribute_count + k] = a * attribute_weights[k];
} }
@@ -580,7 +659,7 @@ static float quadricError(const Quadric& Q, const QuadricGrad* G, size_t attribu
} }
// TODO: weight normalization is breaking attribute error somehow // TODO: weight normalization is breaking attribute error somehow
float s = 1;// Q.w == 0.f ? 0.f : 1.f / Q.w; float s = 1; // Q.w == 0.f ? 0.f : 1.f / Q.w;
return fabsf(r) * s; return fabsf(r) * s;
} }
@@ -813,7 +892,13 @@ static bool hasTriangleFlip(const Vector3& a, const Vector3& b, const Vector3& c
Vector3 nbc = {eb.y * ec.z - eb.z * ec.y, eb.z * ec.x - eb.x * ec.z, eb.x * ec.y - eb.y * ec.x}; Vector3 nbc = {eb.y * ec.z - eb.z * ec.y, eb.z * ec.x - eb.x * ec.z, eb.x * ec.y - eb.y * ec.x};
Vector3 nbd = {eb.y * ed.z - eb.z * ed.y, eb.z * ed.x - eb.x * ed.z, eb.x * ed.y - eb.y * ed.x}; Vector3 nbd = {eb.y * ed.z - eb.z * ed.y, eb.z * ed.x - eb.x * ed.z, eb.x * ed.y - eb.y * ed.x};
return nbc.x * nbd.x + nbc.y * nbd.y + nbc.z * nbd.z <= 0; float ndp = nbc.x * nbd.x + nbc.y * nbd.y + nbc.z * nbd.z;
float abc = nbc.x * nbc.x + nbc.y * nbc.y + nbc.z * nbc.z;
float abd = nbd.x * nbd.x + nbd.y * nbd.y + nbd.z * nbd.z;
// scale is cos(angle); somewhat arbitrarily set to ~75 degrees
// note that the "pure" check is ndp <= 0 (90 degree cutoff) but that allows flipping through a series of close-to-90 collapses
return ndp <= 0.25f * sqrtf(abc * abd);
} }
static bool hasTriangleFlips(const EdgeAdjacency& adjacency, const Vector3* vertex_positions, const unsigned int* collapse_remap, unsigned int i0, unsigned int i1) static bool hasTriangleFlips(const EdgeAdjacency& adjacency, const Vector3* vertex_positions, const unsigned int* collapse_remap, unsigned int i0, unsigned int i1)
@@ -1305,7 +1390,7 @@ static void fillCellQuadrics(Quadric* cell_quadrics, const unsigned int* indices
unsigned int c1 = vertex_cells[i1]; unsigned int c1 = vertex_cells[i1];
unsigned int c2 = vertex_cells[i2]; unsigned int c2 = vertex_cells[i2];
bool single_cell = (c0 == c1) & (c0 == c2); int single_cell = (c0 == c1) & (c0 == c2);
Quadric Q; Quadric Q;
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], single_cell ? 3.f : 1.f); quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], single_cell ? 3.f : 1.f);
@@ -1325,7 +1410,7 @@ static void fillCellQuadrics(Quadric* cell_quadrics, const unsigned int* indices
static void fillCellReservoirs(Reservoir* cell_reservoirs, size_t cell_count, const Vector3* vertex_positions, const float* vertex_colors, size_t vertex_colors_stride, size_t vertex_count, const unsigned int* vertex_cells) static void fillCellReservoirs(Reservoir* cell_reservoirs, size_t cell_count, const Vector3* vertex_positions, const float* vertex_colors, size_t vertex_colors_stride, size_t vertex_count, const unsigned int* vertex_cells)
{ {
static const float dummy_color[] = { 0.f, 0.f, 0.f }; static const float dummy_color[] = {0.f, 0.f, 0.f};
size_t vertex_colors_stride_float = vertex_colors_stride / sizeof(float); size_t vertex_colors_stride_float = vertex_colors_stride / sizeof(float);
@@ -1380,7 +1465,7 @@ 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, const float* vertex_colors, size_t vertex_colors_stride, float color_weight, size_t vertex_count) 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, const float* vertex_colors, size_t vertex_colors_stride, float color_weight, size_t vertex_count)
{ {
static const float dummy_color[] = { 0.f, 0.f, 0.f }; static const float dummy_color[] = {0.f, 0.f, 0.f};
size_t vertex_colors_stride_float = vertex_colors_stride / sizeof(float); size_t vertex_colors_stride_float = vertex_colors_stride / sizeof(float);
@@ -1468,7 +1553,7 @@ MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop = NULL;
MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = NULL; MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = NULL;
#endif #endif
size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
{ {
using namespace meshopt; using namespace meshopt;
@@ -1476,7 +1561,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256); assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);
assert(vertex_positions_stride % sizeof(float) == 0); assert(vertex_positions_stride % sizeof(float) == 0);
assert(target_index_count <= index_count); assert(target_index_count <= index_count);
assert((options & ~(meshopt_SimplifyLockBorder)) == 0); assert((options & ~(meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute)) == 0);
assert(vertex_attributes_stride >= attribute_count * sizeof(float) && vertex_attributes_stride <= 256); assert(vertex_attributes_stride >= attribute_count * sizeof(float) && vertex_attributes_stride <= 256);
assert(vertex_attributes_stride % sizeof(float) == 0); assert(vertex_attributes_stride % sizeof(float) == 0);
assert(attribute_count <= kMaxAttributes); assert(attribute_count <= kMaxAttributes);
@@ -1484,22 +1569,30 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
meshopt_Allocator allocator; meshopt_Allocator allocator;
unsigned int* result = destination; unsigned int* result = destination;
if (result != indices)
memcpy(result, indices, index_count * sizeof(unsigned int));
// build an index remap and update indices/vertex_count to minimize the subsequent work
// note: as a consequence, errors will be computed relative to the subset extent
unsigned int* sparse_remap = NULL;
if (options & meshopt_SimplifySparse)
sparse_remap = buildSparseRemap(result, index_count, vertex_count, &vertex_count, allocator);
// build adjacency information // build adjacency information
EdgeAdjacency adjacency = {}; EdgeAdjacency adjacency = {};
prepareEdgeAdjacency(adjacency, index_count, vertex_count, allocator); prepareEdgeAdjacency(adjacency, index_count, vertex_count, allocator);
updateEdgeAdjacency(adjacency, indices, index_count, vertex_count, NULL); updateEdgeAdjacency(adjacency, result, index_count, vertex_count, NULL);
// build position remap that maps each vertex to the one with identical position // build position remap that maps each vertex to the one with identical position
unsigned int* remap = allocator.allocate<unsigned int>(vertex_count); unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
unsigned int* wedge = allocator.allocate<unsigned int>(vertex_count); unsigned int* wedge = allocator.allocate<unsigned int>(vertex_count);
buildPositionRemap(remap, wedge, vertex_positions_data, vertex_count, vertex_positions_stride, allocator); buildPositionRemap(remap, wedge, vertex_positions_data, vertex_count, vertex_positions_stride, sparse_remap, allocator);
// classify vertices; vertex kind determines collapse rules, see kCanCollapse // classify vertices; vertex kind determines collapse rules, see kCanCollapse
unsigned char* vertex_kind = allocator.allocate<unsigned char>(vertex_count); unsigned char* vertex_kind = allocator.allocate<unsigned char>(vertex_count);
unsigned int* loop = allocator.allocate<unsigned int>(vertex_count); unsigned int* loop = allocator.allocate<unsigned int>(vertex_count);
unsigned int* loopback = allocator.allocate<unsigned int>(vertex_count); unsigned int* loopback = allocator.allocate<unsigned int>(vertex_count);
classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge, options); classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge, vertex_lock, sparse_remap, options);
#if TRACE #if TRACE
size_t unique_positions = 0; size_t unique_positions = 0;
@@ -1517,14 +1610,14 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
#endif #endif
Vector3* vertex_positions = allocator.allocate<Vector3>(vertex_count); Vector3* vertex_positions = allocator.allocate<Vector3>(vertex_count);
rescalePositions(vertex_positions, vertex_positions_data, vertex_count, vertex_positions_stride); float vertex_scale = rescalePositions(vertex_positions, vertex_positions_data, vertex_count, vertex_positions_stride, sparse_remap);
float* vertex_attributes = NULL; float* vertex_attributes = NULL;
if (attribute_count) if (attribute_count)
{ {
vertex_attributes = allocator.allocate<float>(vertex_count * attribute_count); vertex_attributes = allocator.allocate<float>(vertex_count * attribute_count);
rescaleAttributes(vertex_attributes, vertex_attributes_data, vertex_count, vertex_attributes_stride, attribute_weights, attribute_count); rescaleAttributes(vertex_attributes, vertex_attributes_data, vertex_count, vertex_attributes_stride, attribute_weights, attribute_count, sparse_remap);
} }
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count); Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
@@ -1542,14 +1635,11 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
memset(attribute_gradients, 0, vertex_count * attribute_count * sizeof(QuadricGrad)); memset(attribute_gradients, 0, vertex_count * attribute_count * sizeof(QuadricGrad));
} }
fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap); fillFaceQuadrics(vertex_quadrics, result, index_count, vertex_positions, remap);
fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback); fillEdgeQuadrics(vertex_quadrics, result, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
if (attribute_count) if (attribute_count)
fillAttributeQuadrics(attribute_quadrics, attribute_gradients, indices, index_count, vertex_positions, vertex_attributes, attribute_count, remap); fillAttributeQuadrics(attribute_quadrics, attribute_gradients, result, index_count, vertex_positions, vertex_attributes, attribute_count, remap);
if (result != indices)
memcpy(result, indices, index_count * sizeof(unsigned int));
#if TRACE #if TRACE
size_t pass_count = 0; size_t pass_count = 0;
@@ -1566,7 +1656,8 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
float result_error = 0; float result_error = 0;
// target_error input is linear; we need to adjust it to match quadricError units // target_error input is linear; we need to adjust it to match quadricError units
float error_limit = target_error * target_error; float error_scale = (options & meshopt_SimplifyErrorAbsolute) ? vertex_scale : 1.f;
float error_limit = (target_error * target_error) / (error_scale * error_scale);
while (result_count > target_index_count) while (result_count > target_index_count)
{ {
@@ -1611,7 +1702,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
} }
#if TRACE #if TRACE
printf("result: %d triangles, error: %e; total %d passes\n", int(result_count), sqrtf(result_error), int(pass_count)); printf("result: %d triangles, error: %e; total %d passes\n", int(result_count / 3), sqrtf(result_error), int(pass_count));
#endif #endif
#ifndef NDEBUG #ifndef NDEBUG
@@ -1625,21 +1716,26 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
memcpy(meshopt_simplifyDebugLoopBack, loopback, vertex_count * sizeof(unsigned int)); memcpy(meshopt_simplifyDebugLoopBack, loopback, vertex_count * sizeof(unsigned int));
#endif #endif
// convert resulting indices back into the dense space of the larger mesh
if (sparse_remap)
for (size_t i = 0; i < result_count; ++i)
result[i] = sparse_remap[result[i]];
// result_error is quadratic; we need to remap it back to linear // result_error is quadratic; we need to remap it back to linear
if (out_result_error) if (out_result_error)
*out_result_error = sqrtf(result_error); *out_result_error = sqrtf(result_error) * error_scale;
return result_count; return result_count;
} }
size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
{ {
return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, NULL, 0, NULL, 0, target_index_count, target_error, options, out_result_error); return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, NULL, 0, NULL, 0, NULL, target_index_count, target_error, options, out_result_error);
} }
size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float* out_result_error) size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
{ {
return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, target_index_count, target_error, options, out_result_error); return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, target_index_count, target_error, options, out_result_error);
} }
size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* out_result_error) size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* out_result_error)
@@ -1692,14 +1788,14 @@ size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* ind
// we clamp the prediction of the grid size to make sure that the search converges // we clamp the prediction of the grid size to make sure that the search converges
int grid_size = next_grid_size; int grid_size = next_grid_size;
grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid) ? max_grid - 1 : grid_size; grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid ? max_grid - 1 : grid_size);
computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size); computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
size_t triangles = countTriangles(vertex_ids, indices, index_count); size_t triangles = countTriangles(vertex_ids, indices, index_count);
#if TRACE #if TRACE
printf("pass %d (%s): grid size %d, triangles %d, %s\n", printf("pass %d (%s): grid size %d, triangles %d, %s\n",
pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses) ? "lerp" : "binary", pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses ? "lerp" : "binary"),
grid_size, int(triangles), grid_size, int(triangles),
(triangles <= target_index_count / 3) ? "under" : "over"); (triangles <= target_index_count / 3) ? "under" : "over");
#endif #endif
@@ -1824,14 +1920,14 @@ size_t meshopt_simplifyPoints(unsigned int* destination, const float* vertex_pos
// we clamp the prediction of the grid size to make sure that the search converges // we clamp the prediction of the grid size to make sure that the search converges
int grid_size = next_grid_size; int grid_size = next_grid_size;
grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid) ? max_grid - 1 : grid_size; grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid ? max_grid - 1 : grid_size);
computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size); computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
size_t vertices = countVertexCells(table, table_size, vertex_ids, vertex_count); size_t vertices = countVertexCells(table, table_size, vertex_ids, vertex_count);
#if TRACE #if TRACE
printf("pass %d (%s): grid size %d, vertices %d, %s\n", printf("pass %d (%s): grid size %d, vertices %d, %s\n",
pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses) ? "lerp" : "binary", pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses ? "lerp" : "binary"),
grid_size, int(vertices), grid_size, int(vertices),
(vertices <= target_vertex_count) ? "under" : "over"); (vertices <= target_vertex_count) ? "under" : "over");
#endif #endif

View File

@@ -18,7 +18,7 @@ static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int
for (size_t i = 0; i < buffer_size; ++i) for (size_t i = 0; i < buffer_size; ++i)
{ {
unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]]; unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]];
unsigned int v = (va < vb && va < vc) ? va : (vb < vc) ? vb : vc; unsigned int v = (va < vb && va < vc) ? va : (vb < vc ? vb : vc);
if (v < iv) if (v < iv)
{ {

View File

@@ -195,9 +195,8 @@ void meshopt_optimizeVertexCacheTable(unsigned int* destination, const unsigned
TriangleAdjacency adjacency = {}; TriangleAdjacency adjacency = {};
buildTriangleAdjacency(adjacency, indices, index_count, vertex_count, allocator); buildTriangleAdjacency(adjacency, indices, index_count, vertex_count, allocator);
// live triangle counts // live triangle counts; note, we alias adjacency.counts as we remove triangles after emitting them so the counts always match
unsigned int* live_triangles = allocator.allocate<unsigned int>(vertex_count); unsigned int* live_triangles = adjacency.counts;
memcpy(live_triangles, adjacency.counts, vertex_count * sizeof(unsigned int));
// emitted flags // emitted flags
unsigned char* emitted_flags = allocator.allocate<unsigned char>(face_count); unsigned char* emitted_flags = allocator.allocate<unsigned char>(face_count);
@@ -261,20 +260,16 @@ void meshopt_optimizeVertexCacheTable(unsigned int* destination, const unsigned
unsigned int index = cache[i]; unsigned int index = cache[i];
cache_new[cache_write] = index; cache_new[cache_write] = index;
cache_write += (index != a && index != b && index != c); cache_write += (index != a) & (index != b) & (index != c);
} }
unsigned int* cache_temp = cache; unsigned int* cache_temp = cache;
cache = cache_new, cache_new = cache_temp; cache = cache_new, cache_new = cache_temp;
cache_count = cache_write > cache_size ? cache_size : cache_write; cache_count = cache_write > cache_size ? cache_size : cache_write;
// update live triangle counts
live_triangles[a]--;
live_triangles[b]--;
live_triangles[c]--;
// remove emitted triangle from adjacency data // remove emitted triangle from adjacency data
// this makes sure that we spend less time traversing these lists on subsequent iterations // this makes sure that we spend less time traversing these lists on subsequent iterations
// live triangle counts are updated as a byproduct of these adjustments
for (size_t k = 0; k < 3; ++k) for (size_t k = 0; k < 3; ++k)
{ {
unsigned int index = indices[current_triangle * 3 + k]; unsigned int index = indices[current_triangle * 3 + k];

View File

@@ -245,7 +245,7 @@ static unsigned char* encodeBytes(unsigned char* data, unsigned char* data_end,
} }
} }
int bitslog2 = (best_bits == 1) ? 0 : (best_bits == 2) ? 1 : (best_bits == 4) ? 2 : 3; int bitslog2 = (best_bits == 1) ? 0 : (best_bits == 2 ? 1 : (best_bits == 4 ? 2 : 3));
assert((1 << bitslog2) == best_bits); assert((1 << bitslog2) == best_bits);
size_t header_offset = i / kByteGroupSize; size_t header_offset = i / kByteGroupSize;