Path: blob/master/thirdparty/meshoptimizer/indexgenerator.cpp
9896 views
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details1#include "meshoptimizer.h"23#include <assert.h>4#include <string.h>56// This work is based on:7// Matthias Teschner, Bruno Heidelberger, Matthias Mueller, Danat Pomeranets, Markus Gross. Optimized Spatial Hashing for Collision Detection of Deformable Objects. 20038// John McDonald, Mark Kilgard. Crack-Free Point-Normal Triangles using Adjacent Edge Normals. 20109// John Hable. Variable Rate Shading with Visibility Buffer Rendering. 202410namespace meshopt11{1213static unsigned int hashUpdate4(unsigned int h, const unsigned char* key, size_t len)14{15// MurmurHash216const unsigned int m = 0x5bd1e995;17const int r = 24;1819while (len >= 4)20{21unsigned int k = *reinterpret_cast<const unsigned int*>(key);2223k *= m;24k ^= k >> r;25k *= m;2627h *= m;28h ^= k;2930key += 4;31len -= 4;32}3334return h;35}3637struct VertexHasher38{39const unsigned char* vertices;40size_t vertex_size;41size_t vertex_stride;4243size_t hash(unsigned int index) const44{45return hashUpdate4(0, vertices + index * vertex_stride, vertex_size);46}4748bool equal(unsigned int lhs, unsigned int rhs) const49{50return memcmp(vertices + lhs * vertex_stride, vertices + rhs * vertex_stride, vertex_size) == 0;51}52};5354struct VertexStreamHasher55{56const meshopt_Stream* streams;57size_t stream_count;5859size_t hash(unsigned int index) const60{61unsigned int h = 0;6263for (size_t i = 0; i < stream_count; ++i)64{65const meshopt_Stream& s = streams[i];66const unsigned char* data = static_cast<const unsigned char*>(s.data);6768h = hashUpdate4(h, data + index * s.stride, s.size);69}7071return h;72}7374bool equal(unsigned int lhs, unsigned int rhs) const75{76for (size_t i = 0; i < stream_count; ++i)77{78const meshopt_Stream& s = streams[i];79const unsigned char* data = static_cast<const unsigned char*>(s.data);8081if (memcmp(data + lhs * s.stride, data + rhs * s.stride, s.size) != 0)82return false;83}8485return true;86}87};8889struct VertexCustomHasher90{91const float* vertex_positions;92size_t vertex_stride_float;9394int (*callback)(void*, unsigned int, unsigned int);95void* context;9697size_t hash(unsigned int index) const98{99const unsigned int* key = reinterpret_cast<const unsigned int*>(vertex_positions + index * vertex_stride_float);100101unsigned int x = key[0], y = key[1], z = key[2];102103// replace negative zero with zero104x = (x == 0x80000000) ? 0 : x;105y = (y == 0x80000000) ? 0 : y;106z = (z == 0x80000000) ? 0 : z;107108// scramble bits to make sure that integer coordinates have entropy in lower bits109x ^= x >> 17;110y ^= y >> 17;111z ^= z >> 17;112113// Optimized Spatial Hashing for Collision Detection of Deformable Objects114return (x * 73856093) ^ (y * 19349663) ^ (z * 83492791);115}116117bool equal(unsigned int lhs, unsigned int rhs) const118{119const float* lp = vertex_positions + lhs * vertex_stride_float;120const float* rp = vertex_positions + rhs * vertex_stride_float;121122if (lp[0] != rp[0] || lp[1] != rp[1] || lp[2] != rp[2])123return false;124125return callback ? callback(context, lhs, rhs) : true;126}127};128129struct EdgeHasher130{131const unsigned int* remap;132133size_t hash(unsigned long long edge) const134{135unsigned int e0 = unsigned(edge >> 32);136unsigned int e1 = unsigned(edge);137138unsigned int h1 = remap[e0];139unsigned int h2 = remap[e1];140141const unsigned int m = 0x5bd1e995;142143// MurmurHash64B finalizer144h1 ^= h2 >> 18;145h1 *= m;146h2 ^= h1 >> 22;147h2 *= m;148h1 ^= h2 >> 17;149h1 *= m;150h2 ^= h1 >> 19;151h2 *= m;152153return h2;154}155156bool equal(unsigned long long lhs, unsigned long long rhs) const157{158unsigned int l0 = unsigned(lhs >> 32);159unsigned int l1 = unsigned(lhs);160161unsigned int r0 = unsigned(rhs >> 32);162unsigned int r1 = unsigned(rhs);163164return remap[l0] == remap[r0] && remap[l1] == remap[r1];165}166};167168static size_t hashBuckets(size_t count)169{170size_t buckets = 1;171while (buckets < count + count / 4)172buckets *= 2;173174return buckets;175}176177template <typename T, typename Hash>178static T* hashLookup(T* table, size_t buckets, const Hash& hash, const T& key, const T& empty)179{180assert(buckets > 0);181assert((buckets & (buckets - 1)) == 0);182183size_t hashmod = buckets - 1;184size_t bucket = hash.hash(key) & hashmod;185186for (size_t probe = 0; probe <= hashmod; ++probe)187{188T& item = table[bucket];189190if (item == empty)191return &item;192193if (hash.equal(item, key))194return &item;195196// hash collision, quadratic probing197bucket = (bucket + probe + 1) & hashmod;198}199200assert(false && "Hash table is full"); // unreachable201return NULL;202}203204static void buildPositionRemap(unsigned int* remap, const float* vertex_positions, size_t vertex_count, size_t vertex_positions_stride, meshopt_Allocator& allocator)205{206VertexHasher vertex_hasher = {reinterpret_cast<const unsigned char*>(vertex_positions), 3 * sizeof(float), vertex_positions_stride};207208size_t vertex_table_size = hashBuckets(vertex_count);209unsigned int* vertex_table = allocator.allocate<unsigned int>(vertex_table_size);210memset(vertex_table, -1, vertex_table_size * sizeof(unsigned int));211212for (size_t i = 0; i < vertex_count; ++i)213{214unsigned int index = unsigned(i);215unsigned int* entry = hashLookup(vertex_table, vertex_table_size, vertex_hasher, index, ~0u);216217if (*entry == ~0u)218*entry = index;219220remap[index] = *entry;221}222223allocator.deallocate(vertex_table);224}225226template <typename Hash>227static size_t generateVertexRemap(unsigned int* remap, const unsigned int* indices, size_t index_count, size_t vertex_count, const Hash& hash, meshopt_Allocator& allocator)228{229memset(remap, -1, vertex_count * sizeof(unsigned int));230231size_t table_size = hashBuckets(vertex_count);232unsigned int* table = allocator.allocate<unsigned int>(table_size);233memset(table, -1, table_size * sizeof(unsigned int));234235unsigned int next_vertex = 0;236237for (size_t i = 0; i < index_count; ++i)238{239unsigned int index = indices ? indices[i] : unsigned(i);240assert(index < vertex_count);241242if (remap[index] != ~0u)243continue;244245unsigned int* entry = hashLookup(table, table_size, hash, index, ~0u);246247if (*entry == ~0u)248{249*entry = index;250remap[index] = next_vertex++;251}252else253{254assert(remap[*entry] != ~0u);255remap[index] = remap[*entry];256}257}258259assert(next_vertex <= vertex_count);260return next_vertex;261}262263template <size_t BlockSize>264static void remapVertices(void* destination, const void* vertices, size_t vertex_count, size_t vertex_size, const unsigned int* remap)265{266size_t block_size = BlockSize == 0 ? vertex_size : BlockSize;267assert(block_size == vertex_size);268269for (size_t i = 0; i < vertex_count; ++i)270if (remap[i] != ~0u)271{272assert(remap[i] < vertex_count);273memcpy(static_cast<unsigned char*>(destination) + remap[i] * block_size, static_cast<const unsigned char*>(vertices) + i * block_size, block_size);274}275}276277template <typename Hash>278static void generateShadowBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, const Hash& hash, meshopt_Allocator& allocator)279{280unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);281memset(remap, -1, vertex_count * sizeof(unsigned int));282283size_t table_size = hashBuckets(vertex_count);284unsigned int* table = allocator.allocate<unsigned int>(table_size);285memset(table, -1, table_size * sizeof(unsigned int));286287for (size_t i = 0; i < index_count; ++i)288{289unsigned int index = indices[i];290assert(index < vertex_count);291292if (remap[index] == ~0u)293{294unsigned int* entry = hashLookup(table, table_size, hash, index, ~0u);295296if (*entry == ~0u)297*entry = index;298299remap[index] = *entry;300}301302destination[i] = remap[index];303}304}305306} // namespace meshopt307308size_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)309{310using namespace meshopt;311312assert(indices || index_count == vertex_count);313assert(!indices || index_count % 3 == 0);314assert(vertex_size > 0 && vertex_size <= 256);315316meshopt_Allocator allocator;317VertexHasher hasher = {static_cast<const unsigned char*>(vertices), vertex_size, vertex_size};318319return generateVertexRemap(destination, indices, index_count, vertex_count, hasher, allocator);320}321322size_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)323{324using namespace meshopt;325326assert(indices || index_count == vertex_count);327assert(index_count % 3 == 0);328assert(stream_count > 0 && stream_count <= 16);329330for (size_t i = 0; i < stream_count; ++i)331{332assert(streams[i].size > 0 && streams[i].size <= 256);333assert(streams[i].size <= streams[i].stride);334}335336meshopt_Allocator allocator;337VertexStreamHasher hasher = {streams, stream_count};338339return generateVertexRemap(destination, indices, index_count, vertex_count, hasher, allocator);340}341342size_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)343{344using namespace meshopt;345346assert(indices || index_count == vertex_count);347assert(!indices || index_count % 3 == 0);348assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);349assert(vertex_positions_stride % sizeof(float) == 0);350351meshopt_Allocator allocator;352VertexCustomHasher hasher = {vertex_positions, vertex_positions_stride / sizeof(float), callback, context};353354return generateVertexRemap(destination, indices, index_count, vertex_count, hasher, allocator);355}356357void meshopt_remapVertexBuffer(void* destination, const void* vertices, size_t vertex_count, size_t vertex_size, const unsigned int* remap)358{359using namespace meshopt;360361assert(vertex_size > 0 && vertex_size <= 256);362363meshopt_Allocator allocator;364365// support in-place remap366if (destination == vertices)367{368unsigned char* vertices_copy = allocator.allocate<unsigned char>(vertex_count * vertex_size);369memcpy(vertices_copy, vertices, vertex_count * vertex_size);370vertices = vertices_copy;371}372373// specialize the loop for common vertex sizes to ensure memcpy is compiled as an inlined intrinsic374switch (vertex_size)375{376case 4:377return remapVertices<4>(destination, vertices, vertex_count, vertex_size, remap);378379case 8:380return remapVertices<8>(destination, vertices, vertex_count, vertex_size, remap);381382case 12:383return remapVertices<12>(destination, vertices, vertex_count, vertex_size, remap);384385case 16:386return remapVertices<16>(destination, vertices, vertex_count, vertex_size, remap);387388default:389return remapVertices<0>(destination, vertices, vertex_count, vertex_size, remap);390}391}392393void meshopt_remapIndexBuffer(unsigned int* destination, const unsigned int* indices, size_t index_count, const unsigned int* remap)394{395assert(index_count % 3 == 0);396397for (size_t i = 0; i < index_count; ++i)398{399unsigned int index = indices ? indices[i] : unsigned(i);400assert(remap[index] != ~0u);401402destination[i] = remap[index];403}404}405406void 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)407{408using namespace meshopt;409410assert(indices);411assert(index_count % 3 == 0);412assert(vertex_size > 0 && vertex_size <= 256);413assert(vertex_size <= vertex_stride);414415meshopt_Allocator allocator;416VertexHasher hasher = {static_cast<const unsigned char*>(vertices), vertex_size, vertex_stride};417418generateShadowBuffer(destination, indices, index_count, vertex_count, hasher, allocator);419}420421void 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)422{423using namespace meshopt;424425assert(indices);426assert(index_count % 3 == 0);427assert(stream_count > 0 && stream_count <= 16);428429for (size_t i = 0; i < stream_count; ++i)430{431assert(streams[i].size > 0 && streams[i].size <= 256);432assert(streams[i].size <= streams[i].stride);433}434435meshopt_Allocator allocator;436VertexStreamHasher hasher = {streams, stream_count};437438generateShadowBuffer(destination, indices, index_count, vertex_count, hasher, allocator);439}440441void 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)442{443using namespace meshopt;444445assert(index_count % 3 == 0);446assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);447assert(vertex_positions_stride % sizeof(float) == 0);448449meshopt_Allocator allocator;450451static const int next[4] = {1, 2, 0, 1};452453// build position remap: for each vertex, which other (canonical) vertex does it map to?454unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);455buildPositionRemap(remap, vertex_positions, vertex_count, vertex_positions_stride, allocator);456457// build edge set; this stores all triangle edges but we can look these up by any other wedge458EdgeHasher edge_hasher = {remap};459460size_t edge_table_size = hashBuckets(index_count);461unsigned long long* edge_table = allocator.allocate<unsigned long long>(edge_table_size);462unsigned int* edge_vertex_table = allocator.allocate<unsigned int>(edge_table_size);463464memset(edge_table, -1, edge_table_size * sizeof(unsigned long long));465memset(edge_vertex_table, -1, edge_table_size * sizeof(unsigned int));466467for (size_t i = 0; i < index_count; i += 3)468{469for (int e = 0; e < 3; ++e)470{471unsigned int i0 = indices[i + e];472unsigned int i1 = indices[i + next[e]];473unsigned int i2 = indices[i + next[e + 1]];474assert(i0 < vertex_count && i1 < vertex_count && i2 < vertex_count);475476unsigned long long edge = ((unsigned long long)i0 << 32) | i1;477unsigned long long* entry = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);478479if (*entry == ~0ull)480{481*entry = edge;482483// store vertex opposite to the edge484edge_vertex_table[entry - edge_table] = i2;485}486}487}488489// build resulting index buffer: 6 indices for each input triangle490for (size_t i = 0; i < index_count; i += 3)491{492unsigned int patch[6];493494for (int e = 0; e < 3; ++e)495{496unsigned int i0 = indices[i + e];497unsigned int i1 = indices[i + next[e]];498assert(i0 < vertex_count && i1 < vertex_count);499500// note: this refers to the opposite edge!501unsigned long long edge = ((unsigned long long)i1 << 32) | i0;502unsigned long long* oppe = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);503504patch[e * 2 + 0] = i0;505patch[e * 2 + 1] = (*oppe == ~0ull) ? i0 : edge_vertex_table[oppe - edge_table];506}507508memcpy(destination + i * 2, patch, sizeof(patch));509}510}511512void 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)513{514using namespace meshopt;515516assert(index_count % 3 == 0);517assert(vertex_positions_stride >= 12 && vertex_positions_stride <= 256);518assert(vertex_positions_stride % sizeof(float) == 0);519520meshopt_Allocator allocator;521522static const int next[3] = {1, 2, 0};523524// build position remap: for each vertex, which other (canonical) vertex does it map to?525unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);526buildPositionRemap(remap, vertex_positions, vertex_count, vertex_positions_stride, allocator);527528// build edge set; this stores all triangle edges but we can look these up by any other wedge529EdgeHasher edge_hasher = {remap};530531size_t edge_table_size = hashBuckets(index_count);532unsigned long long* edge_table = allocator.allocate<unsigned long long>(edge_table_size);533memset(edge_table, -1, edge_table_size * sizeof(unsigned long long));534535for (size_t i = 0; i < index_count; i += 3)536{537for (int e = 0; e < 3; ++e)538{539unsigned int i0 = indices[i + e];540unsigned int i1 = indices[i + next[e]];541assert(i0 < vertex_count && i1 < vertex_count);542543unsigned long long edge = ((unsigned long long)i0 << 32) | i1;544unsigned long long* entry = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);545546if (*entry == ~0ull)547*entry = edge;548}549}550551// build resulting index buffer: 12 indices for each input triangle552for (size_t i = 0; i < index_count; i += 3)553{554unsigned int patch[12];555556for (int e = 0; e < 3; ++e)557{558unsigned int i0 = indices[i + e];559unsigned int i1 = indices[i + next[e]];560assert(i0 < vertex_count && i1 < vertex_count);561562// note: this refers to the opposite edge!563unsigned long long edge = ((unsigned long long)i1 << 32) | i0;564unsigned long long oppe = *hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);565566// use the same edge if opposite edge doesn't exist (border)567oppe = (oppe == ~0ull) ? edge : oppe;568569// triangle index (0, 1, 2)570patch[e] = i0;571572// opposite edge (3, 4; 5, 6; 7, 8)573patch[3 + e * 2 + 0] = unsigned(oppe);574patch[3 + e * 2 + 1] = unsigned(oppe >> 32);575576// dominant vertex (9, 10, 11)577patch[9 + e] = remap[i0];578}579580memcpy(destination + i * 4, patch, sizeof(patch));581}582}583584size_t meshopt_generateProvokingIndexBuffer(unsigned int* destination, unsigned int* reorder, const unsigned int* indices, size_t index_count, size_t vertex_count)585{586assert(index_count % 3 == 0);587588meshopt_Allocator allocator;589590unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);591memset(remap, -1, vertex_count * sizeof(unsigned int));592593// compute vertex valence; this is used to prioritize least used corner594// note: we use 8-bit counters for performance; for outlier vertices the valence is incorrect but that just affects the heuristic595unsigned char* valence = allocator.allocate<unsigned char>(vertex_count);596memset(valence, 0, vertex_count);597598for (size_t i = 0; i < index_count; ++i)599{600unsigned int index = indices[i];601assert(index < vertex_count);602603valence[index]++;604}605606unsigned int reorder_offset = 0;607608// assign provoking vertices; leave the rest for the next pass609for (size_t i = 0; i < index_count; i += 3)610{611unsigned int a = indices[i + 0], b = indices[i + 1], c = indices[i + 2];612assert(a < vertex_count && b < vertex_count && c < vertex_count);613614// try to rotate triangle such that provoking vertex hasn't been seen before615// if multiple vertices are new, prioritize the one with least valence616// this reduces the risk that a future triangle will have all three vertices seen617unsigned int va = remap[a] == ~0u ? valence[a] : ~0u;618unsigned int vb = remap[b] == ~0u ? valence[b] : ~0u;619unsigned int vc = remap[c] == ~0u ? valence[c] : ~0u;620621if (vb != ~0u && vb <= va && vb <= vc)622{623// abc -> bca624unsigned int t = a;625a = b, b = c, c = t;626}627else if (vc != ~0u && vc <= va && vc <= vb)628{629// abc -> cab630unsigned int t = c;631c = b, b = a, a = t;632}633634unsigned int newidx = reorder_offset;635636// now remap[a] = ~0u or all three vertices are old637// recording remap[a] makes it possible to remap future references to the same index, conserving space638if (remap[a] == ~0u)639remap[a] = newidx;640641// we need to clone the provoking vertex to get a unique index642// if all three are used the choice is arbitrary since no future triangle will be able to reuse any of these643reorder[reorder_offset++] = a;644645// note: first vertex is final, the other two will be fixed up in next pass646destination[i + 0] = newidx;647destination[i + 1] = b;648destination[i + 2] = c;649650// update vertex valences for corner heuristic651valence[a]--;652valence[b]--;653valence[c]--;654}655656// remap or clone non-provoking vertices (iterating to skip provoking vertices)657int step = 1;658659for (size_t i = 1; i < index_count; i += step, step ^= 3)660{661unsigned int index = destination[i];662663if (remap[index] == ~0u)664{665// we haven't seen the vertex before as a provoking vertex666// to maintain the reference to the original vertex we need to clone it667unsigned int newidx = reorder_offset;668669remap[index] = newidx;670reorder[reorder_offset++] = index;671}672673destination[i] = remap[index];674}675676assert(reorder_offset <= vertex_count + index_count / 3);677return reorder_offset;678}679680681