mirror of
https://github.com/godotengine/godot.git
synced 2024-11-24 21:22:48 +00:00
Update meshoptimizer to 0.22
The Godot-specific patch is just a single line now; removing this patch will likely require adjusting Godot importer code to handle error limits better. This also adds new SIMPLIFY_ options; Godot is currently not using any of these but might use SIMPLIFY_PRUNE and SIMPLIFY_SPARSE in the future.
This commit is contained in:
parent
1015a481ff
commit
e2cc0e484e
@ -341,7 +341,7 @@ License: Apache-2.0
|
||||
|
||||
Files: ./thirdparty/meshoptimizer/
|
||||
Comment: meshoptimizer
|
||||
Copyright: 2016-2023, Arseny Kapoulkine
|
||||
Copyright: 2016-2024, Arseny Kapoulkine
|
||||
License: Expat
|
||||
|
||||
Files: ./thirdparty/mingw-std-threads/
|
||||
|
@ -503,6 +503,7 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, float p_normal_spli
|
||||
merged_normals_f32.ptr(),
|
||||
sizeof(float) * 3, // Attribute stride
|
||||
normal_weights, 3,
|
||||
nullptr, // Vertex lock
|
||||
index_target,
|
||||
max_mesh_error,
|
||||
simplify_options,
|
||||
|
@ -80,13 +80,19 @@ public:
|
||||
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
|
||||
/* Improve simplification performance assuming input indices are a sparse subset of the mesh. Note that error becomes relative to subset extents. */
|
||||
SIMPLIFY_SPARSE = 1 << 1, // From meshopt_SimplifySparse
|
||||
/* Treat error limit and resulting error as absolute instead of relative to mesh extents. */
|
||||
SIMPLIFY_ERROR_ABSOLUTE = 1 << 2, // From meshopt_SimplifyErrorAbsolute
|
||||
/* Remove disconnected parts of the mesh during simplification incrementally, regardless of the topological restrictions inside components. */
|
||||
SIMPLIFY_PRUNE = 1 << 3, // From meshopt_SimplifyPrune
|
||||
};
|
||||
|
||||
typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count);
|
||||
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, unsigned int options, float *r_error);
|
||||
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, const float *attributes, size_t attribute_stride, const float *attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float *result_error);
|
||||
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, const float *attributes, size_t attribute_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);
|
||||
static SimplifyWithAttribFunc simplify_with_attrib_func;
|
||||
typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
|
||||
static SimplifyScaleFunc simplify_scale_func;
|
||||
|
2
thirdparty/README.md
vendored
2
thirdparty/README.md
vendored
@ -565,7 +565,7 @@ File extracted from upstream release tarball:
|
||||
## meshoptimizer
|
||||
|
||||
- Upstream: https://github.com/zeux/meshoptimizer
|
||||
- Version: 0.20 (c21d3be6ddf627f8ca852ba4b6db9903b0557858, 2023)
|
||||
- Version: 0.22 (4affad044571506a5724c9a6f15424f43e86f731, 2024)
|
||||
- License: MIT
|
||||
|
||||
Files extracted from upstream repository:
|
||||
|
2
thirdparty/meshoptimizer/LICENSE.md
vendored
2
thirdparty/meshoptimizer/LICENSE.md
vendored
@ -1,6 +1,6 @@
|
||||
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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
2
thirdparty/meshoptimizer/allocator.cpp
vendored
2
thirdparty/meshoptimizer/allocator.cpp
vendored
@ -1,7 +1,7 @@
|
||||
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
|
||||
#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::deallocate = deallocate;
|
||||
|
137
thirdparty/meshoptimizer/clusterizer.cpp
vendored
137
thirdparty/meshoptimizer/clusterizer.cpp
vendored
@ -238,7 +238,7 @@ static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int
|
||||
|
||||
bool result = false;
|
||||
|
||||
unsigned int used_extra = (av == 0xff) + (bv == 0xff) + (cv == 0xff);
|
||||
int used_extra = (av == 0xff) + (bv == 0xff) + (cv == 0xff);
|
||||
|
||||
if (meshlet.vertex_count + used_extra > max_vertices || meshlet.triangle_count >= max_triangles)
|
||||
{
|
||||
@ -283,10 +283,10 @@ static bool appendMeshlet(meshopt_Meshlet& meshlet, unsigned int a, unsigned int
|
||||
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)
|
||||
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 best_triangle = ~0u;
|
||||
unsigned int best_extra = 5;
|
||||
int best_priority = 5;
|
||||
float best_score = FLT_MAX;
|
||||
|
||||
for (size_t i = 0; i < meshlet.vertex_count; ++i)
|
||||
@ -301,20 +301,26 @@ static unsigned int getNeighborTriangle(const meshopt_Meshlet& meshlet, const Co
|
||||
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);
|
||||
int extra = (used[a] == 0xff) + (used[b] == 0xff) + (used[c] == 0xff);
|
||||
assert(extra <= 2);
|
||||
|
||||
int priority = -1;
|
||||
|
||||
// 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++;
|
||||
}
|
||||
if (extra == 0)
|
||||
priority = 0;
|
||||
// artificially increase the priority of dangling triangles as they're expensive to add to new meshlets
|
||||
else if (live_triangles[a] == 1 || live_triangles[b] == 1 || live_triangles[c] == 1)
|
||||
priority = 1;
|
||||
// if two vertices have live count of 2, removing this triangle will make another triangle dangling which is good for overall flow
|
||||
else if ((live_triangles[a] == 2) + (live_triangles[b] == 2) + (live_triangles[c] == 2) >= 2)
|
||||
priority = 1 + extra;
|
||||
// otherwise adjust priority to be after the above cases, 3 or 4 based on used[] count
|
||||
else
|
||||
priority = 2 + extra;
|
||||
|
||||
// since topology-based priority is always more important than the score, we can skip scoring in some cases
|
||||
if (extra > best_extra)
|
||||
if (priority > best_priority)
|
||||
continue;
|
||||
|
||||
float score = 0;
|
||||
@ -341,18 +347,15 @@ static unsigned int getNeighborTriangle(const meshopt_Meshlet& meshlet, const Co
|
||||
|
||||
// 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)
|
||||
if (priority < best_priority || score < best_score)
|
||||
{
|
||||
best_triangle = triangle;
|
||||
best_extra = extra;
|
||||
best_priority = priority;
|
||||
best_score = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (out_extra)
|
||||
*out_extra = best_extra;
|
||||
|
||||
return best_triangle;
|
||||
}
|
||||
|
||||
@ -441,7 +444,7 @@ static size_t kdtreeBuild(size_t offset, KDNode* nodes, size_t node_count, const
|
||||
}
|
||||
|
||||
// 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];
|
||||
size_t middle = kdtreePartition(indices, count, points, stride, axis, split);
|
||||
@ -588,13 +591,13 @@ size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_ve
|
||||
{
|
||||
Cone meshlet_cone = getMeshletCone(meshlet_cone_acc, meshlet.triangle_count);
|
||||
|
||||
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);
|
||||
unsigned int best_triangle = getNeighborTriangle(meshlet, &meshlet_cone, meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, cone_weight);
|
||||
int best_extra = best_triangle == ~0u ? -1 : (used[indices[best_triangle * 3 + 0]] == 0xff) + (used[indices[best_triangle * 3 + 1]] == 0xff) + (used[indices[best_triangle * 3 + 2]] == 0xff);
|
||||
|
||||
// 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))
|
||||
{
|
||||
best_triangle = getNeighborTriangle(meshlet, NULL, meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, 0.f, NULL);
|
||||
best_triangle = getNeighborTriangle(meshlet, NULL, meshlet_vertices, indices, adjacency, triangles, live_triangles, used, meshlet_expected_radius, 0.f);
|
||||
}
|
||||
|
||||
// when we run out of neighboring triangles we need to switch to spatial search; we currently just pick the closest triangle irrespective of connectivity
|
||||
@ -882,3 +885,93 @@ meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices
|
||||
|
||||
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));
|
||||
}
|
||||
|
8
thirdparty/meshoptimizer/indexcodec.cpp
vendored
8
thirdparty/meshoptimizer/indexcodec.cpp
vendored
@ -33,7 +33,7 @@ static int rotateTriangle(unsigned int a, unsigned int b, unsigned int c, unsign
|
||||
{
|
||||
(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)
|
||||
@ -217,7 +217,7 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
|
||||
int fe = fer >> 2;
|
||||
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)
|
||||
{
|
||||
@ -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
|
||||
int fea = (a == 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 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);
|
||||
|
||||
// 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);
|
||||
|
97
thirdparty/meshoptimizer/indexgenerator.cpp
vendored
97
thirdparty/meshoptimizer/indexgenerator.cpp
vendored
@ -6,6 +6,7 @@
|
||||
|
||||
// This work is based on:
|
||||
// John McDonald, Mark Kilgard. Crack-Free Point-Normal Triangles using Adjacent Edge Normals. 2010
|
||||
// John Hable. Variable Rate Shading with Visibility Buffer Rendering. 2024
|
||||
namespace meshopt
|
||||
{
|
||||
|
||||
@ -576,3 +577,99 @@ void meshopt_generateTessellationIndexBuffer(unsigned int* destination, const un
|
||||
memcpy(destination + i * 4, patch, sizeof(patch));
|
||||
}
|
||||
}
|
||||
|
||||
size_t meshopt_generateProvokingIndexBuffer(unsigned int* destination, unsigned int* reorder, const unsigned int* indices, size_t index_count, size_t vertex_count)
|
||||
{
|
||||
assert(index_count % 3 == 0);
|
||||
|
||||
meshopt_Allocator allocator;
|
||||
|
||||
unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
|
||||
memset(remap, -1, vertex_count * sizeof(unsigned int));
|
||||
|
||||
// compute vertex valence; this is used to prioritize least used corner
|
||||
// note: we use 8-bit counters for performance; for outlier vertices the valence is incorrect but that just affects the heuristic
|
||||
unsigned char* valence = allocator.allocate<unsigned char>(vertex_count);
|
||||
memset(valence, 0, vertex_count);
|
||||
|
||||
for (size_t i = 0; i < index_count; ++i)
|
||||
{
|
||||
unsigned int index = indices[i];
|
||||
assert(index < vertex_count);
|
||||
|
||||
valence[index]++;
|
||||
}
|
||||
|
||||
unsigned int reorder_offset = 0;
|
||||
|
||||
// assign provoking vertices; leave the rest for the next pass
|
||||
for (size_t i = 0; i < index_count; i += 3)
|
||||
{
|
||||
unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2];
|
||||
assert(a < vertex_count && b < vertex_count && c < vertex_count);
|
||||
|
||||
// try to rotate triangle such that provoking vertex hasn't been seen before
|
||||
// if multiple vertices are new, prioritize the one with least valence
|
||||
// this reduces the risk that a future triangle will have all three vertices seen
|
||||
unsigned int va = remap[a] == ~0u ? valence[a] : ~0u;
|
||||
unsigned int vb = remap[b] == ~0u ? valence[b] : ~0u;
|
||||
unsigned int vc = remap[c] == ~0u ? valence[c] : ~0u;
|
||||
|
||||
if (vb != ~0u && vb <= va && vb <= vc)
|
||||
{
|
||||
// abc -> bca
|
||||
unsigned int t = a;
|
||||
a = b, b = c, c = t;
|
||||
}
|
||||
else if (vc != ~0u && vc <= va && vc <= vb)
|
||||
{
|
||||
// abc -> cab
|
||||
unsigned int t = c;
|
||||
c = b, b = a, a = t;
|
||||
}
|
||||
|
||||
unsigned int newidx = reorder_offset;
|
||||
|
||||
// now remap[a] = ~0u or all three vertices are old
|
||||
// recording remap[a] makes it possible to remap future references to the same index, conserving space
|
||||
if (remap[a] == ~0u)
|
||||
remap[a] = newidx;
|
||||
|
||||
// we need to clone the provoking vertex to get a unique index
|
||||
// if all three are used the choice is arbitrary since no future triangle will be able to reuse any of these
|
||||
reorder[reorder_offset++] = a;
|
||||
|
||||
// note: first vertex is final, the other two will be fixed up in next pass
|
||||
destination[i + 0] = newidx;
|
||||
destination[i + 1] = b;
|
||||
destination[i + 2] = c;
|
||||
|
||||
// update vertex valences for corner heuristic
|
||||
valence[a]--;
|
||||
valence[b]--;
|
||||
valence[c]--;
|
||||
}
|
||||
|
||||
// remap or clone non-provoking vertices (iterating to skip provoking vertices)
|
||||
int step = 1;
|
||||
|
||||
for (size_t i = 1; i < index_count; i += step, step ^= 3)
|
||||
{
|
||||
unsigned int index = destination[i];
|
||||
|
||||
if (remap[index] == ~0u)
|
||||
{
|
||||
// we haven't seen the vertex before as a provoking vertex
|
||||
// to maintain the reference to the original vertex we need to clone it
|
||||
unsigned int newidx = reorder_offset;
|
||||
|
||||
remap[index] = newidx;
|
||||
reorder[reorder_offset++] = index;
|
||||
}
|
||||
|
||||
destination[i] = remap[index];
|
||||
}
|
||||
|
||||
assert(reorder_offset <= vertex_count + index_count / 3);
|
||||
return reorder_offset;
|
||||
}
|
||||
|
143
thirdparty/meshoptimizer/meshoptimizer.h
vendored
143
thirdparty/meshoptimizer/meshoptimizer.h
vendored
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* meshoptimizer - version 0.20
|
||||
* meshoptimizer - version 0.22
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* This library is distributed under the MIT License. See notice at the end of this file.
|
||||
@ -12,7 +12,7 @@
|
||||
#include <stddef.h>
|
||||
|
||||
/* Version macro; major * 1000 + minor * 10 + patch */
|
||||
#define MESHOPTIMIZER_VERSION 200 /* 0.20 */
|
||||
#define MESHOPTIMIZER_VERSION 220 /* 0.22 */
|
||||
|
||||
/* If no API is defined, assume default */
|
||||
#ifndef MESHOPTIMIZER_API
|
||||
@ -29,11 +29,14 @@
|
||||
#endif
|
||||
|
||||
/* Experimental APIs have unstable interface and might have implementation that's not fully tested or optimized */
|
||||
#ifndef MESHOPTIMIZER_EXPERIMENTAL
|
||||
#define MESHOPTIMIZER_EXPERIMENTAL MESHOPTIMIZER_API
|
||||
#endif
|
||||
|
||||
/* C interface */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -137,6 +140,19 @@ MESHOPTIMIZER_API void meshopt_generateAdjacencyIndexBuffer(unsigned int* destin
|
||||
*/
|
||||
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);
|
||||
|
||||
/**
|
||||
* Experimental: Generate index buffer that can be used for visibility buffer rendering and returns the size of the reorder table
|
||||
* Each triangle's provoking vertex index is equal to primitive id; this allows passing it to the fragment shader using nointerpolate attribute.
|
||||
* This is important for performance on hardware where primitive id can't be accessed efficiently in fragment shader.
|
||||
* The reorder table stores the original vertex id for each vertex in the new index buffer, and should be used in the vertex shader to load vertex data.
|
||||
* The provoking vertex is assumed to be the first vertex in the triangle; if this is not the case (OpenGL), rotate each triangle (abc -> bca) before rendering.
|
||||
* For maximum efficiency the input index buffer should be optimized for vertex cache first.
|
||||
*
|
||||
* destination must contain enough space for the resulting index buffer (index_count elements)
|
||||
* reorder must contain enough space for the worst case reorder table (vertex_count + index_count/3 elements)
|
||||
*/
|
||||
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_generateProvokingIndexBuffer(unsigned int* destination, unsigned int* reorder, const unsigned int* indices, size_t index_count, size_t vertex_count);
|
||||
|
||||
/**
|
||||
* Vertex transform cache optimizer
|
||||
* Reorders indices to reduce the number of GPU vertex shader invocations
|
||||
@ -254,6 +270,7 @@ MESHOPTIMIZER_API int meshopt_decodeIndexSequence(void* destination, size_t inde
|
||||
* Returns encoded data size on success, 0 on error; the only error condition is if buffer doesn't have enough space
|
||||
* This function works for a single vertex stream; for multiple vertex streams, call meshopt_encodeVertexBuffer for each stream.
|
||||
* Note that all vertex_size bytes of each vertex are encoded verbatim, including padding which should be zero-initialized.
|
||||
* For maximum efficiency the vertex buffer being encoded has to be quantized and optimized for locality of reference (cache/fetch) first.
|
||||
*
|
||||
* buffer must contain enough space for the encoded vertex buffer (use meshopt_encodeVertexBufferBound to compute worst case size)
|
||||
*/
|
||||
@ -289,9 +306,9 @@ MESHOPTIMIZER_API int meshopt_decodeVertexBuffer(void* destination, size_t verte
|
||||
* meshopt_decodeFilterExp decodes exponential encoding of floating-point data with 8-bit exponent and 24-bit integer mantissa as 2^E*M.
|
||||
* Each 32-bit component is decoded in isolation; stride must be divisible by 4.
|
||||
*/
|
||||
MESHOPTIMIZER_EXPERIMENTAL void meshopt_decodeFilterOct(void* buffer, size_t count, size_t stride);
|
||||
MESHOPTIMIZER_EXPERIMENTAL void meshopt_decodeFilterQuat(void* buffer, size_t count, size_t stride);
|
||||
MESHOPTIMIZER_EXPERIMENTAL void meshopt_decodeFilterExp(void* buffer, size_t count, size_t stride);
|
||||
MESHOPTIMIZER_API void meshopt_decodeFilterOct(void* buffer, size_t count, size_t stride);
|
||||
MESHOPTIMIZER_API void meshopt_decodeFilterQuat(void* buffer, size_t count, size_t stride);
|
||||
MESHOPTIMIZER_API void meshopt_decodeFilterExp(void* buffer, size_t count, size_t stride);
|
||||
|
||||
/**
|
||||
* Vertex buffer filter encoders
|
||||
@ -311,32 +328,40 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_decodeFilterExp(void* buffer, size_t cou
|
||||
*/
|
||||
enum meshopt_EncodeExpMode
|
||||
{
|
||||
/* When encoding exponents, use separate values for each component (maximum quality) */
|
||||
meshopt_EncodeExpSeparate,
|
||||
/* When encoding exponents, use shared value for all components of each vector (better compression) */
|
||||
meshopt_EncodeExpSharedVector,
|
||||
/* When encoding exponents, use shared value for each component of all vectors (best compression) */
|
||||
meshopt_EncodeExpSharedComponent,
|
||||
/* When encoding exponents, use separate values for each component (maximum quality) */
|
||||
meshopt_EncodeExpSeparate,
|
||||
/* When encoding exponents, use shared value for all components of each vector (better compression) */
|
||||
meshopt_EncodeExpSharedVector,
|
||||
/* When encoding exponents, use shared value for each component of all vectors (best compression) */
|
||||
meshopt_EncodeExpSharedComponent,
|
||||
/* Experimental: When encoding exponents, use separate values for each component, but clamp to 0 (good quality if very small values are not important) */
|
||||
meshopt_EncodeExpClamped,
|
||||
};
|
||||
|
||||
MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data);
|
||||
MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterQuat(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, enum meshopt_EncodeExpMode mode);
|
||||
MESHOPTIMIZER_API void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data);
|
||||
MESHOPTIMIZER_API void meshopt_encodeFilterQuat(void* destination, size_t count, size_t stride, int bits, const float* data);
|
||||
MESHOPTIMIZER_API void meshopt_encodeFilterExp(void* destination, size_t count, size_t stride, int bits, const float* data, enum meshopt_EncodeExpMode mode);
|
||||
|
||||
/**
|
||||
* 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,
|
||||
/* 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,
|
||||
/* 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,
|
||||
/* Experimental: remove disconnected parts of the mesh during simplification incrementally, regardless of the topological restrictions inside components. */
|
||||
meshopt_SimplifyPrune = 1 << 3,
|
||||
};
|
||||
|
||||
/**
|
||||
* Mesh simplifier
|
||||
* 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.
|
||||
* 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 without them prior to simplification.
|
||||
* Returns the number of indices after simplification, with destination containing new index data
|
||||
* The resulting index buffer references vertices from the original vertex buffer.
|
||||
* If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended.
|
||||
@ -351,15 +376,15 @@ MESHOPTIMIZER_API size_t meshopt_simplify(unsigned int* destination, const unsig
|
||||
|
||||
/**
|
||||
* Experimental: Mesh simplifier with attribute metric
|
||||
* The algorithm ehnahces meshopt_simplify by incorporating attribute values into the error metric used to prioritize simplification order; see meshopt_simplify documentation for details.
|
||||
* The algorithm enhances meshopt_simplify by incorporating attribute values into the error metric used to prioritize simplification order; see meshopt_simplify documentation for details.
|
||||
* Note that the number of attributes affects memory requirements and running time; this algorithm requires ~1.5x more memory and time compared to meshopt_simplify when using 4 scalar attributes.
|
||||
*
|
||||
* 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_count must be <= 16
|
||||
* TODO target_error/result_error currently use combined distance+attribute error; this may change in the future
|
||||
* attribute_weights should have attribute_count floats in total; the weights determine relative priority of attributes between each other and wrt position
|
||||
* attribute_count must be <= 32
|
||||
* 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
|
||||
*/
|
||||
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)
|
||||
@ -386,6 +411,7 @@ MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifySloppy(unsigned int* destinati
|
||||
* 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
|
||||
* vertex_colors should can be NULL; when it's not NULL, it should have float3 color in the first 12 bytes of each vertex
|
||||
* color_weight determines relative priority of color wrt position; 1.0 is a safe default
|
||||
*/
|
||||
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyPoints(unsigned int* destination, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_colors, size_t vertex_colors_stride, float color_weight, size_t target_vertex_count);
|
||||
|
||||
@ -464,6 +490,13 @@ struct meshopt_VertexFetchStatistics
|
||||
*/
|
||||
MESHOPTIMIZER_API struct meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const unsigned int* indices, size_t index_count, size_t vertex_count, size_t vertex_size);
|
||||
|
||||
/**
|
||||
* Meshlet is a small mesh cluster (subset) that consists of:
|
||||
* - triangles, an 8-bit micro triangle (index) buffer, that for each triangle specifies three local vertices to use;
|
||||
* - vertices, a 32-bit vertex indirection buffer, that for each local vertex specifies which mesh vertex to fetch vertex attributes from.
|
||||
*
|
||||
* For efficiency, meshlet triangles and vertices are packed into two large arrays; this structure contains offsets and counts to access the data.
|
||||
*/
|
||||
struct meshopt_Meshlet
|
||||
{
|
||||
/* offsets within meshlet_vertices and meshlet_triangles arrays with meshlet data */
|
||||
@ -479,6 +512,7 @@ struct meshopt_Meshlet
|
||||
* Meshlet builder
|
||||
* Splits the mesh into a set of meshlets where each meshlet has a micro index buffer indexing into meshlet vertices that refer to the original vertex buffer
|
||||
* The resulting data can be used to render meshes using NVidia programmable mesh shading pipeline, or in other cluster-based renderers.
|
||||
* When targeting mesh shading hardware, for maximum efficiency meshlets should be further optimized using meshopt_optimizeMeshlet.
|
||||
* When using buildMeshlets, vertex positions need to be provided to minimize the size of the resulting clusters.
|
||||
* When using buildMeshletsScan, for maximum efficiency the index buffer being converted has to be optimized for vertex cache first.
|
||||
*
|
||||
@ -486,13 +520,23 @@ struct meshopt_Meshlet
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
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_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
|
||||
{
|
||||
/* bounding sphere, useful for frustum and occlusion culling */
|
||||
@ -529,7 +573,8 @@ struct meshopt_Bounds
|
||||
* Real-Time Rendering 4th Edition, section 19.3).
|
||||
*
|
||||
* 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)
|
||||
* vertex_count should specify the number of vertices in the entire mesh, not cluster or meshlet
|
||||
* index_count/3 and triangle_count must not exceed implementation limits (<= 512)
|
||||
*/
|
||||
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_computeMeshletBounds(const unsigned int* meshlet_vertices, const unsigned char* meshlet_triangles, size_t triangle_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
|
||||
@ -559,7 +604,7 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_spatialSortTriangles(unsigned int* desti
|
||||
* Note that all algorithms only allocate memory for temporary use.
|
||||
* allocate/deallocate are always called in a stack-like order - last pointer to be allocated is deallocated first.
|
||||
*/
|
||||
MESHOPTIMIZER_API void meshopt_setAllocator(void* (MESHOPTIMIZER_ALLOC_CALLCONV *allocate)(size_t), void (MESHOPTIMIZER_ALLOC_CALLCONV *deallocate)(void*));
|
||||
MESHOPTIMIZER_API void meshopt_setAllocator(void* (MESHOPTIMIZER_ALLOC_CALLCONV* allocate)(size_t), void (MESHOPTIMIZER_ALLOC_CALLCONV* deallocate)(void*));
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
@ -627,6 +672,8 @@ inline void meshopt_generateAdjacencyIndexBuffer(T* destination, const T* indice
|
||||
template <typename T>
|
||||
inline void meshopt_generateTessellationIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
|
||||
template <typename T>
|
||||
inline size_t meshopt_generateProvokingIndexBuffer(T* destination, unsigned int* reorder, const T* indices, size_t index_count, size_t vertex_count);
|
||||
template <typename T>
|
||||
inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count);
|
||||
template <typename T>
|
||||
inline void meshopt_optimizeVertexCacheStrip(T* destination, const T* indices, size_t index_count, size_t vertex_count);
|
||||
@ -649,7 +696,7 @@ inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const
|
||||
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);
|
||||
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>
|
||||
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>
|
||||
@ -705,15 +752,15 @@ public:
|
||||
template <typename T>
|
||||
struct StorageT
|
||||
{
|
||||
static void* (MESHOPTIMIZER_ALLOC_CALLCONV *allocate)(size_t);
|
||||
static void (MESHOPTIMIZER_ALLOC_CALLCONV *deallocate)(void*);
|
||||
static void* (MESHOPTIMIZER_ALLOC_CALLCONV* allocate)(size_t);
|
||||
static void (MESHOPTIMIZER_ALLOC_CALLCONV* deallocate)(void*);
|
||||
};
|
||||
|
||||
typedef StorageT<void> Storage;
|
||||
|
||||
meshopt_Allocator()
|
||||
: blocks()
|
||||
, count(0)
|
||||
: blocks()
|
||||
, count(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -723,7 +770,8 @@ public:
|
||||
Storage::deallocate(blocks[i - 1]);
|
||||
}
|
||||
|
||||
template <typename T> T* allocate(size_t size)
|
||||
template <typename T>
|
||||
T* allocate(size_t size)
|
||||
{
|
||||
assert(count < sizeof(blocks) / sizeof(blocks[0]));
|
||||
T* result = static_cast<T*>(Storage::allocate(size > size_t(-1) / sizeof(T) ? size_t(-1) : size * sizeof(T)));
|
||||
@ -744,8 +792,10 @@ private:
|
||||
};
|
||||
|
||||
// This makes sure that allocate/deallocate are lazily generated in translation units that need them and are deduplicated by the linker
|
||||
template <typename T> void* (MESHOPTIMIZER_ALLOC_CALLCONV *meshopt_Allocator::StorageT<T>::allocate)(size_t) = operator new;
|
||||
template <typename T> void (MESHOPTIMIZER_ALLOC_CALLCONV *meshopt_Allocator::StorageT<T>::deallocate)(void*) = operator delete;
|
||||
template <typename T>
|
||||
void* (MESHOPTIMIZER_ALLOC_CALLCONV* meshopt_Allocator::StorageT<T>::allocate)(size_t) = operator new;
|
||||
template <typename T>
|
||||
void (MESHOPTIMIZER_ALLOC_CALLCONV* meshopt_Allocator::StorageT<T>::deallocate)(void*) = operator delete;
|
||||
#endif
|
||||
|
||||
/* Inline implementation for C++ templated wrappers */
|
||||
@ -860,6 +910,19 @@ inline void meshopt_generateTessellationIndexBuffer(T* destination, const T* ind
|
||||
meshopt_generateTessellationIndexBuffer(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline size_t meshopt_generateProvokingIndexBuffer(T* destination, unsigned int* reorder, const T* indices, size_t index_count, size_t vertex_count)
|
||||
{
|
||||
meshopt_IndexAdapter<T> in(NULL, indices, index_count);
|
||||
meshopt_IndexAdapter<T> out(destination, NULL, index_count);
|
||||
|
||||
size_t bound = vertex_count + (index_count / 3);
|
||||
assert(size_t(T(bound - 1)) == bound - 1); // bound - 1 must fit in T
|
||||
(void)bound;
|
||||
|
||||
return meshopt_generateProvokingIndexBuffer(out.data, reorder, in.data, index_count, vertex_count);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count)
|
||||
{
|
||||
@ -956,12 +1019,12 @@ inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_co
|
||||
}
|
||||
|
||||
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> out(destination, NULL, index_count);
|
||||
meshopt_IndexAdapter<T> in(NULL, indices, 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>
|
||||
@ -1050,7 +1113,7 @@ inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_
|
||||
#endif
|
||||
|
||||
/**
|
||||
* 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 of this software and associated documentation
|
||||
|
@ -53,11 +53,10 @@ static void rasterize(OverdrawBuffer* buffer, float v1x, float v1y, float v1z, f
|
||||
// flip backfacing triangles to simplify rasterization logic
|
||||
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;
|
||||
t = v2x, v2x = v3x, v3x = 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
|
||||
v1z = kViewport - v1z;
|
||||
|
@ -1,39 +1,13 @@
|
||||
diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
|
||||
index 5ba8570076..6f8b0e520e 100644
|
||||
index af64cbda49..8a7072fe4e 100644
|
||||
--- a/thirdparty/meshoptimizer/simplifier.cpp
|
||||
+++ b/thirdparty/meshoptimizer/simplifier.cpp
|
||||
@@ -476,6 +476,8 @@ struct Collapse
|
||||
float error;
|
||||
unsigned int errorui;
|
||||
};
|
||||
+
|
||||
+ float distance_error;
|
||||
};
|
||||
@@ -2048,7 +2048,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
||||
|
||||
static float normalize(Vector3& v)
|
||||
@@ -941,6 +943,8 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
|
||||
float ei = quadricError(vertex_quadrics[remap[i0]], vertex_positions[i1]);
|
||||
float ej = quadricError(vertex_quadrics[remap[j0]], vertex_positions[j1]);
|
||||
// result_error is quadratic; we need to remap it back to linear
|
||||
if (out_result_error)
|
||||
- *out_result_error = sqrtf(result_error) * error_scale;
|
||||
+ *out_result_error = sqrtf(vertex_error) * error_scale;
|
||||
|
||||
+ float dei = ei, dej = ej;
|
||||
+
|
||||
if (attribute_count)
|
||||
{
|
||||
ei += quadricError(attribute_quadrics[remap[i0]], &attribute_gradients[remap[i0] * attribute_count], attribute_count, vertex_positions[i1], &vertex_attributes[i1 * attribute_count]);
|
||||
@@ -951,6 +955,7 @@ static void rankEdgeCollapses(Collapse* collapses, size_t collapse_count, const
|
||||
c.v0 = ei <= ej ? i0 : j0;
|
||||
c.v1 = ei <= ej ? i1 : j1;
|
||||
c.error = ei <= ej ? ei : ej;
|
||||
+ c.distance_error = ei <= ej ? dei : dej;
|
||||
}
|
||||
return result_count;
|
||||
}
|
||||
|
||||
@@ -1097,7 +1102,7 @@ static size_t performEdgeCollapses(unsigned int* collapse_remap, unsigned char*
|
||||
triangle_collapses += (vertex_kind[i0] == Kind_Border) ? 1 : 2;
|
||||
edge_collapses++;
|
||||
|
||||
- result_error = result_error < c.error ? c.error : result_error;
|
||||
+ result_error = result_error < c.distance_error ? c.distance_error : result_error;
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
|
12
thirdparty/meshoptimizer/quantization.cpp
vendored
12
thirdparty/meshoptimizer/quantization.cpp
vendored
@ -3,9 +3,15 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
union FloatBits
|
||||
{
|
||||
float f;
|
||||
unsigned int ui;
|
||||
};
|
||||
|
||||
unsigned short meshopt_quantizeHalf(float v)
|
||||
{
|
||||
union { float f; unsigned int ui; } u = {v};
|
||||
FloatBits u = {v};
|
||||
unsigned int ui = u.ui;
|
||||
|
||||
int s = (ui >> 16) & 0x8000;
|
||||
@ -30,7 +36,7 @@ float meshopt_quantizeFloat(float v, int N)
|
||||
{
|
||||
assert(N >= 0 && N <= 23);
|
||||
|
||||
union { float f; unsigned int ui; } u = {v};
|
||||
FloatBits u = {v};
|
||||
unsigned int ui = u.ui;
|
||||
|
||||
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
|
||||
r += (em >= (31 << 10)) ? (112 << 23) : 0;
|
||||
|
||||
union { float f; unsigned int ui; } u;
|
||||
FloatBits u;
|
||||
u.ui = s | r;
|
||||
return u.f;
|
||||
}
|
||||
|
711
thirdparty/meshoptimizer/simplifier.cpp
vendored
711
thirdparty/meshoptimizer/simplifier.cpp
vendored
File diff suppressed because it is too large
Load Diff
13
thirdparty/meshoptimizer/stripifier.cpp
vendored
13
thirdparty/meshoptimizer/stripifier.cpp
vendored
@ -10,15 +10,15 @@
|
||||
namespace meshopt
|
||||
{
|
||||
|
||||
static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence)
|
||||
static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned char* valence)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
unsigned int iv = ~0u;
|
||||
|
||||
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 v = (va < vb && va < vc) ? va : (vb < vc) ? vb : vc;
|
||||
unsigned char 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);
|
||||
|
||||
if (v < iv)
|
||||
{
|
||||
@ -71,8 +71,9 @@ size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices,
|
||||
size_t strip_size = 0;
|
||||
|
||||
// compute vertex valence; this is used to prioritize starting triangle for strips
|
||||
unsigned int* valence = allocator.allocate<unsigned int>(vertex_count);
|
||||
memset(valence, 0, vertex_count * sizeof(unsigned int));
|
||||
// note: we use 8-bit counters for performance; for outlier vertices the valence is incorrect but that just affects the heuristic
|
||||
unsigned char* valence = allocator.allocate<unsigned char>(vertex_count);
|
||||
memset(valence, 0, vertex_count);
|
||||
|
||||
for (size_t i = 0; i < index_count; ++i)
|
||||
{
|
||||
@ -151,7 +152,7 @@ size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices,
|
||||
{
|
||||
// if we didn't find anything, we need to find the next new triangle
|
||||
// we use a heuristic to maximize the strip length
|
||||
unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]);
|
||||
unsigned int i = findStripFirst(buffer, buffer_size, valence);
|
||||
unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
|
||||
|
||||
// ordered removal from the buffer
|
||||
|
13
thirdparty/meshoptimizer/vcacheoptimizer.cpp
vendored
13
thirdparty/meshoptimizer/vcacheoptimizer.cpp
vendored
@ -195,9 +195,8 @@ void meshopt_optimizeVertexCacheTable(unsigned int* destination, const unsigned
|
||||
TriangleAdjacency adjacency = {};
|
||||
buildTriangleAdjacency(adjacency, indices, index_count, vertex_count, allocator);
|
||||
|
||||
// live triangle counts
|
||||
unsigned int* live_triangles = allocator.allocate<unsigned int>(vertex_count);
|
||||
memcpy(live_triangles, adjacency.counts, vertex_count * sizeof(unsigned int));
|
||||
// live triangle counts; note, we alias adjacency.counts as we remove triangles after emitting them so the counts always match
|
||||
unsigned int* live_triangles = adjacency.counts;
|
||||
|
||||
// emitted flags
|
||||
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];
|
||||
|
||||
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;
|
||||
cache = cache_new, cache_new = cache_temp;
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
unsigned int index = indices[current_triangle * 3 + k];
|
||||
|
83
thirdparty/meshoptimizer/vertexcodec.cpp
vendored
83
thirdparty/meshoptimizer/vertexcodec.cpp
vendored
@ -90,6 +90,14 @@
|
||||
#include <wasm_simd128.h>
|
||||
#endif
|
||||
|
||||
#ifndef TRACE
|
||||
#define TRACE 0
|
||||
#endif
|
||||
|
||||
#if TRACE
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef SIMD_WASM
|
||||
#define wasmx_splat_v32x4(v, i) wasm_i32x4_shuffle(v, v, i, i, i, i)
|
||||
#define wasmx_unpacklo_v8x16(a, b) wasm_i8x16_shuffle(a, b, 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23)
|
||||
@ -135,6 +143,19 @@ inline unsigned char unzigzag8(unsigned char v)
|
||||
return -(v & 1) ^ (v >> 1);
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
struct Stats
|
||||
{
|
||||
size_t size;
|
||||
size_t header; // bytes for header
|
||||
size_t bitg[4]; // bytes for bit groups
|
||||
size_t bitc[8]; // bit consistency: how many bits are shared between all bytes in a group
|
||||
};
|
||||
|
||||
static Stats* bytestats = NULL;
|
||||
static Stats vertexstats[256];
|
||||
#endif
|
||||
|
||||
static bool encodeBytesGroupZero(const unsigned char* buffer)
|
||||
{
|
||||
for (size_t i = 0; i < kByteGroupSize; ++i)
|
||||
@ -245,7 +266,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);
|
||||
|
||||
size_t header_offset = i / kByteGroupSize;
|
||||
@ -256,8 +277,16 @@ static unsigned char* encodeBytes(unsigned char* data, unsigned char* data_end,
|
||||
|
||||
assert(data + best_size == next);
|
||||
data = next;
|
||||
|
||||
#if TRACE
|
||||
bytestats->bitg[bitslog2] += best_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
bytestats->header += header_size;
|
||||
#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -286,9 +315,31 @@ static unsigned char* encodeVertexBlock(unsigned char* data, unsigned char* data
|
||||
vertex_offset += vertex_size;
|
||||
}
|
||||
|
||||
#if TRACE
|
||||
const unsigned char* olddata = data;
|
||||
bytestats = &vertexstats[k];
|
||||
|
||||
for (size_t ig = 0; ig < vertex_count; ig += kByteGroupSize)
|
||||
{
|
||||
unsigned char last = (ig == 0) ? last_vertex[k] : vertex_data[vertex_size * (ig - 1) + k];
|
||||
unsigned char delta = 0xff;
|
||||
|
||||
for (size_t i = ig; i < ig + kByteGroupSize && i < vertex_count; ++i)
|
||||
delta &= ~(vertex_data[vertex_size * i + k] ^ last);
|
||||
|
||||
for (int j = 0; j < 8; ++j)
|
||||
bytestats->bitc[j] += (vertex_count - ig < kByteGroupSize ? vertex_count - ig : kByteGroupSize) * ((delta >> j) & 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
data = encodeBytes(data, data_end, buffer, (vertex_count + kByteGroupSize - 1) & ~(kByteGroupSize - 1));
|
||||
if (!data)
|
||||
return NULL;
|
||||
|
||||
#if TRACE
|
||||
bytestats = NULL;
|
||||
vertexstats[k].size += data - olddata;
|
||||
#endif
|
||||
}
|
||||
|
||||
memcpy(last_vertex, &vertex_data[vertex_size * (vertex_count - 1)], vertex_size);
|
||||
@ -383,6 +434,7 @@ static const unsigned char* decodeVertexBlock(const unsigned char* data, const u
|
||||
unsigned char transposed[kVertexBlockSizeBytes];
|
||||
|
||||
size_t vertex_count_aligned = (vertex_count + kByteGroupSize - 1) & ~(kByteGroupSize - 1);
|
||||
assert(vertex_count <= vertex_count_aligned);
|
||||
|
||||
for (size_t k = 0; k < vertex_size; ++k)
|
||||
{
|
||||
@ -1095,6 +1147,10 @@ size_t meshopt_encodeVertexBuffer(unsigned char* buffer, size_t buffer_size, con
|
||||
assert(vertex_size > 0 && vertex_size <= 256);
|
||||
assert(vertex_size % 4 == 0);
|
||||
|
||||
#if TRACE
|
||||
memset(vertexstats, 0, sizeof(vertexstats));
|
||||
#endif
|
||||
|
||||
const unsigned char* vertex_data = static_cast<const unsigned char*>(vertices);
|
||||
|
||||
unsigned char* data = buffer;
|
||||
@ -1147,6 +1203,30 @@ size_t meshopt_encodeVertexBuffer(unsigned char* buffer, size_t buffer_size, con
|
||||
assert(data >= buffer + tail_size);
|
||||
assert(data <= buffer + buffer_size);
|
||||
|
||||
#if TRACE
|
||||
size_t total_size = data - buffer;
|
||||
|
||||
for (size_t k = 0; k < vertex_size; ++k)
|
||||
{
|
||||
const Stats& vsk = vertexstats[k];
|
||||
|
||||
printf("%2d: %7d bytes [%4.1f%%] %.1f bpv", int(k), int(vsk.size), double(vsk.size) / double(total_size) * 100, double(vsk.size) / double(vertex_count) * 8);
|
||||
|
||||
size_t total_k = vsk.header + vsk.bitg[0] + vsk.bitg[1] + vsk.bitg[2] + vsk.bitg[3];
|
||||
|
||||
printf(" |\thdr [%5.1f%%] bitg 1-3 [%4.1f%% %4.1f%% %4.1f%%]",
|
||||
double(vsk.header) / double(total_k) * 100, double(vsk.bitg[1]) / double(total_k) * 100,
|
||||
double(vsk.bitg[2]) / double(total_k) * 100, double(vsk.bitg[3]) / double(total_k) * 100);
|
||||
|
||||
printf(" |\tbitc [%3.0f%% %3.0f%% %3.0f%% %3.0f%% %3.0f%% %3.0f%% %3.0f%% %3.0f%%]",
|
||||
double(vsk.bitc[0]) / double(vertex_count) * 100, double(vsk.bitc[1]) / double(vertex_count) * 100,
|
||||
double(vsk.bitc[2]) / double(vertex_count) * 100, double(vsk.bitc[3]) / double(vertex_count) * 100,
|
||||
double(vsk.bitc[4]) / double(vertex_count) * 100, double(vsk.bitc[5]) / double(vertex_count) * 100,
|
||||
double(vsk.bitc[6]) / double(vertex_count) * 100, double(vsk.bitc[7]) / double(vertex_count) * 100);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return data - buffer;
|
||||
}
|
||||
|
||||
@ -1246,3 +1326,4 @@ int meshopt_decodeVertexBuffer(void* destination, size_t vertex_count, size_t ve
|
||||
#undef SIMD_WASM
|
||||
#undef SIMD_FALLBACK
|
||||
#undef SIMD_TARGET
|
||||
#undef SIMD_LATENCYOPT
|
||||
|
17
thirdparty/meshoptimizer/vertexfilter.cpp
vendored
17
thirdparty/meshoptimizer/vertexfilter.cpp
vendored
@ -807,7 +807,7 @@ inline int optlog2(float v)
|
||||
|
||||
u.f = v;
|
||||
// +1 accounts for implicit 1. in mantissa; denormalized numbers will end up clamped to min_exp by calling code
|
||||
return u.ui == 0 ? 0 : int((u.ui >> 23) & 0xff) - 127 + 1;
|
||||
return v == 0 ? 0 : int((u.ui >> 23) & 0xff) - 127 + 1;
|
||||
}
|
||||
|
||||
// optimized variant of ldexp
|
||||
@ -1010,6 +1010,20 @@ void meshopt_encodeFilterExp(void* destination_, size_t count, size_t stride, in
|
||||
component_exp[j] = (min_exp < e) ? e : min_exp;
|
||||
}
|
||||
}
|
||||
else if (mode == meshopt_EncodeExpClamped)
|
||||
{
|
||||
for (size_t j = 0; j < stride_float; ++j)
|
||||
{
|
||||
int e = optlog2(v[j]);
|
||||
|
||||
component_exp[j] = (0 < e) ? e : 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the code below assumes component_exp is initialized outside of the loop
|
||||
assert(mode == meshopt_EncodeExpSharedComponent);
|
||||
}
|
||||
|
||||
for (size_t j = 0; j < stride_float; ++j)
|
||||
{
|
||||
@ -1020,7 +1034,6 @@ void meshopt_encodeFilterExp(void* destination_, size_t count, size_t stride, in
|
||||
|
||||
// compute renormalized rounded mantissa for each component
|
||||
int mmask = (1 << 24) - 1;
|
||||
|
||||
int m = int(v[j] * optexp2(-exp) + (v[j] >= 0 ? 0.5f : -0.5f));
|
||||
|
||||
d[j] = (m & mmask) | (unsigned(exp) << 24);
|
||||
|
Loading…
Reference in New Issue
Block a user