Update meshoptimizer to version v0.21
This commit is contained in:
2
Source/ThirdParty/meshoptimizer/LICENSE.md
vendored
2
Source/ThirdParty/meshoptimizer/LICENSE.md
vendored
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016-2023 Arseny Kapoulkine
|
Copyright (c) 2016-2024 Arseny Kapoulkine
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
|
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
|
||||||
#include "meshoptimizer.h"
|
#include "meshoptimizer.h"
|
||||||
|
|
||||||
void meshopt_setAllocator(void* (MESHOPTIMIZER_ALLOC_CALLCONV *allocate)(size_t), void (MESHOPTIMIZER_ALLOC_CALLCONV *deallocate)(void*))
|
void meshopt_setAllocator(void*(MESHOPTIMIZER_ALLOC_CALLCONV* allocate)(size_t), void(MESHOPTIMIZER_ALLOC_CALLCONV* deallocate)(void*))
|
||||||
{
|
{
|
||||||
meshopt_Allocator::Storage::allocate = allocate;
|
meshopt_Allocator::Storage::allocate = allocate;
|
||||||
meshopt_Allocator::Storage::deallocate = deallocate;
|
meshopt_Allocator::Storage::deallocate = deallocate;
|
||||||
|
|||||||
92
Source/ThirdParty/meshoptimizer/clusterizer.cpp
vendored
92
Source/ThirdParty/meshoptimizer/clusterizer.cpp
vendored
@@ -441,7 +441,7 @@ static size_t kdtreeBuild(size_t offset, KDNode* nodes, size_t node_count, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
// split axis is one where the variance is largest
|
// split axis is one where the variance is largest
|
||||||
unsigned int axis = vars[0] >= vars[1] && vars[0] >= vars[2] ? 0 : vars[1] >= vars[2] ? 1 : 2;
|
unsigned int axis = (vars[0] >= vars[1] && vars[0] >= vars[2]) ? 0 : (vars[1] >= vars[2] ? 1 : 2);
|
||||||
|
|
||||||
float split = mean[axis];
|
float split = mean[axis];
|
||||||
size_t middle = kdtreePartition(indices, count, points, stride, axis, split);
|
size_t middle = kdtreePartition(indices, count, points, stride, axis, split);
|
||||||
@@ -882,3 +882,93 @@ meshopt_Bounds meshopt_computeMeshletBounds(const unsigned int* meshlet_vertices
|
|||||||
|
|
||||||
return meshopt_computeClusterBounds(indices, triangle_count * 3, vertex_positions, vertex_count, vertex_positions_stride);
|
return meshopt_computeClusterBounds(indices, triangle_count * 3, vertex_positions, vertex_count, vertex_positions_stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void meshopt_optimizeMeshlet(unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t triangle_count, size_t vertex_count)
|
||||||
|
{
|
||||||
|
using namespace meshopt;
|
||||||
|
|
||||||
|
assert(triangle_count <= kMeshletMaxTriangles);
|
||||||
|
assert(vertex_count <= kMeshletMaxVertices);
|
||||||
|
|
||||||
|
unsigned char* indices = meshlet_triangles;
|
||||||
|
unsigned int* vertices = meshlet_vertices;
|
||||||
|
|
||||||
|
// cache tracks vertex timestamps (corresponding to triangle index! all 3 vertices are added at the same time and never removed)
|
||||||
|
unsigned char cache[kMeshletMaxVertices];
|
||||||
|
memset(cache, 0, vertex_count);
|
||||||
|
|
||||||
|
// note that we start from a value that means all vertices aren't in cache
|
||||||
|
unsigned char cache_last = 128;
|
||||||
|
const unsigned char cache_cutoff = 3; // 3 triangles = ~5..9 vertices depending on reuse
|
||||||
|
|
||||||
|
for (size_t i = 0; i < triangle_count; ++i)
|
||||||
|
{
|
||||||
|
int next = -1;
|
||||||
|
int next_match = -1;
|
||||||
|
|
||||||
|
for (size_t j = i; j < triangle_count; ++j)
|
||||||
|
{
|
||||||
|
unsigned char a = indices[j * 3 + 0], b = indices[j * 3 + 1], c = indices[j * 3 + 2];
|
||||||
|
assert(a < vertex_count && b < vertex_count && c < vertex_count);
|
||||||
|
|
||||||
|
// score each triangle by how many vertices are in cache
|
||||||
|
// note: the distance is computed using unsigned 8-bit values, so cache timestamp overflow is handled gracefully
|
||||||
|
int aok = (unsigned char)(cache_last - cache[a]) < cache_cutoff;
|
||||||
|
int bok = (unsigned char)(cache_last - cache[b]) < cache_cutoff;
|
||||||
|
int cok = (unsigned char)(cache_last - cache[c]) < cache_cutoff;
|
||||||
|
|
||||||
|
if (aok + bok + cok > next_match)
|
||||||
|
{
|
||||||
|
next = (int)j;
|
||||||
|
next_match = aok + bok + cok;
|
||||||
|
|
||||||
|
// note that we could end up with all 3 vertices in the cache, but 2 is enough for ~strip traversal
|
||||||
|
if (next_match >= 2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(next >= 0);
|
||||||
|
|
||||||
|
unsigned char a = indices[next * 3 + 0], b = indices[next * 3 + 1], c = indices[next * 3 + 2];
|
||||||
|
|
||||||
|
// shift triangles before the next one forward so that we always keep an ordered partition
|
||||||
|
// note: this could have swapped triangles [i] and [next] but that distorts the order and may skew the output sequence
|
||||||
|
memmove(indices + (i + 1) * 3, indices + i * 3, (next - i) * 3 * sizeof(unsigned char));
|
||||||
|
|
||||||
|
indices[i * 3 + 0] = a;
|
||||||
|
indices[i * 3 + 1] = b;
|
||||||
|
indices[i * 3 + 2] = c;
|
||||||
|
|
||||||
|
// cache timestamp is the same between all vertices of each triangle to reduce overflow
|
||||||
|
cache_last++;
|
||||||
|
cache[a] = cache_last;
|
||||||
|
cache[b] = cache_last;
|
||||||
|
cache[c] = cache_last;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reorder meshlet vertices for access locality assuming index buffer is scanned sequentially
|
||||||
|
unsigned int order[kMeshletMaxVertices];
|
||||||
|
|
||||||
|
unsigned char remap[kMeshletMaxVertices];
|
||||||
|
memset(remap, -1, vertex_count);
|
||||||
|
|
||||||
|
size_t vertex_offset = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < triangle_count * 3; ++i)
|
||||||
|
{
|
||||||
|
unsigned char& r = remap[indices[i]];
|
||||||
|
|
||||||
|
if (r == 0xff)
|
||||||
|
{
|
||||||
|
r = (unsigned char)(vertex_offset);
|
||||||
|
order[vertex_offset] = vertices[indices[i]];
|
||||||
|
vertex_offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
indices[i] = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(vertex_offset <= vertex_count);
|
||||||
|
memcpy(vertices, order, vertex_offset * sizeof(unsigned int));
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ static int rotateTriangle(unsigned int a, unsigned int b, unsigned int c, unsign
|
|||||||
{
|
{
|
||||||
(void)a;
|
(void)a;
|
||||||
|
|
||||||
return (b == next) ? 1 : (c == next) ? 2 : 0;
|
return (b == next) ? 1 : (c == next ? 2 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getEdgeFifo(EdgeFifo fifo, unsigned int a, unsigned int b, unsigned int c, size_t offset)
|
static int getEdgeFifo(EdgeFifo fifo, unsigned int a, unsigned int b, unsigned int c, size_t offset)
|
||||||
@@ -217,7 +217,7 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
|
|||||||
int fe = fer >> 2;
|
int fe = fer >> 2;
|
||||||
int fc = getVertexFifo(vertexfifo, c, vertexfifooffset);
|
int fc = getVertexFifo(vertexfifo, c, vertexfifooffset);
|
||||||
|
|
||||||
int fec = (fc >= 1 && fc < fecmax) ? fc : (c == next) ? (next++, 0) : 15;
|
int fec = (fc >= 1 && fc < fecmax) ? fc : (c == next ? (next++, 0) : 15);
|
||||||
|
|
||||||
if (fec == 15 && version >= 1)
|
if (fec == 15 && version >= 1)
|
||||||
{
|
{
|
||||||
@@ -267,8 +267,8 @@ size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, cons
|
|||||||
|
|
||||||
// after rotation, a is almost always equal to next, so we don't waste bits on FIFO encoding for a
|
// after rotation, a is almost always equal to next, so we don't waste bits on FIFO encoding for a
|
||||||
int fea = (a == next) ? (next++, 0) : 15;
|
int fea = (a == next) ? (next++, 0) : 15;
|
||||||
int feb = (fb >= 0 && fb < 14) ? (fb + 1) : (b == next) ? (next++, 0) : 15;
|
int feb = (fb >= 0 && fb < 14) ? fb + 1 : (b == next ? (next++, 0) : 15);
|
||||||
int fec = (fc >= 0 && fc < 14) ? (fc + 1) : (c == next) ? (next++, 0) : 15;
|
int fec = (fc >= 0 && fc < 14) ? fc + 1 : (c == next ? (next++, 0) : 15);
|
||||||
|
|
||||||
// we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise
|
// we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise
|
||||||
unsigned char codeaux = (unsigned char)((feb << 4) | fec);
|
unsigned char codeaux = (unsigned char)((feb << 4) | fec);
|
||||||
|
|||||||
53
Source/ThirdParty/meshoptimizer/meshoptimizer.h
vendored
53
Source/ThirdParty/meshoptimizer/meshoptimizer.h
vendored
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* meshoptimizer - version 0.20
|
* meshoptimizer - version 0.21
|
||||||
*
|
*
|
||||||
* Copyright (C) 2016-2023, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
* Copyright (C) 2016-2024, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
||||||
* Report bugs and download new versions at https://github.com/zeux/meshoptimizer
|
* Report bugs and download new versions at https://github.com/zeux/meshoptimizer
|
||||||
*
|
*
|
||||||
* This library is distributed under the MIT License. See notice at the end of this file.
|
* This library is distributed under the MIT License. See notice at the end of this file.
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
/* Version macro; major * 1000 + minor * 10 + patch */
|
/* Version macro; major * 1000 + minor * 10 + patch */
|
||||||
#define MESHOPTIMIZER_VERSION 200 /* 0.20 */
|
#define MESHOPTIMIZER_VERSION 210 /* 0.21 */
|
||||||
|
|
||||||
/* If no API is defined, assume default */
|
/* If no API is defined, assume default */
|
||||||
#ifndef MESHOPTIMIZER_API
|
#ifndef MESHOPTIMIZER_API
|
||||||
@@ -311,12 +311,12 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_decodeFilterExp(void* buffer, size_t cou
|
|||||||
*/
|
*/
|
||||||
enum meshopt_EncodeExpMode
|
enum meshopt_EncodeExpMode
|
||||||
{
|
{
|
||||||
/* When encoding exponents, use separate values for each component (maximum quality) */
|
/* When encoding exponents, use separate values for each component (maximum quality) */
|
||||||
meshopt_EncodeExpSeparate,
|
meshopt_EncodeExpSeparate,
|
||||||
/* When encoding exponents, use shared value for all components of each vector (better compression) */
|
/* When encoding exponents, use shared value for all components of each vector (better compression) */
|
||||||
meshopt_EncodeExpSharedVector,
|
meshopt_EncodeExpSharedVector,
|
||||||
/* When encoding exponents, use shared value for each component of all vectors (best compression) */
|
/* When encoding exponents, use shared value for each component of all vectors (best compression) */
|
||||||
meshopt_EncodeExpSharedComponent,
|
meshopt_EncodeExpSharedComponent,
|
||||||
};
|
};
|
||||||
|
|
||||||
MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data);
|
MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data);
|
||||||
@@ -328,8 +328,12 @@ MESHOPTIMIZER_EXPERIMENTAL void meshopt_encodeFilterExp(void* destination, size_
|
|||||||
*/
|
*/
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
/* Do not move vertices that are located on the topological border (vertices on triangle edges that don't have a paired triangle). Useful for simplifying portions of the larger mesh. */
|
/* Do not move vertices that are located on the topological border (vertices on triangle edges that don't have a paired triangle). Useful for simplifying portions of the larger mesh. */
|
||||||
meshopt_SimplifyLockBorder = 1 << 0,
|
meshopt_SimplifyLockBorder = 1 << 0,
|
||||||
|
/* Improve simplification performance assuming input indices are a sparse subset of the mesh. Note that error becomes relative to subset extents. */
|
||||||
|
meshopt_SimplifySparse = 1 << 1,
|
||||||
|
/* Treat error limit and resulting error as absolute instead of relative to mesh extents. */
|
||||||
|
meshopt_SimplifyErrorAbsolute = 1 << 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -357,9 +361,10 @@ MESHOPTIMIZER_API size_t meshopt_simplify(unsigned int* destination, const unsig
|
|||||||
* vertex_attributes should have attribute_count floats for each vertex
|
* vertex_attributes should have attribute_count floats for each vertex
|
||||||
* attribute_weights should have attribute_count floats in total; the weights determine relative priority of attributes between each other and wrt position. The recommended weight range is [1e-3..1e-1], assuming attribute data is in [0..1] range.
|
* attribute_weights should have attribute_count floats in total; the weights determine relative priority of attributes between each other and wrt position. The recommended weight range is [1e-3..1e-1], assuming attribute data is in [0..1] range.
|
||||||
* attribute_count must be <= 16
|
* attribute_count must be <= 16
|
||||||
|
* vertex_lock can be NULL; when it's not NULL, it should have a value for each vertex; 1 denotes vertices that can't be moved
|
||||||
* TODO target_error/result_error currently use combined distance+attribute error; this may change in the future
|
* TODO target_error/result_error currently use combined distance+attribute error; this may change in the future
|
||||||
*/
|
*/
|
||||||
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float* result_error);
|
MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* result_error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Experimental: Mesh simplifier (sloppy)
|
* Experimental: Mesh simplifier (sloppy)
|
||||||
@@ -486,13 +491,23 @@ struct meshopt_Meshlet
|
|||||||
* meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices
|
* meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices
|
||||||
* meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3
|
* meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3
|
||||||
* vertex_positions should have float3 position in the first 12 bytes of each vertex
|
* vertex_positions should have float3 position in the first 12 bytes of each vertex
|
||||||
* max_vertices and max_triangles must not exceed implementation limits (max_vertices <= 255 - not 256!, max_triangles <= 512)
|
* max_vertices and max_triangles must not exceed implementation limits (max_vertices <= 255 - not 256!, max_triangles <= 512; max_triangles must be divisible by 4)
|
||||||
* cone_weight should be set to 0 when cone culling is not used, and a value between 0 and 1 otherwise to balance between cluster size and cone culling efficiency
|
* cone_weight should be set to 0 when cone culling is not used, and a value between 0 and 1 otherwise to balance between cluster size and cone culling efficiency
|
||||||
*/
|
*/
|
||||||
MESHOPTIMIZER_API size_t meshopt_buildMeshlets(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight);
|
MESHOPTIMIZER_API size_t meshopt_buildMeshlets(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t max_triangles, float cone_weight);
|
||||||
MESHOPTIMIZER_API size_t meshopt_buildMeshletsScan(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles);
|
MESHOPTIMIZER_API size_t meshopt_buildMeshletsScan(struct meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const unsigned int* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles);
|
||||||
MESHOPTIMIZER_API size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles);
|
MESHOPTIMIZER_API size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Experimental: Meshlet optimizer
|
||||||
|
* Reorders meshlet vertices and triangles to maximize locality to improve rasterizer throughput
|
||||||
|
*
|
||||||
|
* meshlet_triangles and meshlet_vertices must refer to meshlet triangle and vertex index data; when buildMeshlets* is used, these
|
||||||
|
* need to be computed from meshlet's vertex_offset and triangle_offset
|
||||||
|
* triangle_count and vertex_count must not exceed implementation limits (vertex_count <= 255 - not 256!, triangle_count <= 512)
|
||||||
|
*/
|
||||||
|
MESHOPTIMIZER_EXPERIMENTAL void meshopt_optimizeMeshlet(unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t triangle_count, size_t vertex_count);
|
||||||
|
|
||||||
struct meshopt_Bounds
|
struct meshopt_Bounds
|
||||||
{
|
{
|
||||||
/* bounding sphere, useful for frustum and occlusion culling */
|
/* bounding sphere, useful for frustum and occlusion culling */
|
||||||
@@ -649,7 +664,7 @@ inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = NULL);
|
inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = NULL);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = NULL);
|
inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options = 0, float* result_error = NULL);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline size_t meshopt_simplifySloppy(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error = NULL);
|
inline size_t meshopt_simplifySloppy(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error = NULL);
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -956,12 +971,12 @@ inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_co
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float* result_error)
|
inline size_t meshopt_simplifyWithAttributes(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* result_error)
|
||||||
{
|
{
|
||||||
meshopt_IndexAdapter<T> in(NULL, indices, index_count);
|
meshopt_IndexAdapter<T> in(NULL, indices, index_count);
|
||||||
meshopt_IndexAdapter<T> out(destination, NULL, index_count);
|
meshopt_IndexAdapter<T> out(destination, NULL, index_count);
|
||||||
|
|
||||||
return meshopt_simplifyWithAttributes(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, vertex_attributes, vertex_attributes_stride, attribute_weights, attribute_count, target_index_count, target_error, options, result_error);
|
return meshopt_simplifyWithAttributes(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, vertex_attributes, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, target_index_count, target_error, options, result_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -1050,7 +1065,7 @@ inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright (c) 2016-2023 Arseny Kapoulkine
|
* Copyright (c) 2016-2024 Arseny Kapoulkine
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person
|
* Permission is hereby granted, free of charge, to any person
|
||||||
* obtaining a copy of this software and associated documentation
|
* obtaining a copy of this software and associated documentation
|
||||||
|
|||||||
@@ -53,11 +53,10 @@ static void rasterize(OverdrawBuffer* buffer, float v1x, float v1y, float v1z, f
|
|||||||
// flip backfacing triangles to simplify rasterization logic
|
// flip backfacing triangles to simplify rasterization logic
|
||||||
if (sign)
|
if (sign)
|
||||||
{
|
{
|
||||||
// flipping v2 & v3 preserves depth gradients since they're based on v1
|
// flipping v2 & v3 preserves depth gradients since they're based on v1; only v1z is used below
|
||||||
float t;
|
float t;
|
||||||
t = v2x, v2x = v3x, v3x = t;
|
t = v2x, v2x = v3x, v3x = t;
|
||||||
t = v2y, v2y = v3y, v3y = t;
|
t = v2y, v2y = v3y, v3y = t;
|
||||||
t = v2z, v2z = v3z, v3z = t;
|
|
||||||
|
|
||||||
// flip depth since we rasterize backfacing triangles to second buffer with reverse Z; only v1z is used below
|
// flip depth since we rasterize backfacing triangles to second buffer with reverse Z; only v1z is used below
|
||||||
v1z = kViewport - v1z;
|
v1z = kViewport - v1z;
|
||||||
|
|||||||
12
Source/ThirdParty/meshoptimizer/quantization.cpp
vendored
12
Source/ThirdParty/meshoptimizer/quantization.cpp
vendored
@@ -3,9 +3,15 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
union FloatBits
|
||||||
|
{
|
||||||
|
float f;
|
||||||
|
unsigned int ui;
|
||||||
|
};
|
||||||
|
|
||||||
unsigned short meshopt_quantizeHalf(float v)
|
unsigned short meshopt_quantizeHalf(float v)
|
||||||
{
|
{
|
||||||
union { float f; unsigned int ui; } u = {v};
|
FloatBits u = {v};
|
||||||
unsigned int ui = u.ui;
|
unsigned int ui = u.ui;
|
||||||
|
|
||||||
int s = (ui >> 16) & 0x8000;
|
int s = (ui >> 16) & 0x8000;
|
||||||
@@ -30,7 +36,7 @@ float meshopt_quantizeFloat(float v, int N)
|
|||||||
{
|
{
|
||||||
assert(N >= 0 && N <= 23);
|
assert(N >= 0 && N <= 23);
|
||||||
|
|
||||||
union { float f; unsigned int ui; } u = {v};
|
FloatBits u = {v};
|
||||||
unsigned int ui = u.ui;
|
unsigned int ui = u.ui;
|
||||||
|
|
||||||
const int mask = (1 << (23 - N)) - 1;
|
const int mask = (1 << (23 - N)) - 1;
|
||||||
@@ -64,7 +70,7 @@ float meshopt_dequantizeHalf(unsigned short h)
|
|||||||
// 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255
|
// 112 is an exponent bias fixup; since we already applied it once, applying it twice converts 31 to 255
|
||||||
r += (em >= (31 << 10)) ? (112 << 23) : 0;
|
r += (em >= (31 << 10)) ? (112 << 23) : 0;
|
||||||
|
|
||||||
union { float f; unsigned int ui; } u;
|
FloatBits u;
|
||||||
u.ui = s | r;
|
u.ui = s | r;
|
||||||
return u.f;
|
return u.f;
|
||||||
}
|
}
|
||||||
|
|||||||
172
Source/ThirdParty/meshoptimizer/simplifier.cpp
vendored
172
Source/ThirdParty/meshoptimizer/simplifier.cpp
vendored
@@ -111,10 +111,12 @@ struct PositionHasher
|
|||||||
{
|
{
|
||||||
const float* vertex_positions;
|
const float* vertex_positions;
|
||||||
size_t vertex_stride_float;
|
size_t vertex_stride_float;
|
||||||
|
const unsigned int* sparse_remap;
|
||||||
|
|
||||||
size_t hash(unsigned int index) const
|
size_t hash(unsigned int index) const
|
||||||
{
|
{
|
||||||
const unsigned int* key = reinterpret_cast<const unsigned int*>(vertex_positions + index * vertex_stride_float);
|
unsigned int ri = sparse_remap ? sparse_remap[index] : index;
|
||||||
|
const unsigned int* key = reinterpret_cast<const unsigned int*>(vertex_positions + ri * vertex_stride_float);
|
||||||
|
|
||||||
// scramble bits to make sure that integer coordinates have entropy in lower bits
|
// scramble bits to make sure that integer coordinates have entropy in lower bits
|
||||||
unsigned int x = key[0] ^ (key[0] >> 17);
|
unsigned int x = key[0] ^ (key[0] >> 17);
|
||||||
@@ -127,7 +129,25 @@ struct PositionHasher
|
|||||||
|
|
||||||
bool equal(unsigned int lhs, unsigned int rhs) const
|
bool equal(unsigned int lhs, unsigned int rhs) const
|
||||||
{
|
{
|
||||||
return memcmp(vertex_positions + lhs * vertex_stride_float, vertex_positions + rhs * vertex_stride_float, sizeof(float) * 3) == 0;
|
unsigned int li = sparse_remap ? sparse_remap[lhs] : lhs;
|
||||||
|
unsigned int ri = sparse_remap ? sparse_remap[rhs] : rhs;
|
||||||
|
|
||||||
|
return memcmp(vertex_positions + li * vertex_stride_float, vertex_positions + ri * vertex_stride_float, sizeof(float) * 3) == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RemapHasher
|
||||||
|
{
|
||||||
|
unsigned int* remap;
|
||||||
|
|
||||||
|
size_t hash(unsigned int id) const
|
||||||
|
{
|
||||||
|
return id * 0x5bd1e995;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool equal(unsigned int lhs, unsigned int rhs) const
|
||||||
|
{
|
||||||
|
return remap[lhs] == rhs;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -167,9 +187,9 @@ static T* hashLookup2(T* table, size_t buckets, const Hash& hash, const T& key,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, meshopt_Allocator& allocator)
|
static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const unsigned int* sparse_remap, meshopt_Allocator& allocator)
|
||||||
{
|
{
|
||||||
PositionHasher hasher = {vertex_positions_data, vertex_positions_stride / sizeof(float)};
|
PositionHasher hasher = {vertex_positions_data, vertex_positions_stride / sizeof(float), sparse_remap};
|
||||||
|
|
||||||
size_t table_size = hashBuckets2(vertex_count);
|
size_t table_size = hashBuckets2(vertex_count);
|
||||||
unsigned int* table = allocator.allocate<unsigned int>(table_size);
|
unsigned int* table = allocator.allocate<unsigned int>(table_size);
|
||||||
@@ -205,6 +225,57 @@ static void buildPositionRemap(unsigned int* remap, unsigned int* wedge, const f
|
|||||||
allocator.deallocate(table);
|
allocator.deallocate(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int* buildSparseRemap(unsigned int* indices, size_t index_count, size_t vertex_count, size_t* out_vertex_count, meshopt_Allocator& allocator)
|
||||||
|
{
|
||||||
|
// use a bit set to compute the precise number of unique vertices
|
||||||
|
unsigned char* filter = allocator.allocate<unsigned char>((vertex_count + 7) / 8);
|
||||||
|
memset(filter, 0, (vertex_count + 7) / 8);
|
||||||
|
|
||||||
|
size_t unique = 0;
|
||||||
|
for (size_t i = 0; i < index_count; ++i)
|
||||||
|
{
|
||||||
|
unsigned int index = indices[i];
|
||||||
|
assert(index < vertex_count);
|
||||||
|
|
||||||
|
unique += (filter[index / 8] & (1 << (index % 8))) == 0;
|
||||||
|
filter[index / 8] |= 1 << (index % 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int* remap = allocator.allocate<unsigned int>(unique);
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
// temporary map dense => sparse; we allocate it last so that we can deallocate it
|
||||||
|
size_t revremap_size = hashBuckets2(unique);
|
||||||
|
unsigned int* revremap = allocator.allocate<unsigned int>(revremap_size);
|
||||||
|
memset(revremap, -1, revremap_size * sizeof(unsigned int));
|
||||||
|
|
||||||
|
// fill remap, using revremap as a helper, and rewrite indices in the same pass
|
||||||
|
RemapHasher hasher = {remap};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < index_count; ++i)
|
||||||
|
{
|
||||||
|
unsigned int index = indices[i];
|
||||||
|
|
||||||
|
unsigned int* entry = hashLookup2(revremap, revremap_size, hasher, index, ~0u);
|
||||||
|
|
||||||
|
if (*entry == ~0u)
|
||||||
|
{
|
||||||
|
remap[offset] = index;
|
||||||
|
*entry = unsigned(offset);
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
|
||||||
|
indices[i] = *entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocator.deallocate(revremap);
|
||||||
|
|
||||||
|
assert(offset == unique);
|
||||||
|
*out_vertex_count = unique;
|
||||||
|
|
||||||
|
return remap;
|
||||||
|
}
|
||||||
|
|
||||||
enum VertexKind
|
enum VertexKind
|
||||||
{
|
{
|
||||||
Kind_Manifold, // not on an attribute seam, not on any boundary
|
Kind_Manifold, // not on an attribute seam, not on any boundary
|
||||||
@@ -252,7 +323,7 @@ static bool hasEdge(const EdgeAdjacency& adjacency, unsigned int a, unsigned int
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned int* loopback, size_t vertex_count, const EdgeAdjacency& adjacency, const unsigned int* remap, const unsigned int* wedge, unsigned int options)
|
static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned int* loopback, size_t vertex_count, const EdgeAdjacency& adjacency, const unsigned int* remap, const unsigned int* wedge, const unsigned char* vertex_lock, const unsigned int* sparse_remap, unsigned int options)
|
||||||
{
|
{
|
||||||
memset(loop, -1, vertex_count * sizeof(unsigned int));
|
memset(loop, -1, vertex_count * sizeof(unsigned int));
|
||||||
memset(loopback, -1, vertex_count * sizeof(unsigned int));
|
memset(loopback, -1, vertex_count * sizeof(unsigned int));
|
||||||
@@ -298,7 +369,12 @@ static void classifyVertices(unsigned char* result, unsigned int* loop, unsigned
|
|||||||
{
|
{
|
||||||
if (remap[i] == i)
|
if (remap[i] == i)
|
||||||
{
|
{
|
||||||
if (wedge[i] == i)
|
if (vertex_lock && vertex_lock[sparse_remap ? sparse_remap[i] : i])
|
||||||
|
{
|
||||||
|
// vertex is explicitly locked
|
||||||
|
result[i] = Kind_Locked;
|
||||||
|
}
|
||||||
|
else if (wedge[i] == i)
|
||||||
{
|
{
|
||||||
// no attribute seam, need to check if it's manifold
|
// no attribute seam, need to check if it's manifold
|
||||||
unsigned int openi = openinc[i], openo = openout[i];
|
unsigned int openi = openinc[i], openo = openout[i];
|
||||||
@@ -378,7 +454,7 @@ struct Vector3
|
|||||||
float x, y, z;
|
float x, y, z;
|
||||||
};
|
};
|
||||||
|
|
||||||
static float rescalePositions(Vector3* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride)
|
static float rescalePositions(Vector3* result, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const unsigned int* sparse_remap = NULL)
|
||||||
{
|
{
|
||||||
size_t vertex_stride_float = vertex_positions_stride / sizeof(float);
|
size_t vertex_stride_float = vertex_positions_stride / sizeof(float);
|
||||||
|
|
||||||
@@ -387,7 +463,8 @@ static float rescalePositions(Vector3* result, const float* vertex_positions_dat
|
|||||||
|
|
||||||
for (size_t i = 0; i < vertex_count; ++i)
|
for (size_t i = 0; i < vertex_count; ++i)
|
||||||
{
|
{
|
||||||
const float* v = vertex_positions_data + i * vertex_stride_float;
|
unsigned int ri = sparse_remap ? sparse_remap[i] : unsigned(i);
|
||||||
|
const float* v = vertex_positions_data + ri * vertex_stride_float;
|
||||||
|
|
||||||
if (result)
|
if (result)
|
||||||
{
|
{
|
||||||
@@ -426,15 +503,17 @@ static float rescalePositions(Vector3* result, const float* vertex_positions_dat
|
|||||||
return extent;
|
return extent;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rescaleAttributes(float* result, const float* vertex_attributes_data, size_t vertex_count, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count)
|
static void rescaleAttributes(float* result, const float* vertex_attributes_data, size_t vertex_count, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned int* sparse_remap)
|
||||||
{
|
{
|
||||||
size_t vertex_attributes_stride_float = vertex_attributes_stride / sizeof(float);
|
size_t vertex_attributes_stride_float = vertex_attributes_stride / sizeof(float);
|
||||||
|
|
||||||
for (size_t i = 0; i < vertex_count; ++i)
|
for (size_t i = 0; i < vertex_count; ++i)
|
||||||
{
|
{
|
||||||
|
unsigned int ri = sparse_remap ? sparse_remap[i] : unsigned(i);
|
||||||
|
|
||||||
for (size_t k = 0; k < attribute_count; ++k)
|
for (size_t k = 0; k < attribute_count; ++k)
|
||||||
{
|
{
|
||||||
float a = vertex_attributes_data[i * vertex_attributes_stride_float + k];
|
float a = vertex_attributes_data[ri * vertex_attributes_stride_float + k];
|
||||||
|
|
||||||
result[i * attribute_count + k] = a * attribute_weights[k];
|
result[i * attribute_count + k] = a * attribute_weights[k];
|
||||||
}
|
}
|
||||||
@@ -580,7 +659,7 @@ static float quadricError(const Quadric& Q, const QuadricGrad* G, size_t attribu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: weight normalization is breaking attribute error somehow
|
// TODO: weight normalization is breaking attribute error somehow
|
||||||
float s = 1;// Q.w == 0.f ? 0.f : 1.f / Q.w;
|
float s = 1; // Q.w == 0.f ? 0.f : 1.f / Q.w;
|
||||||
|
|
||||||
return fabsf(r) * s;
|
return fabsf(r) * s;
|
||||||
}
|
}
|
||||||
@@ -813,7 +892,13 @@ static bool hasTriangleFlip(const Vector3& a, const Vector3& b, const Vector3& c
|
|||||||
Vector3 nbc = {eb.y * ec.z - eb.z * ec.y, eb.z * ec.x - eb.x * ec.z, eb.x * ec.y - eb.y * ec.x};
|
Vector3 nbc = {eb.y * ec.z - eb.z * ec.y, eb.z * ec.x - eb.x * ec.z, eb.x * ec.y - eb.y * ec.x};
|
||||||
Vector3 nbd = {eb.y * ed.z - eb.z * ed.y, eb.z * ed.x - eb.x * ed.z, eb.x * ed.y - eb.y * ed.x};
|
Vector3 nbd = {eb.y * ed.z - eb.z * ed.y, eb.z * ed.x - eb.x * ed.z, eb.x * ed.y - eb.y * ed.x};
|
||||||
|
|
||||||
return nbc.x * nbd.x + nbc.y * nbd.y + nbc.z * nbd.z <= 0;
|
float ndp = nbc.x * nbd.x + nbc.y * nbd.y + nbc.z * nbd.z;
|
||||||
|
float abc = nbc.x * nbc.x + nbc.y * nbc.y + nbc.z * nbc.z;
|
||||||
|
float abd = nbd.x * nbd.x + nbd.y * nbd.y + nbd.z * nbd.z;
|
||||||
|
|
||||||
|
// scale is cos(angle); somewhat arbitrarily set to ~75 degrees
|
||||||
|
// note that the "pure" check is ndp <= 0 (90 degree cutoff) but that allows flipping through a series of close-to-90 collapses
|
||||||
|
return ndp <= 0.25f * sqrtf(abc * abd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hasTriangleFlips(const EdgeAdjacency& adjacency, const Vector3* vertex_positions, const unsigned int* collapse_remap, unsigned int i0, unsigned int i1)
|
static bool hasTriangleFlips(const EdgeAdjacency& adjacency, const Vector3* vertex_positions, const unsigned int* collapse_remap, unsigned int i0, unsigned int i1)
|
||||||
@@ -1305,7 +1390,7 @@ static void fillCellQuadrics(Quadric* cell_quadrics, const unsigned int* indices
|
|||||||
unsigned int c1 = vertex_cells[i1];
|
unsigned int c1 = vertex_cells[i1];
|
||||||
unsigned int c2 = vertex_cells[i2];
|
unsigned int c2 = vertex_cells[i2];
|
||||||
|
|
||||||
bool single_cell = (c0 == c1) & (c0 == c2);
|
int single_cell = (c0 == c1) & (c0 == c2);
|
||||||
|
|
||||||
Quadric Q;
|
Quadric Q;
|
||||||
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], single_cell ? 3.f : 1.f);
|
quadricFromTriangle(Q, vertex_positions[i0], vertex_positions[i1], vertex_positions[i2], single_cell ? 3.f : 1.f);
|
||||||
@@ -1325,7 +1410,7 @@ static void fillCellQuadrics(Quadric* cell_quadrics, const unsigned int* indices
|
|||||||
|
|
||||||
static void fillCellReservoirs(Reservoir* cell_reservoirs, size_t cell_count, const Vector3* vertex_positions, const float* vertex_colors, size_t vertex_colors_stride, size_t vertex_count, const unsigned int* vertex_cells)
|
static void fillCellReservoirs(Reservoir* cell_reservoirs, size_t cell_count, const Vector3* vertex_positions, const float* vertex_colors, size_t vertex_colors_stride, size_t vertex_count, const unsigned int* vertex_cells)
|
||||||
{
|
{
|
||||||
static const float dummy_color[] = { 0.f, 0.f, 0.f };
|
static const float dummy_color[] = {0.f, 0.f, 0.f};
|
||||||
|
|
||||||
size_t vertex_colors_stride_float = vertex_colors_stride / sizeof(float);
|
size_t vertex_colors_stride_float = vertex_colors_stride / sizeof(float);
|
||||||
|
|
||||||
@@ -1380,7 +1465,7 @@ static void fillCellRemap(unsigned int* cell_remap, float* cell_errors, size_t c
|
|||||||
|
|
||||||
static void fillCellRemap(unsigned int* cell_remap, float* cell_errors, size_t cell_count, const unsigned int* vertex_cells, const Reservoir* cell_reservoirs, const Vector3* vertex_positions, const float* vertex_colors, size_t vertex_colors_stride, float color_weight, size_t vertex_count)
|
static void fillCellRemap(unsigned int* cell_remap, float* cell_errors, size_t cell_count, const unsigned int* vertex_cells, const Reservoir* cell_reservoirs, const Vector3* vertex_positions, const float* vertex_colors, size_t vertex_colors_stride, float color_weight, size_t vertex_count)
|
||||||
{
|
{
|
||||||
static const float dummy_color[] = { 0.f, 0.f, 0.f };
|
static const float dummy_color[] = {0.f, 0.f, 0.f};
|
||||||
|
|
||||||
size_t vertex_colors_stride_float = vertex_colors_stride / sizeof(float);
|
size_t vertex_colors_stride_float = vertex_colors_stride / sizeof(float);
|
||||||
|
|
||||||
@@ -1468,7 +1553,7 @@ MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop = NULL;
|
|||||||
MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = NULL;
|
MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
|
size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
|
||||||
{
|
{
|
||||||
using namespace meshopt;
|
using namespace meshopt;
|
||||||
|
|
||||||
@@ -1476,7 +1561,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
|||||||
assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);
|
assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);
|
||||||
assert(vertex_positions_stride % sizeof(float) == 0);
|
assert(vertex_positions_stride % sizeof(float) == 0);
|
||||||
assert(target_index_count <= index_count);
|
assert(target_index_count <= index_count);
|
||||||
assert((options & ~(meshopt_SimplifyLockBorder)) == 0);
|
assert((options & ~(meshopt_SimplifyLockBorder | meshopt_SimplifySparse | meshopt_SimplifyErrorAbsolute)) == 0);
|
||||||
assert(vertex_attributes_stride >= attribute_count * sizeof(float) && vertex_attributes_stride <= 256);
|
assert(vertex_attributes_stride >= attribute_count * sizeof(float) && vertex_attributes_stride <= 256);
|
||||||
assert(vertex_attributes_stride % sizeof(float) == 0);
|
assert(vertex_attributes_stride % sizeof(float) == 0);
|
||||||
assert(attribute_count <= kMaxAttributes);
|
assert(attribute_count <= kMaxAttributes);
|
||||||
@@ -1484,22 +1569,30 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
|||||||
meshopt_Allocator allocator;
|
meshopt_Allocator allocator;
|
||||||
|
|
||||||
unsigned int* result = destination;
|
unsigned int* result = destination;
|
||||||
|
if (result != indices)
|
||||||
|
memcpy(result, indices, index_count * sizeof(unsigned int));
|
||||||
|
|
||||||
|
// build an index remap and update indices/vertex_count to minimize the subsequent work
|
||||||
|
// note: as a consequence, errors will be computed relative to the subset extent
|
||||||
|
unsigned int* sparse_remap = NULL;
|
||||||
|
if (options & meshopt_SimplifySparse)
|
||||||
|
sparse_remap = buildSparseRemap(result, index_count, vertex_count, &vertex_count, allocator);
|
||||||
|
|
||||||
// build adjacency information
|
// build adjacency information
|
||||||
EdgeAdjacency adjacency = {};
|
EdgeAdjacency adjacency = {};
|
||||||
prepareEdgeAdjacency(adjacency, index_count, vertex_count, allocator);
|
prepareEdgeAdjacency(adjacency, index_count, vertex_count, allocator);
|
||||||
updateEdgeAdjacency(adjacency, indices, index_count, vertex_count, NULL);
|
updateEdgeAdjacency(adjacency, result, index_count, vertex_count, NULL);
|
||||||
|
|
||||||
// build position remap that maps each vertex to the one with identical position
|
// build position remap that maps each vertex to the one with identical position
|
||||||
unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
|
unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
|
||||||
unsigned int* wedge = allocator.allocate<unsigned int>(vertex_count);
|
unsigned int* wedge = allocator.allocate<unsigned int>(vertex_count);
|
||||||
buildPositionRemap(remap, wedge, vertex_positions_data, vertex_count, vertex_positions_stride, allocator);
|
buildPositionRemap(remap, wedge, vertex_positions_data, vertex_count, vertex_positions_stride, sparse_remap, allocator);
|
||||||
|
|
||||||
// classify vertices; vertex kind determines collapse rules, see kCanCollapse
|
// classify vertices; vertex kind determines collapse rules, see kCanCollapse
|
||||||
unsigned char* vertex_kind = allocator.allocate<unsigned char>(vertex_count);
|
unsigned char* vertex_kind = allocator.allocate<unsigned char>(vertex_count);
|
||||||
unsigned int* loop = allocator.allocate<unsigned int>(vertex_count);
|
unsigned int* loop = allocator.allocate<unsigned int>(vertex_count);
|
||||||
unsigned int* loopback = allocator.allocate<unsigned int>(vertex_count);
|
unsigned int* loopback = allocator.allocate<unsigned int>(vertex_count);
|
||||||
classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge, options);
|
classifyVertices(vertex_kind, loop, loopback, vertex_count, adjacency, remap, wedge, vertex_lock, sparse_remap, options);
|
||||||
|
|
||||||
#if TRACE
|
#if TRACE
|
||||||
size_t unique_positions = 0;
|
size_t unique_positions = 0;
|
||||||
@@ -1517,14 +1610,14 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
Vector3* vertex_positions = allocator.allocate<Vector3>(vertex_count);
|
Vector3* vertex_positions = allocator.allocate<Vector3>(vertex_count);
|
||||||
rescalePositions(vertex_positions, vertex_positions_data, vertex_count, vertex_positions_stride);
|
float vertex_scale = rescalePositions(vertex_positions, vertex_positions_data, vertex_count, vertex_positions_stride, sparse_remap);
|
||||||
|
|
||||||
float* vertex_attributes = NULL;
|
float* vertex_attributes = NULL;
|
||||||
|
|
||||||
if (attribute_count)
|
if (attribute_count)
|
||||||
{
|
{
|
||||||
vertex_attributes = allocator.allocate<float>(vertex_count * attribute_count);
|
vertex_attributes = allocator.allocate<float>(vertex_count * attribute_count);
|
||||||
rescaleAttributes(vertex_attributes, vertex_attributes_data, vertex_count, vertex_attributes_stride, attribute_weights, attribute_count);
|
rescaleAttributes(vertex_attributes, vertex_attributes_data, vertex_count, vertex_attributes_stride, attribute_weights, attribute_count, sparse_remap);
|
||||||
}
|
}
|
||||||
|
|
||||||
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
|
Quadric* vertex_quadrics = allocator.allocate<Quadric>(vertex_count);
|
||||||
@@ -1542,14 +1635,11 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
|||||||
memset(attribute_gradients, 0, vertex_count * attribute_count * sizeof(QuadricGrad));
|
memset(attribute_gradients, 0, vertex_count * attribute_count * sizeof(QuadricGrad));
|
||||||
}
|
}
|
||||||
|
|
||||||
fillFaceQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap);
|
fillFaceQuadrics(vertex_quadrics, result, index_count, vertex_positions, remap);
|
||||||
fillEdgeQuadrics(vertex_quadrics, indices, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
|
fillEdgeQuadrics(vertex_quadrics, result, index_count, vertex_positions, remap, vertex_kind, loop, loopback);
|
||||||
|
|
||||||
if (attribute_count)
|
if (attribute_count)
|
||||||
fillAttributeQuadrics(attribute_quadrics, attribute_gradients, indices, index_count, vertex_positions, vertex_attributes, attribute_count, remap);
|
fillAttributeQuadrics(attribute_quadrics, attribute_gradients, result, index_count, vertex_positions, vertex_attributes, attribute_count, remap);
|
||||||
|
|
||||||
if (result != indices)
|
|
||||||
memcpy(result, indices, index_count * sizeof(unsigned int));
|
|
||||||
|
|
||||||
#if TRACE
|
#if TRACE
|
||||||
size_t pass_count = 0;
|
size_t pass_count = 0;
|
||||||
@@ -1566,7 +1656,8 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
|||||||
float result_error = 0;
|
float result_error = 0;
|
||||||
|
|
||||||
// target_error input is linear; we need to adjust it to match quadricError units
|
// target_error input is linear; we need to adjust it to match quadricError units
|
||||||
float error_limit = target_error * target_error;
|
float error_scale = (options & meshopt_SimplifyErrorAbsolute) ? vertex_scale : 1.f;
|
||||||
|
float error_limit = (target_error * target_error) / (error_scale * error_scale);
|
||||||
|
|
||||||
while (result_count > target_index_count)
|
while (result_count > target_index_count)
|
||||||
{
|
{
|
||||||
@@ -1611,7 +1702,7 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if TRACE
|
#if TRACE
|
||||||
printf("result: %d triangles, error: %e; total %d passes\n", int(result_count), sqrtf(result_error), int(pass_count));
|
printf("result: %d triangles, error: %e; total %d passes\n", int(result_count / 3), sqrtf(result_error), int(pass_count));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@@ -1625,21 +1716,26 @@ size_t meshopt_simplifyEdge(unsigned int* destination, const unsigned int* indic
|
|||||||
memcpy(meshopt_simplifyDebugLoopBack, loopback, vertex_count * sizeof(unsigned int));
|
memcpy(meshopt_simplifyDebugLoopBack, loopback, vertex_count * sizeof(unsigned int));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// convert resulting indices back into the dense space of the larger mesh
|
||||||
|
if (sparse_remap)
|
||||||
|
for (size_t i = 0; i < result_count; ++i)
|
||||||
|
result[i] = sparse_remap[result[i]];
|
||||||
|
|
||||||
// result_error is quadratic; we need to remap it back to linear
|
// result_error is quadratic; we need to remap it back to linear
|
||||||
if (out_result_error)
|
if (out_result_error)
|
||||||
*out_result_error = sqrtf(result_error);
|
*out_result_error = sqrtf(result_error) * error_scale;
|
||||||
|
|
||||||
return result_count;
|
return result_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
|
size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
|
||||||
{
|
{
|
||||||
return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, NULL, 0, NULL, 0, target_index_count, target_error, options, out_result_error);
|
return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, NULL, 0, NULL, 0, NULL, target_index_count, target_error, options, out_result_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
|
size_t meshopt_simplifyWithAttributes(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, const float* vertex_attributes_data, size_t vertex_attributes_stride, const float* attribute_weights, size_t attribute_count, const unsigned char* vertex_lock, size_t target_index_count, float target_error, unsigned int options, float* out_result_error)
|
||||||
{
|
{
|
||||||
return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, target_index_count, target_error, options, out_result_error);
|
return meshopt_simplifyEdge(destination, indices, index_count, vertex_positions_data, vertex_count, vertex_positions_stride, vertex_attributes_data, vertex_attributes_stride, attribute_weights, attribute_count, vertex_lock, target_index_count, target_error, options, out_result_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* out_result_error)
|
size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* out_result_error)
|
||||||
@@ -1692,14 +1788,14 @@ size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* ind
|
|||||||
|
|
||||||
// we clamp the prediction of the grid size to make sure that the search converges
|
// we clamp the prediction of the grid size to make sure that the search converges
|
||||||
int grid_size = next_grid_size;
|
int grid_size = next_grid_size;
|
||||||
grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid) ? max_grid - 1 : grid_size;
|
grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid ? max_grid - 1 : grid_size);
|
||||||
|
|
||||||
computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
|
computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
|
||||||
size_t triangles = countTriangles(vertex_ids, indices, index_count);
|
size_t triangles = countTriangles(vertex_ids, indices, index_count);
|
||||||
|
|
||||||
#if TRACE
|
#if TRACE
|
||||||
printf("pass %d (%s): grid size %d, triangles %d, %s\n",
|
printf("pass %d (%s): grid size %d, triangles %d, %s\n",
|
||||||
pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses) ? "lerp" : "binary",
|
pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses ? "lerp" : "binary"),
|
||||||
grid_size, int(triangles),
|
grid_size, int(triangles),
|
||||||
(triangles <= target_index_count / 3) ? "under" : "over");
|
(triangles <= target_index_count / 3) ? "under" : "over");
|
||||||
#endif
|
#endif
|
||||||
@@ -1824,14 +1920,14 @@ size_t meshopt_simplifyPoints(unsigned int* destination, const float* vertex_pos
|
|||||||
|
|
||||||
// we clamp the prediction of the grid size to make sure that the search converges
|
// we clamp the prediction of the grid size to make sure that the search converges
|
||||||
int grid_size = next_grid_size;
|
int grid_size = next_grid_size;
|
||||||
grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid) ? max_grid - 1 : grid_size;
|
grid_size = (grid_size <= min_grid) ? min_grid + 1 : (grid_size >= max_grid ? max_grid - 1 : grid_size);
|
||||||
|
|
||||||
computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
|
computeVertexIds(vertex_ids, vertex_positions, vertex_count, grid_size);
|
||||||
size_t vertices = countVertexCells(table, table_size, vertex_ids, vertex_count);
|
size_t vertices = countVertexCells(table, table_size, vertex_ids, vertex_count);
|
||||||
|
|
||||||
#if TRACE
|
#if TRACE
|
||||||
printf("pass %d (%s): grid size %d, vertices %d, %s\n",
|
printf("pass %d (%s): grid size %d, vertices %d, %s\n",
|
||||||
pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses) ? "lerp" : "binary",
|
pass, (pass == 0) ? "guess" : (pass <= kInterpolationPasses ? "lerp" : "binary"),
|
||||||
grid_size, int(vertices),
|
grid_size, int(vertices),
|
||||||
(vertices <= target_vertex_count) ? "under" : "over");
|
(vertices <= target_vertex_count) ? "under" : "over");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int
|
|||||||
for (size_t i = 0; i < buffer_size; ++i)
|
for (size_t i = 0; i < buffer_size; ++i)
|
||||||
{
|
{
|
||||||
unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]];
|
unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]];
|
||||||
unsigned int v = (va < vb && va < vc) ? va : (vb < vc) ? vb : vc;
|
unsigned int v = (va < vb && va < vc) ? va : (vb < vc ? vb : vc);
|
||||||
|
|
||||||
if (v < iv)
|
if (v < iv)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -195,9 +195,8 @@ void meshopt_optimizeVertexCacheTable(unsigned int* destination, const unsigned
|
|||||||
TriangleAdjacency adjacency = {};
|
TriangleAdjacency adjacency = {};
|
||||||
buildTriangleAdjacency(adjacency, indices, index_count, vertex_count, allocator);
|
buildTriangleAdjacency(adjacency, indices, index_count, vertex_count, allocator);
|
||||||
|
|
||||||
// live triangle counts
|
// live triangle counts; note, we alias adjacency.counts as we remove triangles after emitting them so the counts always match
|
||||||
unsigned int* live_triangles = allocator.allocate<unsigned int>(vertex_count);
|
unsigned int* live_triangles = adjacency.counts;
|
||||||
memcpy(live_triangles, adjacency.counts, vertex_count * sizeof(unsigned int));
|
|
||||||
|
|
||||||
// emitted flags
|
// emitted flags
|
||||||
unsigned char* emitted_flags = allocator.allocate<unsigned char>(face_count);
|
unsigned char* emitted_flags = allocator.allocate<unsigned char>(face_count);
|
||||||
@@ -261,20 +260,16 @@ void meshopt_optimizeVertexCacheTable(unsigned int* destination, const unsigned
|
|||||||
unsigned int index = cache[i];
|
unsigned int index = cache[i];
|
||||||
|
|
||||||
cache_new[cache_write] = index;
|
cache_new[cache_write] = index;
|
||||||
cache_write += (index != a && index != b && index != c);
|
cache_write += (index != a) & (index != b) & (index != c);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int* cache_temp = cache;
|
unsigned int* cache_temp = cache;
|
||||||
cache = cache_new, cache_new = cache_temp;
|
cache = cache_new, cache_new = cache_temp;
|
||||||
cache_count = cache_write > cache_size ? cache_size : cache_write;
|
cache_count = cache_write > cache_size ? cache_size : cache_write;
|
||||||
|
|
||||||
// update live triangle counts
|
|
||||||
live_triangles[a]--;
|
|
||||||
live_triangles[b]--;
|
|
||||||
live_triangles[c]--;
|
|
||||||
|
|
||||||
// remove emitted triangle from adjacency data
|
// remove emitted triangle from adjacency data
|
||||||
// this makes sure that we spend less time traversing these lists on subsequent iterations
|
// this makes sure that we spend less time traversing these lists on subsequent iterations
|
||||||
|
// live triangle counts are updated as a byproduct of these adjustments
|
||||||
for (size_t k = 0; k < 3; ++k)
|
for (size_t k = 0; k < 3; ++k)
|
||||||
{
|
{
|
||||||
unsigned int index = indices[current_triangle * 3 + k];
|
unsigned int index = indices[current_triangle * 3 + k];
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ static unsigned char* encodeBytes(unsigned char* data, unsigned char* data_end,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int bitslog2 = (best_bits == 1) ? 0 : (best_bits == 2) ? 1 : (best_bits == 4) ? 2 : 3;
|
int bitslog2 = (best_bits == 1) ? 0 : (best_bits == 2 ? 1 : (best_bits == 4 ? 2 : 3));
|
||||||
assert((1 << bitslog2) == best_bits);
|
assert((1 << bitslog2) == best_bits);
|
||||||
|
|
||||||
size_t header_offset = i / kByteGroupSize;
|
size_t header_offset = i / kByteGroupSize;
|
||||||
|
|||||||
Reference in New Issue
Block a user