zeux / meshoptimizer
Showing 2 of 4 files from the diff.

@@ -97,6 +97,22 @@
Loading
97 97
 */
98 98
MESHOPTIMIZER_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);
99 99
100 +
/**
101 +
 * Generate index buffer that can be used for PN-AEN tessellation with crack-free displacement
102 +
 * Each triangle is converted into a 12-vertex patch with the following layout:
103 +
 * - 0, 1, 2: original triangle vertices
104 +
 * - 3, 4: opposing edge for edge 0, 1
105 +
 * - 5, 6: opposing edge for edge 1, 2
106 +
 * - 7, 8: opposing edge for edge 2, 0
107 +
 * - 9, 10, 11: dominant vertices for corners 0, 1, 2
108 +
 * The resulting patch can be rendered with hardware tessellation using PN-AEN and displacement mapping.
109 +
 * See "Tessellation on Any Budget" (John McDonald, GDC 2011) for implementation details.
110 +
 *
111 +
 * destination must contain enough space for the resulting index buffer (index_count*4 elements)
112 +
 * vertex_positions should have float3 position in the first 12 bytes of each vertex - similar to glVertexPointer
113 +
 */
114 +
MESHOPTIMIZER_EXPERIMENTAL 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);
115 +
100 116
/**
101 117
 * Vertex transform cache optimizer
102 118
 * Reorders indices to reduce the number of GPU vertex shader invocations
@@ -522,6 +538,8 @@
Loading
522 538
template <typename T>
523 539
inline void meshopt_generateShadowIndexBufferMulti(T* destination, const T* indices, size_t index_count, size_t vertex_count, const meshopt_Stream* streams, size_t stream_count);
524 540
template <typename T>
541 +
inline 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);
542 +
template <typename T>
525 543
inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count);
526 544
template <typename T>
527 545
inline void meshopt_optimizeVertexCacheStrip(T* destination, const T* indices, size_t index_count, size_t vertex_count);
@@ -772,6 +790,15 @@
Loading
772 790
	meshopt_generateShadowIndexBufferMulti(out.data, in.data, index_count, vertex_count, streams, stream_count);
773 791
}
774 792
793 +
template <typename T>
794 +
inline 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)
795 +
{
796 +
	meshopt_IndexAdapter<T> in(0, indices, index_count);
797 +
	meshopt_IndexAdapter<T> out(destination, 0, index_count * 4);
798 +
799 +
	meshopt_generateTessellationIndexBuffer(out.data, in.data, index_count, vertex_positions, vertex_count, vertex_positions_stride);
800 +
}
801 +
775 802
template <typename T>
776 803
inline void meshopt_optimizeVertexCache(T* destination, const T* indices, size_t index_count, size_t vertex_count)
777 804
{

@@ -4,6 +4,8 @@
Loading
4 4
#include <assert.h>
5 5
#include <string.h>
6 6
7 +
// This work is based on:
8 +
// John McDonald, Mark Kilgard. Crack-Free Point-Normal Triangles using Adjacent Edge Normals. 2010
7 9
namespace meshopt
8 10
{
9 11
@@ -83,6 +85,45 @@
Loading
83 85
	}
84 86
};
85 87
88 +
struct EdgeHasher
89 +
{
90 +
	const unsigned int* remap;
91 +
92 +
	size_t hash(unsigned long long edge) const
93 +
	{
94 +
		unsigned int e0 = unsigned(edge >> 32);
95 +
		unsigned int e1 = unsigned(edge);
96 +
97 +
		unsigned int h1 = remap[e0];
98 +
		unsigned int h2 = remap[e1];
99 +
100 +
		const unsigned int m = 0x5bd1e995;
101 +
102 +
		// MurmurHash64B finalizer
103 +
		h1 ^= h2 >> 18;
104 +
		h1 *= m;
105 +
		h2 ^= h1 >> 22;
106 +
		h2 *= m;
107 +
		h1 ^= h2 >> 17;
108 +
		h1 *= m;
109 +
		h2 ^= h1 >> 19;
110 +
		h2 *= m;
111 +
112 +
		return h2;
113 +
	}
114 +
115 +
	bool equal(unsigned long long lhs, unsigned long long rhs) const
116 +
	{
117 +
		unsigned int l0 = unsigned(lhs >> 32);
118 +
		unsigned int l1 = unsigned(lhs);
119 +
120 +
		unsigned int r0 = unsigned(rhs >> 32);
121 +
		unsigned int r1 = unsigned(rhs);
122 +
123 +
		return remap[l0] == remap[r0] && remap[l1] == remap[r1];
124 +
	}
125 +
};
126 +
86 127
static size_t hashBuckets(size_t count)
87 128
{
88 129
	size_t buckets = 1;
@@ -345,3 +386,91 @@
Loading
345 386
		destination[i] = remap[index];
346 387
	}
347 388
}
389 +
390 +
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)
391 +
{
392 +
	using namespace meshopt;
393 +
394 +
	assert(index_count % 3 == 0);
395 +
	assert(vertex_positions_stride > 0 && vertex_positions_stride <= 256);
396 +
	assert(vertex_positions_stride % sizeof(float) == 0);
397 +
398 +
	meshopt_Allocator allocator;
399 +
400 +
	static const int next[3] = {1, 2, 0};
401 +
402 +
	VertexHasher vertex_hasher = {reinterpret_cast<const unsigned char*>(vertex_positions), 3 * sizeof(float), vertex_positions_stride};
403 +
404 +
	size_t vertex_table_size = hashBuckets(vertex_count);
405 +
	unsigned int* vertex_table = allocator.allocate<unsigned int>(vertex_table_size);
406 +
	memset(vertex_table, -1, vertex_table_size * sizeof(unsigned int));
407 +
408 +
	// build position remap: for each vertex, which other (canonical) vertex does it map to?
409 +
	unsigned int* remap = allocator.allocate<unsigned int>(vertex_count);
410 +
411 +
	for (size_t i = 0; i < vertex_count; ++i)
412 +
	{
413 +
		unsigned int index = unsigned(i);
414 +
		unsigned int* entry = hashLookup(vertex_table, vertex_table_size, vertex_hasher, index, ~0u);
415 +
416 +
		if (*entry == ~0u)
417 +
			*entry = index;
418 +
419 +
		remap[index] = *entry;
420 +
	}
421 +
422 +
	// build edge set; this stores all triangle edges but we can look these up by any other wedge
423 +
	EdgeHasher edge_hasher = {remap};
424 +
425 +
	size_t edge_table_size = hashBuckets(index_count);
426 +
	unsigned long long* edge_table = allocator.allocate<unsigned long long>(edge_table_size);
427 +
	memset(edge_table, -1, edge_table_size * sizeof(unsigned long long));
428 +
429 +
	for (size_t i = 0; i < index_count; i += 3)
430 +
	{
431 +
		for (int e = 0; e < 3; ++e)
432 +
		{
433 +
			unsigned int i0 = indices[i + e];
434 +
			unsigned int i1 = indices[i + next[e]];
435 +
			assert(i0 < vertex_count && i1 < vertex_count);
436 +
437 +
			unsigned long long edge = ((unsigned long long)i0 << 32) | i1;
438 +
			unsigned long long* entry = hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);
439 +
440 +
			if (*entry == ~0ull)
441 +
				*entry = edge;
442 +
		}
443 +
	}
444 +
445 +
	// build resulting index buffer: 12 indices for each input triangle
446 +
	for (size_t i = 0; i < index_count; i += 3)
447 +
	{
448 +
		unsigned int patch[12];
449 +
450 +
		for (int e = 0; e < 3; ++e)
451 +
		{
452 +
			unsigned int i0 = indices[i + e];
453 +
			unsigned int i1 = indices[i + next[e]];
454 +
			assert(i0 < vertex_count && i1 < vertex_count);
455 +
456 +
			// note: this refers to the opposite edge!
457 +
			unsigned long long edge = ((unsigned long long)i1 << 32) | i0;
458 +
			unsigned long long oppe = *hashLookup(edge_table, edge_table_size, edge_hasher, edge, ~0ull);
459 +
460 +
			// use the same edge if opposite edge doesn't exist (border)
461 +
			oppe = (oppe == ~0ull) ? edge : oppe;
462 +
463 +
			// triangle index (0, 1, 2)
464 +
			patch[e] = i0;
465 +
466 +
			// opposite edge (3, 4; 5, 6; 7, 8)
467 +
			patch[3 + e * 2 + 0] = unsigned(oppe);
468 +
			patch[3 + e * 2 + 1] = unsigned(oppe >> 32);
469 +
470 +
			// dominant vertex (9, 10, 11)
471 +
			patch[9 + e] = remap[i0];
472 +
		}
473 +
474 +
		memcpy(destination + i * 4, patch, sizeof(patch));
475 +
	}
476 +
}
Files Coverage
src 99.42%
Project Totals (16 files) 99.42%
1
comment: false
2

3
coverage:
4
  status:
5
    project: off
6
    patch: off
7

8
ignore:
9
  - demo
10
  - tools
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading