Path: blob/master/thirdparty/meshoptimizer/meshoptimizer.h
9896 views
/**1* meshoptimizer - version 0.242*3* Copyright (C) 2016-2025, by Arseny Kapoulkine ([email protected])4* Report bugs and download new versions at https://github.com/zeux/meshoptimizer5*6* This library is distributed under the MIT License. See notice at the end of this file.7*/8#pragma once910#include <assert.h>11#include <stddef.h>1213/* Version macro; major * 1000 + minor * 10 + patch */14#define MESHOPTIMIZER_VERSION 240 /* 0.24 */1516/* If no API is defined, assume default */17#ifndef MESHOPTIMIZER_API18#define MESHOPTIMIZER_API19#endif2021/* Set the calling-convention for alloc/dealloc function pointers */22#ifndef MESHOPTIMIZER_ALLOC_CALLCONV23#ifdef _MSC_VER24#define MESHOPTIMIZER_ALLOC_CALLCONV __cdecl25#else26#define MESHOPTIMIZER_ALLOC_CALLCONV27#endif28#endif2930/* Experimental APIs have unstable interface and might have implementation that's not fully tested or optimized */31#ifndef MESHOPTIMIZER_EXPERIMENTAL32#define MESHOPTIMIZER_EXPERIMENTAL MESHOPTIMIZER_API33#endif3435/* C interface */36#ifdef __cplusplus37extern "C"38{39#endif4041/**42* Vertex attribute stream43* Each element takes size bytes, beginning at data, with stride controlling the spacing between successive elements (stride >= size).44*/45struct meshopt_Stream46{47const void* data;48size_t size;49size_t stride;50};5152/**53* Generates a vertex remap table from the vertex buffer and an optional index buffer and returns number of unique vertices54* As a result, all vertices that are binary equivalent map to the same (new) location, with no gaps in the resulting sequence.55* Resulting remap table maps old vertices to new vertices and can be used in meshopt_remapVertexBuffer/meshopt_remapIndexBuffer.56* Note that binary equivalence considers all vertex_size bytes, including padding which should be zero-initialized.57*58* destination must contain enough space for the resulting remap table (vertex_count elements)59* indices can be NULL if the input is unindexed60*/61MESHOPTIMIZER_API size_t meshopt_generateVertexRemap(unsigned int* destination, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size);6263/**64* Generates a vertex remap table from multiple vertex streams and an optional index buffer and returns number of unique vertices65* As a result, all vertices that are binary equivalent map to the same (new) location, with no gaps in the resulting sequence.66* Resulting remap table maps old vertices to new vertices and can be used in meshopt_remapVertexBuffer/meshopt_remapIndexBuffer.67* To remap vertex buffers, you will need to call meshopt_remapVertexBuffer for each vertex stream.68* Note that binary equivalence considers all size bytes in each stream, including padding which should be zero-initialized.69*70* destination must contain enough space for the resulting remap table (vertex_count elements)71* indices can be NULL if the input is unindexed72* stream_count must be <= 1673*/74MESHOPTIMIZER_API size_t meshopt_generateVertexRemapMulti(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, const struct meshopt_Stream* streams, size_t stream_count);7576/**77* Experimental: Generates a vertex remap table from the vertex buffer and an optional index buffer and returns number of unique vertices78* As a result, all vertices that are equivalent map to the same (new) location, with no gaps in the resulting sequence.79* Equivalence is checked in two steps: vertex positions are compared for equality, and then the user-specified equality function is called (if provided).80* Resulting remap table maps old vertices to new vertices and can be used in meshopt_remapVertexBuffer/meshopt_remapIndexBuffer.81*82* destination must contain enough space for the resulting remap table (vertex_count elements)83* indices can be NULL if the input is unindexed84* vertex_positions should have float3 position in the first 12 bytes of each vertex85* callback can be NULL if no additional equality check is needed; otherwise, it should return 1 if vertices with specified indices are equivalent and 0 if they are not86*/87MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_generateVertexRemapCustom(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, int (*callback)(void*, unsigned int, unsigned int), void* context);8889/**90* Generates vertex buffer from the source vertex buffer and remap table generated by meshopt_generateVertexRemap91*92* destination must contain enough space for the resulting vertex buffer (unique_vertex_count elements, returned by meshopt_generateVertexRemap)93* vertex_count should be the initial vertex count and not the value returned by meshopt_generateVertexRemap94*/95MESHOPTIMIZER_API void meshopt_remapVertexBuffer(void* destination, const void* vertices, size_t vertex_count, size_t vertex_size, const unsigned int* remap);9697/**98* Generate index buffer from the source index buffer and remap table generated by meshopt_generateVertexRemap99*100* destination must contain enough space for the resulting index buffer (index_count elements)101* indices can be NULL if the input is unindexed102*/103MESHOPTIMIZER_API void meshopt_remapIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const unsigned int* remap);104105/**106* Generate index buffer that can be used for more efficient rendering when only a subset of the vertex attributes is necessary107* All vertices that are binary equivalent (wrt first vertex_size bytes) map to the first vertex in the original vertex buffer.108* This makes it possible to use the index buffer for Z pre-pass or shadowmap rendering, while using the original index buffer for regular rendering.109* Note that binary equivalence considers all vertex_size bytes, including padding which should be zero-initialized.110*111* destination must contain enough space for the resulting index buffer (index_count elements)112*/113MESHOPTIMIZER_API void meshopt_generateShadowIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size, size_t vertex_stride);114115/**116* Generate index buffer that can be used for more efficient rendering when only a subset of the vertex attributes is necessary117* All vertices that are binary equivalent (wrt specified streams) map to the first vertex in the original vertex buffer.118* This makes it possible to use the index buffer for Z pre-pass or shadowmap rendering, while using the original index buffer for regular rendering.119* Note that binary equivalence considers all size bytes in each stream, including padding which should be zero-initialized.120*121* destination must contain enough space for the resulting index buffer (index_count elements)122* stream_count must be <= 16123*/124MESHOPTIMIZER_API void meshopt_generateShadowIndexBufferMulti(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, const struct meshopt_Stream* streams, size_t stream_count);125126/**127* Generate index buffer that can be used as a geometry shader input with triangle adjacency topology128* Each triangle is converted into a 6-vertex patch with the following layout:129* - 0, 2, 4: original triangle vertices130* - 1, 3, 5: vertices adjacent to edges 02, 24 and 40131* The resulting patch can be rendered with geometry shaders using e.g. VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY.132* This can be used to implement algorithms like silhouette detection/expansion and other forms of GS-driven rendering.133*134* destination must contain enough space for the resulting index buffer (index_count*2 elements)135* vertex_positions should have float3 position in the first 12 bytes of each vertex136*/137MESHOPTIMIZER_API void meshopt_generateAdjacencyIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);138139/**140* Generate index buffer that can be used for PN-AEN tessellation with crack-free displacement141* Each triangle is converted into a 12-vertex patch with the following layout:142* - 0, 1, 2: original triangle vertices143* - 3, 4: opposing edge for edge 0, 1144* - 5, 6: opposing edge for edge 1, 2145* - 7, 8: opposing edge for edge 2, 0146* - 9, 10, 11: dominant vertices for corners 0, 1, 2147* The resulting patch can be rendered with hardware tessellation using PN-AEN and displacement mapping.148* See "Tessellation on Any Budget" (John McDonald, GDC 2011) for implementation details.149*150* destination must contain enough space for the resulting index buffer (index_count*4 elements)151* vertex_positions should have float3 position in the first 12 bytes of each vertex152*/153MESHOPTIMIZER_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);154155/**156* Generate index buffer that can be used for visibility buffer rendering and returns the size of the reorder table157* Each triangle's provoking vertex index is equal to primitive id; this allows passing it to the fragment shader using nointerpolate attribute.158* This is important for performance on hardware where primitive id can't be accessed efficiently in fragment shader.159* 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.160* 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.161* For maximum efficiency the input index buffer should be optimized for vertex cache first.162*163* destination must contain enough space for the resulting index buffer (index_count elements)164* reorder must contain enough space for the worst case reorder table (vertex_count + index_count/3 elements)165*/166MESHOPTIMIZER_API size_t meshopt_generateProvokingIndexBuffer(unsigned int* destination, unsigned int* reorder, const unsigned int* indices, size_t index_count, size_t vertex_count);167168/**169* Vertex transform cache optimizer170* Reorders indices to reduce the number of GPU vertex shader invocations171* If index buffer contains multiple ranges for multiple draw calls, this functions needs to be called on each range individually.172*173* destination must contain enough space for the resulting index buffer (index_count elements)174*/175MESHOPTIMIZER_API void meshopt_optimizeVertexCache(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count);176177/**178* Vertex transform cache optimizer for strip-like caches179* Produces inferior results to meshopt_optimizeVertexCache from the GPU vertex cache perspective180* However, the resulting index order is more optimal if the goal is to reduce the triangle strip length or improve compression efficiency181*182* destination must contain enough space for the resulting index buffer (index_count elements)183*/184MESHOPTIMIZER_API void meshopt_optimizeVertexCacheStrip(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count);185186/**187* Vertex transform cache optimizer for FIFO caches188* Reorders indices to reduce the number of GPU vertex shader invocations189* Generally takes ~3x less time to optimize meshes but produces inferior results compared to meshopt_optimizeVertexCache190* If index buffer contains multiple ranges for multiple draw calls, this functions needs to be called on each range individually.191*192* destination must contain enough space for the resulting index buffer (index_count elements)193* cache_size should be less than the actual GPU cache size to avoid cache thrashing194*/195MESHOPTIMIZER_API void meshopt_optimizeVertexCacheFifo(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int cache_size);196197/**198* Overdraw optimizer199* Reorders indices to reduce the number of GPU vertex shader invocations and the pixel overdraw200* If index buffer contains multiple ranges for multiple draw calls, this functions needs to be called on each range individually.201*202* destination must contain enough space for the resulting index buffer (index_count elements)203* indices must contain index data that is the result of meshopt_optimizeVertexCache (*not* the original mesh indices!)204* vertex_positions should have float3 position in the first 12 bytes of each vertex205* threshold indicates how much the overdraw optimizer can degrade vertex cache efficiency (1.05 = up to 5%) to reduce overdraw more efficiently206*/207MESHOPTIMIZER_API void meshopt_optimizeOverdraw(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, float threshold);208209/**210* Vertex fetch cache optimizer211* Reorders vertices and changes indices to reduce the amount of GPU memory fetches during vertex processing212* Returns the number of unique vertices, which is the same as input vertex count unless some vertices are unused213* This functions works for a single vertex stream; for multiple vertex streams, use meshopt_optimizeVertexFetchRemap + meshopt_remapVertexBuffer for each stream.214*215* destination must contain enough space for the resulting vertex buffer (vertex_count elements)216* indices is used both as an input and as an output index buffer217*/218MESHOPTIMIZER_API size_t meshopt_optimizeVertexFetch(void* destination, unsigned int* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size);219220/**221* Vertex fetch cache optimizer222* Generates vertex remap to reduce the amount of GPU memory fetches during vertex processing223* Returns the number of unique vertices, which is the same as input vertex count unless some vertices are unused224* The resulting remap table should be used to reorder vertex/index buffers using meshopt_remapVertexBuffer/meshopt_remapIndexBuffer225*226* destination must contain enough space for the resulting remap table (vertex_count elements)227*/228MESHOPTIMIZER_API size_t meshopt_optimizeVertexFetchRemap(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count);229230/**231* Index buffer encoder232* Encodes index data into an array of bytes that is generally much smaller (<1.5 bytes/triangle) and compresses better (<1 bytes/triangle) compared to original.233* Input index buffer must represent a triangle list.234* Returns encoded data size on success, 0 on error; the only error condition is if buffer doesn't have enough space235* For maximum efficiency the index buffer being encoded has to be optimized for vertex cache and vertex fetch first.236*237* buffer must contain enough space for the encoded index buffer (use meshopt_encodeIndexBufferBound to compute worst case size)238*/239MESHOPTIMIZER_API size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, const unsigned int* indices, size_t index_count);240MESHOPTIMIZER_API size_t meshopt_encodeIndexBufferBound(size_t index_count, size_t vertex_count);241242/**243* Set index encoder format version244* version must specify the data format version to encode; valid values are 0 (decodable by all library versions) and 1 (decodable by 0.14+)245*/246MESHOPTIMIZER_API void meshopt_encodeIndexVersion(int version);247248/**249* Index buffer decoder250* Decodes index data from an array of bytes generated by meshopt_encodeIndexBuffer251* Returns 0 if decoding was successful, and an error code otherwise252* The decoder is safe to use for untrusted input, but it may produce garbage data (e.g. out of range indices).253*254* destination must contain enough space for the resulting index buffer (index_count elements)255*/256MESHOPTIMIZER_API int meshopt_decodeIndexBuffer(void* destination, size_t index_count, size_t index_size, const unsigned char* buffer, size_t buffer_size);257258/**259* Get encoded index format version260* Returns format version of the encoded index buffer/sequence, or -1 if the buffer header is invalid261* Note that a non-negative value doesn't guarantee that the buffer will be decoded correctly if the input is malformed.262*/263MESHOPTIMIZER_API int meshopt_decodeIndexVersion(const unsigned char* buffer, size_t buffer_size);264265/**266* Index sequence encoder267* Encodes index sequence into an array of bytes that is generally smaller and compresses better compared to original.268* Input index sequence can represent arbitrary topology; for triangle lists meshopt_encodeIndexBuffer is likely to be better.269* Returns encoded data size on success, 0 on error; the only error condition is if buffer doesn't have enough space270*271* buffer must contain enough space for the encoded index sequence (use meshopt_encodeIndexSequenceBound to compute worst case size)272*/273MESHOPTIMIZER_API size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const unsigned int* indices, size_t index_count);274MESHOPTIMIZER_API size_t meshopt_encodeIndexSequenceBound(size_t index_count, size_t vertex_count);275276/**277* Index sequence decoder278* Decodes index data from an array of bytes generated by meshopt_encodeIndexSequence279* Returns 0 if decoding was successful, and an error code otherwise280* The decoder is safe to use for untrusted input, but it may produce garbage data (e.g. out of range indices).281*282* destination must contain enough space for the resulting index sequence (index_count elements)283*/284MESHOPTIMIZER_API int meshopt_decodeIndexSequence(void* destination, size_t index_count, size_t index_size, const unsigned char* buffer, size_t buffer_size);285286/**287* Vertex buffer encoder288* Encodes vertex data into an array of bytes that is generally smaller and compresses better compared to original.289* Returns encoded data size on success, 0 on error; the only error condition is if buffer doesn't have enough space290* This function works for a single vertex stream; for multiple vertex streams, call meshopt_encodeVertexBuffer for each stream.291* Note that all vertex_size bytes of each vertex are encoded verbatim, including padding which should be zero-initialized.292* For maximum efficiency the vertex buffer being encoded has to be quantized and optimized for locality of reference (cache/fetch) first.293*294* buffer must contain enough space for the encoded vertex buffer (use meshopt_encodeVertexBufferBound to compute worst case size)295*/296MESHOPTIMIZER_API size_t meshopt_encodeVertexBuffer(unsigned char* buffer, size_t buffer_size, const void* vertices, size_t vertex_count, size_t vertex_size);297MESHOPTIMIZER_API size_t meshopt_encodeVertexBufferBound(size_t vertex_count, size_t vertex_size);298299/**300* Experimental: Vertex buffer encoder301* Encodes vertex data just like meshopt_encodeVertexBuffer, but allows to override compression level.302* For compression level to take effect, the vertex encoding version must be set to 1.303* The default compression level implied by meshopt_encodeVertexBuffer is 2.304*305* level should be in the range [0, 3] with 0 being the fastest and 3 being the slowest and producing the best compression ratio.306* version should be -1 to use the default version (specified via meshopt_encodeVertexVersion), or 0/1 to override the version; per above, level won't take effect if version is 0.307*/308MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_encodeVertexBufferLevel(unsigned char* buffer, size_t buffer_size, const void* vertices, size_t vertex_count, size_t vertex_size, int level, int version);309310/**311* Set vertex encoder format version312* version must specify the data format version to encode; valid values are 0 (decodable by all library versions) and 1 (decodable by 0.23+)313*/314MESHOPTIMIZER_API void meshopt_encodeVertexVersion(int version);315316/**317* Vertex buffer decoder318* Decodes vertex data from an array of bytes generated by meshopt_encodeVertexBuffer319* Returns 0 if decoding was successful, and an error code otherwise320* The decoder is safe to use for untrusted input, but it may produce garbage data.321*322* destination must contain enough space for the resulting vertex buffer (vertex_count * vertex_size bytes)323*/324MESHOPTIMIZER_API int meshopt_decodeVertexBuffer(void* destination, size_t vertex_count, size_t vertex_size, const unsigned char* buffer, size_t buffer_size);325326/**327* Get encoded vertex format version328* Returns format version of the encoded vertex buffer, or -1 if the buffer header is invalid329* Note that a non-negative value doesn't guarantee that the buffer will be decoded correctly if the input is malformed.330*/331MESHOPTIMIZER_API int meshopt_decodeVertexVersion(const unsigned char* buffer, size_t buffer_size);332333/**334* Vertex buffer filters335* These functions can be used to filter output of meshopt_decodeVertexBuffer in-place.336*337* meshopt_decodeFilterOct decodes octahedral encoding of a unit vector with K-bit (K <= 16) signed X/Y as an input; Z must store 1.0f.338* Each component is stored as an 8-bit or 16-bit normalized integer; stride must be equal to 4 or 8. W is preserved as is.339*340* meshopt_decodeFilterQuat decodes 3-component quaternion encoding with K-bit (4 <= K <= 16) component encoding and a 2-bit component index indicating which component to reconstruct.341* Each component is stored as an 16-bit integer; stride must be equal to 8.342*343* meshopt_decodeFilterExp decodes exponential encoding of floating-point data with 8-bit exponent and 24-bit integer mantissa as 2^E*M.344* Each 32-bit component is decoded in isolation; stride must be divisible by 4.345*/346MESHOPTIMIZER_API void meshopt_decodeFilterOct(void* buffer, size_t count, size_t stride);347MESHOPTIMIZER_API void meshopt_decodeFilterQuat(void* buffer, size_t count, size_t stride);348MESHOPTIMIZER_API void meshopt_decodeFilterExp(void* buffer, size_t count, size_t stride);349350/**351* Vertex buffer filter encoders352* These functions can be used to encode data in a format that meshopt_decodeFilter can decode353*354* meshopt_encodeFilterOct encodes unit vectors with K-bit (K <= 16) signed X/Y as an output.355* Each component is stored as an 8-bit or 16-bit normalized integer; stride must be equal to 4 or 8. W is preserved as is.356* Input data must contain 4 floats for every vector (count*4 total).357*358* meshopt_encodeFilterQuat encodes unit quaternions with K-bit (4 <= K <= 16) component encoding.359* Each component is stored as an 16-bit integer; stride must be equal to 8.360* Input data must contain 4 floats for every quaternion (count*4 total).361*362* meshopt_encodeFilterExp encodes arbitrary (finite) floating-point data with 8-bit exponent and K-bit integer mantissa (1 <= K <= 24).363* Exponent can be shared between all components of a given vector as defined by stride or all values of a given component; stride must be divisible by 4.364* Input data must contain stride/4 floats for every vector (count*stride/4 total).365*/366enum meshopt_EncodeExpMode367{368/* When encoding exponents, use separate values for each component (maximum quality) */369meshopt_EncodeExpSeparate,370/* When encoding exponents, use shared value for all components of each vector (better compression) */371meshopt_EncodeExpSharedVector,372/* When encoding exponents, use shared value for each component of all vectors (best compression) */373meshopt_EncodeExpSharedComponent,374/* When encoding exponents, use separate values for each component, but clamp to 0 (good quality if very small values are not important) */375meshopt_EncodeExpClamped,376};377378MESHOPTIMIZER_API void meshopt_encodeFilterOct(void* destination, size_t count, size_t stride, int bits, const float* data);379MESHOPTIMIZER_API void meshopt_encodeFilterQuat(void* destination, size_t count, size_t stride, int bits, const float* data);380MESHOPTIMIZER_API void meshopt_encodeFilterExp(void* destination, size_t count, size_t stride, int bits, const float* data, enum meshopt_EncodeExpMode mode);381382/**383* Simplification options384*/385enum386{387/* 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. */388meshopt_SimplifyLockBorder = 1 << 0,389/* Improve simplification performance assuming input indices are a sparse subset of the mesh. Note that error becomes relative to subset extents. */390meshopt_SimplifySparse = 1 << 1,391/* Treat error limit and resulting error as absolute instead of relative to mesh extents. */392meshopt_SimplifyErrorAbsolute = 1 << 2,393/* Experimental: remove disconnected parts of the mesh during simplification incrementally, regardless of the topological restrictions inside components. */394meshopt_SimplifyPrune = 1 << 3,395};396397/**398* Mesh simplifier399* Reduces the number of triangles in the mesh, attempting to preserve mesh appearance as much as possible400* The algorithm tries to preserve mesh topology and can stop short of the target goal based on topology constraints or target error.401* If not all attributes from the input mesh are required, it's recommended to reindex the mesh without them prior to simplification.402* Returns the number of indices after simplification, with destination containing new index data403* The resulting index buffer references vertices from the original vertex buffer.404* If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended.405*406* destination must contain enough space for the target index buffer, worst case is index_count elements (*not* target_index_count)!407* vertex_positions should have float3 position in the first 12 bytes of each vertex408* target_error represents the error relative to mesh extents that can be tolerated, e.g. 0.01 = 1% deformation; value range [0..1]409* options must be a bitmask composed of meshopt_SimplifyX options; 0 is a safe default410* result_error can be NULL; when it's not NULL, it will contain the resulting (relative) error after simplification411*/412MESHOPTIMIZER_API size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* result_error);413414/**415* Mesh simplifier with attribute metric416* The algorithm enhances meshopt_simplify by incorporating attribute values into the error metric used to prioritize simplification order; see meshopt_simplify documentation for details.417* 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.418*419* vertex_attributes should have attribute_count floats for each vertex420* attribute_weights should have attribute_count floats in total; the weights determine relative priority of attributes between each other and wrt position421* attribute_count must be <= 32422* 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 moved423*/424MESHOPTIMIZER_API 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);425426/**427* Experimental: Mesh simplifier (sloppy)428* Reduces the number of triangles in the mesh, sacrificing mesh appearance for simplification performance429* The algorithm doesn't preserve mesh topology but can stop short of the target goal based on target error.430* Returns the number of indices after simplification, with destination containing new index data431* The resulting index buffer references vertices from the original vertex buffer.432* If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended.433*434* destination must contain enough space for the target index buffer, worst case is index_count elements (*not* target_index_count)!435* vertex_positions should have float3 position in the first 12 bytes of each vertex436* target_error represents the error relative to mesh extents that can be tolerated, e.g. 0.01 = 1% deformation; value range [0..1]437* result_error can be NULL; when it's not NULL, it will contain the resulting (relative) error after simplification438*/439MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifySloppy(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* result_error);440441/**442* Experimental: Mesh simplifier (pruner)443* Reduces the number of triangles in the mesh by removing small isolated parts of the mesh444* Returns the number of indices after simplification, with destination containing new index data445* The resulting index buffer references vertices from the original vertex buffer.446* If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended.447*448* destination must contain enough space for the target index buffer, worst case is index_count elements449* vertex_positions should have float3 position in the first 12 bytes of each vertex450* target_error represents the error relative to mesh extents that can be tolerated, e.g. 0.01 = 1% deformation; value range [0..1]451*/452MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_simplifyPrune(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, float target_error);453454/**455* Point cloud simplifier456* Reduces the number of points in the cloud to reach the given target457* Returns the number of points after simplification, with destination containing new index data458* The resulting index buffer references vertices from the original vertex buffer.459* If the original vertex data isn't required, creating a compact vertex buffer using meshopt_optimizeVertexFetch is recommended.460*461* destination must contain enough space for the target index buffer (target_vertex_count elements)462* vertex_positions should have float3 position in the first 12 bytes of each vertex463* vertex_colors can be NULL; when it's not NULL, it should have float3 color in the first 12 bytes of each vertex464* color_weight determines relative priority of color wrt position; 1.0 is a safe default465*/466MESHOPTIMIZER_API 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);467468/**469* Returns the error scaling factor used by the simplifier to convert between absolute and relative extents470*471* Absolute error must be *divided* by the scaling factor before passing it to meshopt_simplify as target_error472* Relative error returned by meshopt_simplify via result_error must be *multiplied* by the scaling factor to get absolute error.473*/474MESHOPTIMIZER_API float meshopt_simplifyScale(const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);475476/**477* Mesh stripifier478* Converts a previously vertex cache optimized triangle list to triangle strip, stitching strips using restart index or degenerate triangles479* Returns the number of indices in the resulting strip, with destination containing new index data480* For maximum efficiency the index buffer being converted has to be optimized for vertex cache first.481* Using restart indices can result in ~10% smaller index buffers, but on some GPUs restart indices may result in decreased performance.482*483* destination must contain enough space for the target index buffer, worst case can be computed with meshopt_stripifyBound484* restart_index should be 0xffff or 0xffffffff depending on index size, or 0 to use degenerate triangles485*/486MESHOPTIMIZER_API size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int restart_index);487MESHOPTIMIZER_API size_t meshopt_stripifyBound(size_t index_count);488489/**490* Mesh unstripifier491* Converts a triangle strip to a triangle list492* Returns the number of indices in the resulting list, with destination containing new index data493*494* destination must contain enough space for the target index buffer, worst case can be computed with meshopt_unstripifyBound495*/496MESHOPTIMIZER_API size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count, unsigned int restart_index);497MESHOPTIMIZER_API size_t meshopt_unstripifyBound(size_t index_count);498499struct meshopt_VertexCacheStatistics500{501unsigned int vertices_transformed;502unsigned int warps_executed;503float acmr; /* transformed vertices / triangle count; best case 0.5, worst case 3.0, optimum depends on topology */504float atvr; /* transformed vertices / vertex count; best case 1.0, worst case 6.0, optimum is 1.0 (each vertex is transformed once) */505};506507/**508* Vertex transform cache analyzer509* Returns cache hit statistics using a simplified FIFO model510* Results may not match actual GPU performance511*/512MESHOPTIMIZER_API struct meshopt_VertexCacheStatistics meshopt_analyzeVertexCache(const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int cache_size, unsigned int warp_size, unsigned int primgroup_size);513514struct meshopt_VertexFetchStatistics515{516unsigned int bytes_fetched;517float overfetch; /* fetched bytes / vertex buffer size; best case 1.0 (each byte is fetched once) */518};519520/**521* Vertex fetch cache analyzer522* Returns cache hit statistics using a simplified direct mapped model523* Results may not match actual GPU performance524*/525MESHOPTIMIZER_API struct meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const unsigned int* indices, size_t index_count, size_t vertex_count, size_t vertex_size);526527struct meshopt_OverdrawStatistics528{529unsigned int pixels_covered;530unsigned int pixels_shaded;531float overdraw; /* shaded pixels / covered pixels; best case 1.0 */532};533534/**535* Overdraw analyzer536* Returns overdraw statistics using a software rasterizer537* Results may not match actual GPU performance538*539* vertex_positions should have float3 position in the first 12 bytes of each vertex540*/541MESHOPTIMIZER_API struct meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);542543struct meshopt_CoverageStatistics544{545float coverage[3];546float extent; /* viewport size in mesh coordinates */547};548549/**550* Experimental: Coverage analyzer551* Returns coverage statistics (ratio of viewport pixels covered from each axis) using a software rasterizer552*553* vertex_positions should have float3 position in the first 12 bytes of each vertex554*/555MESHOPTIMIZER_EXPERIMENTAL struct meshopt_CoverageStatistics meshopt_analyzeCoverage(const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);556557/**558* Meshlet is a small mesh cluster (subset) that consists of:559* - triangles, an 8-bit micro triangle (index) buffer, that for each triangle specifies three local vertices to use;560* - vertices, a 32-bit vertex indirection buffer, that for each local vertex specifies which mesh vertex to fetch vertex attributes from.561*562* For efficiency, meshlet triangles and vertices are packed into two large arrays; this structure contains offsets and counts to access the data.563*/564struct meshopt_Meshlet565{566/* offsets within meshlet_vertices and meshlet_triangles arrays with meshlet data */567unsigned int vertex_offset;568unsigned int triangle_offset;569570/* number of vertices and triangles used in the meshlet; data is stored in consecutive range defined by offset and count */571unsigned int vertex_count;572unsigned int triangle_count;573};574575/**576* Meshlet builder577* 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 buffer578* The resulting data can be used to render meshes using NVidia programmable mesh shading pipeline, or in other cluster-based renderers.579* When targeting mesh shading hardware, for maximum efficiency meshlets should be further optimized using meshopt_optimizeMeshlet.580* When using buildMeshlets, vertex positions need to be provided to minimize the size of the resulting clusters.581* When using buildMeshletsScan, for maximum efficiency the index buffer being converted has to be optimized for vertex cache first.582*583* meshlets must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound584* meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices585* meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3586* vertex_positions should have float3 position in the first 12 bytes of each vertex587* max_vertices and max_triangles must not exceed implementation limits (max_vertices <= 256, max_triangles <= 512; max_triangles must be divisible by 4)588* 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 efficiency589*/590MESHOPTIMIZER_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);591MESHOPTIMIZER_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);592MESHOPTIMIZER_API size_t meshopt_buildMeshletsBound(size_t index_count, size_t max_vertices, size_t max_triangles);593594/**595* Experimental: Meshlet builder with flexible cluster sizes596* Splits the mesh into a set of meshlets, similarly to meshopt_buildMeshlets, but allows to specify minimum and maximum number of triangles per meshlet.597* Clusters between min and max triangle counts are split when the cluster size would have exceeded the expected cluster size by more than split_factor.598* Additionally, allows to switch to axis aligned clusters by setting cone_weight to a negative value.599*600* meshlets must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound using min_triangles (not max!)601* meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices602* meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3603* vertex_positions should have float3 position in the first 12 bytes of each vertex604* max_vertices, min_triangles and max_triangles must not exceed implementation limits (max_vertices <= 256, max_triangles <= 512; min_triangles <= max_triangles; both min_triangles and max_triangles must be divisible by 4)605* 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; additionally, cone_weight can be set to a negative value to prioritize axis aligned clusters (for raytracing) instead606* split_factor should be set to a non-negative value; when greater than 0, clusters that have large bounds may be split unless they are under the min_triangles threshold607*/608MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshletsFlex(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 min_triangles, size_t max_triangles, float cone_weight, float split_factor);609610/**611* Experimental: Meshlet builder that produces clusters optimized for raytracing612* Splits the mesh into a set of meshlets, similarly to meshopt_buildMeshlets, but optimizes cluster subdivision for raytracing and allows to specify minimum and maximum number of triangles per meshlet.613*614* meshlets must contain enough space for all meshlets, worst case size can be computed with meshopt_buildMeshletsBound using min_triangles (not max!)615* meshlet_vertices must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_vertices616* meshlet_triangles must contain enough space for all meshlets, worst case size is equal to max_meshlets * max_triangles * 3617* vertex_positions should have float3 position in the first 12 bytes of each vertex618* max_vertices, min_triangles and max_triangles must not exceed implementation limits (max_vertices <= 256, max_triangles <= 512; min_triangles <= max_triangles; both min_triangles and max_triangles must be divisible by 4)619* fill_weight allows to prioritize clusters that are closer to maximum size at some cost to SAH quality; 0.5 is a safe default620*/621MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_buildMeshletsSpatial(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 min_triangles, size_t max_triangles, float fill_weight);622623/**624* Meshlet optimizer625* Reorders meshlet vertices and triangles to maximize locality to improve rasterizer throughput626*627* meshlet_triangles and meshlet_vertices must refer to meshlet triangle and vertex index data; when buildMeshlets* is used, these628* need to be computed from meshlet's vertex_offset and triangle_offset629* triangle_count and vertex_count must not exceed implementation limits (vertex_count <= 256, triangle_count <= 512)630*/631MESHOPTIMIZER_API void meshopt_optimizeMeshlet(unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, size_t triangle_count, size_t vertex_count);632633struct meshopt_Bounds634{635/* bounding sphere, useful for frustum and occlusion culling */636float center[3];637float radius;638639/* normal cone, useful for backface culling */640float cone_apex[3];641float cone_axis[3];642float cone_cutoff; /* = cos(angle/2) */643644/* normal cone axis and cutoff, stored in 8-bit SNORM format; decode using x/127.0 */645signed char cone_axis_s8[3];646signed char cone_cutoff_s8;647};648649/**650* Cluster bounds generator651* Creates bounding volumes that can be used for frustum, backface and occlusion culling.652*653* For backface culling with orthographic projection, use the following formula to reject backfacing clusters:654* dot(view, cone_axis) >= cone_cutoff655*656* For perspective projection, you can use the formula that needs cone apex in addition to axis & cutoff:657* dot(normalize(cone_apex - camera_position), cone_axis) >= cone_cutoff658*659* Alternatively, you can use the formula that doesn't need cone apex and uses bounding sphere instead:660* dot(normalize(center - camera_position), cone_axis) >= cone_cutoff + radius / length(center - camera_position)661* or an equivalent formula that doesn't have a singularity at center = camera_position:662* dot(center - camera_position, cone_axis) >= cone_cutoff * length(center - camera_position) + radius663*664* The formula that uses the apex is slightly more accurate but needs the apex; if you are already using bounding sphere665* to do frustum/occlusion culling, the formula that doesn't use the apex may be preferable (for derivation see666* Real-Time Rendering 4th Edition, section 19.3).667*668* vertex_positions should have float3 position in the first 12 bytes of each vertex669* vertex_count should specify the number of vertices in the entire mesh, not cluster or meshlet670* index_count/3 and triangle_count must not exceed implementation limits (<= 512)671*/672MESHOPTIMIZER_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);673MESHOPTIMIZER_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);674675/**676* Experimental: Sphere bounds generator677* Creates bounding sphere around a set of points or a set of spheres; returns the center and radius of the sphere, with other fields of the result set to 0.678*679* positions should have float3 position in the first 12 bytes of each element680* radii can be NULL; when it's not NULL, it should have a non-negative float radius in the first 4 bytes of each element681*/682MESHOPTIMIZER_EXPERIMENTAL struct meshopt_Bounds meshopt_computeSphereBounds(const float* positions, size_t count, size_t positions_stride, const float* radii, size_t radii_stride);683684/**685* Experimental: Cluster partitioner686* Partitions clusters into groups of similar size, prioritizing grouping clusters that share vertices or are close to each other.687*688* destination must contain enough space for the resulting partiotion data (cluster_count elements)689* destination[i] will contain the partition id for cluster i, with the total number of partitions returned by the function690* cluster_indices should have the vertex indices referenced by each cluster, stored sequentially691* cluster_index_counts should have the number of indices in each cluster; sum of all cluster_index_counts must be equal to total_index_count692* vertex_positions should have float3 position in the first 12 bytes of each vertex (or can be NULL if not used)693* target_partition_size is a target size for each partition, in clusters; the resulting partitions may be smaller or larger694*/695MESHOPTIMIZER_EXPERIMENTAL size_t meshopt_partitionClusters(unsigned int* destination, const unsigned int* cluster_indices, size_t total_index_count, const unsigned int* cluster_index_counts, size_t cluster_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_partition_size);696697/**698* Spatial sorter699* Generates a remap table that can be used to reorder points for spatial locality.700* Resulting remap table maps old vertices to new vertices and can be used in meshopt_remapVertexBuffer.701*702* destination must contain enough space for the resulting remap table (vertex_count elements)703* vertex_positions should have float3 position in the first 12 bytes of each vertex704*/705MESHOPTIMIZER_API void meshopt_spatialSortRemap(unsigned int* destination, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);706707/**708* Spatial sorter709* Reorders triangles for spatial locality, and generates a new index buffer. The resulting index buffer can be used with other functions like optimizeVertexCache.710*711* destination must contain enough space for the resulting index buffer (index_count elements)712* vertex_positions should have float3 position in the first 12 bytes of each vertex713*/714MESHOPTIMIZER_API void meshopt_spatialSortTriangles(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);715716/**717* Experimental: Spatial clusterizer718* Reorders points into clusters optimized for spatial locality, and generates a new index buffer.719* Ensures the output can be split into cluster_size chunks where each chunk has good positional locality. Only the last chunk will be smaller than cluster_size.720*721* destination must contain enough space for the resulting index buffer (vertex_count elements)722* vertex_positions should have float3 position in the first 12 bytes of each vertex723*/724MESHOPTIMIZER_EXPERIMENTAL void meshopt_spatialClusterPoints(unsigned int* destination, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t cluster_size);725726/**727* Quantize a float into half-precision (as defined by IEEE-754 fp16) floating point value728* Generates +-inf for overflow, preserves NaN, flushes denormals to zero, rounds to nearest729* Representable magnitude range: [6e-5; 65504]730* Maximum relative reconstruction error: 5e-4731*/732MESHOPTIMIZER_API unsigned short meshopt_quantizeHalf(float v);733734/**735* Quantize a float into a floating point value with a limited number of significant mantissa bits, preserving the IEEE-754 fp32 binary representation736* Generates +-inf for overflow, preserves NaN, flushes denormals to zero, rounds to nearest737* Assumes N is in a valid mantissa precision range, which is 1..23738*/739MESHOPTIMIZER_API float meshopt_quantizeFloat(float v, int N);740741/**742* Reverse quantization of a half-precision (as defined by IEEE-754 fp16) floating point value743* Preserves Inf/NaN, flushes denormals to zero744*/745MESHOPTIMIZER_API float meshopt_dequantizeHalf(unsigned short h);746747/**748* Set allocation callbacks749* These callbacks will be used instead of the default operator new/operator delete for all temporary allocations in the library.750* Note that all algorithms only allocate memory for temporary use.751* allocate/deallocate are always called in a stack-like order - last pointer to be allocated is deallocated first.752*/753MESHOPTIMIZER_API void meshopt_setAllocator(void* (MESHOPTIMIZER_ALLOC_CALLCONV* allocate)(size_t), void (MESHOPTIMIZER_ALLOC_CALLCONV* deallocate)(void*));754755#ifdef __cplusplus756} /* extern "C" */757#endif758759/* Quantization into fixed point normalized formats; these are only available as inline C++ functions */760#ifdef __cplusplus761/**762* Quantize a float in [0..1] range into an N-bit fixed point unorm value763* Assumes reconstruction function (q / (2^N-1)), which is the case for fixed-function normalized fixed point conversion764* Maximum reconstruction error: 1/2^(N+1)765*/766inline int meshopt_quantizeUnorm(float v, int N);767768/**769* Quantize a float in [-1..1] range into an N-bit fixed point snorm value770* Assumes reconstruction function (q / (2^(N-1)-1)), which is the case for fixed-function normalized fixed point conversion (except early OpenGL versions)771* Maximum reconstruction error: 1/2^N772*/773inline int meshopt_quantizeSnorm(float v, int N);774#endif775776/**777* C++ template interface778*779* These functions mirror the C interface the library provides, providing template-based overloads so that780* the caller can use an arbitrary type for the index data, both for input and output.781* When the supplied type is the same size as that of unsigned int, the wrappers are zero-cost; when it's not,782* the wrappers end up allocating memory and copying index data to convert from one type to another.783*/784#if defined(__cplusplus) && !defined(MESHOPTIMIZER_NO_WRAPPERS)785template <typename T>786inline size_t meshopt_generateVertexRemap(unsigned int* destination, const T* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size);787template <typename T>788inline size_t meshopt_generateVertexRemapMulti(unsigned int* destination, const T* indices, size_t index_count, size_t vertex_count, const meshopt_Stream* streams, size_t stream_count);789template <typename F>790inline size_t meshopt_generateVertexRemapCustom(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, F callback);791template <typename T, typename F>792inline size_t meshopt_generateVertexRemapCustom(unsigned int* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, F callback);793template <typename T>794inline void meshopt_remapIndexBuffer(T* destination, const T* indices, size_t index_count, const unsigned int* remap);795template <typename T>796inline void meshopt_generateShadowIndexBuffer(T* destination, const T* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size, size_t vertex_stride);797template <typename T>798inline void meshopt_generateShadowIndexBufferMulti(T* destination, const T* indices, size_t index_count, size_t vertex_count, const meshopt_Stream* streams, size_t stream_count);799template <typename T>800inline void meshopt_generateAdjacencyIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);801template <typename T>802inline 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);803template <typename T>804inline size_t meshopt_generateProvokingIndexBuffer(T* destination, unsigned int* reorder, const T* indices, size_t index_count, size_t vertex_count);805template <typename T>806inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count);807template <typename T>808inline void meshopt_optimizeVertexCacheStrip(T* destination, const T* indices, size_t index_count, size_t vertex_count);809template <typename T>810inline void meshopt_optimizeVertexCacheFifo(T* destination, const T* indices, size_t index_count, size_t vertex_count, unsigned int cache_size);811template <typename T>812inline void meshopt_optimizeOverdraw(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, float threshold);813template <typename T>814inline size_t meshopt_optimizeVertexFetchRemap(unsigned int* destination, const T* indices, size_t index_count, size_t vertex_count);815template <typename T>816inline size_t meshopt_optimizeVertexFetch(void* destination, T* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size);817template <typename T>818inline size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, const T* indices, size_t index_count);819template <typename T>820inline int meshopt_decodeIndexBuffer(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size);821template <typename T>822inline size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const T* indices, size_t index_count);823template <typename T>824inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size);825inline size_t meshopt_encodeVertexBufferLevel(unsigned char* buffer, size_t buffer_size, const void* vertices, size_t vertex_count, size_t vertex_size, int level);826template <typename T>827inline 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);828template <typename T>829inline 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);830template <typename T>831inline 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);832template <typename T>833inline size_t meshopt_simplifyPrune(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, float target_error);834template <typename T>835inline size_t meshopt_stripify(T* destination, const T* indices, size_t index_count, size_t vertex_count, T restart_index);836template <typename T>837inline size_t meshopt_unstripify(T* destination, const T* indices, size_t index_count, T restart_index);838template <typename T>839inline meshopt_VertexCacheStatistics meshopt_analyzeVertexCache(const T* indices, size_t index_count, size_t vertex_count, unsigned int cache_size, unsigned int warp_size, unsigned int buffer_size);840template <typename T>841inline meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const T* indices, size_t index_count, size_t vertex_count, size_t vertex_size);842template <typename T>843inline meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);844template <typename T>845inline meshopt_CoverageStatistics meshopt_analyzeCoverage(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);846template <typename T>847inline size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* 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);848template <typename T>849inline size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles);850template <typename T>851inline size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor);852template <typename T>853inline size_t meshopt_buildMeshletsSpatial(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float fill_weight);854template <typename T>855inline meshopt_Bounds meshopt_computeClusterBounds(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);856template <typename T>857inline size_t meshopt_partitionClusters(unsigned int* destination, const T* cluster_indices, size_t total_index_count, const unsigned int* cluster_index_counts, size_t cluster_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_partition_size);858template <typename T>859inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride);860#endif861862/* Inline implementation */863#ifdef __cplusplus864inline int meshopt_quantizeUnorm(float v, int N)865{866const float scale = float((1 << N) - 1);867868v = (v >= 0) ? v : 0;869v = (v <= 1) ? v : 1;870871return int(v * scale + 0.5f);872}873874inline int meshopt_quantizeSnorm(float v, int N)875{876const float scale = float((1 << (N - 1)) - 1);877878float round = (v >= 0 ? 0.5f : -0.5f);879880v = (v >= -1) ? v : -1;881v = (v <= +1) ? v : +1;882883return int(v * scale + round);884}885#endif886887/* Internal implementation helpers */888#ifdef __cplusplus889class meshopt_Allocator890{891public:892template <typename T>893struct StorageT894{895static void* (MESHOPTIMIZER_ALLOC_CALLCONV* allocate)(size_t);896static void (MESHOPTIMIZER_ALLOC_CALLCONV* deallocate)(void*);897};898899typedef StorageT<void> Storage;900901meshopt_Allocator()902: blocks()903, count(0)904{905}906907~meshopt_Allocator()908{909for (size_t i = count; i > 0; --i)910Storage::deallocate(blocks[i - 1]);911}912913template <typename T>914T* allocate(size_t size)915{916assert(count < sizeof(blocks) / sizeof(blocks[0]));917T* result = static_cast<T*>(Storage::allocate(size > size_t(-1) / sizeof(T) ? size_t(-1) : size * sizeof(T)));918blocks[count++] = result;919return result;920}921922void deallocate(void* ptr)923{924assert(count > 0 && blocks[count - 1] == ptr);925Storage::deallocate(ptr);926count--;927}928929private:930void* blocks[24];931size_t count;932};933934// This makes sure that allocate/deallocate are lazily generated in translation units that need them and are deduplicated by the linker935template <typename T>936void* (MESHOPTIMIZER_ALLOC_CALLCONV* meshopt_Allocator::StorageT<T>::allocate)(size_t) = operator new;937template <typename T>938void (MESHOPTIMIZER_ALLOC_CALLCONV* meshopt_Allocator::StorageT<T>::deallocate)(void*) = operator delete;939#endif940941/* Inline implementation for C++ templated wrappers */942#if defined(__cplusplus) && !defined(MESHOPTIMIZER_NO_WRAPPERS)943template <typename T, bool ZeroCopy = sizeof(T) == sizeof(unsigned int)>944struct meshopt_IndexAdapter;945946template <typename T>947struct meshopt_IndexAdapter<T, false>948{949T* result;950unsigned int* data;951size_t count;952953meshopt_IndexAdapter(T* result_, const T* input, size_t count_)954: result(result_)955, data(NULL)956, count(count_)957{958size_t size = count > size_t(-1) / sizeof(unsigned int) ? size_t(-1) : count * sizeof(unsigned int);959960data = static_cast<unsigned int*>(meshopt_Allocator::Storage::allocate(size));961962if (input)963{964for (size_t i = 0; i < count; ++i)965data[i] = input[i];966}967}968969~meshopt_IndexAdapter()970{971if (result)972{973for (size_t i = 0; i < count; ++i)974result[i] = T(data[i]);975}976977meshopt_Allocator::Storage::deallocate(data);978}979};980981template <typename T>982struct meshopt_IndexAdapter<T, true>983{984unsigned int* data;985986meshopt_IndexAdapter(T* result, const T* input, size_t)987: data(reinterpret_cast<unsigned int*>(result ? result : const_cast<T*>(input)))988{989}990};991992template <typename T>993inline size_t meshopt_generateVertexRemap(unsigned int* destination, const T* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size)994{995meshopt_IndexAdapter<T> in(NULL, indices, indices ? index_count : 0);996997return meshopt_generateVertexRemap(destination, indices ? in.data : NULL, index_count, vertices, vertex_count, vertex_size);998}9991000template <typename T>1001inline size_t meshopt_generateVertexRemapMulti(unsigned int* destination, const T* indices, size_t index_count, size_t vertex_count, const meshopt_Stream* streams, size_t stream_count)1002{1003meshopt_IndexAdapter<T> in(NULL, indices, indices ? index_count : 0);10041005return meshopt_generateVertexRemapMulti(destination, indices ? in.data : NULL, index_count, vertex_count, streams, stream_count);1006}10071008template <typename F>1009inline size_t meshopt_generateVertexRemapCustom(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, F callback)1010{1011struct Call1012{1013static int compare(void* context, unsigned int lhs, unsigned int rhs) { return (*static_cast<F*>(context))(lhs, rhs) ? 1 : 0; }1014};10151016return meshopt_generateVertexRemapCustom(destination, indices, index_count, vertex_positions, vertex_count, vertex_positions_stride, &Call::compare, &callback);1017}10181019template <typename T, typename F>1020inline size_t meshopt_generateVertexRemapCustom(unsigned int* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, F callback)1021{1022struct Call1023{1024static int compare(void* context, unsigned int lhs, unsigned int rhs) { return (*static_cast<F*>(context))(lhs, rhs) ? 1 : 0; }1025};10261027meshopt_IndexAdapter<T> in(NULL, indices, indices ? index_count : 0);10281029return meshopt_generateVertexRemapCustom(destination, indices ? in.data : NULL, index_count, vertex_positions, vertex_count, vertex_positions_stride, &Call::compare, &callback);1030}10311032template <typename T>1033inline void meshopt_remapIndexBuffer(T* destination, const T* indices, size_t index_count, const unsigned int* remap)1034{1035meshopt_IndexAdapter<T> in(NULL, indices, indices ? index_count : 0);1036meshopt_IndexAdapter<T> out(destination, 0, index_count);10371038meshopt_remapIndexBuffer(out.data, indices ? in.data : NULL, index_count, remap);1039}10401041template <typename T>1042inline void meshopt_generateShadowIndexBuffer(T* destination, const T* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size, size_t vertex_stride)1043{1044meshopt_IndexAdapter<T> in(NULL, indices, index_count);1045meshopt_IndexAdapter<T> out(destination, NULL, index_count);10461047meshopt_generateShadowIndexBuffer(out.data, in.data, index_count, vertices, vertex_count, vertex_size, vertex_stride);1048}10491050template <typename T>1051inline void meshopt_generateShadowIndexBufferMulti(T* destination, const T* indices, size_t index_count, size_t vertex_count, const meshopt_Stream* streams, size_t stream_count)1052{1053meshopt_IndexAdapter<T> in(NULL, indices, index_count);1054meshopt_IndexAdapter<T> out(destination, NULL, index_count);10551056meshopt_generateShadowIndexBufferMulti(out.data, in.data, index_count, vertex_count, streams, stream_count);1057}10581059template <typename T>1060inline void meshopt_generateAdjacencyIndexBuffer(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)1061{1062meshopt_IndexAdapter<T> in(NULL, indices, index_count);1063meshopt_IndexAdapter<T> out(destination, NULL, index_count * 2);10641065meshopt_generateAdjacencyIndexBuffer(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);1066}10671068template <typename T>1069inline 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)1070{1071meshopt_IndexAdapter<T> in(NULL, indices, index_count);1072meshopt_IndexAdapter<T> out(destination, NULL, index_count * 4);10731074meshopt_generateTessellationIndexBuffer(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);1075}10761077template <typename T>1078inline size_t meshopt_generateProvokingIndexBuffer(T* destination, unsigned int* reorder, const T* indices, size_t index_count, size_t vertex_count)1079{1080meshopt_IndexAdapter<T> in(NULL, indices, index_count);1081meshopt_IndexAdapter<T> out(destination, NULL, index_count);10821083size_t bound = vertex_count + (index_count / 3);1084assert(size_t(T(bound - 1)) == bound - 1); // bound - 1 must fit in T1085(void)bound;10861087return meshopt_generateProvokingIndexBuffer(out.data, reorder, in.data, index_count, vertex_count);1088}10891090template <typename T>1091inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count)1092{1093meshopt_IndexAdapter<T> in(NULL, indices, index_count);1094meshopt_IndexAdapter<T> out(destination, NULL, index_count);10951096meshopt_optimizeVertexCache(out.data, in.data, index_count, vertex_count);1097}10981099template <typename T>1100inline void meshopt_optimizeVertexCacheStrip(T* destination, const T* indices, size_t index_count, size_t vertex_count)1101{1102meshopt_IndexAdapter<T> in(NULL, indices, index_count);1103meshopt_IndexAdapter<T> out(destination, NULL, index_count);11041105meshopt_optimizeVertexCacheStrip(out.data, in.data, index_count, vertex_count);1106}11071108template <typename T>1109inline void meshopt_optimizeVertexCacheFifo(T* destination, const T* indices, size_t index_count, size_t vertex_count, unsigned int cache_size)1110{1111meshopt_IndexAdapter<T> in(NULL, indices, index_count);1112meshopt_IndexAdapter<T> out(destination, NULL, index_count);11131114meshopt_optimizeVertexCacheFifo(out.data, in.data, index_count, vertex_count, cache_size);1115}11161117template <typename T>1118inline void meshopt_optimizeOverdraw(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, float threshold)1119{1120meshopt_IndexAdapter<T> in(NULL, indices, index_count);1121meshopt_IndexAdapter<T> out(destination, NULL, index_count);11221123meshopt_optimizeOverdraw(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, threshold);1124}11251126template <typename T>1127inline size_t meshopt_optimizeVertexFetchRemap(unsigned int* destination, const T* indices, size_t index_count, size_t vertex_count)1128{1129meshopt_IndexAdapter<T> in(NULL, indices, index_count);11301131return meshopt_optimizeVertexFetchRemap(destination, in.data, index_count, vertex_count);1132}11331134template <typename T>1135inline size_t meshopt_optimizeVertexFetch(void* destination, T* indices, size_t index_count, const void* vertices, size_t vertex_count, size_t vertex_size)1136{1137meshopt_IndexAdapter<T> inout(indices, indices, index_count);11381139return meshopt_optimizeVertexFetch(destination, inout.data, index_count, vertices, vertex_count, vertex_size);1140}11411142template <typename T>1143inline size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, const T* indices, size_t index_count)1144{1145meshopt_IndexAdapter<T> in(NULL, indices, index_count);11461147return meshopt_encodeIndexBuffer(buffer, buffer_size, in.data, index_count);1148}11491150template <typename T>1151inline int meshopt_decodeIndexBuffer(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size)1152{1153char index_size_valid[sizeof(T) == 2 || sizeof(T) == 4 ? 1 : -1];1154(void)index_size_valid;11551156return meshopt_decodeIndexBuffer(destination, index_count, sizeof(T), buffer, buffer_size);1157}11581159template <typename T>1160inline size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const T* indices, size_t index_count)1161{1162meshopt_IndexAdapter<T> in(NULL, indices, index_count);11631164return meshopt_encodeIndexSequence(buffer, buffer_size, in.data, index_count);1165}11661167template <typename T>1168inline int meshopt_decodeIndexSequence(T* destination, size_t index_count, const unsigned char* buffer, size_t buffer_size)1169{1170char index_size_valid[sizeof(T) == 2 || sizeof(T) == 4 ? 1 : -1];1171(void)index_size_valid;11721173return meshopt_decodeIndexSequence(destination, index_count, sizeof(T), buffer, buffer_size);1174}11751176inline size_t meshopt_encodeVertexBufferLevel(unsigned char* buffer, size_t buffer_size, const void* vertices, size_t vertex_count, size_t vertex_size, int level)1177{1178return meshopt_encodeVertexBufferLevel(buffer, buffer_size, vertices, vertex_count, vertex_size, level, -1);1179}11801181template <typename T>1182inline size_t meshopt_simplify(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float* result_error)1183{1184meshopt_IndexAdapter<T> in(NULL, indices, index_count);1185meshopt_IndexAdapter<T> out(destination, NULL, index_count);11861187return meshopt_simplify(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, target_index_count, target_error, options, result_error);1188}11891190template <typename T>1191inline 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)1192{1193meshopt_IndexAdapter<T> in(NULL, indices, index_count);1194meshopt_IndexAdapter<T> out(destination, NULL, index_count);11951196return 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);1197}11981199template <typename T>1200inline 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)1201{1202meshopt_IndexAdapter<T> in(NULL, indices, index_count);1203meshopt_IndexAdapter<T> out(destination, NULL, index_count);12041205return meshopt_simplifySloppy(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, target_index_count, target_error, result_error);1206}12071208template <typename T>1209inline size_t meshopt_simplifyPrune(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, float target_error)1210{1211meshopt_IndexAdapter<T> in(NULL, indices, index_count);1212meshopt_IndexAdapter<T> out(destination, NULL, index_count);12131214return meshopt_simplifyPrune(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, target_error);1215}12161217template <typename T>1218inline size_t meshopt_stripify(T* destination, const T* indices, size_t index_count, size_t vertex_count, T restart_index)1219{1220meshopt_IndexAdapter<T> in(NULL, indices, index_count);1221meshopt_IndexAdapter<T> out(destination, NULL, (index_count / 3) * 5);12221223return meshopt_stripify(out.data, in.data, index_count, vertex_count, unsigned(restart_index));1224}12251226template <typename T>1227inline size_t meshopt_unstripify(T* destination, const T* indices, size_t index_count, T restart_index)1228{1229meshopt_IndexAdapter<T> in(NULL, indices, index_count);1230meshopt_IndexAdapter<T> out(destination, NULL, (index_count - 2) * 3);12311232return meshopt_unstripify(out.data, in.data, index_count, unsigned(restart_index));1233}12341235template <typename T>1236inline meshopt_VertexCacheStatistics meshopt_analyzeVertexCache(const T* indices, size_t index_count, size_t vertex_count, unsigned int cache_size, unsigned int warp_size, unsigned int buffer_size)1237{1238meshopt_IndexAdapter<T> in(NULL, indices, index_count);12391240return meshopt_analyzeVertexCache(in.data, index_count, vertex_count, cache_size, warp_size, buffer_size);1241}12421243template <typename T>1244inline meshopt_VertexFetchStatistics meshopt_analyzeVertexFetch(const T* indices, size_t index_count, size_t vertex_count, size_t vertex_size)1245{1246meshopt_IndexAdapter<T> in(NULL, indices, index_count);12471248return meshopt_analyzeVertexFetch(in.data, index_count, vertex_count, vertex_size);1249}12501251template <typename T>1252inline meshopt_OverdrawStatistics meshopt_analyzeOverdraw(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)1253{1254meshopt_IndexAdapter<T> in(NULL, indices, index_count);12551256return meshopt_analyzeOverdraw(in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);1257}12581259template <typename T>1260inline meshopt_CoverageStatistics meshopt_analyzeCoverage(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)1261{1262meshopt_IndexAdapter<T> in(NULL, indices, index_count);12631264return meshopt_analyzeCoverage(in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);1265}12661267template <typename T>1268inline size_t meshopt_buildMeshlets(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* 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)1269{1270meshopt_IndexAdapter<T> in(NULL, indices, index_count);12711272return meshopt_buildMeshlets(meshlets, meshlet_vertices, meshlet_triangles, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, max_triangles, cone_weight);1273}12741275template <typename T>1276inline size_t meshopt_buildMeshletsScan(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, size_t vertex_count, size_t max_vertices, size_t max_triangles)1277{1278meshopt_IndexAdapter<T> in(NULL, indices, index_count);12791280return meshopt_buildMeshletsScan(meshlets, meshlet_vertices, meshlet_triangles, in.data, index_count, vertex_count, max_vertices, max_triangles);1281}12821283template <typename T>1284inline size_t meshopt_buildMeshletsFlex(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float cone_weight, float split_factor)1285{1286meshopt_IndexAdapter<T> in(NULL, indices, index_count);12871288return meshopt_buildMeshletsFlex(meshlets, meshlet_vertices, meshlet_triangles, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, min_triangles, max_triangles, cone_weight, split_factor);1289}12901291template <typename T>1292inline size_t meshopt_buildMeshletsSpatial(meshopt_Meshlet* meshlets, unsigned int* meshlet_vertices, unsigned char* meshlet_triangles, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t max_vertices, size_t min_triangles, size_t max_triangles, float fill_weight)1293{1294meshopt_IndexAdapter<T> in(NULL, indices, index_count);12951296return meshopt_buildMeshletsSpatial(meshlets, meshlet_vertices, meshlet_triangles, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride, max_vertices, min_triangles, max_triangles, fill_weight);1297}12981299template <typename T>1300inline meshopt_Bounds meshopt_computeClusterBounds(const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)1301{1302meshopt_IndexAdapter<T> in(NULL, indices, index_count);13031304return meshopt_computeClusterBounds(in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);1305}13061307template <typename T>1308inline size_t meshopt_partitionClusters(unsigned int* destination, const T* cluster_indices, size_t total_index_count, const unsigned int* cluster_index_counts, size_t cluster_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_partition_size)1309{1310meshopt_IndexAdapter<T> in(NULL, cluster_indices, total_index_count);13111312return meshopt_partitionClusters(destination, in.data, total_index_count, cluster_index_counts, cluster_count, vertex_positions, vertex_count, vertex_positions_stride, target_partition_size);1313}13141315template <typename T>1316inline void meshopt_spatialSortTriangles(T* destination, const T* indices, size_t index_count, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride)1317{1318meshopt_IndexAdapter<T> in(NULL, indices, index_count);1319meshopt_IndexAdapter<T> out(destination, NULL, index_count);13201321meshopt_spatialSortTriangles(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);1322}1323#endif13241325/**1326* Copyright (c) 2016-2025 Arseny Kapoulkine1327*1328* Permission is hereby granted, free of charge, to any person1329* obtaining a copy of this software and associated documentation1330* files (the "Software"), to deal in the Software without1331* restriction, including without limitation the rights to use,1332* copy, modify, merge, publish, distribute, sublicense, and/or sell1333* copies of the Software, and to permit persons to whom the1334* Software is furnished to do so, subject to the following1335* conditions:1336*1337* The above copyright notice and this permission notice shall be1338* included in all copies or substantial portions of the Software.1339*1340* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,1341* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES1342* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND1343* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT1344* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,1345* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING1346* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR1347* OTHER DEALINGS IN THE SOFTWARE.1348*/134913501351