Path: blob/master/thirdparty/meshoptimizer/stripifier.cpp
9898 views
// This file is part of meshoptimizer library; see meshoptimizer.h for version/license details1#include "meshoptimizer.h"23#include <assert.h>4#include <limits.h>5#include <string.h>67// This work is based on:8// Francine Evans, Steven Skiena and Amitabh Varshney. Optimizing Triangle Strips for Fast Rendering. 19969namespace meshopt10{1112static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned char* valence)13{14unsigned int index = 0;15unsigned int iv = ~0u;1617for (size_t i = 0; i < buffer_size; ++i)18{19unsigned char va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]];20unsigned int v = (va < vb && va < vc) ? va : (vb < vc ? vb : vc);2122if (v < iv)23{24index = unsigned(i);25iv = v;26}27}2829return index;30}3132static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1)33{34for (size_t i = 0; i < buffer_size; ++i)35{36unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];3738if (e0 == a && e1 == b)39return (int(i) << 2) | 2;40else if (e0 == b && e1 == c)41return (int(i) << 2) | 0;42else if (e0 == c && e1 == a)43return (int(i) << 2) | 1;44}4546return -1;47}4849} // namespace meshopt5051size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int restart_index)52{53assert(destination != indices);54assert(index_count % 3 == 0);5556using namespace meshopt;5758meshopt_Allocator allocator;5960const size_t buffer_capacity = 8;6162unsigned int buffer[buffer_capacity][3] = {};63unsigned int buffer_size = 0;6465size_t index_offset = 0;6667unsigned int strip[2] = {};68unsigned int parity = 0;6970size_t strip_size = 0;7172// compute vertex valence; this is used to prioritize starting triangle for strips73// note: we use 8-bit counters for performance; for outlier vertices the valence is incorrect but that just affects the heuristic74unsigned char* valence = allocator.allocate<unsigned char>(vertex_count);75memset(valence, 0, vertex_count);7677for (size_t i = 0; i < index_count; ++i)78{79unsigned int index = indices[i];80assert(index < vertex_count);8182valence[index]++;83}8485int next = -1;8687while (buffer_size > 0 || index_offset < index_count)88{89assert(next < 0 || (size_t(next >> 2) < buffer_size && (next & 3) < 3));9091// fill triangle buffer92while (buffer_size < buffer_capacity && index_offset < index_count)93{94buffer[buffer_size][0] = indices[index_offset + 0];95buffer[buffer_size][1] = indices[index_offset + 1];96buffer[buffer_size][2] = indices[index_offset + 2];9798buffer_size++;99index_offset += 3;100}101102assert(buffer_size > 0);103104if (next >= 0)105{106unsigned int i = next >> 2;107unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];108unsigned int v = buffer[i][next & 3];109110// ordered removal from the buffer111memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0]));112buffer_size--;113114// update vertex valences for strip start heuristic115valence[a]--;116valence[b]--;117valence[c]--;118119// find next triangle (note that edge order flips on every iteration)120// in some cases we need to perform a swap to pick a different outgoing triangle edge121// for [a b c], the default strip edge is [b c], but we might want to use [a c]122int cont = findStripNext(buffer, buffer_size, parity ? strip[1] : v, parity ? v : strip[1]);123int swap = cont < 0 ? findStripNext(buffer, buffer_size, parity ? v : strip[0], parity ? strip[0] : v) : -1;124125if (cont < 0 && swap >= 0)126{127// [a b c] => [a b a c]128destination[strip_size++] = strip[0];129destination[strip_size++] = v;130131// next strip has same winding132// ? a b => b a v133strip[1] = v;134135next = swap;136}137else138{139// emit the next vertex in the strip140destination[strip_size++] = v;141142// next strip has flipped winding143strip[0] = strip[1];144strip[1] = v;145parity ^= 1;146147next = cont;148}149}150else151{152// if we didn't find anything, we need to find the next new triangle153// we use a heuristic to maximize the strip length154unsigned int i = findStripFirst(buffer, buffer_size, valence);155unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];156157// ordered removal from the buffer158memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0]));159buffer_size--;160161// update vertex valences for strip start heuristic162valence[a]--;163valence[b]--;164valence[c]--;165166// we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration167int ea = findStripNext(buffer, buffer_size, c, b);168int eb = findStripNext(buffer, buffer_size, a, c);169int ec = findStripNext(buffer, buffer_size, b, a);170171// in some cases we can have several matching edges; since we can pick any edge, we pick the one with the smallest172// triangle index in the buffer. this reduces the effect of stripification on ACMR and additionally - for unclear173// reasons - slightly improves the stripification efficiency174int mine = INT_MAX;175mine = (ea >= 0 && mine > ea) ? ea : mine;176mine = (eb >= 0 && mine > eb) ? eb : mine;177mine = (ec >= 0 && mine > ec) ? ec : mine;178179if (ea == mine)180{181// keep abc182next = ea;183}184else if (eb == mine)185{186// abc -> bca187unsigned int t = a;188a = b, b = c, c = t;189190next = eb;191}192else if (ec == mine)193{194// abc -> cab195unsigned int t = c;196c = b, b = a, a = t;197198next = ec;199}200201if (restart_index)202{203if (strip_size)204destination[strip_size++] = restart_index;205206destination[strip_size++] = a;207destination[strip_size++] = b;208destination[strip_size++] = c;209210// new strip always starts with the same edge winding211strip[0] = b;212strip[1] = c;213parity = 1;214}215else216{217if (strip_size)218{219// connect last strip using degenerate triangles220destination[strip_size++] = strip[1];221destination[strip_size++] = a;222}223224// note that we may need to flip the emitted triangle based on parity225// we always end up with outgoing edge "cb" in the end226unsigned int e0 = parity ? c : b;227unsigned int e1 = parity ? b : c;228229destination[strip_size++] = a;230destination[strip_size++] = e0;231destination[strip_size++] = e1;232233strip[0] = e0;234strip[1] = e1;235parity ^= 1;236}237}238}239240return strip_size;241}242243size_t meshopt_stripifyBound(size_t index_count)244{245assert(index_count % 3 == 0);246247// worst case without restarts is 2 degenerate indices and 3 indices per triangle248// worst case with restarts is 1 restart index and 3 indices per triangle249return (index_count / 3) * 5;250}251252size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count, unsigned int restart_index)253{254assert(destination != indices);255256size_t offset = 0;257size_t start = 0;258259for (size_t i = 0; i < index_count; ++i)260{261if (restart_index && indices[i] == restart_index)262{263start = i + 1;264}265else if (i - start >= 2)266{267unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i];268269// flip winding for odd triangles270if ((i - start) & 1)271{272unsigned int t = a;273a = b, b = t;274}275276// although we use restart indices, strip swaps still produce degenerate triangles, so skip them277if (a != b && a != c && b != c)278{279destination[offset + 0] = a;280destination[offset + 1] = b;281destination[offset + 2] = c;282offset += 3;283}284}285}286287return offset;288}289290size_t meshopt_unstripifyBound(size_t index_count)291{292assert(index_count == 0 || index_count >= 3);293294return (index_count == 0) ? 0 : (index_count - 2) * 3;295}296297298