meshoptimizer: Sync with upstream commit 4a287848f

4a287848fd
This commit is contained in:
Rémi Verschelde 2022-12-22 16:22:33 +01:00 committed by Lyuma
parent 0810ecaafd
commit cf9df3b5d4
19 changed files with 295 additions and 187 deletions

View File

@ -288,7 +288,7 @@ License: Apache-2.0
Files: ./thirdparty/meshoptimizer/ Files: ./thirdparty/meshoptimizer/
Comment: meshoptimizer Comment: meshoptimizer
Copyright: 2016-2021, Arseny Kapoulkine Copyright: 2016-2022, Arseny Kapoulkine
License: Expat License: Expat
Files: ./thirdparty/minimp3/ Files: ./thirdparty/minimp3/

View File

@ -542,13 +542,14 @@ void OccluderInstance3D::_bake_surface(const Transform3D &p_transform, Array p_s
float error = -1.0f; float error = -1.0f;
int target_index_count = MIN(indices.size(), 36); int target_index_count = MIN(indices.size(), 36);
const int simplify_options = SurfaceTool::SIMPLIFY_LOCK_BORDER;
uint32_t index_count = SurfaceTool::simplify_func( uint32_t index_count = SurfaceTool::simplify_func(
(unsigned int *)indices.ptrw(), (unsigned int *)indices.ptrw(),
(unsigned int *)indices.ptr(), (unsigned int *)indices.ptr(),
indices.size(), indices.size(),
vertices_f32.ptr(), vertices.size(), sizeof(float) * 3, vertices_f32.ptr(), vertices.size(), sizeof(float) * 3,
target_index_count, target_error, &error); target_index_count, target_error, simplify_options, &error);
indices.resize(index_count); indices.resize(index_count);
} }

View File

@ -452,6 +452,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
new_indices.resize(index_count); new_indices.resize(index_count);
Vector<float> merged_normals_f32 = vector3_to_float32_array(merged_normals.ptr(), merged_normals.size()); Vector<float> merged_normals_f32 = vector3_to_float32_array(merged_normals.ptr(), merged_normals.size());
const int simplify_options = SurfaceTool::SIMPLIFY_LOCK_BORDER;
size_t new_index_count = SurfaceTool::simplify_with_attrib_func( size_t new_index_count = SurfaceTool::simplify_with_attrib_func(
(unsigned int *)new_indices.ptrw(), (unsigned int *)new_indices.ptrw(),
@ -460,6 +461,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
sizeof(float) * 3, // Vertex stride sizeof(float) * 3, // Vertex stride
index_target, index_target,
max_mesh_error, max_mesh_error,
simplify_options,
&mesh_error, &mesh_error,
merged_normals_f32.ptr(), merged_normals_f32.ptr(),
normal_weights.ptr(), 3); normal_weights.ptr(), 3);

View File

@ -1307,7 +1307,8 @@ Vector<int> SurfaceTool::generate_lod(float p_threshold, int p_target_index_coun
} }
float error; float error;
uint32_t index_count = simplify_func((unsigned int *)lod.ptrw(), (unsigned int *)index_array.ptr(), index_array.size(), vertices.ptr(), vertex_array.size(), sizeof(float) * 3, p_target_index_count, p_threshold, &error); const int simplify_options = SIMPLIFY_LOCK_BORDER;
uint32_t index_count = simplify_func((unsigned int *)lod.ptrw(), (unsigned int *)index_array.ptr(), index_array.size(), vertices.ptr(), vertex_array.size(), sizeof(float) * 3, p_target_index_count, p_threshold, simplify_options, &error);
ERR_FAIL_COND_V(index_count == 0, lod); ERR_FAIL_COND_V(index_count == 0, lod);
lod.resize(index_count); lod.resize(index_count);

View File

@ -74,11 +74,16 @@ public:
SKIN_8_WEIGHTS SKIN_8_WEIGHTS
}; };
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. */
SIMPLIFY_LOCK_BORDER = 1 << 0, // From meshopt_SimplifyLockBorder
};
typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count); typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count);
static OptimizeVertexCacheFunc optimize_vertex_cache_func; static OptimizeVertexCacheFunc optimize_vertex_cache_func;
typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *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 *r_error); typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *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, float *r_error);
static SimplifyFunc simplify_func; static SimplifyFunc simplify_func;
typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, float *result_error, const float *attributes, const float *attribute_weights, size_t attribute_count); typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, unsigned int options, float *result_error, const float *attributes, const float *attribute_weights, size_t attribute_count);
static SimplifyWithAttribFunc simplify_with_attrib_func; static SimplifyWithAttribFunc simplify_with_attrib_func;
typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride); typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
static SimplifyScaleFunc simplify_scale_func; static SimplifyScaleFunc simplify_scale_func;

View File

@ -376,7 +376,7 @@ File extracted from upstream release tarball:
## meshoptimizer ## meshoptimizer
- Upstream: https://github.com/zeux/meshoptimizer - Upstream: https://github.com/zeux/meshoptimizer
- Version: git (ea4558d1c0f217f1d67ed7fe0b07896ece88ae18, 2022) - Version: git (4a287848fd664ae1c3fc8e5e008560534ceeb526, 2022)
- License: MIT - License: MIT
Files extracted from upstream repository: Files extracted from upstream repository:

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2016-2021 Arseny Kapoulkine Copyright (c) 2016-2022 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

@ -283,6 +283,79 @@ static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int
return result; return result;
} }
static unsigned int getNeighborTriangle(const meshopt_Meshlet& meshlet, const Cone* meshlet_cone, unsigned int* meshlet_vertices, const unsigned int* indices, const TriangleAdjacency2& adjacency, const Cone* triangles, const unsigned int* live_triangles, const unsigned char* used, float meshlet_expected_radius, float cone_weight, unsigned int* out_extra)
{
unsigned int best_triangle = ~0u;
unsigned int best_extra = 5;
float best_score = FLT_MAX;
for (size_t i = 0; i < meshlet.vertex_count; ++i)
{
unsigned int index = meshlet_vertices[meshlet.vertex_offset + i];
unsigned int* neighbors = &adjacency.data[0] + adjacency.offsets[index];
size_t neighbors_size = adjacency.counts[index];
for (size_t j = 0; j < neighbors_size; ++j)
{
unsigned int triangle = neighbors[j];
unsigned int a = indices[triangle * 3 + 0], b = indices[triangle * 3 + 1], c = indices[triangle * 3 + 2];
unsigned int extra = (used[a] == 0xff) + (used[b] == 0xff) + (used[c] == 0xff);
// triangles that don't add new vertices to meshlets are max. priority
if (extra != 0)
{
// artificially increase the priority of dangling triangles as they're expensive to add to new meshlets
if (live_triangles[a] == 1 || live_triangles[b] == 1 || live_triangles[c] == 1)
extra = 0;
extra++;
}
// since topology-based priority is always more important than the score, we can skip scoring in some cases
if (extra > best_extra)
continue;
float score = 0;
// caller selects one of two scoring functions: geometrical (based on meshlet cone) or topological (based on remaining triangles)
if (meshlet_cone)
{
const Cone& tri_cone = triangles[triangle];
float distance2 =
(tri_cone.px - meshlet_cone->px) * (tri_cone.px - meshlet_cone->px) +
(tri_cone.py - meshlet_cone->py) * (tri_cone.py - meshlet_cone->py) +
(tri_cone.pz - meshlet_cone->pz) * (tri_cone.pz - meshlet_cone->pz);
float spread = tri_cone.nx * meshlet_cone->nx + tri_cone.ny * meshlet_cone->ny + tri_cone.nz * meshlet_cone->nz;
score = getMeshletScore(distance2, spread, cone_weight, meshlet_expected_radius);
}
else
{
// each live_triangles entry is >= 1 since it includes the current triangle we're processing
score = float(live_triangles[a] + live_triangles[b] + live_triangles[c] - 3);
}
// note that topology-based priority is always more important than the score
// this helps maintain reasonable effectiveness of meshlet data and reduces scoring cost
if (extra < best_extra || score < best_score)
{
best_triangle = triangle;
best_extra = extra;
best_score = score;
}
}
}
if (out_extra)
*out_extra = best_extra;
return best_triangle;
}
struct KDNode struct KDNode
{ {
union union
@ -464,13 +537,15 @@ size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_ve
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && 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(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices); assert(max_vertices >= 3 && max_vertices <= kMeshletMaxVertices);
assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles); assert(max_triangles >= 1 && max_triangles <= kMeshletMaxTriangles);
assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned assert(max_triangles % 4 == 0); // ensures the caller will compute output space properly as index data is 4b aligned
assert(cone_weight >= 0 && cone_weight <= 1);
meshopt_Allocator allocator; meshopt_Allocator allocator;
TriangleAdjacency2 adjacency = {}; TriangleAdjacency2 adjacency = {};
@ -511,65 +586,18 @@ size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_ve
for (;;) for (;;)
{ {
unsigned int best_triangle = ~0u;
unsigned int best_extra = 5;
float best_score = FLT_MAX;
Cone meshlet_cone = getMeshletCone(meshlet_cone_acc, meshlet.triangle_count); Cone meshlet_cone = getMeshletCone(meshlet_cone_acc, meshlet.triangle_count);
for (size_t i = 0; i < meshlet.vertex_count; ++i) unsigned int best_extra = 0;
unsigned int best_triangle = getNeighborTriangle(meshlet, &meshlet_cone, meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, cone_weight, &best_extra);
// if the best triangle doesn't fit into current meshlet, the spatial scoring we've used is not very meaningful, so we re-select using topological scoring
if (best_triangle != ~0u && (meshlet.vertex_count + best_extra > max_vertices || meshlet.triangle_count >= max_triangles))
{ {
unsigned int index = meshlet_vertices[meshlet.vertex_offset + i]; best_triangle = getNeighborTriangle(meshlet, NULL, meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, 0.f, NULL);
unsigned int* neighbours = &adjacency.data[0] + adjacency.offsets[index];
size_t neighbours_size = adjacency.counts[index];
for (size_t j = 0; j < neighbours_size; ++j)
{
unsigned int triangle = neighbours[j];
assert(!emitted_flags[triangle]);
unsigned int a = indices[triangle * 3 + 0], b = indices[triangle * 3 + 1], c = indices[triangle * 3 + 2];
assert(a < vertex_count && b < vertex_count && c < vertex_count);
unsigned int extra = (used[a] == 0xff) + (used[b] == 0xff) + (used[c] == 0xff);
// triangles that don't add new vertices to meshlets are max. priority
if (extra != 0)
{
// artificially increase the priority of dangling triangles as they're expensive to add to new meshlets
if (live_triangles[a] == 1 || live_triangles[b] == 1 || live_triangles[c] == 1)
extra = 0;
extra++;
}
// since topology-based priority is always more important than the score, we can skip scoring in some cases
if (extra > best_extra)
continue;
const Cone& tri_cone = triangles[triangle];
float distance2 =
(tri_cone.px - meshlet_cone.px) * (tri_cone.px - meshlet_cone.px) +
(tri_cone.py - meshlet_cone.py) * (tri_cone.py - meshlet_cone.py) +
(tri_cone.pz - meshlet_cone.pz) * (tri_cone.pz - meshlet_cone.pz);
float spread = tri_cone.nx * meshlet_cone.nx + tri_cone.ny * meshlet_cone.ny + tri_cone.nz * meshlet_cone.nz;
float score = getMeshletScore(distance2, spread, cone_weight, meshlet_expected_radius);
// note that topology-based priority is always more important than the score
// this helps maintain reasonable effectiveness of meshlet data and reduces scoring cost
if (extra < best_extra || score < best_score)
{
best_triangle = triangle;
best_extra = extra;
best_score = score;
}
}
} }
// when we run out of neighboring triangles we need to switch to spatial search; we currently just pick the closest triangle irrespective of connectivity
if (best_triangle == ~0u) if (best_triangle == ~0u)
{ {
float position[3] = {meshlet_cone.px, meshlet_cone.py, meshlet_cone.pz}; float position[3] = {meshlet_cone.px, meshlet_cone.py, meshlet_cone.pz};
@ -604,16 +632,16 @@ size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_ve
{ {
unsigned int index = indices[best_triangle * 3 + k]; unsigned int index = indices[best_triangle * 3 + k];
unsigned int* neighbours = &adjacency.data[0] + adjacency.offsets[index]; unsigned int* neighbors = &adjacency.data[0] + adjacency.offsets[index];
size_t neighbours_size = adjacency.counts[index]; size_t neighbors_size = adjacency.counts[index];
for (size_t i = 0; i < neighbours_size; ++i) for (size_t i = 0; i < neighbors_size; ++i)
{ {
unsigned int tri = neighbours[i]; unsigned int tri = neighbors[i];
if (tri == best_triangle) if (tri == best_triangle)
{ {
neighbours[i] = neighbours[neighbours_size - 1]; neighbors[i] = neighbors[neighbors_size - 1];
adjacency.counts[index]--; adjacency.counts[index]--;
break; break;
} }
@ -687,7 +715,7 @@ meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(index_count / 3 <= kMeshletMaxTriangles); assert(index_count / 3 <= kMeshletMaxTriangles);
assert(vertex_positions_stride > 0 && 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);
(void)vertex_count; (void)vertex_count;
@ -839,7 +867,7 @@ meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices
using namespace meshopt; using namespace meshopt;
assert(triangle_count <= kMeshletMaxTriangles); assert(triangle_count <= kMeshletMaxTriangles);
assert(vertex_positions_stride > 0 && 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);
unsigned int indices[kMeshletMaxTriangles * 3]; unsigned int indices[kMeshletMaxTriangles * 3];

View File

@ -187,7 +187,7 @@ size_t meshopt_generateVertexRemap(unsigned int* destination, const unsigned int
using namespace meshopt; using namespace meshopt;
assert(indices || index_count == vertex_count); assert(indices || index_count == vertex_count);
assert(index_count % 3 == 0); assert(!indices || index_count % 3 == 0);
assert(vertex_size > 0 && vertex_size <= 256); assert(vertex_size > 0 && vertex_size <= 256);
meshopt_Allocator allocator; meshopt_Allocator allocator;
@ -412,7 +412,7 @@ void meshopt_generateAdjacencyIndexBuffer(unsigned int* destination, const unsig
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && 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);
meshopt_Allocator allocator; meshopt_Allocator allocator;
@ -483,7 +483,7 @@ void meshopt_generateTessellationIndexBuffer(unsigned int* destination, const un
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && 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);
meshopt_Allocator allocator; meshopt_Allocator allocator;

View File

@ -1,7 +1,7 @@
/** /**
* meshoptimizer - version 0.17 * meshoptimizer - version 0.18
* *
* Copyright (C) 2016-2021, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) * Copyright (C) 2016-2022, 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 170 /* 0.17 */ #define MESHOPTIMIZER_VERSION 180 /* 0.18 */
/* If no API is defined, assume default */ /* If no API is defined, assume default */
#ifndef MESHOPTIMIZER_API #ifndef MESHOPTIMIZER_API
@ -37,8 +37,8 @@ extern "C" {
#endif #endif
/** /**
* Vertex attribute stream, similar to glVertexPointer * Vertex attribute stream
* Each element takes size bytes, with stride controlling the spacing between successive elements. * Each element takes size bytes, beginning at data, with stride controlling the spacing between successive elements (stride >= size).
*/ */
struct meshopt_Stream struct meshopt_Stream
{ {
@ -115,7 +115,7 @@ MESHOPTIMIZER_API void meshopt_generateShadowIndexBufferMulti(unsigned int* dest
* This can be used to implement algorithms like silhouette detection/expansion and other forms of GS-driven rendering. * This can be used to implement algorithms like silhouette detection/expansion and other forms of GS-driven rendering.
* *
* destination must contain enough space for the resulting index buffer (index_count*2 elements) * destination must contain enough space for the resulting index buffer (index_count*2 elements)
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
*/ */
MESHOPTIMIZER_API void meshopt_generateAdjacencyIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); MESHOPTIMIZER_API void meshopt_generateAdjacencyIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
@ -131,7 +131,7 @@ MESHOPTIMIZER_API void meshopt_generateAdjacencyIndexBuffer(unsigned int* destin
* See "Tessellation on Any Budget" (John McDonald, GDC 2011) for implementation details. * See "Tessellation on Any Budget" (John McDonald, GDC 2011) for implementation details.
* *
* destination must contain enough space for the resulting index buffer (index_count*4 elements) * destination must contain enough space for the resulting index buffer (index_count*4 elements)
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
*/ */
MESHOPTIMIZER_API void meshopt_generateTessellationIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); MESHOPTIMIZER_API void meshopt_generateTessellationIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
@ -171,7 +171,7 @@ MESHOPTIMIZER_API void meshopt_optimizeVertexCacheFifo(unsigned int* destination
* *
* destination must contain enough space for the resulting index buffer (index_count elements) * destination must contain enough space for the resulting index buffer (index_count elements)
* indices must contain index data that is the result of meshopt_optimizeVertexCache (*not* the original mesh indices!) * indices must contain index data that is the result of meshopt_optimizeVertexCache (*not* the original mesh indices!)
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
* threshold indicates how much the overdraw optimizer can degrade vertex cache efficiency (1.05 = up to 5%) to reduce overdraw more efficiently * threshold indicates how much the overdraw optimizer can degrade vertex cache efficiency (1.05 = up to 5%) to reduce overdraw more efficiently
*/ */
MESHOPTIMIZER_API void meshopt_optimizeOverdraw(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, float threshold); MESHOPTIMIZER_API void meshopt_optimizeOverdraw(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, float threshold);
@ -313,7 +313,21 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterQuat(void* destination, size
MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_t count, size_t stride, int bits, const float* data); MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_t count, size_t stride, int bits, const float* data);
/** /**
* Experimental: Mesh simplifier * Simplification options
*/
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. */
meshopt_SimplifyLockBorder = 1 << 0,
};
/**
* Experimental: Mesh simplifier with attribute metric; attributes follow xyz position data atm (vertex data must contain 3 + attribute_count floats per vertex)
*/
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, unsigned int options, float* result_error, const float* attributes, const float* attribute_weights, size_t attribute_count);
/**
* Mesh simplifier
* Reduces the number of triangles in the mesh, attempting to preserve mesh appearance as much as possible * Reduces the number of triangles in the mesh, attempting to preserve mesh appearance as much as possible
* The algorithm tries to preserve mesh topology and can stop short of the target goal based on topology constraints or target error. * The algorithm tries to preserve mesh topology and can stop short of the target goal based on topology constraints or target error.
* If not all attributes from the input mesh are required, it's recommended to reindex the mesh using meshopt_generateShadowIndexBuffer prior to simplification. * If not all attributes from the input mesh are required, it's recommended to reindex the mesh using meshopt_generateShadowIndexBuffer prior to simplification.
@ -322,16 +336,12 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_
* If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended. * If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended.
* *
* destination must contain enough space for the target index buffer, worst case is index_count elements (*not* target_index_count)! * destination must contain enough space for the target index buffer, worst case is index_count elements (*not* target_index_count)!
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
* target_error represents the error relative to mesh extents that can be tolerated, e.g. 0.01 = 1% deformation * target_error represents the error relative to mesh extents that can be tolerated, e.g. 0.01 = 1% deformation; value range [0..1]
* options must be a bitmask composed of meshopt_SimplifyX options; 0 is a safe default
* result_error can be NULL; when it's not NULL, it will contain the resulting (relative) error after simplification * result_error can be NULL; when it's not NULL, it will contain the resulting (relative) error after simplification
*/ */
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplify(unsigned int* destination, const unsigned int* 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); MESHOPTIMIZER_API size_t meshopt_simplify(unsigned int* destination, const unsigned int* 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, float* result_error);
/**
* Experimental: Mesh simplifier with attribute metric; attributes follow xyz position data atm (vertex data must contain 3 + attribute_count floats per vertex)
*/
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, float* result_error, const float* attributes, const float* attribute_weights, size_t attribute_count);
/** /**
* Experimental: Mesh simplifier (sloppy) * Experimental: Mesh simplifier (sloppy)
@ -342,8 +352,8 @@ MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* d
* If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended. * If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended.
* *
* destination must contain enough space for the target index buffer, worst case is index_count elements (*not* target_index_count)! * destination must contain enough space for the target index buffer, worst case is index_count elements (*not* target_index_count)!
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
* target_error represents the error relative to mesh extents that can be tolerated, e.g. 0.01 = 1% deformation * target_error represents the error relative to mesh extents that can be tolerated, e.g. 0.01 = 1% deformation; value range [0..1]
* result_error can be NULL; when it's not NULL, it will contain the resulting (relative) error after simplification * result_error can be NULL; when it's not NULL, it will contain the resulting (relative) error after simplification
*/ */
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* 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); MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* 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);
@ -356,17 +366,17 @@ MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifySloppy(unsigned int* destinati
* If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended. * If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended.
* *
* destination must contain enough space for the target index buffer (target_vertex_count elements) * destination must contain enough space for the target index buffer (target_vertex_count elements)
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
*/ */
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyPoints(unsigned int* destination, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_vertex_count); MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyPoints(unsigned int* destination, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_vertex_count);
/** /**
* Experimental: Returns the error scaling factor used by the simplifier to convert between absolute and relative extents * Returns the error scaling factor used by the simplifier to convert between absolute and relative extents
* *
* Absolute error must be *divided* by the scaling factor before passing it to meshopt_simplify as target_error * Absolute error must be *divided* by the scaling factor before passing it to meshopt_simplify as target_error
* Relative error returned by meshopt_simplify via result_error must be *multiplied* by the scaling factor to get absolute error. * Relative error returned by meshopt_simplify via result_error must be *multiplied* by the scaling factor to get absolute error.
*/ */
MESHOPTIMIZER_EXPERIMENTAL float meshopt_simplifyScale(const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); MESHOPTIMIZER_API float meshopt_simplifyScale(const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
/** /**
* Mesh stripifier * Mesh stripifier
@ -418,7 +428,7 @@ struct meshopt_OverdrawStatistics
* Returns overdraw statistics using a software rasterizer * Returns overdraw statistics using a software rasterizer
* Results may not match actual GPU performance * Results may not match actual GPU performance
* *
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
*/ */
MESHOPTIMIZER_API struct meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); MESHOPTIMIZER_API struct meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
@ -456,7 +466,7 @@ struct meshopt_Meshlet
* meshlets must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound * meshlets must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound
* 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 - similar to glVertexPointer * 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)
* 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
*/ */
@ -498,7 +508,7 @@ struct meshopt_Bounds
* The formula that uses the apex is slightly more accurate but needs the apex; if you are already using bounding sphere * The formula that uses the apex is slightly more accurate but needs the apex; if you are already using bounding sphere
* to do frustum/occlusion culling, the formula that doesn't use the apex may be preferable. * to do frustum/occlusion culling, the formula that doesn't use the apex may be preferable.
* *
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
* index_count/3 should be less than or equal to 512 (the function assumes clusters of limited size) * index_count/3 should be less than or equal to 512 (the function assumes clusters of limited size)
*/ */
MESHOPTIMIZER_API struct meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); MESHOPTIMIZER_API struct meshopt_Bounds meshopt_computeClusterBounds(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
@ -518,7 +528,7 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_spatialSortRemap(unsigned int* destinati
* Reorders triangles for spatial locality, and generates a new index buffer. The resulting index buffer can be used with other functions like optimizeVertexCache. * Reorders triangles for spatial locality, and generates a new index buffer. The resulting index buffer can be used with other functions like optimizeVertexCache.
* *
* destination must contain enough space for the resulting index buffer (index_count elements) * destination must contain enough space for the resulting index buffer (index_count elements)
* vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer * vertex_positions should have float3 position in the first 12 bytes of each vertex
*/ */
MESHOPTIMIZER_EXPERIMENTAL void meshopt_spatialSortTriangles(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride); MESHOPTIMIZER_EXPERIMENTAL void meshopt_spatialSortTriangles(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
@ -610,7 +620,7 @@ inline size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_s
template <typename T> template <typename T>
inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size); inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size);
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, float* result_error = 0); 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 = 0);
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 = 0); 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 = 0);
template <typename T> template <typename T>
@ -945,12 +955,12 @@ 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, float* result_error) 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, float* result_error)
{ {
meshopt_IndexAdapter<T> in(0, indices, index_count); meshopt_IndexAdapter<T> in(0, indices, index_count);
meshopt_IndexAdapter<T> out(destination, 0, index_count); meshopt_IndexAdapter<T> out(destination, 0, index_count);
return meshopt_simplify(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, target_index_count, target_error, result_error); return meshopt_simplify(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, target_index_count, target_error, options, result_error);
} }
template <typename T> template <typename T>
@ -1039,7 +1049,7 @@ inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_
#endif #endif
/** /**
* Copyright (c) 2016-2021 Arseny Kapoulkine * Copyright (c) 2016-2022 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

@ -147,7 +147,7 @@ meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const unsigned int* indices,
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && 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);
meshopt_Allocator allocator; meshopt_Allocator allocator;

View File

@ -272,7 +272,7 @@ void meshopt_optimizeOverdraw(unsigned int* destination, const unsigned int* ind
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && 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);
meshopt_Allocator allocator; meshopt_Allocator allocator;

View File

@ -1,5 +1,5 @@
diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
index 5e92e2dc73..e40c141e76 100644 index d8d4a67391..3847afc736 100644
--- a/thirdparty/meshoptimizer/simplifier.cpp --- a/thirdparty/meshoptimizer/simplifier.cpp
+++ b/thirdparty/meshoptimizer/simplifier.cpp +++ b/thirdparty/meshoptimizer/simplifier.cpp
@@ -20,7 +20,7 @@ @@ -20,7 +20,7 @@
@ -11,7 +11,7 @@ index 5e92e2dc73..e40c141e76 100644
// This work is based on: // This work is based on:
// Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997 // Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
@@ -453,6 +453,7 @@ struct Collapse @@ -458,6 +458,7 @@ struct Collapse
float error; float error;
unsigned int errorui; unsigned int errorui;
}; };
@ -19,7 +19,7 @@ index 5e92e2dc73..e40c141e76 100644
}; };
static float normalize(Vector3& v) static float normalize(Vector3& v)
@@ -533,6 +534,34 @@ static float quadricError(const Quadric& Q, const Vector3& v) @@ -538,6 +539,34 @@ static float quadricError(const Quadric& Q, const Vector3& v)
return fabsf(r) * s; return fabsf(r) * s;
} }
@ -54,7 +54,7 @@ index 5e92e2dc73..e40c141e76 100644
static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, float w) static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, float w)
{ {
float aw = a * w; float aw = a * w;
@@ -688,7 +717,7 @@ static void quadricUpdateAttributes(Quadric& Q, const Vector3& p0, const Vector3 @@ -693,7 +722,7 @@ static void quadricUpdateAttributes(Quadric& Q, const Vector3& p0, const Vector3
} }
#endif #endif
@ -63,7 +63,7 @@ index 5e92e2dc73..e40c141e76 100644
{ {
for (size_t i = 0; i < index_count; i += 3) for (size_t i = 0; i < index_count; i += 3)
{ {
@@ -698,6 +727,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic @@ -703,6 +732,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
Quadric Q; Quadric Q;
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f); quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f);
@ -73,7 +73,7 @@ index 5e92e2dc73..e40c141e76 100644
#if ATTRIBUTES #if ATTRIBUTES
quadricUpdateAttributes(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], Q.w); quadricUpdateAttributes(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], Q.w);
@@ -708,7 +740,7 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic @@ -713,7 +745,7 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
} }
} }
@ -82,7 +82,7 @@ index 5e92e2dc73..e40c141e76 100644
{ {
for (size_t i = 0; i < index_count; i += 3) for (size_t i = 0; i < index_count; i += 3)
{ {
@@ -752,6 +784,9 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic @@ -757,6 +789,9 @@ static void fillEdgeQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
quadricAdd(vertex_quadrics[remap[i0]], Q); quadricAdd(vertex_quadrics[remap[i0]], Q);
quadricAdd(vertex_quadrics[remap[i1]], Q); quadricAdd(vertex_quadrics[remap[i1]], Q);
@ -92,7 +92,7 @@ index 5e92e2dc73..e40c141e76 100644
} }
} }
} }
@@ -856,7 +891,7 @@ static size_t pickEdgeCollapses(Collapse* collapses, const unsigned int* indices @@ -861,7 +896,7 @@ static size_t pickEdgeCollapses(Collapse* collapses, const unsigned int* indices
return collapse_count; return collapse_count;
} }
@ -101,7 +101,7 @@ index 5e92e2dc73..e40c141e76 100644
{ {
for (size_t i = 0; i < collapse_count; ++i) for (size_t i = 0; i < collapse_count; ++i)
{ {
@@ -876,10 +911,14 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const @@ -881,10 +916,14 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
float ei = quadricError(qi, vertex_positions[i1]); float ei = quadricError(qi, vertex_positions[i1]);
float ej = quadricError(qj, vertex_positions[j1]); float ej = quadricError(qj, vertex_positions[j1]);
@ -116,7 +116,7 @@ index 5e92e2dc73..e40c141e76 100644
} }
} }
@@ -976,7 +1015,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse @@ -981,7 +1020,7 @@ static void sortEdgeCollapses(unsigned int* sort_order, const Collapse* collapse
} }
} }
@ -125,7 +125,7 @@ index 5e92e2dc73..e40c141e76 100644
{ {
size_t edge_collapses = 0; size_t edge_collapses = 0;
size_t triangle_collapses = 0; size_t triangle_collapses = 0;
@@ -1038,6 +1077,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* @@ -1043,6 +1082,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
assert(collapse_remap[r1] == r1); assert(collapse_remap[r1] == r1);
quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]); quadricAdd(vertex_quadrics[r1], vertex_quadrics[r0]);
@ -133,7 +133,7 @@ index 5e92e2dc73..e40c141e76 100644
if (vertex_kind[i0] == Kind_Complex) if (vertex_kind[i0] == Kind_Complex)
{ {
@@ -1075,7 +1115,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char* @@ -1080,7 +1120,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2; triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
edge_collapses++; edge_collapses++;
@ -142,7 +142,7 @@ index 5e92e2dc73..e40c141e76 100644
} }
#if TRACE #if TRACE
@@ -1463,9 +1503,11 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned @@ -1469,9 +1509,11 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count); Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric)); memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric));
@ -156,7 +156,7 @@ index 5e92e2dc73..e40c141e76 100644
if (result != indices) if (result != indices)
memcpy(result, indices, index_count * sizeof(unsigned int)); memcpy(result, indices, index_count * sizeof(unsigned int));
@@ -1496,7 +1538,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned @@ -1502,7 +1544,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
if (edge_collapse_count == 0) if (edge_collapse_count == 0)
break; break;
@ -165,7 +165,7 @@ index 5e92e2dc73..e40c141e76 100644
#if TRACE > 1 #if TRACE > 1
dumpEdgeCollapses(edge_collapses, edge_collapse_count, vertex_kind); dumpEdgeCollapses(edge_collapses, edge_collapse_count, vertex_kind);
@@ -1515,7 +1557,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned @@ -1521,7 +1563,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
printf("pass %d: ", int(pass_count++)); printf("pass %d: ", int(pass_count++));
#endif #endif

View File

@ -1,21 +1,21 @@
diff --git a/thirdparty/meshoptimizer/meshoptimizer.h b/thirdparty/meshoptimizer/meshoptimizer.h diff --git a/thirdparty/meshoptimizer/meshoptimizer.h b/thirdparty/meshoptimizer/meshoptimizer.h
index be4b765d97..463fad29da 100644 index d95725dd71..46d28d3ea3 100644
--- a/thirdparty/meshoptimizer/meshoptimizer.h --- a/thirdparty/meshoptimizer/meshoptimizer.h
+++ b/thirdparty/meshoptimizer/meshoptimizer.h +++ b/thirdparty/meshoptimizer/meshoptimizer.h
@@ -328,6 +328,11 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_ @@ -321,6 +321,11 @@ enum
*/ meshopt_SimplifyLockBorder = 1 << 0,
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplify(unsigned int* destination, const unsigned int* 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); };
+/** +/**
+ * Experimental: Mesh simplifier with attribute metric; attributes follow xyz position data atm (vertex data must contain 3 + attribute_count floats per vertex) + * Experimental: Mesh simplifier with attribute metric; attributes follow xyz position data atm (vertex data must contain 3 + attribute_count floats per vertex)
+ */ + */
+MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, float* result_error, const float* attributes, const float* attribute_weights, size_t attribute_count); +MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, unsigned int options, float* result_error, const float* attributes, const float* attribute_weights, size_t attribute_count);
+ +
/** /**
* Experimental: Mesh simplifier (sloppy) * Mesh simplifier
* Reduces the number of triangles in the mesh, sacrificing mesh appearance for simplification performance * Reduces the number of triangles in the mesh, attempting to preserve mesh appearance as much as possible
diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
index a74b08a97d..5e92e2dc73 100644 index 5f0e9bac31..797329b010 100644
--- a/thirdparty/meshoptimizer/simplifier.cpp --- a/thirdparty/meshoptimizer/simplifier.cpp
+++ b/thirdparty/meshoptimizer/simplifier.cpp +++ b/thirdparty/meshoptimizer/simplifier.cpp
@@ -20,6 +20,8 @@ @@ -20,6 +20,8 @@
@ -27,7 +27,7 @@ index a74b08a97d..5e92e2dc73 100644
// This work is based on: // This work is based on:
// Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997 // Michael Garland and Paul S. Heckbert. Surface simplification using quadric error metrics. 1997
// Michael Garland. Quadric-based polygonal surface simplification. 1999 // Michael Garland. Quadric-based polygonal surface simplification. 1999
@@ -371,6 +373,10 @@ static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned @@ -376,6 +378,10 @@ static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned
struct Vector3 struct Vector3
{ {
float x, y, z; float x, y, z;
@ -38,7 +38,7 @@ index a74b08a97d..5e92e2dc73 100644
}; };
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)
@@ -427,6 +433,13 @@ struct Quadric @@ -432,6 +438,13 @@ struct Quadric
float a10, a20, a21; float a10, a20, a21;
float b0, b1, b2, c; float b0, b1, b2, c;
float w; float w;
@ -52,7 +52,7 @@ index a74b08a97d..5e92e2dc73 100644
}; };
struct Collapse struct Collapse
@@ -469,6 +482,16 @@ static void quadricAdd(Quadric& Q, const Quadric& R) @@ -474,6 +487,16 @@ static void quadricAdd(Quadric& Q, const Quadric& R)
Q.b2 += R.b2; Q.b2 += R.b2;
Q.c += R.c; Q.c += R.c;
Q.w += R.w; Q.w += R.w;
@ -69,7 +69,7 @@ index a74b08a97d..5e92e2dc73 100644
} }
static float quadricError(const Quadric& Q, const Vector3& v) static float quadricError(const Quadric& Q, const Vector3& v)
@@ -494,6 +517,17 @@ static float quadricError(const Quadric& Q, const Vector3& v) @@ -499,6 +522,17 @@ static float quadricError(const Quadric& Q, const Vector3& v)
r += ry * v.y; r += ry * v.y;
r += rz * v.z; r += rz * v.z;
@ -87,7 +87,7 @@ index a74b08a97d..5e92e2dc73 100644
float s = Q.w == 0.f ? 0.f : 1.f / Q.w; float s = Q.w == 0.f ? 0.f : 1.f / Q.w;
return fabsf(r) * s; return fabsf(r) * s;
@@ -517,6 +551,13 @@ static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, flo @@ -522,6 +556,13 @@ static void quadricFromPlane(Quadric& Q, float a, float b, float c, float d, flo
Q.b2 = c * dw; Q.b2 = c * dw;
Q.c = d * dw; Q.c = d * dw;
Q.w = w; Q.w = w;
@ -101,7 +101,7 @@ index a74b08a97d..5e92e2dc73 100644
} }
static void quadricFromPoint(Quadric& Q, float x, float y, float z, float w) static void quadricFromPoint(Quadric& Q, float x, float y, float z, float w)
@@ -569,6 +610,84 @@ static void quadricFromTriangleEdge(Quadric& Q, const Vector3& p0, const Vector3 @@ -574,6 +615,84 @@ static void quadricFromTriangleEdge(Quadric& Q, const Vector3& p0, const Vector3
quadricFromPlane(Q, normal.x, normal.y, normal.z, -distance, length * weight); quadricFromPlane(Q, normal.x, normal.y, normal.z, -distance, length * weight);
} }
@ -186,7 +186,7 @@ index a74b08a97d..5e92e2dc73 100644
static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap) static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indices, size_t index_count, const Vector3* vertex_positions, const unsigned int* remap)
{ {
for (size_t i = 0; i < index_count; i += 3) for (size_t i = 0; i < index_count; i += 3)
@@ -580,6 +699,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic @@ -585,6 +704,9 @@ static void fillFaceQuadrics(Quadric* vertex_quadrics, const unsigned int* indic
Quadric Q; Quadric Q;
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f); quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], 1.f);
@ -196,29 +196,30 @@ index a74b08a97d..5e92e2dc73 100644
quadricAdd(vertex_quadrics[remap[i0]], Q); quadricAdd(vertex_quadrics[remap[i0]], Q);
quadricAdd(vertex_quadrics[remap[i1]], Q); quadricAdd(vertex_quadrics[remap[i1]], Q);
quadricAdd(vertex_quadrics[remap[i2]], Q); quadricAdd(vertex_quadrics[remap[i2]], Q);
@@ -1273,13 +1395,19 @@ MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = 0; @@ -1278,14 +1400,20 @@ MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = 0;
#endif #endif
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, 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_simplifyWithAttributes(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, target_index_count, target_error, out_result_error, 0, 0, 0); + return meshopt_simplifyWithAttributes(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, target_index_count, target_error, options, out_result_error, 0, 0, 0);
+} +}
+ +
+size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, float* out_result_error, const float* attributes, const float* attribute_weights, size_t attribute_count) +size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, unsigned int options, float* out_result_error, const float* attributes, const float* attribute_weights, size_t attribute_count)
{ {
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
- assert(vertex_positions_stride > 0 && 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(vertex_stride > 0 && vertex_stride <= 256); + assert(vertex_stride >= 12 && vertex_stride <= 256);
+ assert(vertex_stride % sizeof(float) == 0); + assert(vertex_stride % sizeof(float) == 0);
assert(target_index_count <= index_count); assert(target_index_count <= index_count);
assert((options & ~(meshopt_SimplifyLockBorder)) == 0);
+ assert(attribute_count <= ATTRIBUTES); + assert(attribute_count <= ATTRIBUTES);
meshopt_Allocator allocator; meshopt_Allocator allocator;
@@ -1293,7 +1421,7 @@ size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, @@ -1299,7 +1427,7 @@ size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices,
// 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);
@ -227,7 +228,7 @@ index a74b08a97d..5e92e2dc73 100644
// 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);
@@ -1317,7 +1445,21 @@ size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, @@ -1323,7 +1451,21 @@ size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices,
#endif #endif
Vector3* vertex_positions = allocator.allocate<Vector3>(vertex_count); Vector3* vertex_positions = allocator.allocate<Vector3>(vertex_count);
@ -250,7 +251,7 @@ index a74b08a97d..5e92e2dc73 100644
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count); Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric)); memset(vertex_quadrics, 0, vertex_count * sizeof(Quadric));
@@ -1409,7 +1551,9 @@ size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, @@ -1415,7 +1557,9 @@ size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices,
// 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)

View File

@ -254,7 +254,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) 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)
{ {
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));
@ -364,6 +364,11 @@ static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned
} }
} }
if (options & meshopt_SimplifyLockBorder)
for (size_t i = 0; i < vertex_count; ++i)
if (result[i] == Kind_Border)
result[i] = Kind_Locked;
#if TRACE #if TRACE
printf("locked: many open edges %d, disconnected seam %d, many seam edges %d, many wedges %d\n", printf("locked: many open edges %d, disconnected seam %d, many seam edges %d, many wedges %d\n",
int(stats[0]), int(stats[1]), int(stats[2]), int(stats[3])); int(stats[0]), int(stats[1]), int(stats[2]), int(stats[3]));
@ -1434,19 +1439,20 @@ MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop = 0;
MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = 0; MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = 0;
#endif #endif
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, 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_simplifyWithAttributes(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, target_index_count, target_error, out_result_error, 0, 0, 0); return meshopt_simplifyWithAttributes(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, target_index_count, target_error, options, out_result_error, 0, 0, 0);
} }
size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, float* out_result_error, const float* attributes, const float* attribute_weights, size_t attribute_count) size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_data, size_t vertex_count, size_t vertex_stride, size_t target_index_count, float target_error, unsigned int options, float* out_result_error, const float* attributes, const float* attribute_weights, size_t attribute_count)
{ {
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(vertex_stride > 0 && vertex_stride <= 256); assert(vertex_stride >= 12 && vertex_stride <= 256);
assert(vertex_stride % sizeof(float) == 0); assert(vertex_stride % sizeof(float) == 0);
assert(target_index_count <= index_count); assert(target_index_count <= index_count);
assert((options & ~(meshopt_SimplifyLockBorder)) == 0);
assert(attribute_count <= ATTRIBUTES); assert(attribute_count <= ATTRIBUTES);
meshopt_Allocator allocator; meshopt_Allocator allocator;
@ -1467,7 +1473,7 @@ size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned
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); classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge, options);
#if TRACE #if TRACE
size_t unique_positions = 0; size_t unique_positions = 0;
@ -1605,7 +1611,7 @@ size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* ind
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && 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);
@ -1736,7 +1742,7 @@ size_t meshopt_simplifyPoints(unsigned int* destination, const float* vertex_pos
{ {
using namespace meshopt; using namespace meshopt;
assert(vertex_positions_stride > 0 && 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_vertex_count <= vertex_count); assert(target_vertex_count <= vertex_count);
@ -1848,7 +1854,7 @@ float meshopt_simplifyScale(const float* vertex_positions, size_t vertex_count,
{ {
using namespace meshopt; using namespace meshopt;
assert(vertex_positions_stride > 0 && 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);
float extent = rescalePositions(NULL, vertex_positions, vertex_count, vertex_positions_stride); float extent = rescalePositions(NULL, vertex_positions, vertex_count, vertex_positions_stride);

View File

@ -113,7 +113,7 @@ void meshopt_spatialSortRemap(unsigned int* destination, const float* vertex_pos
{ {
using namespace meshopt; using namespace meshopt;
assert(vertex_positions_stride > 0 && 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);
meshopt_Allocator allocator; meshopt_Allocator allocator;
@ -144,7 +144,7 @@ void meshopt_spatialSortTriangles(unsigned int* destination, const unsigned int*
using namespace meshopt; using namespace meshopt;
assert(index_count % 3 == 0); assert(index_count % 3 == 0);
assert(vertex_positions_stride > 0 && 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);
(void)vertex_count; (void)vertex_count;

View File

@ -110,7 +110,7 @@ static unsigned int getNextVertexDeadEnd(const unsigned int* dead_end, unsigned
return ~0u; return ~0u;
} }
static unsigned int getNextVertexNeighbour(const unsigned int* next_candidates_begin, const unsigned int* next_candidates_end, const unsigned int* live_triangles, const unsigned int* cache_timestamps, unsigned int timestamp, unsigned int cache_size) static unsigned int getNextVertexNeighbor(const unsigned int* next_candidates_begin, const unsigned int* next_candidates_end, const unsigned int* live_triangles, const unsigned int* cache_timestamps, unsigned int timestamp, unsigned int cache_size)
{ {
unsigned int best_candidate = ~0u; unsigned int best_candidate = ~0u;
int best_priority = -1; int best_priority = -1;
@ -281,16 +281,16 @@ void meshopt_optimizeVertexCacheTable(unsigned int* destination, const unsigned
{ {
unsigned int index = indices[current_triangle * 3 + k]; unsigned int index = indices[current_triangle * 3 + k];
unsigned int* neighbours = &adjacency.data[0] + adjacency.offsets[index]; unsigned int* neighbors = &adjacency.data[0] + adjacency.offsets[index];
size_t neighbours_size = adjacency.counts[index]; size_t neighbors_size = adjacency.counts[index];
for (size_t i = 0; i < neighbours_size; ++i) for (size_t i = 0; i < neighbors_size; ++i)
{ {
unsigned int tri = neighbours[i]; unsigned int tri = neighbors[i];
if (tri == current_triangle) if (tri == current_triangle)
{ {
neighbours[i] = neighbours[neighbours_size - 1]; neighbors[i] = neighbors[neighbors_size - 1];
adjacency.counts[index]--; adjacency.counts[index]--;
break; break;
} }
@ -314,10 +314,10 @@ void meshopt_optimizeVertexCacheTable(unsigned int* destination, const unsigned
vertex_scores[index] = score; vertex_scores[index] = score;
// update scores of vertex triangles // update scores of vertex triangles
const unsigned int* neighbours_begin = &adjacency.data[0] + adjacency.offsets[index]; const unsigned int* neighbors_begin = &adjacency.data[0] + adjacency.offsets[index];
const unsigned int* neighbours_end = neighbours_begin + adjacency.counts[index]; const unsigned int* neighbors_end = neighbors_begin + adjacency.counts[index];
for (const unsigned int* it = neighbours_begin; it != neighbours_end; ++it) for (const unsigned int* it = neighbors_begin; it != neighbors_end; ++it)
{ {
unsigned int tri = *it; unsigned int tri = *it;
assert(!emitted_flags[tri]); assert(!emitted_flags[tri]);
@ -412,11 +412,11 @@ void meshopt_optimizeVertexCacheFifo(unsigned int* destination, const unsigned i
{ {
const unsigned int* next_candidates_begin = &dead_end[0] + dead_end_top; const unsigned int* next_candidates_begin = &dead_end[0] + dead_end_top;
// emit all vertex neighbours // emit all vertex neighbors
const unsigned int* neighbours_begin = &adjacency.data[0] + adjacency.offsets[current_vertex]; const unsigned int* neighbors_begin = &adjacency.data[0] + adjacency.offsets[current_vertex];
const unsigned int* neighbours_end = neighbours_begin + adjacency.counts[current_vertex]; const unsigned int* neighbors_end = neighbors_begin + adjacency.counts[current_vertex];
for (const unsigned int* it = neighbours_begin; it != neighbours_end; ++it) for (const unsigned int* it = neighbors_begin; it != neighbors_end; ++it)
{ {
unsigned int triangle = *it; unsigned int triangle = *it;
@ -461,7 +461,7 @@ void meshopt_optimizeVertexCacheFifo(unsigned int* destination, const unsigned i
const unsigned int* next_candidates_end = &dead_end[0] + dead_end_top; const unsigned int* next_candidates_end = &dead_end[0] + dead_end_top;
// get next vertex // get next vertex
current_vertex = getNextVertexNeighbour(next_candidates_begin, next_candidates_end, &live_triangles[0], &cache_timestamps[0], timestamp, cache_size); current_vertex = getNextVertexNeighbor(next_candidates_begin, next_candidates_end, &live_triangles[0], &cache_timestamps[0], timestamp, cache_size);
if (current_vertex == ~0u) if (current_vertex == ~0u)
{ {

View File

@ -50,6 +50,12 @@
#define SIMD_TARGET #define SIMD_TARGET
#endif #endif
// When targeting AArch64/x64, optimize for latency to allow decoding of individual 16-byte groups to overlap
// We don't do this for 32-bit systems because we need 64-bit math for this and this will hurt in-order CPUs
#if defined(__x86_64__) || defined(_M_X64) || defined(__aarch64__) || defined(_M_ARM64)
#define SIMD_LATENCYOPT
#endif
#endif // !MESHOPTIMIZER_NO_SIMD #endif // !MESHOPTIMIZER_NO_SIMD
#ifdef SIMD_SSE #ifdef SIMD_SSE
@ -472,6 +478,18 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi
typedef int unaligned_int; typedef int unaligned_int;
#endif #endif
#ifdef SIMD_LATENCYOPT
unsigned int data32;
memcpy(&data32, data, 4);
data32 &= data32 >> 1;
// arrange bits such that low bits of nibbles of data64 contain all 2-bit elements of data32
unsigned long long data64 = ((unsigned long long)data32 << 30) | (data32 & 0x3fffffff);
// adds all 1-bit nibbles together; the sum fits in 4 bits because datacnt=16 would have used mode 3
int datacnt = int(((data64 & 0x1111111111111111ull) * 0x1111111111111111ull) >> 60);
#endif
__m128i sel2 = _mm_cvtsi32_si128(*reinterpret_cast<const unaligned_int*>(data)); __m128i sel2 = _mm_cvtsi32_si128(*reinterpret_cast<const unaligned_int*>(data));
__m128i rest = _mm_loadu_si128(reinterpret_cast<const __m128i*>(data + 4)); __m128i rest = _mm_loadu_si128(reinterpret_cast<const __m128i*>(data + 4));
@ -490,11 +508,25 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi
_mm_storeu_si128(reinterpret_cast<__m128i*>(buffer), result); _mm_storeu_si128(reinterpret_cast<__m128i*>(buffer), result);
#ifdef SIMD_LATENCYOPT
return data + 4 + datacnt;
#else
return data + 4 + kDecodeBytesGroupCount[mask0] + kDecodeBytesGroupCount[mask1]; return data + 4 + kDecodeBytesGroupCount[mask0] + kDecodeBytesGroupCount[mask1];
#endif
} }
case 2: case 2:
{ {
#ifdef SIMD_LATENCYOPT
unsigned long long data64;
memcpy(&data64, data, 8);
data64 &= data64 >> 1;
data64 &= data64 >> 2;
// adds all 1-bit nibbles together; the sum fits in 4 bits because datacnt=16 would have used mode 3
int datacnt = int(((data64 & 0x1111111111111111ull) * 0x1111111111111111ull) >> 60);
#endif
__m128i sel4 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(data)); __m128i sel4 = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(data));
__m128i rest = _mm_loadu_si128(reinterpret_cast<const __m128i*>(data + 8)); __m128i rest = _mm_loadu_si128(reinterpret_cast<const __m128i*>(data + 8));
@ -512,7 +544,11 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi
_mm_storeu_si128(reinterpret_cast<__m128i*>(buffer), result); _mm_storeu_si128(reinterpret_cast<__m128i*>(buffer), result);
#ifdef SIMD_LATENCYOPT
return data + 8 + datacnt;
#else
return data + 8 + kDecodeBytesGroupCount[mask0] + kDecodeBytesGroupCount[mask1]; return data + 8 + kDecodeBytesGroupCount[mask0] + kDecodeBytesGroupCount[mask1];
#endif
} }
case 3: case 3:
@ -604,24 +640,13 @@ static uint8x16_t shuffleBytes(unsigned char mask0, unsigned char mask1, uint8x8
static void neonMoveMask(uint8x16_t mask, unsigned char& mask0, unsigned char& mask1) static void neonMoveMask(uint8x16_t mask, unsigned char& mask0, unsigned char& mask1)
{ {
static const unsigned char byte_mask_data[16] = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128}; // magic constant found using z3 SMT assuming mask has 8 groups of 0xff or 0x00
const uint64_t magic = 0x000103070f1f3f80ull;
uint8x16_t byte_mask = vld1q_u8(byte_mask_data); uint64x2_t mask2 = vreinterpretq_u64_u8(mask);
uint8x16_t masked = vandq_u8(mask, byte_mask);
#ifdef __aarch64__ mask0 = uint8_t((vgetq_lane_u64(mask2, 0) * magic) >> 56);
// aarch64 has horizontal sums; MSVC doesn't expose this via arm64_neon.h so this path is exclusive to clang/gcc mask1 = uint8_t((vgetq_lane_u64(mask2, 1) * magic) >> 56);
mask0 = vaddv_u8(vget_low_u8(masked));
mask1 = vaddv_u8(vget_high_u8(masked));
#else
// we need horizontal sums of each half of masked, which can be done in 3 steps (yielding sums of sizes 2, 4, 8)
uint8x8_t sum1 = vpadd_u8(vget_low_u8(masked), vget_high_u8(masked));
uint8x8_t sum2 = vpadd_u8(sum1, sum1);
uint8x8_t sum3 = vpadd_u8(sum2, sum2);
mask0 = vget_lane_u8(sum3, 0);
mask1 = vget_lane_u8(sum3, 1);
#endif
} }
static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsigned char* buffer, int bitslog2) static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsigned char* buffer, int bitslog2)
@ -639,6 +664,18 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi
case 1: case 1:
{ {
#ifdef SIMD_LATENCYOPT
unsigned int data32;
memcpy(&data32, data, 4);
data32 &= data32 >> 1;
// arrange bits such that low bits of nibbles of data64 contain all 2-bit elements of data32
unsigned long long data64 = ((unsigned long long)data32 << 30) | (data32 & 0x3fffffff);
// adds all 1-bit nibbles together; the sum fits in 4 bits because datacnt=16 would have used mode 3
int datacnt = int(((data64 & 0x1111111111111111ull) * 0x1111111111111111ull) >> 60);
#endif
uint8x8_t sel2 = vld1_u8(data); uint8x8_t sel2 = vld1_u8(data);
uint8x8_t sel22 = vzip_u8(vshr_n_u8(sel2, 4), sel2).val[0]; uint8x8_t sel22 = vzip_u8(vshr_n_u8(sel2, 4), sel2).val[0];
uint8x8x2_t sel2222 = vzip_u8(vshr_n_u8(sel22, 2), sel22); uint8x8x2_t sel2222 = vzip_u8(vshr_n_u8(sel22, 2), sel22);
@ -655,11 +692,25 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi
vst1q_u8(buffer, result); vst1q_u8(buffer, result);
#ifdef SIMD_LATENCYOPT
return data + 4 + datacnt;
#else
return data + 4 + kDecodeBytesGroupCount[mask0] + kDecodeBytesGroupCount[mask1]; return data + 4 + kDecodeBytesGroupCount[mask0] + kDecodeBytesGroupCount[mask1];
#endif
} }
case 2: case 2:
{ {
#ifdef SIMD_LATENCYOPT
unsigned long long data64;
memcpy(&data64, data, 8);
data64 &= data64 >> 1;
data64 &= data64 >> 2;
// adds all 1-bit nibbles together; the sum fits in 4 bits because datacnt=16 would have used mode 3
int datacnt = int(((data64 & 0x1111111111111111ull) * 0x1111111111111111ull) >> 60);
#endif
uint8x8_t sel4 = vld1_u8(data); uint8x8_t sel4 = vld1_u8(data);
uint8x8x2_t sel44 = vzip_u8(vshr_n_u8(sel4, 4), vand_u8(sel4, vdup_n_u8(15))); uint8x8x2_t sel44 = vzip_u8(vshr_n_u8(sel4, 4), vand_u8(sel4, vdup_n_u8(15)));
uint8x16_t sel = vcombine_u8(sel44.val[0], sel44.val[1]); uint8x16_t sel = vcombine_u8(sel44.val[0], sel44.val[1]);
@ -675,7 +726,11 @@ static const unsigned char* decodeBytesGroupSimd(const unsigned char* data, unsi
vst1q_u8(buffer, result); vst1q_u8(buffer, result);
#ifdef SIMD_LATENCYOPT
return data + 8 + datacnt;
#else
return data + 8 + kDecodeBytesGroupCount[mask0] + kDecodeBytesGroupCount[mask1]; return data + 8 + kDecodeBytesGroupCount[mask0] + kDecodeBytesGroupCount[mask1];
#endif
} }
case 3: case 3:
@ -715,7 +770,6 @@ static void wasmMoveMask(v128_t mask, unsigned char& mask0, unsigned char& mask1
// magic constant found using z3 SMT assuming mask has 8 groups of 0xff or 0x00 // magic constant found using z3 SMT assuming mask has 8 groups of 0xff or 0x00
const uint64_t magic = 0x000103070f1f3f80ull; const uint64_t magic = 0x000103070f1f3f80ull;
// TODO: This can use v8x16_bitmask in the future
mask0 = uint8_t((wasm_i64x2_extract_lane(mask, 0) * magic) >> 56); mask0 = uint8_t((wasm_i64x2_extract_lane(mask, 0) * magic) >> 56);
mask1 = uint8_t((wasm_i64x2_extract_lane(mask, 1) * magic) >> 56); mask1 = uint8_t((wasm_i64x2_extract_lane(mask, 1) * magic) >> 56);
} }

View File

@ -931,7 +931,7 @@ void meshopt_encodeFilterExp(void* destination_, size_t count, size_t stride, in
const float* v = &data[i * stride_float]; const float* v = &data[i * stride_float];
unsigned int* d = &destination[i * stride_float]; unsigned int* d = &destination[i * stride_float];
// use maximum exponent to encode values; this guarantess that mantissa is [-1, 1] // use maximum exponent to encode values; this guarantees that mantissa is [-1, 1]
int exp = -100; int exp = -100;
for (size_t j = 0; j < stride_float; ++j) for (size_t j = 0; j < stride_float; ++j)