mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-22 19:50:08 +00:00
Improve Far::PatchMap to better support sparse and triangular PatchTables:
- identify the subrange of originating patch faces while assigning handles - limit queries and memory allocation to the identified subrange of faces - separate quadtree construction for quad and triangular patches
This commit is contained in:
parent
f512d71777
commit
4e0a96f964
@ -31,145 +31,176 @@ namespace OPENSUBDIV_VERSION {
|
||||
|
||||
namespace Far {
|
||||
|
||||
// Constructor
|
||||
PatchMap::PatchMap( PatchTable const & patchTable ) {
|
||||
initialize( patchTable );
|
||||
}
|
||||
//
|
||||
// Inline quadtree assembly methods used by the constructor:
|
||||
//
|
||||
|
||||
// sets all the children to point to the patch of given index
|
||||
inline void
|
||||
PatchMap::QuadNode::SetChildren(int index) {
|
||||
|
||||
// sets all the children to point to the patch of index patchIdx
|
||||
void
|
||||
PatchMap::QuadNode::SetChild(int patchIdx) {
|
||||
for (int i=0; i<4; ++i) {
|
||||
children[i].isSet=true;
|
||||
children[i].isLeaf=true;
|
||||
children[i].idx=patchIdx;
|
||||
children[i].isSet = true;
|
||||
children[i].isLeaf = true;
|
||||
children[i].index = index;
|
||||
}
|
||||
}
|
||||
|
||||
// sets the child in "quadrant" to point to the node or patch of the given index
|
||||
void
|
||||
PatchMap::QuadNode::SetChild(unsigned char quadrant, int idx, bool isLeaf) {
|
||||
assert(quadrant<4);
|
||||
inline void
|
||||
PatchMap::QuadNode::SetChild(int quadrant, int index, bool isLeaf) {
|
||||
|
||||
assert(!children[quadrant].isSet);
|
||||
children[quadrant].isSet = true;
|
||||
children[quadrant].isLeaf = isLeaf;
|
||||
children[quadrant].idx = idx;
|
||||
children[quadrant].index = index;
|
||||
}
|
||||
|
||||
// adds a child to a parent node and pushes it back on the tree
|
||||
PatchMap::QuadNode *
|
||||
PatchMap::addChild( QuadTree & quadtree, QuadNode * parent, int quadrant ) {
|
||||
quadtree.push_back(QuadNode());
|
||||
int idx = (int)quadtree.size()-1;
|
||||
parent->SetChild(quadrant, idx, false);
|
||||
return &(quadtree[idx]);
|
||||
inline void
|
||||
PatchMap::assignRootNode(QuadNode * node, int index) {
|
||||
|
||||
// Assign the given index to all children of the node (all leaves)
|
||||
node->SetChildren(index);
|
||||
}
|
||||
|
||||
void
|
||||
PatchMap::initialize( PatchTable const & patchTable ) {
|
||||
inline PatchMap::QuadNode *
|
||||
PatchMap::assignLeafOrChildNode(QuadNode * node, bool isLeaf, int quadrant, int index) {
|
||||
|
||||
// Assign the node given if it is a leaf node, otherwise traverse
|
||||
// the node -- creating/assigning a new child node if needed
|
||||
|
||||
if (isLeaf) {
|
||||
node->SetChild(quadrant, index, true);
|
||||
return node;
|
||||
}
|
||||
if (node->children[quadrant].isSet) {
|
||||
return &_quadtree[node->children[quadrant].index];
|
||||
} else {
|
||||
int newChildNodeIndex = (int)_quadtree.size();
|
||||
_quadtree.push_back(QuadNode());
|
||||
node->SetChild(quadrant, newChildNodeIndex, false);
|
||||
return &_quadtree[newChildNodeIndex];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Constructor and initialization methods for the handles and quadtree:
|
||||
//
|
||||
PatchMap::PatchMap(PatchTable const & patchTable) :
|
||||
_minPatchFace(-1), _maxPatchFace(-1), _maxDepth(0) {
|
||||
|
||||
_patchesAreTriangular =
|
||||
patchTable.GetVaryingPatchDescriptor().GetNumControlVertices() == 3;
|
||||
|
||||
int nfaces = 0,
|
||||
narrays = (int)patchTable.GetNumPatchArrays(),
|
||||
npatches = (int)patchTable.GetNumPatchesTotal();
|
||||
if (patchTable.GetNumPatchesTotal() > 0) {
|
||||
initializeHandles(patchTable);
|
||||
initializeQuadtree(patchTable);
|
||||
}
|
||||
}
|
||||
|
||||
if (! narrays || ! npatches)
|
||||
return;
|
||||
void
|
||||
PatchMap::initializeHandles(PatchTable const & patchTable) {
|
||||
|
||||
// populate subpatch handles vector
|
||||
_handles.resize(npatches);
|
||||
//
|
||||
// Populate the vector of patch Handles. Keep track of the min and max
|
||||
// face indices to allocate resources accordingly and limit queries:
|
||||
//
|
||||
_minPatchFace = (int) patchTable.GetPatchParamTable()[0].GetFaceId();
|
||||
_maxPatchFace = _minPatchFace;
|
||||
|
||||
for (int parray=0, current=0; parray<narrays; ++parray) {
|
||||
int numArrays = (int) patchTable.GetNumPatchArrays();
|
||||
int numPatches = (int) patchTable.GetNumPatchesTotal();
|
||||
|
||||
ConstPatchParamArray params = patchTable.GetPatchParams(parray);
|
||||
_handles.resize(numPatches);
|
||||
|
||||
int ringsize = patchTable.GetPatchArrayDescriptor(parray).GetNumControlVertices();
|
||||
for (int pArray = 0, handleIndex = 0; pArray < numArrays; ++pArray) {
|
||||
|
||||
for (Index j=0; j < patchTable.GetNumPatches(parray); ++j) {
|
||||
ConstPatchParamArray params = patchTable.GetPatchParams(pArray);
|
||||
|
||||
Handle & h = _handles[current];
|
||||
int patchSize = patchTable.GetPatchArrayDescriptor(pArray).GetNumControlVertices();
|
||||
|
||||
h.arrayIndex = parray;
|
||||
h.patchIndex = current;
|
||||
h.vertIndex = j * ringsize;
|
||||
for (Index j=0; j < patchTable.GetNumPatches(pArray); ++j, ++handleIndex) {
|
||||
|
||||
nfaces = std::max(nfaces, (int)params[j].GetFaceId());
|
||||
Handle & h = _handles[handleIndex];
|
||||
|
||||
++current;
|
||||
h.arrayIndex = pArray;
|
||||
h.patchIndex = handleIndex;
|
||||
h.vertIndex = j * patchSize;
|
||||
|
||||
int patchFaceId = params[j].GetFaceId();
|
||||
_minPatchFace = std::min(_minPatchFace, patchFaceId);
|
||||
_maxPatchFace = std::max(_maxPatchFace, patchFaceId);
|
||||
}
|
||||
}
|
||||
++nfaces;
|
||||
// temporary vector to hold the quadtree while under construction
|
||||
std::vector<QuadNode> quadtree;
|
||||
}
|
||||
|
||||
// reserve memory for the octree nodes (size is a worse-case approximation)
|
||||
quadtree.reserve( nfaces + npatches );
|
||||
void
|
||||
PatchMap::initializeQuadtree(PatchTable const & patchTable) {
|
||||
|
||||
// each coarse face has a root node associated to it that we need to initialize
|
||||
quadtree.resize(nfaces);
|
||||
//
|
||||
// Reserve quadtree nodes for the worst case and prune later. Set the
|
||||
// initial size to accomodate the root node of each patch face:
|
||||
//
|
||||
int nPatchFaces = (_maxPatchFace - _minPatchFace) + 1;
|
||||
|
||||
// populate the quadtree from the FarPatchArrays sub-patches
|
||||
for (Index parray=0, handleIndex=0; parray<narrays; ++parray) {
|
||||
int nHandles = (int)_handles.size();
|
||||
|
||||
ConstPatchParamArray params = patchTable.GetPatchParams(parray);
|
||||
_quadtree.reserve(nPatchFaces + nHandles);
|
||||
_quadtree.resize(nPatchFaces);
|
||||
|
||||
for (int i=0; i < patchTable.GetNumPatches(parray); ++i, ++handleIndex) {
|
||||
PatchParamTable const & params = patchTable.GetPatchParamTable();
|
||||
|
||||
PatchParam const & param = params[i];
|
||||
for (int handle = 0; handle < nHandles; ++handle) {
|
||||
|
||||
QuadNode * node = &quadtree[ param.GetFaceId() ];
|
||||
PatchParam const & param = params[handle];
|
||||
|
||||
int rootDepth = param.NonQuadRoot();
|
||||
int depth = param.GetDepth();
|
||||
int depth = param.GetDepth();
|
||||
int rootDepth = param.NonQuadRoot();
|
||||
|
||||
if (depth == rootDepth) {
|
||||
// special case : root level face with no sub-patches
|
||||
node->SetChild( handleIndex );
|
||||
continue;
|
||||
}
|
||||
_maxDepth = std::max(_maxDepth, depth);
|
||||
|
||||
// We can use the PatchParam bits directly to determine the quadrants
|
||||
// in which to place the patch -- just need to adjust the UV bits for
|
||||
// the special case of a rotated triangular patch:
|
||||
//
|
||||
QuadNode * node = &_quadtree[param.GetFaceId() - _minPatchFace];
|
||||
|
||||
if (depth == rootDepth) {
|
||||
assignRootNode(node, handle);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_patchesAreTriangular) {
|
||||
// Use the UV bits of the PatchParam directly for quad patches:
|
||||
int u = param.GetU();
|
||||
int v = param.GetV();
|
||||
|
||||
if (_patchesAreTriangular && param.IsTriangleRotated()) {
|
||||
u = (1 << depth) - u;
|
||||
v = (1 << depth) - v;
|
||||
}
|
||||
|
||||
for (int j = rootDepth + 1; j<= depth; ++j) {
|
||||
for (int j = rootDepth + 1; j <= depth; ++j) {
|
||||
int uBit = (u >> (depth - j)) & 1;
|
||||
int vBit = (v >> (depth - j)) & 1;
|
||||
|
||||
int quadrant = (vBit << 1) | uBit;
|
||||
|
||||
if (j == depth) {
|
||||
// we have reached the depth of the sub-patch : add a leaf
|
||||
assert( ! node->children[quadrant].isSet );
|
||||
node->SetChild(quadrant, handleIndex, true);
|
||||
} else {
|
||||
// travel down the child node of the corresponding quadrant
|
||||
if (! node->children[quadrant].isSet) {
|
||||
// create a new branch in the quadrant
|
||||
node = addChild(quadtree, node, quadrant);
|
||||
} else {
|
||||
// travel down an existing branch
|
||||
node = &(quadtree[ node->children[quadrant].idx ]);
|
||||
}
|
||||
}
|
||||
node = assignLeafOrChildNode(node, (j == depth), quadrant, handle);
|
||||
}
|
||||
} else {
|
||||
// Use an interior UV point of triangles to identify quadrants:
|
||||
double u = 0.25;
|
||||
double v = 0.25;
|
||||
param.UnnormalizeTriangle(u, v);
|
||||
|
||||
double median = 0.5;
|
||||
bool triRotated = false;
|
||||
|
||||
for (int j = rootDepth + 1; j <= depth; ++j, median *= 0.5) {
|
||||
int quadrant = transformUVToTriQuadrant(median, u, v, triRotated);
|
||||
|
||||
node = assignLeafOrChildNode(node, (j == depth), quadrant, handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy the resulting quadtree to eliminate un-unused vector capacity
|
||||
_quadtree = quadtree;
|
||||
// Swap the Node vector with a copy to reduce worst case memory allocation:
|
||||
QuadTree tmpTree = _quadtree;
|
||||
_quadtree.swap(tmpTree);
|
||||
}
|
||||
|
||||
|
||||
} // end namespace Far
|
||||
|
||||
} // end namespace OPENSUBDIV_VERSION
|
||||
|
@ -53,60 +53,66 @@ public:
|
||||
|
||||
/// \brief Constructor
|
||||
///
|
||||
/// @param patchTable A valid set of PatchTable
|
||||
/// @param patchTable A valid PatchTable
|
||||
///
|
||||
PatchMap( PatchTable const & patchTable );
|
||||
|
||||
/// \brief Returns a handle to the sub-patch of the face at the given (u,v).
|
||||
/// Note : the faceid corresponds to quadrangulated face indices (ie. quads
|
||||
/// count as 1 index, non-quads add as many indices as they have vertices)
|
||||
/// Note that the patch face ID corresponds to potentially quadrangulated
|
||||
/// face indices and not the base face indices (see Far::PtexIndices for more
|
||||
/// details).
|
||||
///
|
||||
/// @param faceid The index of the face
|
||||
/// @param patchFaceId The index of the face
|
||||
///
|
||||
/// @param u Local u parameter
|
||||
///
|
||||
/// @param v Local v parameter
|
||||
///
|
||||
/// @return A patch handle or NULL if the face does not exist or the
|
||||
/// limit surface is tagged as a hole at the given location
|
||||
/// @return A patch handle or 0 if the face is not supported (index
|
||||
/// out of bounds) or is tagged as a hole
|
||||
///
|
||||
Handle const * FindPatch( int faceid, double u, double v ) const;
|
||||
Handle const * FindPatch( int patchFaceId, double u, double v ) const;
|
||||
|
||||
private:
|
||||
void initializeHandles(PatchTable const & patchTable);
|
||||
void initializeQuadtree(PatchTable const & patchTable);
|
||||
|
||||
inline void initialize( PatchTable const & patchTable );
|
||||
|
||||
// Quadtree node with 4 children
|
||||
private:
|
||||
// Quadtree node with 4 children, tree is just a vector of nodes
|
||||
struct QuadNode {
|
||||
QuadNode() { std::memset(this, 0, sizeof(QuadNode)); }
|
||||
|
||||
struct Child {
|
||||
unsigned int isSet:1, // true if the child has been set
|
||||
isLeaf:1, // true if the child is a QuadNode
|
||||
idx:30; // child index (either QuadNode or Handle)
|
||||
unsigned int isSet : 1; // true if the child has been set
|
||||
unsigned int isLeaf : 1; // true if the child is a QuadNode
|
||||
unsigned int index : 30; // child index (either QuadNode or Handle)
|
||||
};
|
||||
|
||||
// sets all the children to point to the patch of index patchIdx
|
||||
void SetChild(int patchIdx);
|
||||
// sets all the children to point to the patch of given index
|
||||
void SetChildren(int index);
|
||||
|
||||
// sets the child in "quadrant" to point to the node or patch of the given index
|
||||
void SetChild(unsigned char quadrant, int child, bool isLeaf=true);
|
||||
void SetChild(int quadrant, int index, bool isLeaf);
|
||||
|
||||
Child children[4];
|
||||
};
|
||||
|
||||
typedef std::vector<QuadNode> QuadTree;
|
||||
|
||||
// adds a child to a parent node and pushes it back on the tree
|
||||
static QuadNode * addChild( QuadTree & quadtree, QuadNode * parent, int quadrant );
|
||||
// Internal methods supporting quadtree construction and queries
|
||||
void assignRootNode(QuadNode * node, int index);
|
||||
QuadNode * assignLeafOrChildNode(QuadNode * node, bool isLeaf, int quad, int index);
|
||||
|
||||
// identify and transform a (u,v) pair by its containing quadrant
|
||||
template <class T>
|
||||
static int transformToQuadQuadrant(T const & median, T & u, T & v);
|
||||
static int transformUVToQuadQuadrant(T const & median, T & u, T & v);
|
||||
template <class T>
|
||||
static int transformToTriQuadrant(T const & median, T & u, T & v, bool & rotated);
|
||||
static int transformUVToTriQuadrant(T const & median, T & u, T & v, bool & rotated);
|
||||
|
||||
private:
|
||||
bool _patchesAreTriangular; // tri and quad assembly and search requirements differ
|
||||
|
||||
bool _patchesAreTriangular;
|
||||
int _minPatchFace; // minimum patch face index supported by the map
|
||||
int _maxPatchFace; // maximum patch face index supported by the map
|
||||
int _maxDepth; // maximum depth of a patch in the tree
|
||||
|
||||
std::vector<Handle> _handles; // all the patches in the PatchTable
|
||||
std::vector<QuadNode> _quadtree; // quadtree nodes
|
||||
@ -119,22 +125,22 @@ private:
|
||||
// Quadrant indexing for tri and quad patches -- consistent with PatchParam's
|
||||
// usage of UV bits:
|
||||
//
|
||||
// (0,1) o (0,1) o-----o-----o (1,1)
|
||||
// |\ | | |
|
||||
// | \ | 2 | 3 |
|
||||
// | 2 \ | | |
|
||||
// o-----o o-----o-----o
|
||||
// |\ 3 |\ | | |
|
||||
// | \ | \ | 0 | 1 |
|
||||
// | 0 \| 1 \ | | |
|
||||
// (0,0) o-----o-----o (1,0) (0,0) o-----o-----o (1,0)
|
||||
// (0,1) o-----o-----o (1,1) (0,1) o (1,0) o-----o-----o (0,0)
|
||||
// | | | |\ \ 1 |\ 0 |
|
||||
// | 2 | 3 | | \ \ | \ |
|
||||
// | | | | 2 \ \| 3 \|
|
||||
// o-----o-----o o-----o o-----o
|
||||
// | | | |\ 3 |\ \ 2 |
|
||||
// | 0 | 1 | | \ | \ \ |
|
||||
// | | | | 0 \| 1 \ \|
|
||||
// (0,0) o-----o-----o (1,0) (0,0) o-----o-----o (1,0) o (0,1)
|
||||
//
|
||||
// The triangular case also takes and returns/affects the rotation of the
|
||||
// quadrant being searched and identified (quadrant 3 imparts a rotation).
|
||||
//
|
||||
template <class T>
|
||||
inline int
|
||||
PatchMap::transformToQuadQuadrant(T const & median, T & u, T & v) {
|
||||
PatchMap::transformUVToQuadQuadrant(T const & median, T & u, T & v) {
|
||||
|
||||
int uHalf = (u >= median);
|
||||
if (uHalf) u -= median;
|
||||
@ -147,7 +153,7 @@ PatchMap::transformToQuadQuadrant(T const & median, T & u, T & v) {
|
||||
|
||||
template <class T>
|
||||
int inline
|
||||
PatchMap::transformToTriQuadrant(T const & median, T & u, T & v, bool & rotated) {
|
||||
PatchMap::transformUVToTriQuadrant(T const & median, T & u, T & v, bool & rotated) {
|
||||
|
||||
if (!rotated) {
|
||||
if (u >= median) {
|
||||
@ -159,20 +165,26 @@ PatchMap::transformToTriQuadrant(T const & median, T & u, T & v, bool & rotated)
|
||||
return 2;
|
||||
}
|
||||
if ((u + v) >= median) {
|
||||
rotated = !rotated;
|
||||
rotated = true;
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
if (u < median) return 1;
|
||||
if (v < median) return 2;
|
||||
if (u < median) {
|
||||
v -= median;
|
||||
return 1;
|
||||
}
|
||||
if (v < median) {
|
||||
u -= median;
|
||||
return 2;
|
||||
}
|
||||
u -= median;
|
||||
v -= median;
|
||||
if ((u + v) >= median) {
|
||||
rotated = !rotated;
|
||||
return 0;
|
||||
if ((u + v) < median) {
|
||||
rotated = true;
|
||||
return 3;
|
||||
}
|
||||
return 3;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,33 +192,38 @@ PatchMap::transformToTriQuadrant(T const & median, T & u, T & v, bool & rotated)
|
||||
inline PatchMap::Handle const *
|
||||
PatchMap::FindPatch( int faceid, double u, double v ) const {
|
||||
|
||||
if (faceid>=(int)_quadtree.size())
|
||||
return 0;
|
||||
//
|
||||
// Reject patch faces not supported by this map, or those corresponding
|
||||
// to holes or otherwise unassigned (the root node for a patch will
|
||||
// have all or no quadrants set):
|
||||
//
|
||||
if ((faceid < _minPatchFace) || (faceid > _maxPatchFace)) return 0;
|
||||
|
||||
QuadNode const * node = &_quadtree[faceid - _minPatchFace];
|
||||
|
||||
if (!node->children[0].isSet) return 0;
|
||||
|
||||
//
|
||||
// Search the tree for the sub-patch containing the given (u,v)
|
||||
//
|
||||
assert( (u>=0.0) && (u<=1.0) && (v>=0.0) && (v<=1.0) );
|
||||
|
||||
QuadNode const * node = &_quadtree[faceid];
|
||||
|
||||
double median = 0.5;
|
||||
bool triRotated = false;
|
||||
|
||||
double half = 0.5;
|
||||
|
||||
// Patch depth is limited to 4 bits by PatchParam
|
||||
int maxDepth = (1 << 4);
|
||||
for (int depth = 0; depth < maxDepth; ++depth, half *= 0.5) {
|
||||
for (int depth = 0; depth <= _maxDepth; ++depth, median *= 0.5) {
|
||||
|
||||
int quadrant = _patchesAreTriangular
|
||||
? transformToTriQuadrant(half, u, v, triRotated)
|
||||
: transformToQuadQuadrant(half, u, v);
|
||||
? transformUVToTriQuadrant(median, u, v, triRotated)
|
||||
: transformUVToQuadQuadrant(median, u, v);
|
||||
|
||||
// is the quadrant a hole ?
|
||||
if (! node->children[quadrant].isSet)
|
||||
return 0;
|
||||
// holes should have been rejected at the root node of the face
|
||||
assert(node->children[quadrant].isSet);
|
||||
|
||||
if (node->children[quadrant].isLeaf) {
|
||||
return &_handles[node->children[quadrant].idx];
|
||||
return &_handles[node->children[quadrant].index];
|
||||
} else {
|
||||
node = &_quadtree[node->children[quadrant].idx];
|
||||
node = &_quadtree[node->children[quadrant].index];
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
|
Loading…
Reference in New Issue
Block a user