mirror of
				https://github.com/godotengine/godot.git
				synced 2025-10-30 05:01:10 +00:00 
			
		
		
		
	
		
			
	
	
		
			296 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
		
		
			
		
	
	
			296 lines
		
	
	
	
		
			7.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
|   | // This file is part of meshoptimizer library; see meshoptimizer.h for version/license details
 | ||
|  | #include "meshoptimizer.h"
 | ||
|  | 
 | ||
|  | #include <assert.h>
 | ||
|  | #include <limits.h>
 | ||
|  | #include <string.h>
 | ||
|  | 
 | ||
|  | // This work is based on:
 | ||
|  | // Francine Evans, Steven Skiena and Amitabh Varshney. Optimizing Triangle Strips for Fast Rendering. 1996
 | ||
|  | namespace meshopt | ||
|  | { | ||
|  | 
 | ||
|  | static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence) | ||
|  | { | ||
|  | 	unsigned int index = 0; | ||
|  | 	unsigned int iv = ~0u; | ||
|  | 
 | ||
|  | 	for (size_t i = 0; i < buffer_size; ++i) | ||
|  | 	{ | ||
|  | 		unsigned int va = valence[buffer[i][0]], vb = valence[buffer[i][1]], vc = valence[buffer[i][2]]; | ||
|  | 		unsigned int v = (va < vb && va < vc) ? va : (vb < vc) ? vb : vc; | ||
|  | 
 | ||
|  | 		if (v < iv) | ||
|  | 		{ | ||
|  | 			index = unsigned(i); | ||
|  | 			iv = v; | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return index; | ||
|  | } | ||
|  | 
 | ||
|  | static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1) | ||
|  | { | ||
|  | 	for (size_t i = 0; i < buffer_size; ++i) | ||
|  | 	{ | ||
|  | 		unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2]; | ||
|  | 
 | ||
|  | 		if (e0 == a && e1 == b) | ||
|  | 			return (int(i) << 2) | 2; | ||
|  | 		else if (e0 == b && e1 == c) | ||
|  | 			return (int(i) << 2) | 0; | ||
|  | 		else if (e0 == c && e1 == a) | ||
|  | 			return (int(i) << 2) | 1; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return -1; | ||
|  | } | ||
|  | 
 | ||
|  | } // namespace meshopt
 | ||
|  | 
 | ||
|  | size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count, unsigned int restart_index) | ||
|  | { | ||
|  | 	assert(destination != indices); | ||
|  | 	assert(index_count % 3 == 0); | ||
|  | 
 | ||
|  | 	using namespace meshopt; | ||
|  | 
 | ||
|  | 	meshopt_Allocator allocator; | ||
|  | 
 | ||
|  | 	const size_t buffer_capacity = 8; | ||
|  | 
 | ||
|  | 	unsigned int buffer[buffer_capacity][3] = {}; | ||
|  | 	unsigned int buffer_size = 0; | ||
|  | 
 | ||
|  | 	size_t index_offset = 0; | ||
|  | 
 | ||
|  | 	unsigned int strip[2] = {}; | ||
|  | 	unsigned int parity = 0; | ||
|  | 
 | ||
|  | 	size_t strip_size = 0; | ||
|  | 
 | ||
|  | 	// compute vertex valence; this is used to prioritize starting triangle for strips
 | ||
|  | 	unsigned int* valence = allocator.allocate<unsigned int>(vertex_count); | ||
|  | 	memset(valence, 0, vertex_count * sizeof(unsigned int)); | ||
|  | 
 | ||
|  | 	for (size_t i = 0; i < index_count; ++i) | ||
|  | 	{ | ||
|  | 		unsigned int index = indices[i]; | ||
|  | 		assert(index < vertex_count); | ||
|  | 
 | ||
|  | 		valence[index]++; | ||
|  | 	} | ||
|  | 
 | ||
|  | 	int next = -1; | ||
|  | 
 | ||
|  | 	while (buffer_size > 0 || index_offset < index_count) | ||
|  | 	{ | ||
|  | 		assert(next < 0 || (size_t(next >> 2) < buffer_size && (next & 3) < 3)); | ||
|  | 
 | ||
|  | 		// fill triangle buffer
 | ||
|  | 		while (buffer_size < buffer_capacity && index_offset < index_count) | ||
|  | 		{ | ||
|  | 			buffer[buffer_size][0] = indices[index_offset + 0]; | ||
|  | 			buffer[buffer_size][1] = indices[index_offset + 1]; | ||
|  | 			buffer[buffer_size][2] = indices[index_offset + 2]; | ||
|  | 
 | ||
|  | 			buffer_size++; | ||
|  | 			index_offset += 3; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		assert(buffer_size > 0); | ||
|  | 
 | ||
|  | 		if (next >= 0) | ||
|  | 		{ | ||
|  | 			unsigned int i = next >> 2; | ||
|  | 			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2]; | ||
|  | 			unsigned int v = buffer[i][next & 3]; | ||
|  | 
 | ||
|  | 			// ordered removal from the buffer
 | ||
|  | 			memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0])); | ||
|  | 			buffer_size--; | ||
|  | 
 | ||
|  | 			// update vertex valences for strip start heuristic
 | ||
|  | 			valence[a]--; | ||
|  | 			valence[b]--; | ||
|  | 			valence[c]--; | ||
|  | 
 | ||
|  | 			// find next triangle (note that edge order flips on every iteration)
 | ||
|  | 			// in some cases we need to perform a swap to pick a different outgoing triangle edge
 | ||
|  | 			// for [a b c], the default strip edge is [b c], but we might want to use [a c]
 | ||
|  | 			int cont = findStripNext(buffer, buffer_size, parity ? strip[1] : v, parity ? v : strip[1]); | ||
|  | 			int swap = cont < 0 ? findStripNext(buffer, buffer_size, parity ? v : strip[0], parity ? strip[0] : v) : -1; | ||
|  | 
 | ||
|  | 			if (cont < 0 && swap >= 0) | ||
|  | 			{ | ||
|  | 				// [a b c] => [a b a c]
 | ||
|  | 				destination[strip_size++] = strip[0]; | ||
|  | 				destination[strip_size++] = v; | ||
|  | 
 | ||
|  | 				// next strip has same winding
 | ||
|  | 				// ? a b => b a v
 | ||
|  | 				strip[1] = v; | ||
|  | 
 | ||
|  | 				next = swap; | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				// emit the next vertex in the strip
 | ||
|  | 				destination[strip_size++] = v; | ||
|  | 
 | ||
|  | 				// next strip has flipped winding
 | ||
|  | 				strip[0] = strip[1]; | ||
|  | 				strip[1] = v; | ||
|  | 				parity ^= 1; | ||
|  | 
 | ||
|  | 				next = cont; | ||
|  | 			} | ||
|  | 		} | ||
|  | 		else | ||
|  | 		{ | ||
|  | 			// if we didn't find anything, we need to find the next new triangle
 | ||
|  | 			// we use a heuristic to maximize the strip length
 | ||
|  | 			unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]); | ||
|  | 			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2]; | ||
|  | 
 | ||
|  | 			// ordered removal from the buffer
 | ||
|  | 			memmove(buffer[i], buffer[i + 1], (buffer_size - i - 1) * sizeof(buffer[0])); | ||
|  | 			buffer_size--; | ||
|  | 
 | ||
|  | 			// update vertex valences for strip start heuristic
 | ||
|  | 			valence[a]--; | ||
|  | 			valence[b]--; | ||
|  | 			valence[c]--; | ||
|  | 
 | ||
|  | 			// we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration
 | ||
|  | 			int ea = findStripNext(buffer, buffer_size, c, b); | ||
|  | 			int eb = findStripNext(buffer, buffer_size, a, c); | ||
|  | 			int ec = findStripNext(buffer, buffer_size, b, a); | ||
|  | 
 | ||
|  | 			// in some cases we can have several matching edges; since we can pick any edge, we pick the one with the smallest
 | ||
|  | 			// triangle index in the buffer. this reduces the effect of stripification on ACMR and additionally - for unclear
 | ||
|  | 			// reasons - slightly improves the stripification efficiency
 | ||
|  | 			int mine = INT_MAX; | ||
|  | 			mine = (ea >= 0 && mine > ea) ? ea : mine; | ||
|  | 			mine = (eb >= 0 && mine > eb) ? eb : mine; | ||
|  | 			mine = (ec >= 0 && mine > ec) ? ec : mine; | ||
|  | 
 | ||
|  | 			if (ea == mine) | ||
|  | 			{ | ||
|  | 				// keep abc
 | ||
|  | 				next = ea; | ||
|  | 			} | ||
|  | 			else if (eb == mine) | ||
|  | 			{ | ||
|  | 				// abc -> bca
 | ||
|  | 				unsigned int t = a; | ||
|  | 				a = b, b = c, c = t; | ||
|  | 
 | ||
|  | 				next = eb; | ||
|  | 			} | ||
|  | 			else if (ec == mine) | ||
|  | 			{ | ||
|  | 				// abc -> cab
 | ||
|  | 				unsigned int t = c; | ||
|  | 				c = b, b = a, a = t; | ||
|  | 
 | ||
|  | 				next = ec; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			if (restart_index) | ||
|  | 			{ | ||
|  | 				if (strip_size) | ||
|  | 					destination[strip_size++] = restart_index; | ||
|  | 
 | ||
|  | 				destination[strip_size++] = a; | ||
|  | 				destination[strip_size++] = b; | ||
|  | 				destination[strip_size++] = c; | ||
|  | 
 | ||
|  | 				// new strip always starts with the same edge winding
 | ||
|  | 				strip[0] = b; | ||
|  | 				strip[1] = c; | ||
|  | 				parity = 1; | ||
|  | 			} | ||
|  | 			else | ||
|  | 			{ | ||
|  | 				if (strip_size) | ||
|  | 				{ | ||
|  | 					// connect last strip using degenerate triangles
 | ||
|  | 					destination[strip_size++] = strip[1]; | ||
|  | 					destination[strip_size++] = a; | ||
|  | 				} | ||
|  | 
 | ||
|  | 				// note that we may need to flip the emitted triangle based on parity
 | ||
|  | 				// we always end up with outgoing edge "cb" in the end
 | ||
|  | 				unsigned int e0 = parity ? c : b; | ||
|  | 				unsigned int e1 = parity ? b : c; | ||
|  | 
 | ||
|  | 				destination[strip_size++] = a; | ||
|  | 				destination[strip_size++] = e0; | ||
|  | 				destination[strip_size++] = e1; | ||
|  | 
 | ||
|  | 				strip[0] = e0; | ||
|  | 				strip[1] = e1; | ||
|  | 				parity ^= 1; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return strip_size; | ||
|  | } | ||
|  | 
 | ||
|  | size_t meshopt_stripifyBound(size_t index_count) | ||
|  | { | ||
|  | 	assert(index_count % 3 == 0); | ||
|  | 
 | ||
|  | 	// worst case without restarts is 2 degenerate indices and 3 indices per triangle
 | ||
|  | 	// worst case with restarts is 1 restart index and 3 indices per triangle
 | ||
|  | 	return (index_count / 3) * 5; | ||
|  | } | ||
|  | 
 | ||
|  | size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count, unsigned int restart_index) | ||
|  | { | ||
|  | 	assert(destination != indices); | ||
|  | 
 | ||
|  | 	size_t offset = 0; | ||
|  | 	size_t start = 0; | ||
|  | 
 | ||
|  | 	for (size_t i = 0; i < index_count; ++i) | ||
|  | 	{ | ||
|  | 		if (restart_index && indices[i] == restart_index) | ||
|  | 		{ | ||
|  | 			start = i + 1; | ||
|  | 		} | ||
|  | 		else if (i - start >= 2) | ||
|  | 		{ | ||
|  | 			unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i]; | ||
|  | 
 | ||
|  | 			// flip winding for odd triangles
 | ||
|  | 			if ((i - start) & 1) | ||
|  | 			{ | ||
|  | 				unsigned int t = a; | ||
|  | 				a = b, b = t; | ||
|  | 			} | ||
|  | 
 | ||
|  | 			// although we use restart indices, strip swaps still produce degenerate triangles, so skip them
 | ||
|  | 			if (a != b && a != c && b != c) | ||
|  | 			{ | ||
|  | 				destination[offset + 0] = a; | ||
|  | 				destination[offset + 1] = b; | ||
|  | 				destination[offset + 2] = c; | ||
|  | 				offset += 3; | ||
|  | 			} | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return offset; | ||
|  | } | ||
|  | 
 | ||
|  | size_t meshopt_unstripifyBound(size_t index_count) | ||
|  | { | ||
|  | 	assert(index_count == 0 || index_count >= 3); | ||
|  | 
 | ||
|  | 	return (index_count == 0) ? 0 : (index_count - 2) * 3; | ||
|  | } |