Path: blob/master/thirdparty/meshoptimizer/indexcodec.cpp
9903 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// Fabian Giesen. Simple lossless index buffer compression & follow-up. 20138// Conor Stokes. Vertex Cache Optimised Index Buffer Compression. 20149namespace meshopt10{1112const unsigned char kIndexHeader = 0xe0;13const unsigned char kSequenceHeader = 0xd0;1415static int gEncodeIndexVersion = 1;16const int kDecodeIndexVersion = 1;1718typedef unsigned int VertexFifo[16];19typedef unsigned int EdgeFifo[16][2];2021static const unsigned int kTriangleIndexOrder[3][3] = {22{0, 1, 2},23{1, 2, 0},24{2, 0, 1},25};2627static const unsigned char kCodeAuxEncodingTable[16] = {280x00, 0x76, 0x87, 0x56, 0x67, 0x78, 0xa9, 0x86, 0x65, 0x89, 0x68, 0x98, 0x01, 0x69,290, 0, // last two entries aren't used for encoding30};3132static int rotateTriangle(unsigned int a, unsigned int b, unsigned int c, unsigned int next)33{34(void)a;3536return (b == next) ? 1 : (c == next ? 2 : 0);37}3839static int getEdgeFifo(EdgeFifo fifo, unsigned int a, unsigned int b, unsigned int c, size_t offset)40{41for (int i = 0; i < 16; ++i)42{43size_t index = (offset - 1 - i) & 15;4445unsigned int e0 = fifo[index][0];46unsigned int e1 = fifo[index][1];4748if (e0 == a && e1 == b)49return (i << 2) | 0;50if (e0 == b && e1 == c)51return (i << 2) | 1;52if (e0 == c && e1 == a)53return (i << 2) | 2;54}5556return -1;57}5859static void pushEdgeFifo(EdgeFifo fifo, unsigned int a, unsigned int b, size_t& offset)60{61fifo[offset][0] = a;62fifo[offset][1] = b;63offset = (offset + 1) & 15;64}6566static int getVertexFifo(VertexFifo fifo, unsigned int v, size_t offset)67{68for (int i = 0; i < 16; ++i)69{70size_t index = (offset - 1 - i) & 15;7172if (fifo[index] == v)73return i;74}7576return -1;77}7879static void pushVertexFifo(VertexFifo fifo, unsigned int v, size_t& offset, int cond = 1)80{81fifo[offset] = v;82offset = (offset + cond) & 15;83}8485static void encodeVByte(unsigned char*& data, unsigned int v)86{87// encode 32-bit value in up to 5 7-bit groups88do89{90*data++ = (v & 127) | (v > 127 ? 128 : 0);91v >>= 7;92} while (v);93}9495static unsigned int decodeVByte(const unsigned char*& data)96{97unsigned char lead = *data++;9899// fast path: single byte100if (lead < 128)101return lead;102103// slow path: up to 4 extra bytes104// note that this loop always terminates, which is important for malformed data105unsigned int result = lead & 127;106unsigned int shift = 7;107108for (int i = 0; i < 4; ++i)109{110unsigned char group = *data++;111result |= unsigned(group & 127) << shift;112shift += 7;113114if (group < 128)115break;116}117118return result;119}120121static void encodeIndex(unsigned char*& data, unsigned int index, unsigned int last)122{123unsigned int d = index - last;124unsigned int v = (d << 1) ^ (int(d) >> 31);125126encodeVByte(data, v);127}128129static unsigned int decodeIndex(const unsigned char*& data, unsigned int last)130{131unsigned int v = decodeVByte(data);132unsigned int d = (v >> 1) ^ -int(v & 1);133134return last + d;135}136137static int getCodeAuxIndex(unsigned char v, const unsigned char* table)138{139for (int i = 0; i < 16; ++i)140if (table[i] == v)141return i;142143return -1;144}145146static void writeTriangle(void* destination, size_t offset, size_t index_size, unsigned int a, unsigned int b, unsigned int c)147{148if (index_size == 2)149{150static_cast<unsigned short*>(destination)[offset + 0] = (unsigned short)(a);151static_cast<unsigned short*>(destination)[offset + 1] = (unsigned short)(b);152static_cast<unsigned short*>(destination)[offset + 2] = (unsigned short)(c);153}154else155{156static_cast<unsigned int*>(destination)[offset + 0] = a;157static_cast<unsigned int*>(destination)[offset + 1] = b;158static_cast<unsigned int*>(destination)[offset + 2] = c;159}160}161162} // namespace meshopt163164size_t meshopt_encodeIndexBuffer(unsigned char* buffer, size_t buffer_size, const unsigned int* indices, size_t index_count)165{166using namespace meshopt;167168assert(index_count % 3 == 0);169170// the minimum valid encoding is header, 1 byte per triangle and a 16-byte codeaux table171if (buffer_size < 1 + index_count / 3 + 16)172return 0;173174int version = gEncodeIndexVersion;175176buffer[0] = (unsigned char)(kIndexHeader | version);177178EdgeFifo edgefifo;179memset(edgefifo, -1, sizeof(edgefifo));180181VertexFifo vertexfifo;182memset(vertexfifo, -1, sizeof(vertexfifo));183184size_t edgefifooffset = 0;185size_t vertexfifooffset = 0;186187unsigned int next = 0;188unsigned int last = 0;189190unsigned char* code = buffer + 1;191unsigned char* data = code + index_count / 3;192unsigned char* data_safe_end = buffer + buffer_size - 16;193194int fecmax = version >= 1 ? 13 : 15;195196// use static encoding table; it's possible to pack the result and then build an optimal table and repack197// for now we keep it simple and use the table that has been generated based on symbol frequency on a training mesh set198const unsigned char* codeaux_table = kCodeAuxEncodingTable;199200for (size_t i = 0; i < index_count; i += 3)201{202// make sure we have enough space to write a triangle203// each triangle writes at most 16 bytes: 1b for codeaux and 5b for each free index204// after this we can be sure we can write without extra bounds checks205if (data > data_safe_end)206return 0;207208int fer = getEdgeFifo(edgefifo, indices[i + 0], indices[i + 1], indices[i + 2], edgefifooffset);209210if (fer >= 0 && (fer >> 2) < 15)211{212// note: getEdgeFifo implicitly rotates triangles by matching a/b to existing edge213const unsigned int* order = kTriangleIndexOrder[fer & 3];214215unsigned int a = indices[i + order[0]], b = indices[i + order[1]], c = indices[i + order[2]];216217// encode edge index and vertex fifo index, next or free index218int fe = fer >> 2;219int fc = getVertexFifo(vertexfifo, c, vertexfifooffset);220221int fec = (fc >= 1 && fc < fecmax) ? fc : (c == next ? (next++, 0) : 15);222223if (fec == 15 && version >= 1)224{225// encode last-1 and last+1 to optimize strip-like sequences226if (c + 1 == last)227fec = 13, last = c;228if (c == last + 1)229fec = 14, last = c;230}231232*code++ = (unsigned char)((fe << 4) | fec);233234// note that we need to update the last index since free indices are delta-encoded235if (fec == 15)236encodeIndex(data, c, last), last = c;237238// we only need to push third vertex since first two are likely already in the vertex fifo239if (fec == 0 || fec >= fecmax)240pushVertexFifo(vertexfifo, c, vertexfifooffset);241242// we only need to push two new edges to edge fifo since the third one is already there243pushEdgeFifo(edgefifo, c, b, edgefifooffset);244pushEdgeFifo(edgefifo, a, c, edgefifooffset);245}246else247{248int rotation = rotateTriangle(indices[i + 0], indices[i + 1], indices[i + 2], next);249const unsigned int* order = kTriangleIndexOrder[rotation];250251unsigned int a = indices[i + order[0]], b = indices[i + order[1]], c = indices[i + order[2]];252253// if a/b/c are 0/1/2, we emit a reset code254bool reset = false;255256if (a == 0 && b == 1 && c == 2 && next > 0 && version >= 1)257{258reset = true;259next = 0;260261// reset vertex fifo to make sure we don't accidentally reference vertices from that in the future262// this makes sure next continues to get incremented instead of being stuck263memset(vertexfifo, -1, sizeof(vertexfifo));264}265266int fb = getVertexFifo(vertexfifo, b, vertexfifooffset);267int fc = getVertexFifo(vertexfifo, c, vertexfifooffset);268269// after rotation, a is almost always equal to next, so we don't waste bits on FIFO encoding for a270// note: decoder implicitly assumes that if feb=fec=0, then fea=0 (reset code); this is enforced by rotation271int fea = (a == next) ? (next++, 0) : 15;272int feb = (fb >= 0 && fb < 14) ? fb + 1 : (b == next ? (next++, 0) : 15);273int fec = (fc >= 0 && fc < 14) ? fc + 1 : (c == next ? (next++, 0) : 15);274275// we encode feb & fec in 4 bits using a table if possible, and as a full byte otherwise276unsigned char codeaux = (unsigned char)((feb << 4) | fec);277int codeauxindex = getCodeAuxIndex(codeaux, codeaux_table);278279// <14 encodes an index into codeaux table, 14 encodes fea=0, 15 encodes fea=15280if (fea == 0 && codeauxindex >= 0 && codeauxindex < 14 && !reset)281{282*code++ = (unsigned char)((15 << 4) | codeauxindex);283}284else285{286*code++ = (unsigned char)((15 << 4) | 14 | fea);287*data++ = codeaux;288}289290// note that we need to update the last index since free indices are delta-encoded291if (fea == 15)292encodeIndex(data, a, last), last = a;293294if (feb == 15)295encodeIndex(data, b, last), last = b;296297if (fec == 15)298encodeIndex(data, c, last), last = c;299300// only push vertices that weren't already in fifo301if (fea == 0 || fea == 15)302pushVertexFifo(vertexfifo, a, vertexfifooffset);303304if (feb == 0 || feb == 15)305pushVertexFifo(vertexfifo, b, vertexfifooffset);306307if (fec == 0 || fec == 15)308pushVertexFifo(vertexfifo, c, vertexfifooffset);309310// all three edges aren't in the fifo; pushing all of them is important so that we can match them for later triangles311pushEdgeFifo(edgefifo, b, a, edgefifooffset);312pushEdgeFifo(edgefifo, c, b, edgefifooffset);313pushEdgeFifo(edgefifo, a, c, edgefifooffset);314}315}316317// make sure we have enough space to write codeaux table318if (data > data_safe_end)319return 0;320321// add codeaux encoding table to the end of the stream; this is used for decoding codeaux *and* as padding322// we need padding for decoding to be able to assume that each triangle is encoded as <= 16 bytes of extra data323// this is enough space for aux byte + 5 bytes per varint index which is the absolute worst case for any input324for (size_t i = 0; i < 16; ++i)325{326// decoder assumes that table entries never refer to separately encoded indices327assert((codeaux_table[i] & 0xf) != 0xf && (codeaux_table[i] >> 4) != 0xf);328329*data++ = codeaux_table[i];330}331332// since we encode restarts as codeaux without a table reference, we need to make sure 00 is encoded as a table reference333assert(codeaux_table[0] == 0);334335assert(data >= buffer + index_count / 3 + 16);336assert(data <= buffer + buffer_size);337338return data - buffer;339}340341size_t meshopt_encodeIndexBufferBound(size_t index_count, size_t vertex_count)342{343assert(index_count % 3 == 0);344345// compute number of bits required for each index346unsigned int vertex_bits = 1;347348while (vertex_bits < 32 && vertex_count > size_t(1) << vertex_bits)349vertex_bits++;350351// worst-case encoding is 2 header bytes + 3 varint-7 encoded index deltas352unsigned int vertex_groups = (vertex_bits + 1 + 6) / 7;353354return 1 + (index_count / 3) * (2 + 3 * vertex_groups) + 16;355}356357void meshopt_encodeIndexVersion(int version)358{359assert(unsigned(version) <= unsigned(meshopt::kDecodeIndexVersion));360361meshopt::gEncodeIndexVersion = version;362}363364int meshopt_decodeIndexVersion(const unsigned char* buffer, size_t buffer_size)365{366if (buffer_size < 1)367return -1;368369unsigned char header = buffer[0];370371if ((header & 0xf0) != meshopt::kIndexHeader && (header & 0xf0) != meshopt::kSequenceHeader)372return -1;373374int version = header & 0x0f;375if (version > meshopt::kDecodeIndexVersion)376return -1;377378return version;379}380381int meshopt_decodeIndexBuffer(void* destination, size_t index_count, size_t index_size, const unsigned char* buffer, size_t buffer_size)382{383using namespace meshopt;384385assert(index_count % 3 == 0);386assert(index_size == 2 || index_size == 4);387388// the minimum valid encoding is header, 1 byte per triangle and a 16-byte codeaux table389if (buffer_size < 1 + index_count / 3 + 16)390return -2;391392if ((buffer[0] & 0xf0) != kIndexHeader)393return -1;394395int version = buffer[0] & 0x0f;396if (version > kDecodeIndexVersion)397return -1;398399EdgeFifo edgefifo;400memset(edgefifo, -1, sizeof(edgefifo));401402VertexFifo vertexfifo;403memset(vertexfifo, -1, sizeof(vertexfifo));404405size_t edgefifooffset = 0;406size_t vertexfifooffset = 0;407408unsigned int next = 0;409unsigned int last = 0;410411int fecmax = version >= 1 ? 13 : 15;412413// since we store 16-byte codeaux table at the end, triangle data has to begin before data_safe_end414const unsigned char* code = buffer + 1;415const unsigned char* data = code + index_count / 3;416const unsigned char* data_safe_end = buffer + buffer_size - 16;417418const unsigned char* codeaux_table = data_safe_end;419420for (size_t i = 0; i < index_count; i += 3)421{422// make sure we have enough data to read for a triangle423// each triangle reads at most 16 bytes of data: 1b for codeaux and 5b for each free index424// after this we can be sure we can read without extra bounds checks425if (data > data_safe_end)426return -2;427428unsigned char codetri = *code++;429430if (codetri < 0xf0)431{432int fe = codetri >> 4;433434// fifo reads are wrapped around 16 entry buffer435unsigned int a = edgefifo[(edgefifooffset - 1 - fe) & 15][0];436unsigned int b = edgefifo[(edgefifooffset - 1 - fe) & 15][1];437unsigned int c = 0;438439int fec = codetri & 15;440441// note: this is the most common path in the entire decoder442// inside this if we try to stay branchless (by using cmov/etc.) since these aren't predictable443if (fec < fecmax)444{445// fifo reads are wrapped around 16 entry buffer446unsigned int cf = vertexfifo[(vertexfifooffset - 1 - fec) & 15];447c = (fec == 0) ? next : cf;448449int fec0 = fec == 0;450next += fec0;451452// push vertex fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly453pushVertexFifo(vertexfifo, c, vertexfifooffset, fec0);454}455else456{457// fec - (fec ^ 3) decodes 13, 14 into -1, 1458// note that we need to update the last index since free indices are delta-encoded459last = c = (fec != 15) ? last + (fec - (fec ^ 3)) : decodeIndex(data, last);460461// push vertex/edge fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly462pushVertexFifo(vertexfifo, c, vertexfifooffset);463}464465// push edge fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly466pushEdgeFifo(edgefifo, c, b, edgefifooffset);467pushEdgeFifo(edgefifo, a, c, edgefifooffset);468469// output triangle470writeTriangle(destination, i, index_size, a, b, c);471}472else473{474// fast path: read codeaux from the table475if (codetri < 0xfe)476{477unsigned char codeaux = codeaux_table[codetri & 15];478479// note: table can't contain feb/fec=15480int feb = codeaux >> 4;481int fec = codeaux & 15;482483// fifo reads are wrapped around 16 entry buffer484// also note that we increment next for all three vertices before decoding indices - this matches encoder behavior485unsigned int a = next++;486487unsigned int bf = vertexfifo[(vertexfifooffset - feb) & 15];488unsigned int b = (feb == 0) ? next : bf;489490int feb0 = feb == 0;491next += feb0;492493unsigned int cf = vertexfifo[(vertexfifooffset - fec) & 15];494unsigned int c = (fec == 0) ? next : cf;495496int fec0 = fec == 0;497next += fec0;498499// output triangle500writeTriangle(destination, i, index_size, a, b, c);501502// push vertex/edge fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly503pushVertexFifo(vertexfifo, a, vertexfifooffset);504pushVertexFifo(vertexfifo, b, vertexfifooffset, feb0);505pushVertexFifo(vertexfifo, c, vertexfifooffset, fec0);506507pushEdgeFifo(edgefifo, b, a, edgefifooffset);508pushEdgeFifo(edgefifo, c, b, edgefifooffset);509pushEdgeFifo(edgefifo, a, c, edgefifooffset);510}511else512{513// slow path: read a full byte for codeaux instead of using a table lookup514unsigned char codeaux = *data++;515516int fea = codetri == 0xfe ? 0 : 15;517int feb = codeaux >> 4;518int fec = codeaux & 15;519520// reset: codeaux is 0 but encoded as not-a-table521if (codeaux == 0)522next = 0;523524// fifo reads are wrapped around 16 entry buffer525// also note that we increment next for all three vertices before decoding indices - this matches encoder behavior526unsigned int a = (fea == 0) ? next++ : 0;527unsigned int b = (feb == 0) ? next++ : vertexfifo[(vertexfifooffset - feb) & 15];528unsigned int c = (fec == 0) ? next++ : vertexfifo[(vertexfifooffset - fec) & 15];529530// note that we need to update the last index since free indices are delta-encoded531if (fea == 15)532last = a = decodeIndex(data, last);533534if (feb == 15)535last = b = decodeIndex(data, last);536537if (fec == 15)538last = c = decodeIndex(data, last);539540// output triangle541writeTriangle(destination, i, index_size, a, b, c);542543// push vertex/edge fifo must match the encoding step *exactly* otherwise the data will not be decoded correctly544pushVertexFifo(vertexfifo, a, vertexfifooffset);545pushVertexFifo(vertexfifo, b, vertexfifooffset, (feb == 0) | (feb == 15));546pushVertexFifo(vertexfifo, c, vertexfifooffset, (fec == 0) | (fec == 15));547548pushEdgeFifo(edgefifo, b, a, edgefifooffset);549pushEdgeFifo(edgefifo, c, b, edgefifooffset);550pushEdgeFifo(edgefifo, a, c, edgefifooffset);551}552}553}554555// we should've read all data bytes and stopped at the boundary between data and codeaux table556if (data != data_safe_end)557return -3;558559return 0;560}561562size_t meshopt_encodeIndexSequence(unsigned char* buffer, size_t buffer_size, const unsigned int* indices, size_t index_count)563{564using namespace meshopt;565566// the minimum valid encoding is header, 1 byte per index and a 4-byte tail567if (buffer_size < 1 + index_count + 4)568return 0;569570int version = gEncodeIndexVersion;571572buffer[0] = (unsigned char)(kSequenceHeader | version);573574unsigned int last[2] = {};575unsigned int current = 0;576577unsigned char* data = buffer + 1;578unsigned char* data_safe_end = buffer + buffer_size - 4;579580for (size_t i = 0; i < index_count; ++i)581{582// make sure we have enough data to write583// each index writes at most 5 bytes of data; there's a 4 byte tail after data_safe_end584// after this we can be sure we can write without extra bounds checks585if (data >= data_safe_end)586return 0;587588unsigned int index = indices[i];589590// this is a heuristic that switches between baselines when the delta grows too large591// we want the encoded delta to fit into one byte (7 bits), but 2 bits are used for sign and baseline index592// for now we immediately switch the baseline when delta grows too large - this can be adjusted arbitrarily593int cd = int(index - last[current]);594current ^= ((cd < 0 ? -cd : cd) >= 30);595596// encode delta from the last index597unsigned int d = index - last[current];598unsigned int v = (d << 1) ^ (int(d) >> 31);599600// note: low bit encodes the index of the last baseline which will be used for reconstruction601encodeVByte(data, (v << 1) | current);602603// update last for the next iteration that uses it604last[current] = index;605}606607// make sure we have enough space to write tail608if (data > data_safe_end)609return 0;610611for (int k = 0; k < 4; ++k)612*data++ = 0;613614return data - buffer;615}616617size_t meshopt_encodeIndexSequenceBound(size_t index_count, size_t vertex_count)618{619// compute number of bits required for each index620unsigned int vertex_bits = 1;621622while (vertex_bits < 32 && vertex_count > size_t(1) << vertex_bits)623vertex_bits++;624625// worst-case encoding is 1 varint-7 encoded index delta for a K bit value and an extra bit626unsigned int vertex_groups = (vertex_bits + 1 + 1 + 6) / 7;627628return 1 + index_count * vertex_groups + 4;629}630631int meshopt_decodeIndexSequence(void* destination, size_t index_count, size_t index_size, const unsigned char* buffer, size_t buffer_size)632{633using namespace meshopt;634635// the minimum valid encoding is header, 1 byte per index and a 4-byte tail636if (buffer_size < 1 + index_count + 4)637return -2;638639if ((buffer[0] & 0xf0) != kSequenceHeader)640return -1;641642int version = buffer[0] & 0x0f;643if (version > kDecodeIndexVersion)644return -1;645646const unsigned char* data = buffer + 1;647const unsigned char* data_safe_end = buffer + buffer_size - 4;648649unsigned int last[2] = {};650651for (size_t i = 0; i < index_count; ++i)652{653// make sure we have enough data to read654// each index reads at most 5 bytes of data; there's a 4 byte tail after data_safe_end655// after this we can be sure we can read without extra bounds checks656if (data >= data_safe_end)657return -2;658659unsigned int v = decodeVByte(data);660661// decode the index of the last baseline662unsigned int current = v & 1;663v >>= 1;664665// reconstruct index as a delta666unsigned int d = (v >> 1) ^ -int(v & 1);667unsigned int index = last[current] + d;668669// update last for the next iteration that uses it670last[current] = index;671672if (index_size == 2)673{674static_cast<unsigned short*>(destination)[i] = (unsigned short)(index);675}676else677{678static_cast<unsigned int*>(destination)[i] = index;679}680}681682// we should've read all data bytes and stopped at the boundary between data and tail683if (data != data_safe_end)684return -3;685686return 0;687}688689690