879 lines
29 KiB
C++
879 lines
29 KiB
C++
// Modifications copyright Epic Games, Inc. All rights reserved.
|
|
|
|
#ifndef NVTESS_H
|
|
#define NVTESS_H
|
|
|
|
#include <algorithm>
|
|
|
|
namespace nv {
|
|
enum DestBufferMode
|
|
{
|
|
/**
|
|
This buffer contains dominant Edge and Corner Information for each vertex,
|
|
suitable for performing crack-free displacement mapping on top of flat tessellation.
|
|
*/
|
|
DBM_DominantEdgeAndCorner = 0,
|
|
|
|
/**
|
|
This buffer contains indices needed to use with PN-AEN, but does not contain
|
|
information to support crack-free displacement mapping.
|
|
*/
|
|
DBM_PnAenOnly = 1,
|
|
|
|
/**
|
|
This buffer contains PN-AEN information along with dominant corner information. This
|
|
information is sufficient to perform crack-free displacement mapping
|
|
*/
|
|
DBM_PnAenDominantCorner = 2,
|
|
|
|
/**
|
|
This buffer contains PN-AEN information along with dominant corner and edge information.
|
|
Although dominant edges can be inferred from PN-AEN information alone (making this mode
|
|
somewhat bloated compared to DBM_PnAenDominantCorner), it does remove the need for some
|
|
computation in the Hull Shader.
|
|
*/
|
|
DBM_PnAenDominantEdgeAndCorner = 3,
|
|
|
|
DestBufferMode_MAX
|
|
};
|
|
|
|
enum IndexBufferType
|
|
{
|
|
IBT_U16, // 16-bit indices
|
|
IBT_U32, // 32-bit indices
|
|
|
|
IndexBufferType_MAX
|
|
};
|
|
|
|
struct Vector3
|
|
{
|
|
float x, y, z;
|
|
|
|
inline bool operator==(const Vector3& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z; }
|
|
inline bool operator<(const Vector3& rhs) const { return x < rhs.x || y < rhs.y || z < rhs.z; }
|
|
};
|
|
|
|
struct Vector2
|
|
{
|
|
float x, y;
|
|
|
|
inline bool operator==(const Vector2& rhs) const { return x == rhs.x && y == rhs.y; }
|
|
inline bool operator<(const Vector2& rhs) const { return x < rhs.x || (x == rhs.x && y < rhs.y); }
|
|
};
|
|
|
|
struct Vertex
|
|
{
|
|
nv::Vector3 pos;
|
|
nv::Vector2 uv;
|
|
|
|
inline bool operator==(const Vertex& rhs) const { return pos == rhs.pos; }
|
|
inline bool operator<(const Vertex& rhs) const { return pos < rhs.pos; }
|
|
};
|
|
|
|
/**
|
|
This is a simple wrapper class around an index buffer. The index buffer must contain triangle lists,
|
|
strips are not currently supported. This class will be used to pass index buffers into the library
|
|
as well as be used for the mechanism to return the created indices back to the application.
|
|
|
|
The IndexBuffer class can either own the specified void* pointer (it will be freed by calling delete),
|
|
or the application can indicate that it will retain ownership.
|
|
*/
|
|
class IndexBuffer
|
|
{
|
|
private:
|
|
unsigned char* mBufferContents;
|
|
IndexBufferType mIbType;
|
|
unsigned int mLength;
|
|
bool mBufferOwner;
|
|
|
|
public:
|
|
IndexBuffer(void* bufferContents, IndexBufferType ibtype, unsigned int length, bool bufferOwner);
|
|
~IndexBuffer();
|
|
|
|
inline IndexBufferType getType() const { return mIbType; }
|
|
inline unsigned int getLength() const { return mLength; }
|
|
unsigned int operator[](unsigned int index) const;
|
|
};
|
|
|
|
class RenderBuffer
|
|
{
|
|
protected:
|
|
IndexBuffer* mIb;
|
|
|
|
public:
|
|
inline virtual ~RenderBuffer() { delete mIb; }
|
|
|
|
/** Return the vertex information at the specified index. */
|
|
virtual nv::Vertex getVertex(unsigned int index) const = 0;
|
|
|
|
const IndexBuffer* getIb() const { return mIb; }
|
|
};
|
|
|
|
|
|
namespace tess {
|
|
inline unsigned int getIndicesPerPatch(DestBufferMode destBufferMode)
|
|
{
|
|
switch (destBufferMode) {
|
|
case DBM_DominantEdgeAndCorner: return 12;
|
|
case DBM_PnAenOnly: return 9;
|
|
case DBM_PnAenDominantCorner: return 12;
|
|
case DBM_PnAenDominantEdgeAndCorner: return 18;
|
|
default: // todo: Error
|
|
break;
|
|
}
|
|
|
|
return 3;
|
|
}
|
|
|
|
/**
|
|
Returns an index buffer suitable for the technique specified by destBufferMode.
|
|
|
|
Ownership of the return buffer belongs to the application and will need to be freed using
|
|
delete.
|
|
@destBufferMode - One of the modes specified in DestBufferMode.
|
|
@completeBuffer - Whether the buffer returned should have all indices. If true, all
|
|
*/
|
|
IndexBuffer* buildTessellationBuffer(const RenderBuffer* inputBuffer, DestBufferMode destBufferMode, bool completeBuffer);
|
|
};
|
|
};
|
|
|
|
// nvtess.cpp : Defines the exported functions for the DLL application.
|
|
//
|
|
|
|
#include "nvtess.h"
|
|
#include <assert.h>
|
|
|
|
#define USE_HASHMAP 1
|
|
#define NO_STL 1
|
|
|
|
#if USE_HASHMAP
|
|
#if PLATFORM_MAC || PLATFORM_LINUX
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
#else
|
|
#include <hash_map>
|
|
#endif
|
|
#else
|
|
#include <map>
|
|
#endif
|
|
|
|
const unsigned int EdgesPerTriangle = 3;
|
|
const unsigned int IndicesPerTriangle = 3;
|
|
const unsigned int VerticesPerTriangle = 3;
|
|
const unsigned int DuplicateIndexCount = 3;
|
|
|
|
namespace nv {
|
|
|
|
#if NO_STL
|
|
template <typename FromType, typename ToType>
|
|
class FHashMap
|
|
{
|
|
static const size_t INVALID_ENTRY = (size_t)-1;
|
|
struct FFromEntry
|
|
{
|
|
FromType From;
|
|
size_t NextEntry;
|
|
};
|
|
|
|
size_t* HashTable;
|
|
FFromEntry* FromEntries;
|
|
ToType* ToEntries;
|
|
size_t MaxEntries;
|
|
size_t MaxHashes;
|
|
size_t EntryCount;
|
|
|
|
public:
|
|
|
|
// These allow this class to stand in for the STL hash map where needed by this library.
|
|
struct FToRef
|
|
{
|
|
const ToType& second;
|
|
|
|
FORCEINLINE explicit FToRef( const ToType* Ptr ) : second( *Ptr )
|
|
{
|
|
}
|
|
|
|
FORCEINLINE const FToRef* operator->() const
|
|
{
|
|
return this;
|
|
}
|
|
};
|
|
|
|
struct iterator
|
|
{
|
|
const ToType* To;
|
|
|
|
FORCEINLINE explicit iterator( const ToType* Ptr ) : To( Ptr )
|
|
{
|
|
}
|
|
|
|
FORCEINLINE bool operator==( const iterator& Other ) const
|
|
{
|
|
return To == Other.To;
|
|
}
|
|
|
|
FORCEINLINE bool operator!=( const iterator& Other ) const
|
|
{
|
|
return To != Other.To;
|
|
}
|
|
|
|
FORCEINLINE const FToRef operator->() const
|
|
{
|
|
return FToRef( To );
|
|
}
|
|
};
|
|
|
|
typedef iterator const_iterator;
|
|
|
|
explicit FHashMap( const size_t InMaxEntries )
|
|
{
|
|
EntryCount = 0;
|
|
MaxEntries = InMaxEntries;
|
|
MaxHashes = (MaxEntries * 4) / 3;
|
|
HashTable = new size_t[MaxHashes];
|
|
FromEntries = new FFromEntry[MaxEntries];
|
|
ToEntries = new ToType[MaxEntries];
|
|
|
|
for ( size_t HashIndex = 0; HashIndex < MaxHashes; ++HashIndex )
|
|
{
|
|
HashTable[HashIndex] = INVALID_ENTRY;
|
|
}
|
|
}
|
|
|
|
~FHashMap()
|
|
{
|
|
delete [] HashTable;
|
|
delete [] FromEntries;
|
|
delete [] ToEntries;
|
|
}
|
|
|
|
void set( const FromType& From, const ToType& To )
|
|
{
|
|
const size_t Hash = hash_value( From ) % MaxHashes;
|
|
size_t EntryIndex = HashTable[Hash];
|
|
while ( EntryIndex != INVALID_ENTRY )
|
|
{
|
|
const FFromEntry& Entry = FromEntries[EntryIndex];
|
|
if ( Entry.From == From )
|
|
{
|
|
ToEntries[EntryIndex] = To;
|
|
return;
|
|
}
|
|
EntryIndex = Entry.NextEntry;
|
|
}
|
|
assert( EntryCount < MaxEntries );
|
|
FromEntries[EntryCount].NextEntry = HashTable[Hash];
|
|
FromEntries[EntryCount].From = From;
|
|
ToEntries[EntryCount] = To;
|
|
HashTable[Hash] = EntryCount;
|
|
EntryCount++;
|
|
}
|
|
|
|
FORCEINLINE const iterator end() const
|
|
{
|
|
return iterator( 0 );
|
|
}
|
|
|
|
const iterator find( const FromType& From ) const
|
|
{
|
|
const size_t Hash = hash_value( From ) % MaxHashes;
|
|
size_t EntryIndex = HashTable[Hash];
|
|
while ( EntryIndex != INVALID_ENTRY )
|
|
{
|
|
const FFromEntry& Entry = FromEntries[EntryIndex];
|
|
if ( Entry.From == From )
|
|
return iterator( &ToEntries[EntryIndex] );
|
|
EntryIndex = Entry.NextEntry;
|
|
}
|
|
return end();
|
|
}
|
|
};
|
|
#endif // #if NO_STL
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------------
|
|
IndexBuffer::IndexBuffer(void* bufferContents, IndexBufferType ibtype, unsigned int length, bool bufferOwner) :
|
|
mBufferContents((unsigned char*)bufferContents),
|
|
mIbType(ibtype),
|
|
mLength(length),
|
|
mBufferOwner(bufferOwner)
|
|
{ }
|
|
|
|
// ----------------------------------------------------------------------------------------------
|
|
IndexBuffer::~IndexBuffer()
|
|
{
|
|
if (mBufferOwner) {
|
|
delete[] mBufferContents;
|
|
}
|
|
|
|
mBufferContents = 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------------
|
|
unsigned int IndexBuffer::operator[](unsigned int index) const
|
|
{
|
|
switch (mIbType) {
|
|
case IBT_U16: return ((unsigned short*)mBufferContents)[index]; break;
|
|
case IBT_U32: return ((unsigned int*)mBufferContents)[index]; break;
|
|
default: break;
|
|
};
|
|
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
inline size_t hash_value(const Vector3& v3)
|
|
{
|
|
return 31337 * std::hash<float>()(v3.x) + 13 * std::hash<float>()(v3.y) + 3 * std::hash<float>()(v3.z);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
inline size_t hash_value(const Vertex& vert)
|
|
{
|
|
return hash_value(vert.pos);
|
|
}
|
|
|
|
namespace tess {
|
|
// ----------------------------------------------------------------------------------------
|
|
class Edge;
|
|
|
|
inline size_t hash_value(const Edge& edge);
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
class Edge
|
|
{
|
|
unsigned int mIndexFrom;
|
|
unsigned int mIndexTo;
|
|
|
|
nv::Vertex mVertexFrom;
|
|
nv::Vertex mVertexTo;
|
|
|
|
size_t mCachedHash;
|
|
|
|
public:
|
|
Edge() : mCachedHash(0) { }
|
|
|
|
Edge(unsigned int indexFrom, unsigned int indexTo, const nv::Vertex& vFrom, const nv::Vertex& vTo) :
|
|
mIndexFrom(indexFrom), mIndexTo(indexTo), mVertexFrom(vFrom), mVertexTo(vTo)
|
|
{
|
|
// Hash should only consider position, not index. We want values with different indices to compare true.
|
|
mCachedHash = 7 * hash_value(mVertexFrom) + 2 * hash_value(mVertexTo);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
nv::Vertex vertex(unsigned int i) const
|
|
{
|
|
switch(i) {
|
|
case 0: return mVertexFrom; break;
|
|
case 1: return mVertexTo; break;
|
|
default: assert(0); break;
|
|
}
|
|
return nv::Vertex();
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
unsigned int index(unsigned int i) const
|
|
{
|
|
switch(i) {
|
|
case 0: return mIndexFrom; break;
|
|
case 1: return mIndexTo; break;
|
|
default: assert(0); break;
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
Edge reverse() const
|
|
{
|
|
return Edge(mIndexTo, mIndexFrom, mVertexTo, mVertexFrom);
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
// When comparing edges, compare the vertices--not the indices.
|
|
FORCEINLINE bool operator<(const Edge& rhs) const
|
|
{
|
|
// Quick out, otherwise we have to compare vertices.
|
|
if (mIndexFrom == rhs.mIndexFrom && mIndexTo == rhs.mIndexTo) {
|
|
return false;
|
|
}
|
|
|
|
return mVertexFrom < rhs.mVertexFrom
|
|
|| mVertexTo < rhs.mVertexTo;
|
|
}
|
|
|
|
FORCEINLINE bool operator==( const Edge& Other ) const
|
|
{
|
|
return (mIndexFrom == Other.mIndexFrom && mIndexTo == Other.mIndexTo) ||
|
|
(mVertexFrom == Other.mVertexFrom && mVertexTo == Other.mVertexTo);
|
|
}
|
|
|
|
friend size_t hash_value(const Edge& edge);
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
struct Corner
|
|
{
|
|
unsigned int mIndex;
|
|
nv::Vector2 mUV;
|
|
|
|
Corner() : mIndex(0) {}
|
|
|
|
Corner(unsigned int index, nv::Vector2 uv) :
|
|
mIndex(index), mUV(uv)
|
|
{}
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
#if NO_STL
|
|
typedef FHashMap<Edge,Edge> EdgeDict;
|
|
typedef FHashMap<nv::Vector3,Corner> PositionDict;
|
|
#elif USE_HASHMAP
|
|
typedef stdext::hash_map<Edge, Edge> EdgeDict;
|
|
typedef stdext::hash_map<nv::Vector3, Corner> PositionDict;
|
|
#else
|
|
typedef std::map<Edge, Edge> EdgeDict;
|
|
typedef std::map<nv::Vector3, Corner> PositionDict;
|
|
#endif
|
|
|
|
typedef EdgeDict::iterator EdgeDictIt;
|
|
typedef PositionDict::iterator PositionDictIt;
|
|
|
|
void add_if_leastUV(PositionDict& outPd, const nv::Vertex& v, unsigned int i)
|
|
{
|
|
PositionDictIt foundIt = outPd.find(v.pos);
|
|
if(foundIt == outPd.end())
|
|
{
|
|
#if NO_STL
|
|
outPd.set( v.pos, Corner(i, v.uv) );
|
|
#else // #if NO_STL
|
|
outPd[v.pos] = Corner(i, v.uv);
|
|
#endif // #if NO_STL
|
|
}
|
|
else if(v.uv < foundIt->second.mUV)
|
|
{
|
|
#if NO_STL
|
|
outPd.set( v.pos, Corner(i, v.uv) );
|
|
#else // #if NO_STL
|
|
outPd[v.pos] = Corner(i, v.uv);
|
|
#endif // #if NO_STL
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
inline size_t hash_value(const Edge& edge)
|
|
{
|
|
return edge.mCachedHash;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
class Triangle
|
|
{
|
|
Edge mEdge0;
|
|
Edge mEdge1;
|
|
Edge mEdge2;
|
|
|
|
public:
|
|
inline const Edge& edge(unsigned int i) const { return ((Edge*)&mEdge0)[i]; }
|
|
inline unsigned int index(unsigned int i) const { return edge(i).index(0); }
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
Triangle(unsigned int i0, unsigned int i1, unsigned int i2, const nv::Vertex& v0, const nv::Vertex& v1, const nv::Vertex& v2) :
|
|
mEdge0(i0, i1, v0, v1),
|
|
mEdge1(i1, i2, v1, v2),
|
|
mEdge2(i2, i0, v2, v0)
|
|
{ }
|
|
|
|
// ------------------------------------------------------------------------------------
|
|
bool operator<(const Triangle& rhs) const
|
|
{
|
|
return mEdge0 < rhs.mEdge0
|
|
|| mEdge1 < rhs.mEdge1
|
|
|| mEdge2 < rhs.mEdge2;
|
|
}
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
template <typename ElemType>
|
|
IndexBuffer* createIndexBuffer(const unsigned int* newIndices, unsigned int indexCount, IndexBufferType destBufferType)
|
|
{
|
|
ElemType* elemBuffer = new ElemType[indexCount];
|
|
|
|
for (unsigned int u = 0; u < indexCount; ++u) {
|
|
elemBuffer[u] = (ElemType)newIndices[u];
|
|
}
|
|
|
|
return new IndexBuffer((void*)elemBuffer, destBufferType, indexCount, true);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
IndexBuffer* newIndexBuffer(const std::vector<unsigned int>& newIndices, const RenderBuffer* inputBuffer)
|
|
{
|
|
IndexBufferType ibType = inputBuffer->getIb()->getType();
|
|
|
|
switch (ibType) {
|
|
case IBT_U16: return createIndexBuffer<unsigned short>(&newIndices[0], (unsigned int)newIndices.size(), IBT_U16); break;
|
|
case IBT_U32: return createIndexBuffer<unsigned int>(&newIndices[0], (unsigned int)newIndices.size(), IBT_U32); break;
|
|
default: assert(0); break;
|
|
};
|
|
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
void expandIb_DominantEdgeAndCorner(unsigned int* outIb, const Triangle& tri, unsigned int startOutIndex)
|
|
{
|
|
outIb[startOutIndex + 0] = tri.index(0);
|
|
outIb[startOutIndex + 1] = tri.index(1);
|
|
outIb[startOutIndex + 2] = tri.index(2);
|
|
|
|
outIb[startOutIndex + 3] = tri.index(0);
|
|
outIb[startOutIndex + 4] = tri.index(1);
|
|
outIb[startOutIndex + 5] = tri.index(1);
|
|
outIb[startOutIndex + 6] = tri.index(2);
|
|
outIb[startOutIndex + 7] = tri.index(2);
|
|
outIb[startOutIndex + 8] = tri.index(0);
|
|
|
|
outIb[startOutIndex + 9] = tri.index(0);
|
|
outIb[startOutIndex + 10] = tri.index(1);
|
|
outIb[startOutIndex + 11] = tri.index(2);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
void expandIb_PnAenOnly(unsigned int* outIb, const Triangle& tri, unsigned int startOutIndex)
|
|
{
|
|
outIb[startOutIndex + 0] = tri.index(0);
|
|
outIb[startOutIndex + 1] = tri.index(1);
|
|
outIb[startOutIndex + 2] = tri.index(2);
|
|
|
|
outIb[startOutIndex + 3] = tri.index(0);
|
|
outIb[startOutIndex + 4] = tri.index(1);
|
|
outIb[startOutIndex + 5] = tri.index(1);
|
|
outIb[startOutIndex + 6] = tri.index(2);
|
|
outIb[startOutIndex + 7] = tri.index(2);
|
|
outIb[startOutIndex + 8] = tri.index(0);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
void expandIb_PnAenDominantCorner(unsigned int* outIb, const Triangle& tri, unsigned int startOutIndex)
|
|
{
|
|
outIb[startOutIndex + 0] = tri.index(0);
|
|
outIb[startOutIndex + 1] = tri.index(1);
|
|
outIb[startOutIndex + 2] = tri.index(2);
|
|
|
|
outIb[startOutIndex + 3] = tri.index(0);
|
|
outIb[startOutIndex + 4] = tri.index(1);
|
|
outIb[startOutIndex + 5] = tri.index(1);
|
|
outIb[startOutIndex + 6] = tri.index(2);
|
|
outIb[startOutIndex + 7] = tri.index(2);
|
|
outIb[startOutIndex + 8] = tri.index(0);
|
|
|
|
outIb[startOutIndex + 9] = tri.index(0);
|
|
outIb[startOutIndex + 10] = tri.index(1);
|
|
outIb[startOutIndex + 11] = tri.index(2);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
void expandIb_PnAenDominantEdgeAndCorner(unsigned int* outIb, const Triangle& tri, unsigned int startOutIndex)
|
|
{
|
|
outIb[startOutIndex + 0] = tri.index(0);
|
|
outIb[startOutIndex + 1] = tri.index(1);
|
|
outIb[startOutIndex + 2] = tri.index(2);
|
|
|
|
outIb[startOutIndex + 3] = tri.index(0);
|
|
outIb[startOutIndex + 4] = tri.index(1);
|
|
outIb[startOutIndex + 5] = tri.index(1);
|
|
outIb[startOutIndex + 6] = tri.index(2);
|
|
outIb[startOutIndex + 7] = tri.index(2);
|
|
outIb[startOutIndex + 8] = tri.index(0);
|
|
|
|
outIb[startOutIndex + 9] = tri.index(0);
|
|
outIb[startOutIndex + 10] = tri.index(1);
|
|
outIb[startOutIndex + 11] = tri.index(1);
|
|
outIb[startOutIndex + 12] = tri.index(2);
|
|
outIb[startOutIndex + 13] = tri.index(2);
|
|
outIb[startOutIndex + 14] = tri.index(0);
|
|
|
|
outIb[startOutIndex + 15] = tri.index(0);
|
|
outIb[startOutIndex + 16] = tri.index(1);
|
|
outIb[startOutIndex + 17] = tri.index(2);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
void expandIb(unsigned int* outIb, DestBufferMode destBufferMode, EdgeDict& outEd, PositionDict& outPd, const RenderBuffer* inputBuffer)
|
|
{
|
|
const IndexBuffer* inIb = inputBuffer->getIb();
|
|
const unsigned int triCount = inIb->getLength() / IndicesPerTriangle;
|
|
const unsigned int outputIndicesPerPatch = getIndicesPerPatch(destBufferMode);
|
|
|
|
bool requiresEdgeDict = true;
|
|
bool requiresPositionDict = destBufferMode != DBM_PnAenOnly;
|
|
|
|
for (unsigned int u = 0; u < triCount; ++u) {
|
|
|
|
const unsigned int startInIndex = u * IndicesPerTriangle;
|
|
const unsigned int startOutIndex = u * outputIndicesPerPatch;
|
|
|
|
const unsigned int i0 = (*inIb)[startInIndex + 0],
|
|
i1 = (*inIb)[startInIndex + 1],
|
|
i2 = (*inIb)[startInIndex + 2];
|
|
|
|
const Vertex v0 = inputBuffer->getVertex(i0),
|
|
v1 = inputBuffer->getVertex(i1),
|
|
v2 = inputBuffer->getVertex(i2);
|
|
|
|
Triangle tri(i0, i1, i2, v0, v1, v2);
|
|
|
|
switch (destBufferMode) {
|
|
case DBM_DominantEdgeAndCorner:
|
|
expandIb_DominantEdgeAndCorner(outIb, tri, startOutIndex);
|
|
break;
|
|
case DBM_PnAenOnly:
|
|
expandIb_PnAenOnly(outIb, tri, startOutIndex);
|
|
break;
|
|
case DBM_PnAenDominantCorner:
|
|
expandIb_PnAenDominantCorner(outIb, tri, startOutIndex);
|
|
break;
|
|
case DBM_PnAenDominantEdgeAndCorner:
|
|
expandIb_PnAenDominantEdgeAndCorner(outIb, tri, startOutIndex);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
};
|
|
|
|
|
|
if (requiresEdgeDict) {
|
|
Edge rev0 = tri.edge(0).reverse(),
|
|
rev1 = tri.edge(1).reverse(),
|
|
rev2 = tri.edge(2).reverse();
|
|
#if NO_STL
|
|
outEd.set( rev0, rev0 );
|
|
outEd.set( rev1, rev1 );
|
|
outEd.set( rev2, rev2 );
|
|
#else // #if NO_STL
|
|
outEd[rev0] = rev0;
|
|
outEd[rev1] = rev1;
|
|
outEd[rev2] = rev2;
|
|
#endif // #if NO_STL
|
|
}
|
|
|
|
if (requiresPositionDict) {
|
|
add_if_leastUV(outPd, v0, i0);
|
|
add_if_leastUV(outPd, v1, i1);
|
|
add_if_leastUV(outPd, v2, i2);
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
void replacePlaceholderIndices_DominantEdgeAndCorner(unsigned int* outIb, const Triangle& tri, unsigned int startOutIndex, const EdgeDict& edgeDict, const PositionDict& posDict)
|
|
{
|
|
// First, compute dominant edges. We define dominant edges to be the edge that has either the lower
|
|
// minimum index or the lower maximum index. This designation is purely capricious, but it is stable.
|
|
for (unsigned int u = 0; u < EdgesPerTriangle; ++u) {
|
|
EdgeDict::const_iterator fwdIt = edgeDict.find(tri.edge(u));
|
|
EdgeDict::const_iterator revIt = edgeDict.find(tri.edge(u).reverse());
|
|
EdgeDict::const_iterator cit = edgeDict.end();
|
|
|
|
if (fwdIt != edgeDict.end() && revIt != edgeDict.end()) {
|
|
unsigned int eFmin = std::min(fwdIt->second.index(0), fwdIt->second.index(1));
|
|
unsigned int eFmax = std::max(fwdIt->second.index(0), fwdIt->second.index(1));
|
|
unsigned int eRmin = std::min(revIt->second.index(0), revIt->second.index(1));
|
|
unsigned int eRmax = std::max(revIt->second.index(0), revIt->second.index(1));
|
|
|
|
if (eFmin < eRmin) {
|
|
cit = fwdIt;
|
|
} else if (eRmin < eFmin) {
|
|
cit = revIt;
|
|
} else if (eFmax < eRmax) {
|
|
cit = fwdIt;
|
|
} else if (eRmax < eFmax) {
|
|
// Could actually fold this and the final case together,
|
|
// but this is logically easier to understand
|
|
cit = revIt;
|
|
} else {
|
|
// In this case, the indices are the same--so it doesn't matter what we choose.
|
|
cit = revIt;
|
|
}
|
|
} else if (fwdIt != edgeDict.end()) {
|
|
cit = fwdIt;
|
|
} else if (revIt != edgeDict.end()) {
|
|
cit = revIt;
|
|
}
|
|
|
|
if (cit != edgeDict.end()) {
|
|
outIb[startOutIndex + 3 + 2 * u] = cit->second.index(0);
|
|
outIb[startOutIndex + 4 + 2 * u] = cit->second.index(1);
|
|
}
|
|
}
|
|
|
|
// Dominant Positions are much easier.
|
|
for (unsigned u = 0; u < VerticesPerTriangle; ++u) {
|
|
PositionDict::const_iterator pit = posDict.find(tri.edge(u).vertex(0).pos);
|
|
if (pit != posDict.end()) {
|
|
outIb[startOutIndex + 9 + u] = pit->second.mIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
void replacePlaceholderIndices_PnAenOnly(unsigned int* outIb, const Triangle& tri, unsigned int startOutIndex, const EdgeDict& edgeDict, const PositionDict& posDict)
|
|
{
|
|
EdgeDict::const_iterator cit = edgeDict.find(tri.edge(0));
|
|
|
|
if (cit != edgeDict.end()) {
|
|
outIb[startOutIndex + 3] = cit->second.index(0);
|
|
outIb[startOutIndex + 4] = cit->second.index(1);
|
|
}
|
|
|
|
cit = edgeDict.find(tri.edge(1));
|
|
if (cit != edgeDict.end()) {
|
|
outIb[startOutIndex + 5] = cit->second.index(0);
|
|
outIb[startOutIndex + 6] = cit->second.index(1);
|
|
}
|
|
|
|
cit = edgeDict.find(tri.edge(2));
|
|
if (cit != edgeDict.end()) {
|
|
outIb[startOutIndex + 7] = cit->second.index(0);
|
|
outIb[startOutIndex + 8] = cit->second.index(1);
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
void replacePlaceholderIndices_PnAenDominantCorner(unsigned int* outIb, const Triangle& tri, unsigned int startOutIndex, const EdgeDict& edgeDict, const PositionDict& posDict)
|
|
{
|
|
replacePlaceholderIndices_PnAenOnly(outIb, tri, startOutIndex, edgeDict, posDict);
|
|
|
|
// Deal with dominant positions.
|
|
for (unsigned u = 0; u < VerticesPerTriangle; ++u) {
|
|
PositionDict::const_iterator pit = posDict.find(tri.edge(u).vertex(0).pos);
|
|
if (pit != posDict.end()) {
|
|
outIb[startOutIndex + 9 + u] = pit->second.mIndex;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
void replacePlaceholderIndices_PnAenDominantEdgeAndCorner(unsigned int* outIb, const Triangle& tri, unsigned int startOutIndex, const EdgeDict& edgeDict, const PositionDict& posDict)
|
|
{
|
|
replacePlaceholderIndices_PnAenOnly(outIb, tri, startOutIndex, edgeDict, posDict);
|
|
replacePlaceholderIndices_DominantEdgeAndCorner(outIb, tri, startOutIndex + getIndicesPerPatch(DBM_PnAenOnly) - VerticesPerTriangle, edgeDict, posDict);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// TODO: Remove the input buffer, replace with a cached copy of the "vertex" buffer
|
|
void replacePlaceholderIndices(unsigned int* outIb,
|
|
unsigned int indexCount,
|
|
DestBufferMode destBufferMode,
|
|
const EdgeDict& edgeDict,
|
|
const PositionDict& posDict,
|
|
const RenderBuffer* inputBuffer)
|
|
{
|
|
const unsigned int outputIndicesPerPatch = getIndicesPerPatch(destBufferMode);
|
|
const unsigned int triCount = indexCount / outputIndicesPerPatch;
|
|
|
|
for (unsigned int u = 0; u < triCount; ++u) {
|
|
const unsigned int startOutIndex = u * outputIndicesPerPatch;
|
|
|
|
const unsigned int i0 = outIb[startOutIndex + 0],
|
|
i1 = outIb[startOutIndex + 1],
|
|
i2 = outIb[startOutIndex + 2];
|
|
|
|
const Vertex v0 = inputBuffer->getVertex(i0),
|
|
v1 = inputBuffer->getVertex(i1),
|
|
v2 = inputBuffer->getVertex(i2);
|
|
|
|
Triangle tri(i0, i1, i2, v0, v1, v2);
|
|
|
|
switch (destBufferMode) {
|
|
case DBM_DominantEdgeAndCorner:
|
|
replacePlaceholderIndices_DominantEdgeAndCorner(outIb, tri, startOutIndex, edgeDict, posDict);
|
|
break;
|
|
case DBM_PnAenOnly:
|
|
replacePlaceholderIndices_PnAenOnly(outIb, tri, startOutIndex, edgeDict, posDict);
|
|
break;
|
|
case DBM_PnAenDominantCorner:
|
|
replacePlaceholderIndices_PnAenDominantCorner(outIb, tri, startOutIndex, edgeDict, posDict);
|
|
break;
|
|
case DBM_PnAenDominantEdgeAndCorner:
|
|
replacePlaceholderIndices_PnAenDominantEdgeAndCorner(outIb, tri, startOutIndex, edgeDict, posDict);
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
};
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
void stripUnusedIndices(std::vector<unsigned int>& outIb, DestBufferMode destBufferMode)
|
|
{
|
|
const unsigned int indicesPerPatch = getIndicesPerPatch(destBufferMode);
|
|
const unsigned int destIndicesPerPatch = indicesPerPatch - DuplicateIndexCount;
|
|
|
|
const unsigned int numPatches = (unsigned int) outIb.size() / indicesPerPatch;
|
|
const unsigned int newIbSize = numPatches * destIndicesPerPatch;
|
|
|
|
std::vector<unsigned int> newIb(newIbSize);
|
|
|
|
unsigned int sourceIndex = DuplicateIndexCount;
|
|
const unsigned int * __restrict srcIndices = &outIb[0];
|
|
unsigned int * __restrict destIndices = &newIb[0];
|
|
for (unsigned int destIndex = 0; destIndex < newIbSize; ++destIndex) {
|
|
destIndices[destIndex] = srcIndices[sourceIndex++];
|
|
if (sourceIndex % indicesPerPatch == 0) {
|
|
sourceIndex += DuplicateIndexCount;
|
|
}
|
|
}
|
|
|
|
// Pointer swap.
|
|
outIb.swap(newIb);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
IndexBuffer* buildTessellationBuffer(const RenderBuffer* inputBuffer, DestBufferMode destBufferMode, bool completeBuffer)
|
|
{
|
|
#if NO_STL
|
|
EdgeDict edgeDict(inputBuffer->getIb()->getLength());
|
|
PositionDict posDict(inputBuffer->getIb()->getLength());
|
|
#else // #if NO_STL
|
|
EdgeDict edgeDict;
|
|
PositionDict posDict;
|
|
#if USE_HASHMAP
|
|
edgeDict.rehash(inputBuffer->getIb()->getLength() * EdgesPerTriangle / IndicesPerTriangle * 2 + 1);
|
|
posDict.rehash(inputBuffer->getIb()->getLength() * VerticesPerTriangle / IndicesPerTriangle * 2 + 1);
|
|
#endif
|
|
#endif // #if NO_STL
|
|
|
|
std::vector<unsigned int> newIb(getIndicesPerPatch(destBufferMode) * inputBuffer->getIb()->getLength() / IndicesPerTriangle);
|
|
expandIb(&newIb[0], destBufferMode, edgeDict, posDict, inputBuffer);
|
|
|
|
replacePlaceholderIndices(&newIb[0], (unsigned int)newIb.size(), destBufferMode, edgeDict, posDict, inputBuffer);
|
|
if (!completeBuffer) {
|
|
stripUnusedIndices(newIb, destBufferMode);
|
|
}
|
|
|
|
return newIndexBuffer(newIb, inputBuffer);
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
#endif /* NVTESS_H */
|