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:
barry 2018-11-12 10:51:47 -08:00
parent f512d71777
commit 4e0a96f964
2 changed files with 192 additions and 144 deletions

View File

@ -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

View File

@ -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);