mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-29 06:41:09 +00:00
12eea1cf0b
This should fix the exceptions thrown by Windows checking STL vector boundaries. fixes #155
1034 lines
42 KiB
C++
1034 lines
42 KiB
C++
//
|
|
// Copyright (C) Pixar. All rights reserved.
|
|
//
|
|
// This license governs use of the accompanying software. If you
|
|
// use the software, you accept this license. If you do not accept
|
|
// the license, do not use the software.
|
|
//
|
|
// 1. Definitions
|
|
// The terms "reproduce," "reproduction," "derivative works," and
|
|
// "distribution" have the same meaning here as under U.S.
|
|
// copyright law. A "contribution" is the original software, or
|
|
// any additions or changes to the software.
|
|
// A "contributor" is any person or entity that distributes its
|
|
// contribution under this license.
|
|
// "Licensed patents" are a contributor's patent claims that read
|
|
// directly on its contribution.
|
|
//
|
|
// 2. Grant of Rights
|
|
// (A) Copyright Grant- Subject to the terms of this license,
|
|
// including the license conditions and limitations in section 3,
|
|
// each contributor grants you a non-exclusive, worldwide,
|
|
// royalty-free copyright license to reproduce its contribution,
|
|
// prepare derivative works of its contribution, and distribute
|
|
// its contribution or any derivative works that you create.
|
|
// (B) Patent Grant- Subject to the terms of this license,
|
|
// including the license conditions and limitations in section 3,
|
|
// each contributor grants you a non-exclusive, worldwide,
|
|
// royalty-free license under its licensed patents to make, have
|
|
// made, use, sell, offer for sale, import, and/or otherwise
|
|
// dispose of its contribution in the software or derivative works
|
|
// of the contribution in the software.
|
|
//
|
|
// 3. Conditions and Limitations
|
|
// (A) No Trademark License- This license does not grant you
|
|
// rights to use any contributor's name, logo, or trademarks.
|
|
// (B) If you bring a patent claim against any contributor over
|
|
// patents that you claim are infringed by the software, your
|
|
// patent license from such contributor to the software ends
|
|
// automatically.
|
|
// (C) If you distribute any portion of the software, you must
|
|
// retain all copyright, patent, trademark, and attribution
|
|
// notices that are present in the software.
|
|
// (D) If you distribute any portion of the software in source
|
|
// code form, you may do so only under this license by including a
|
|
// complete copy of this license with your distribution. If you
|
|
// distribute any portion of the software in compiled or object
|
|
// code form, you may only do so under a license that complies
|
|
// with this license.
|
|
// (E) The software is licensed "as-is." You bear the risk of
|
|
// using it. The contributors give no express warranties,
|
|
// guarantees or conditions. You may have additional consumer
|
|
// rights under your local laws which this license cannot change.
|
|
// To the extent permitted under your local laws, the contributors
|
|
// exclude the implied warranties of merchantability, fitness for
|
|
// a particular purpose and non-infringement.
|
|
//
|
|
|
|
#ifndef FAR_PATCH_TABLES_FACTORY_H
|
|
#define FAR_PATCH_TABLES_FACTORY_H
|
|
|
|
#include "../version.h"
|
|
|
|
#include "../far/patchTables.h"
|
|
|
|
namespace OpenSubdiv {
|
|
namespace OPENSUBDIV_VERSION {
|
|
|
|
/// \brief A specialized factory for feature adaptive FarPatchTables
|
|
///
|
|
/// FarPatchTables contain the lists of vertices for each patch of an adaptive
|
|
/// mesh representation. This specialized factory is a private helper for FarMeshFactory.
|
|
///
|
|
/// Separating the factory allows us to isolate Far data structures from Hbr dependencies.
|
|
///
|
|
template <class T> class FarPatchTablesFactory {
|
|
|
|
protected:
|
|
template <class X, class Y> friend class FarMeshFactory;
|
|
|
|
/// Factory constructor
|
|
/// @param mesh Hbr mesh to generate tables for
|
|
/// @param nfaces Number of faces in the mesh (cached for speed)
|
|
/// @param remapTable Vertex remapping table generated by FarMeshFactory
|
|
FarPatchTablesFactory( HbrMesh<T> const * mesh, int nfaces, std::vector<int> const & remapTable );
|
|
|
|
/// Returns a FarPatchTables instance
|
|
/// @param maxlevel Highest level of refinement processed
|
|
/// @param maxvalence Maximum vertex valence in the mesh
|
|
/// @param requirePtexCoordinate Flag for generating ptex coordinate
|
|
/// @param requireFVarData Flag for generating face-varying data
|
|
FarPatchTables * Create( int maxlevel, int maxvalence, bool requirePtexCoordinate=false,
|
|
bool requireFVarData=false );
|
|
|
|
private:
|
|
|
|
// Returns true if one of v's neighboring faces has vertices carrying the tag "wasTagged"
|
|
static bool vertexHasTaggedNeighbors(HbrVertex<T> * v);
|
|
|
|
// Returns the rotation for a boundary patch
|
|
static unsigned char computeBoundaryPatchRotation( HbrFace<T> * f );
|
|
|
|
// Returns the rotation for a corner patch
|
|
static unsigned char computeCornerPatchRotation( HbrFace<T> * f );
|
|
|
|
// Populates an array of indices with the "one-ring" vertices for the given face
|
|
void getOneRing( HbrFace<T> * f, int ringsize, unsigned int const * remap, unsigned int * result );
|
|
|
|
// Populates the Gregory patch quad offsets table
|
|
static void getQuadOffsets( HbrFace<T> * f, unsigned int * result );
|
|
|
|
// Hbr mesh accessor
|
|
HbrMesh<T> const * getMesh() const { return _mesh; }
|
|
|
|
// Number of faces in the Hbr mesh (cached for speed)
|
|
int getNumFaces() const { return _nfaces; }
|
|
|
|
template<class X> struct Pointers {
|
|
X R_P, // regular patch
|
|
B_P[4], // boundary patch (4 rotations)
|
|
C_P[4], // corner patch (4 rotations)
|
|
G_P[2]; // gregory patch (boundary & corner)
|
|
|
|
Pointers() { memset(this, 0, sizeof(Pointers<X>)); }
|
|
};
|
|
|
|
typedef Pointers<unsigned int*> IndexPointers;
|
|
typedef Pointers<unsigned char*> LevelPointers;
|
|
typedef Pointers<FarPtexCoord *> PtexPointers;
|
|
typedef Pointers<float *> FVarPointers;
|
|
|
|
struct Counters {
|
|
int R_C, // regular patch
|
|
B_C[4], // boundary patch (4 rotations)
|
|
C_C[4], // corner patch (4 rotations)
|
|
G_C[2]; // gregory patch (boundary & corner)
|
|
|
|
Counters() { memset(this, 0, sizeof(Counters)); }
|
|
};
|
|
|
|
Counters _fullCtr, // counters for full patches
|
|
_transitionCtr[5]; // counters for transition patches
|
|
|
|
HbrMesh<T> const * _mesh;
|
|
|
|
// Reference to the vertex remapping table generated by FarMeshFactory
|
|
std::vector<int> const &_remapTable;
|
|
|
|
int _nfaces;
|
|
};
|
|
|
|
template <class T> bool
|
|
FarPatchTablesFactory<T>::vertexHasTaggedNeighbors(HbrVertex<T> * v) {
|
|
|
|
assert(v);
|
|
|
|
HbrHalfedge<T> * start = v->GetIncidentEdge(),
|
|
* next=start;
|
|
do {
|
|
HbrFace<T> * right = next->GetRightFace(),
|
|
* left = next->GetLeftFace();
|
|
|
|
if (right and (not right->hasTaggedVertices()))
|
|
return true;
|
|
|
|
if (left and (not left->hasTaggedVertices()))
|
|
return true;
|
|
|
|
next = v->GetNextEdge(next);
|
|
|
|
} while (next and next!=start);
|
|
return false;
|
|
}
|
|
|
|
// Returns a rotation index for boundary patches (range [0-3])
|
|
template <class T> unsigned char
|
|
FarPatchTablesFactory<T>::computeBoundaryPatchRotation( HbrFace<T> * f ) {
|
|
unsigned char rot=0;
|
|
for (unsigned char i=0; i<4;++i) {
|
|
if (f->GetVertex(i)->OnBoundary() and
|
|
f->GetVertex((i+1)%4)->OnBoundary())
|
|
break;
|
|
++rot;
|
|
}
|
|
return rot;
|
|
}
|
|
|
|
// Returns a rotation index for corner patches (range [0-3])
|
|
template <class T> unsigned char
|
|
FarPatchTablesFactory<T>::computeCornerPatchRotation( HbrFace<T> * f ) {
|
|
unsigned char rot=0;
|
|
for (unsigned char i=0; i<4; ++i) {
|
|
if (not f->GetVertex((i+3)%4)->OnBoundary())
|
|
break;
|
|
++rot;
|
|
}
|
|
return rot;
|
|
}
|
|
|
|
|
|
template <class T>
|
|
FarPatchTablesFactory<T>::FarPatchTablesFactory( HbrMesh<T> const * mesh, int nfaces, std::vector<int> const & remapTable ) :
|
|
_mesh(mesh),
|
|
_remapTable(remapTable),
|
|
_nfaces(nfaces)
|
|
{
|
|
assert(mesh and nfaces>0);
|
|
|
|
// First pass : identify transition / watertight-critical
|
|
for (int i=0; i<nfaces; ++i) {
|
|
|
|
HbrFace<T> * f = mesh->GetFace(i);
|
|
|
|
if (f->_adaptiveFlags.isTagged and (not f->IsHole())) {
|
|
HbrVertex<T> * v = f->Subdivide();
|
|
assert(v);
|
|
v->_adaptiveFlags.wasTagged=true;
|
|
}
|
|
|
|
int nv = f->GetNumVertices();
|
|
for (int j=0; j<nv; ++j) {
|
|
|
|
if (f->IsCoarse())
|
|
f->GetVertex(j)->_adaptiveFlags.wasTagged=true;
|
|
|
|
HbrHalfedge<T> * e = f->GetEdge(j);
|
|
|
|
// Flag transition edge that require a triangulated transition
|
|
if (f->_adaptiveFlags.isTagged) {
|
|
e->_adaptiveFlags.isTriangleHead=true;
|
|
|
|
// Both half-edges need to be tagged if an opposite exists
|
|
if (e->GetOpposite())
|
|
e->GetOpposite()->_adaptiveFlags.isTriangleHead=true;
|
|
}
|
|
|
|
HbrFace<T> * left = e->GetLeftFace(),
|
|
* right = e->GetRightFace();
|
|
|
|
if (not (left and right))
|
|
continue;
|
|
|
|
// a tagged edge w/ no children is inside a hole
|
|
if (e->HasChild() and (left->_adaptiveFlags.isTagged ^ right->_adaptiveFlags.isTagged)) {
|
|
|
|
e->_adaptiveFlags.isTransition = true;
|
|
|
|
HbrVertex<T> * child = e->Subdivide();
|
|
assert(child);
|
|
|
|
// These edges will require extra rows of CVs to maintain water-tightness
|
|
// Note : vertices inside holes have no children
|
|
if (e->GetOrgVertex()->HasChild()) {
|
|
HbrHalfedge<T> * org = child->GetEdge(e->GetOrgVertex()->Subdivide());
|
|
if (org)
|
|
org->_adaptiveFlags.isWatertightCritical=true;
|
|
}
|
|
|
|
if (e->GetDestVertex()->HasChild()) {
|
|
HbrHalfedge<T> * dst = child->GetEdge(e->GetDestVertex()->Subdivide());
|
|
if (dst)
|
|
dst->_adaptiveFlags.isWatertightCritical=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Second pass : count boundaries / identify transition constellation
|
|
for (int i=0; i<nfaces; ++i) {
|
|
|
|
HbrFace<T> * f = mesh->GetFace(i);
|
|
|
|
if (mesh->GetSubdivision()->FaceIsExtraordinary(mesh,f))
|
|
continue;
|
|
|
|
if (f->IsHole())
|
|
continue;
|
|
|
|
bool isTagged=0, wasTagged=0, isConnected=0, isWatertightCritical=0, isExtraordinary=0;
|
|
int triangleHeads=0, boundaryVerts=0;
|
|
|
|
int nv = f->GetNumVertices();
|
|
for (int j=0; j<nv; ++j) {
|
|
HbrVertex<T> * v = f->GetVertex(j);
|
|
|
|
if (v->OnBoundary()) {
|
|
boundaryVerts++;
|
|
|
|
// Boundary vertices with valence higher than 3 aren't Full Boundary
|
|
// patches, they are Gregory Boundary patches.
|
|
if (v->IsSingular() or v->GetValence()>3)
|
|
isExtraordinary=true;
|
|
|
|
} else if (v->IsExtraordinary())
|
|
isExtraordinary=true;
|
|
|
|
if (f->GetParent() and (not isWatertightCritical))
|
|
isWatertightCritical = vertexHasTaggedNeighbors(v);
|
|
|
|
if (v->_adaptiveFlags.isTagged)
|
|
isTagged=1;
|
|
|
|
if (v->_adaptiveFlags.wasTagged)
|
|
wasTagged=1;
|
|
|
|
// Count the number of triangle heads to find which transition
|
|
// pattern to use.
|
|
HbrHalfedge<T> * e = f->GetEdge(j);
|
|
if (e->_adaptiveFlags.isTriangleHead) {
|
|
|
|
++triangleHeads;
|
|
if (f->GetEdge((j+1)%4)->_adaptiveFlags.isTriangleHead)
|
|
isConnected=true;
|
|
}
|
|
}
|
|
|
|
f->_adaptiveFlags.bverts=boundaryVerts;
|
|
f->_adaptiveFlags.isCritical=isWatertightCritical;
|
|
|
|
// Regular Boundary Patch
|
|
if (wasTagged)
|
|
// XXXX manuelk - need to implement end patches
|
|
f->_adaptiveFlags.patchType = HbrFace<T>::kEnd;
|
|
|
|
if (f->_adaptiveFlags.isTagged)
|
|
continue;
|
|
|
|
assert(f->_adaptiveFlags.rots==0 and nv==4);
|
|
|
|
if (not isTagged and wasTagged) {
|
|
|
|
if (triangleHeads==0) {
|
|
|
|
if (not isExtraordinary and boundaryVerts!=1) {
|
|
|
|
// Full Patches
|
|
f->_adaptiveFlags.patchType = HbrFace<T>::kFull;
|
|
|
|
switch (boundaryVerts) {
|
|
|
|
case 0 : { // Regular patch
|
|
_fullCtr.R_C++;
|
|
} break;
|
|
|
|
case 2 : { // Boundary patch
|
|
f->_adaptiveFlags.rots=computeBoundaryPatchRotation(f);
|
|
_fullCtr.B_C[0]++;
|
|
} break;
|
|
|
|
case 3 : { // Corner patch
|
|
f->_adaptiveFlags.rots=computeCornerPatchRotation(f);
|
|
_fullCtr.C_C[0]++;
|
|
} break;
|
|
|
|
default : break;
|
|
}
|
|
} else {
|
|
|
|
// Default to Gregory Patch
|
|
f->_adaptiveFlags.patchType = HbrFace<T>::kGregory;
|
|
|
|
switch (boundaryVerts) {
|
|
|
|
case 0 : { // Regular Gregory patch
|
|
_fullCtr.G_C[0]++;
|
|
} break;
|
|
|
|
|
|
default : { // Boundary Gregory patch
|
|
_fullCtr.G_C[1]++;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// Transition Patch
|
|
|
|
// Resolve transition constellation : 5 types (see p.5 fig. 7)
|
|
switch (triangleHeads) {
|
|
|
|
case 1 : { for (unsigned char j=0; j<4; ++j) {
|
|
if (f->GetEdge(j)->IsTriangleHead())
|
|
break;
|
|
f->_adaptiveFlags.rots++;
|
|
}
|
|
f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition0;
|
|
} break;
|
|
|
|
case 2 : { for (unsigned char j=0; j<4; ++j) {
|
|
if (isConnected) {
|
|
if (f->GetEdge(j)->IsTriangleHead() and
|
|
f->GetEdge((j+3)%4)->IsTriangleHead())
|
|
break;
|
|
} else {
|
|
if (f->GetEdge(j)->IsTriangleHead())
|
|
break;
|
|
}
|
|
f->_adaptiveFlags.rots++;
|
|
}
|
|
|
|
if (isConnected)
|
|
f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition1;
|
|
else
|
|
f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition4;
|
|
} break;
|
|
|
|
case 3 : { for (unsigned char j=0; j<4; ++j) {
|
|
if (not f->GetEdge(j)->IsTriangleHead())
|
|
break;
|
|
f->_adaptiveFlags.rots++;
|
|
}
|
|
f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition2;
|
|
} break;
|
|
|
|
case 4 : f->_adaptiveFlags.transitionType = HbrFace<T>::kTransition3;
|
|
break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
int tidx = f->_adaptiveFlags.transitionType;
|
|
assert(tidx>=0);
|
|
|
|
// Correct rotations for corners & boundaries
|
|
if (not isExtraordinary and boundaryVerts!=1) {
|
|
|
|
switch (boundaryVerts) {
|
|
|
|
case 0 : { // regular patch
|
|
_transitionCtr[tidx].R_C++;
|
|
} break;
|
|
|
|
case 2 : { // boundary patch
|
|
unsigned char rot=computeBoundaryPatchRotation(f);
|
|
|
|
f->_adaptiveFlags.brots=(4-f->_adaptiveFlags.rots+rot)%4;
|
|
|
|
f->_adaptiveFlags.rots=rot; // override the transition rotation
|
|
|
|
_transitionCtr[tidx].B_C[f->_adaptiveFlags.brots]++;
|
|
} break;
|
|
|
|
case 3 : { // corner patch
|
|
unsigned char rot=computeCornerPatchRotation(f);
|
|
|
|
f->_adaptiveFlags.brots=(4-f->_adaptiveFlags.rots+rot)%4;
|
|
|
|
f->_adaptiveFlags.rots=rot; // override the transition rotation
|
|
|
|
_transitionCtr[tidx].C_C[f->_adaptiveFlags.brots]++;
|
|
} break;
|
|
|
|
default : assert(0); break;
|
|
}
|
|
} else {
|
|
// Use Gregory Patch transition ?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class T> FarPatchTables *
|
|
FarPatchTablesFactory<T>::Create( int maxlevel, int maxvalence, bool requirePtexCoordinate,
|
|
bool requireFVarData ) {
|
|
|
|
assert(getMesh() and getNumFaces()>0);
|
|
|
|
FarPatchTables * result = new FarPatchTables(maxvalence);
|
|
|
|
static const unsigned int remapRegular [16] = {5,6,10,9,4,0,1,2,3,7,11,15,14,13,12,8};
|
|
static const unsigned int remapRegularBoundary[12] = {1,2,6,5,0,3,7,11,10,9,8,4};
|
|
static const unsigned int remapRegularCorner [ 9] = {1,2,5,4,0,8,7,6,3};
|
|
|
|
IndexPointers fptrs, tptrs[5];
|
|
LevelPointers fptrsLv, tptrsLv[5];
|
|
PtexPointers fptrsPtx, tptrsPtx[5];
|
|
FVarPointers fptrsFvd, tptrsFvd[5];
|
|
|
|
// Allocate all index tables
|
|
|
|
// Full Patches
|
|
result->_full._R_IT.first.resize(_fullCtr.R_C*16);
|
|
result->_full._R_IT.second.resize(_fullCtr.R_C);
|
|
fptrs.R_P = result->_full._R_IT.first.empty() ? 0 : &result->_full._R_IT.first[0];
|
|
fptrsLv.R_P = result->_full._R_IT.second.empty() ? 0 : &result->_full._R_IT.second[0];
|
|
|
|
// Full Boundary Patches
|
|
result->_full._B_IT.first.resize(_fullCtr.B_C[0]*12);
|
|
result->_full._B_IT.second.resize(_fullCtr.B_C[0]);
|
|
fptrs.B_P[0] = result->_full._B_IT.first.empty() ? 0 : &result->_full._B_IT.first[0];
|
|
fptrsLv.B_P[0] = result->_full._B_IT.second.empty() ? 0 : &result->_full._B_IT.second[0];
|
|
|
|
// Full Corner Patches
|
|
result->_full._C_IT.first.resize(_fullCtr.C_C[0]*9);
|
|
result->_full._C_IT.second.resize(_fullCtr.C_C[0]);
|
|
fptrs.C_P[0] = result->_full._C_IT.first.empty() ? 0 : &result->_full._C_IT.first[0];
|
|
fptrsLv.C_P[0] = result->_full._C_IT.second.empty() ? 0 : &result->_full._C_IT.second[0];
|
|
|
|
// Full Gregory patches
|
|
result->_full._G_IT.first.resize(_fullCtr.G_C[0]*4);
|
|
result->_full._G_IT.second.resize(_fullCtr.G_C[0]);
|
|
fptrs.G_P[0] = result->_full._G_IT.first.empty() ? 0 : &result->_full._G_IT.first[0];
|
|
fptrsLv.G_P[0] = result->_full._G_IT.second.empty() ? 0 : &result->_full._G_IT.second[0];
|
|
|
|
// Full Gregory Boundary patches
|
|
result->_full._G_B_IT.first.resize(_fullCtr.G_C[1]*4);
|
|
result->_full._G_B_IT.second.resize(_fullCtr.G_C[1]);
|
|
fptrs.G_P[1] = result->_full._G_B_IT.first.empty() ? 0 : &result->_full._G_B_IT.first[0];
|
|
fptrsLv.G_P[1] = result->_full._G_B_IT.second.empty() ? 0 : &result->_full._G_B_IT.second[0];
|
|
|
|
// Quad-offsets tables (for Gregory patches)
|
|
FarPatchTables::QuadOffsetTable quad_G_C0;
|
|
quad_G_C0.resize(_fullCtr.G_C[0]*4);
|
|
|
|
FarPatchTables::QuadOffsetTable quad_G_C1;
|
|
quad_G_C1.resize(_fullCtr.G_C[1]*4);
|
|
|
|
FarPatchTables::QuadOffsetTable::value_type *quad_G_C0_P = quad_G_C0.empty() ? 0 : &quad_G_C0[0];
|
|
FarPatchTables::QuadOffsetTable::value_type *quad_G_C1_P = quad_G_C1.empty() ? 0 : &quad_G_C1[0];
|
|
|
|
// Transition Patches
|
|
for (int i=0; i<5; ++i) {
|
|
|
|
result->_transition[i]._R_IT.first.resize(_transitionCtr[i].R_C*16);
|
|
result->_transition[i]._R_IT.second.resize(_transitionCtr[i].R_C);
|
|
tptrs[i].R_P = result->_transition[i]._R_IT.first.empty() ? 0 : &result->_transition[i]._R_IT.first[0];
|
|
tptrsLv[i].R_P = result->_transition[i]._R_IT.second.empty() ? 0 : &result->_transition[i]._R_IT.second[0];
|
|
|
|
for (int j=0; j<4; ++j) {
|
|
|
|
result->_transition[i]._B_IT[j].first.resize(_transitionCtr[i].B_C[j]*12);
|
|
result->_transition[i]._B_IT[j].second.resize(_transitionCtr[i].B_C[j]);
|
|
tptrs[i].B_P[j] = result->_transition[i]._B_IT[j].first.empty() ? 0 : &result->_transition[i]._B_IT[j].first[0];
|
|
tptrsLv[i].B_P[j] = result->_transition[i]._B_IT[j].second.empty() ? 0 : &result->_transition[i]._B_IT[j].second[0];
|
|
|
|
result->_transition[i]._C_IT[j].first.resize(_transitionCtr[i].C_C[j]*9);
|
|
result->_transition[i]._C_IT[j].second.resize(_transitionCtr[i].C_C[j]);
|
|
tptrs[i].C_P[j] = result->_transition[i]._C_IT[j].first.empty() ? 0 : &result->_transition[i]._C_IT[j].first[0];
|
|
tptrsLv[i].C_P[j] = result->_transition[i]._C_IT[j].second.empty() ? 0 : &result->_transition[i]._C_IT[j].second[0];
|
|
}
|
|
}
|
|
|
|
// Allocate ptex coordinate table if necessary
|
|
if (requirePtexCoordinate) {
|
|
result->_full._R_PTX.resize(_fullCtr.R_C);
|
|
fptrsPtx.R_P = result->_full._R_PTX.empty() ? 0 : &result->_full._R_PTX[0];
|
|
|
|
result->_full._B_PTX.resize(_fullCtr.B_C[0]);
|
|
fptrsPtx.B_P[0] = result->_full._B_PTX.empty() ? 0 : &result->_full._B_PTX[0];
|
|
|
|
result->_full._C_PTX.resize(_fullCtr.C_C[0]);
|
|
fptrsPtx.C_P[0] = result->_full._C_PTX.empty() ? 0 : &result->_full._C_PTX[0];
|
|
|
|
result->_full._G_PTX.resize(_fullCtr.G_C[0]);
|
|
fptrsPtx.G_P[0] = result->_full._G_PTX.empty() ? 0 : &result->_full._G_PTX[0];
|
|
|
|
result->_full._G_B_PTX.resize(_fullCtr.G_C[1]);
|
|
fptrsPtx.G_P[1] = result->_full._G_B_PTX.empty() ? 0 : &result->_full._G_B_PTX[0];
|
|
|
|
for (int i=0; i < 5; ++i) {
|
|
result->_transition[i]._R_PTX.resize(_transitionCtr[i].R_C);
|
|
tptrsPtx[i].R_P = result->_transition[i]._R_PTX.empty() ? 0 : &result->_transition[i]._R_PTX[0];
|
|
|
|
for (int j=0; j < 4; ++j) {
|
|
result->_transition[i]._B_PTX[j].resize(_transitionCtr[i].B_C[j]);
|
|
tptrsPtx[i].B_P[j] = result->_transition[i]._B_PTX[j].empty() ? 0 : &result->_transition[i]._B_PTX[j][0];
|
|
|
|
result->_transition[i]._C_PTX[j].resize(_transitionCtr[i].C_C[j]);
|
|
tptrsPtx[i].C_P[j] = result->_transition[i]._C_PTX[j].empty() ? 0 : &result->_transition[i]._C_PTX[j][0];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Allocate face-varying data table if necessary
|
|
if (requireFVarData) {
|
|
int width = 4*getMesh()->GetTotalFVarWidth();
|
|
result->_full._R_FVD.resize(_fullCtr.R_C*width);
|
|
fptrsFvd.R_P = result->_full._R_FVD.empty() ? 0 : &result->_full._R_FVD[0];
|
|
|
|
result->_full._B_FVD.resize(_fullCtr.B_C[0]*width);
|
|
fptrsFvd.B_P[0] = result->_full._B_FVD.empty() ? 0 : &result->_full._B_FVD[0];
|
|
|
|
result->_full._C_FVD.resize(_fullCtr.C_C[0]*width);
|
|
fptrsFvd.C_P[0] = result->_full._C_FVD.empty() ? 0 : &result->_full._C_FVD[0];
|
|
|
|
result->_full._G_FVD.resize(_fullCtr.G_C[0]*width);
|
|
fptrsFvd.G_P[0] = result->_full._G_FVD.empty() ? 0 : &result->_full._G_FVD[0];
|
|
|
|
result->_full._G_B_FVD.resize(_fullCtr.G_C[1]*width);
|
|
fptrsFvd.G_P[1] = result->_full._G_B_FVD.empty() ? 0 : &result->_full._G_B_FVD[0];
|
|
|
|
for (int i=0; i < 5; ++i) {
|
|
result->_transition[i]._R_FVD.resize(_transitionCtr[i].R_C*width);
|
|
tptrsFvd[i].R_P = result->_transition[i]._R_FVD.empty() ? 0 : &result->_transition[i]._R_FVD[0];
|
|
|
|
for (int j=0; j < 4; ++j) {
|
|
result->_transition[i]._B_FVD[j].resize(_transitionCtr[i].B_C[j]*width);
|
|
tptrsFvd[i].B_P[j] = result->_transition[i]._B_FVD[j].empty() ? 0 : &result->_transition[i]._B_FVD[j][0];
|
|
|
|
result->_transition[i]._C_FVD[j].resize(_transitionCtr[i].C_C[j]*width);
|
|
tptrsFvd[i].C_P[j] = result->_transition[i]._C_FVD[j].empty() ? 0 : &result->_transition[i]._C_FVD[j][0];
|
|
}
|
|
}
|
|
}
|
|
|
|
int currentDepth = 0;
|
|
|
|
int fvarWidth = getMesh()->GetTotalFVarWidth();
|
|
|
|
// Populate patch index tables with vertex indices
|
|
for (int i=0; i<getNumFaces(); ++i) {
|
|
|
|
HbrFace<T> * f = getMesh()->GetFace(i);
|
|
|
|
int depth = f->GetDepth();
|
|
if (depth!=currentDepth) {
|
|
assert(depth==currentDepth+1);
|
|
currentDepth = depth;
|
|
}
|
|
|
|
if (not f->isTransitionPatch() ) {
|
|
|
|
// Full / End patches
|
|
|
|
if (f->_adaptiveFlags.patchType==HbrFace<T>::kFull) {
|
|
if (not f->_adaptiveFlags.isExtraordinary and f->_adaptiveFlags.bverts!=1) {
|
|
|
|
switch (f->_adaptiveFlags.bverts) {
|
|
case 0 : { // Regular Patch (16 CVs)
|
|
getOneRing(f, 16, remapRegular, fptrs.R_P);
|
|
fptrs.R_P+=16;
|
|
*fptrsLv.R_P++ = depth;
|
|
fptrsPtx.R_P = computePtexCoordinate(f, fptrsPtx.R_P);
|
|
fptrsFvd.R_P = computeFVarData(f, fvarWidth, fptrsFvd.R_P, /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
case 2 : { // Boundary Patch (12 CVs)
|
|
f->_adaptiveFlags.brots = (f->_adaptiveFlags.rots+1)%4;
|
|
getOneRing(f, 12, remapRegularBoundary, fptrs.B_P[0]);
|
|
fptrs.B_P[0]+=12;
|
|
*fptrsLv.B_P[0]++ = depth;
|
|
fptrsPtx.B_P[0] = computePtexCoordinate(f, fptrsPtx.B_P[0]);
|
|
fptrsFvd.B_P[0] = computeFVarData(f, fvarWidth, fptrsFvd.B_P[0], /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
case 3 : { // Corner Patch (9 CVs)
|
|
f->_adaptiveFlags.brots = (f->_adaptiveFlags.rots+1)%4;
|
|
getOneRing(f, 9, remapRegularCorner, fptrs.C_P[0]);
|
|
fptrs.C_P[0]+=9;
|
|
*fptrsLv.C_P[0]++ = depth;
|
|
fptrsPtx.C_P[0] = computePtexCoordinate(f, fptrsPtx.C_P[0]);
|
|
fptrsFvd.C_P[0] = computeFVarData(f, fvarWidth, fptrsFvd.C_P[0], /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
default : assert(0);
|
|
}
|
|
}
|
|
} else if (f->_adaptiveFlags.patchType==HbrFace<T>::kGregory) {
|
|
|
|
if (f->_adaptiveFlags.bverts==0) {
|
|
|
|
// Gregory Regular Patch (4 CVs + quad-offsets / valence tables)
|
|
for (int j=0; j<4; ++j)
|
|
fptrs.G_P[0][j] = _remapTable[f->GetVertex(j)->GetID()];
|
|
fptrs.G_P[0]+=4;
|
|
*fptrsLv.G_P[0]++ = depth;
|
|
getQuadOffsets(f, quad_G_C0_P);
|
|
quad_G_C0_P += 4;
|
|
fptrsPtx.G_P[0] = computePtexCoordinate(f, fptrsPtx.G_P[0]);
|
|
fptrsFvd.G_P[0] = computeFVarData(f, fvarWidth, fptrsFvd.G_P[0], /*isAdaptive=*/true);
|
|
} else {
|
|
|
|
// Gregory Boundary Patch (4 CVs + quad-offsets / valence tables)
|
|
for (int j=0; j<4; ++j)
|
|
fptrs.G_P[1][j] = _remapTable[f->GetVertex(j)->GetID()];
|
|
fptrs.G_P[1]+=4;
|
|
*fptrsLv.G_P[1]++ = depth;
|
|
getQuadOffsets(f, quad_G_C1_P);
|
|
quad_G_C1_P += 4;
|
|
fptrsPtx.G_P[1] = computePtexCoordinate(f, fptrsPtx.G_P[1]);
|
|
fptrsFvd.G_P[1] = computeFVarData(f, fvarWidth, fptrsFvd.G_P[1], /*isAdaptive=*/true);
|
|
}
|
|
} else {
|
|
// XXXX manuelk - end patches here
|
|
}
|
|
} else {
|
|
|
|
// Transition patches
|
|
|
|
int tcase = f->_adaptiveFlags.transitionType;
|
|
assert( tcase>=HbrFace<T>::kTransition0 and tcase<=HbrFace<T>::kTransition4 );
|
|
|
|
if (not f->_adaptiveFlags.isExtraordinary and f->_adaptiveFlags.bverts!=1) {
|
|
|
|
switch (f->_adaptiveFlags.bverts) {
|
|
case 0 : { // Regular Transition Patch (16 CVs)
|
|
getOneRing(f, 16, remapRegular, tptrs[tcase].R_P);
|
|
tptrs[tcase].R_P+=16;
|
|
*tptrsLv[tcase].R_P++ = depth;
|
|
tptrsPtx[tcase].R_P = computePtexCoordinate(f, tptrsPtx[tcase].R_P);
|
|
tptrsFvd[tcase].R_P = computeFVarData(f, fvarWidth, tptrsFvd[tcase].R_P, /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
case 2 : { // Boundary Transition Patch (12 CVs)
|
|
unsigned rot = f->_adaptiveFlags.brots;
|
|
getOneRing(f, 12, remapRegularBoundary, tptrs[tcase].B_P[rot]);
|
|
tptrs[tcase].B_P[rot]+=12;
|
|
*tptrsLv[tcase].B_P[rot]++ = depth;
|
|
tptrsPtx[tcase].B_P[rot] = computePtexCoordinate(f, tptrsPtx[tcase].B_P[rot]);
|
|
tptrsFvd[tcase].B_P[rot] = computeFVarData(f, fvarWidth, tptrsFvd[tcase].B_P[rot], /*isAdaptive=*/true);
|
|
} break;
|
|
|
|
case 3 : { // Corner Transition Patch (9 CVs)
|
|
unsigned rot = f->_adaptiveFlags.brots;
|
|
getOneRing(f, 9, remapRegularCorner, tptrs[tcase].C_P[rot]);
|
|
tptrs[tcase].C_P[rot]+=9;
|
|
*tptrsLv[tcase].C_P[rot]++ = depth;
|
|
tptrsPtx[tcase].C_P[rot] = computePtexCoordinate(f, tptrsPtx[tcase].C_P[rot]);
|
|
tptrsFvd[tcase].C_P[rot] = computeFVarData(f, fvarWidth, tptrsFvd[tcase].C_P[rot], /*isAdaptive=*/true);
|
|
} break;
|
|
}
|
|
} else
|
|
// No transition Gregory patches
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
// Build Gregory patches vertex valence indices table
|
|
if ((_fullCtr.G_C[0] > 0) or (_fullCtr.G_C[1] > 0)) {
|
|
|
|
// MAX_VALENCE is a property of hardware shaders and needs to be matched in OSD
|
|
const int perVertexValenceSize = 2*maxvalence + 1;
|
|
|
|
const int nverts = getMesh()->GetNumVertices();
|
|
|
|
FarPatchTables::VertexValenceTable & table = result->_vertexValenceTable;
|
|
table.resize(nverts * perVertexValenceSize);
|
|
|
|
class GatherNeighborsOperator : public HbrVertexOperator<T> {
|
|
public:
|
|
HbrVertex<T> * center;
|
|
FarPatchTables::VertexValenceTable & table;
|
|
int offset, valence;
|
|
std::vector<int> const & remap;
|
|
|
|
GatherNeighborsOperator(FarPatchTables::VertexValenceTable & itable, int ioffset, HbrVertex<T> * v, std::vector<int> const & iremap) :
|
|
center(v), table(itable), offset(ioffset), valence(0), remap(iremap) { }
|
|
|
|
// Operator iterates over neighbor vertices of v and accumulates
|
|
// pairs of indices the neighbor and diagonal vertices
|
|
//
|
|
// Regular case
|
|
// Boundary case
|
|
// o ------- o D3 o
|
|
// D0 N0 | |
|
|
// | | o ------- o D2 o
|
|
// | | D0 N0 | |
|
|
// | | | |
|
|
// o ------- o ------- o | |
|
|
// N1 | V | N3 | |
|
|
// | | o ------- o ------- o
|
|
// | | N1 V N2
|
|
// | |
|
|
// o o ------- o
|
|
// D1 N2 D2
|
|
//
|
|
virtual void operator() (HbrVertex<T> &v) {
|
|
|
|
table[offset++] = remap[v.GetID()];
|
|
|
|
HbrVertex<T> * diagonal=&v;
|
|
|
|
HbrHalfedge<T> * e = center->GetEdge(&v);
|
|
if ( e ) {
|
|
// If v is on a boundary, there may not be a diagonal vertex
|
|
diagonal = e->GetNext()->GetDestVertex();
|
|
}
|
|
//else {
|
|
// diagonal = v.GetQEONext( center );
|
|
//}
|
|
|
|
table[offset++] = remap[diagonal->GetID()];
|
|
|
|
++valence;
|
|
}
|
|
};
|
|
|
|
for (int i=0; i<nverts; ++i) {
|
|
HbrVertex<T> * v = getMesh()->GetVertex(i);
|
|
|
|
int outputVertexID = _remapTable[v->GetID()];
|
|
int offset = outputVertexID * perVertexValenceSize;
|
|
// "offset+1" : the first table entry is the vertex valence, which
|
|
// is gathered by the operator (see note below)
|
|
|
|
GatherNeighborsOperator op( table, offset+1, v, _remapTable );
|
|
v->ApplyOperatorSurroundingVertices( op );
|
|
|
|
// Valence sign bit used to mark boundary vertices
|
|
|
|
table[offset] = v->OnBoundary() ? -op.valence : op.valence;
|
|
|
|
// Note : some topologies can cause v to be singular at certain
|
|
// levels of adaptive refinement, which prevents us from using
|
|
// the GetValence() function. Fortunately, the GatherNeighbors
|
|
// operator above just performed a similar traversal, so it is
|
|
// very convenient to use it to accumulate the actionable valence.
|
|
}
|
|
} else {
|
|
result->_vertexValenceTable.clear();
|
|
}
|
|
|
|
// Combine quad offset buffers
|
|
result->_quadOffsetTable.resize((_fullCtr.G_C[0]+_fullCtr.G_C[1])*4);
|
|
std::copy(quad_G_C0.begin(), quad_G_C0.end(), result->_quadOffsetTable.begin());
|
|
std::copy(quad_G_C1.begin(), quad_G_C1.end(), result->_quadOffsetTable.begin()+_fullCtr.G_C[0]*4);
|
|
|
|
// Set patch counts
|
|
FarPatchCount patchCount;
|
|
patchCount.nonPatch = 0;
|
|
patchCount.regular = (int)result->_full._R_IT.first.size()/16;
|
|
patchCount.boundary = (int)result->_full._B_IT.first.size()/12;
|
|
patchCount.corner = (int)result->_full._C_IT.first.size()/9;
|
|
patchCount.gregory = (int)result->_full._G_IT.first.size()/4;
|
|
patchCount.boundaryGregory = (int)result->_full._G_B_IT.first.size()/4;
|
|
for (int j=0; j<5; ++j) {
|
|
patchCount.transitionRegular[j] = (int)result->_transition[j]._R_IT.first.size()/16;
|
|
for (int k=0; k<4; ++k) {
|
|
patchCount.transitionBoundary[j][k] = (int)result->_transition[j]._B_IT[k].first.size()/12;
|
|
patchCount.transitionCorner[j][k] = (int)result->_transition[j]._C_IT[k].first.size()/9;
|
|
}
|
|
}
|
|
result->_patchCounts.push_back(patchCount);
|
|
|
|
return result;
|
|
}
|
|
|
|
// The One Ring vertices to rule them all !
|
|
template <class T> void
|
|
FarPatchTablesFactory<T>::getOneRing( HbrFace<T> * f, int ringsize, unsigned int const * remap, unsigned int * result) {
|
|
|
|
assert( f and f->GetNumVertices()==4 and ringsize >=4 );
|
|
|
|
int idx=0;
|
|
|
|
for (unsigned char i=0; i<4; ++i)
|
|
result[remap[idx++ % ringsize]] = _remapTable[f->GetVertex( (i+f->_adaptiveFlags.rots)%4 )->GetID()];
|
|
|
|
if (ringsize==16) {
|
|
|
|
// Regular case
|
|
//
|
|
// | | | |
|
|
// | 4 | 15 | 14 | 13
|
|
// ---- o ---- o ---- o ---- o ----
|
|
// | | | |
|
|
// | 5 | 0 | 3 | 12
|
|
// ---- o ---- o ---- o ---- o ----
|
|
// | | | |
|
|
// | 6 | 1 | 2 | 11
|
|
// ---- o ---- o ---- o ---- o ----
|
|
// | | | |
|
|
// | 7 | 8 | 9 | 10
|
|
// ---- o ---- o ---- o ---- o ----
|
|
// | | | |
|
|
// | | | |
|
|
|
|
for (int i=0; i<4; ++i) {
|
|
int rot = i+f->_adaptiveFlags.rots;
|
|
HbrVertex<T> * v0 = f->GetVertex( rot % 4 ),
|
|
* v1 = f->GetVertex( (rot+1) % 4 );
|
|
|
|
HbrHalfedge<T> * e = v0->GetNextEdge( v0->GetNextEdge( v0->GetEdge(v1) ) );
|
|
|
|
for (int j=0; j<3; ++j) {
|
|
e = e->GetNext();
|
|
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
|
|
}
|
|
}
|
|
} else if (ringsize==12) {
|
|
|
|
// Boundary case
|
|
//
|
|
// 4 0 3 5
|
|
// ---- o ---- o ---- o ---- o ----
|
|
// | | | |
|
|
// | 11 | 1 | 2 | 6
|
|
// ---- o ---- o ---- o ---- o ----
|
|
// | | | |
|
|
// | 10 | 9 | 8 | 7
|
|
// ---- o ---- o ---- o ---- o ----
|
|
// | | | |
|
|
// | | | |
|
|
|
|
HbrVertex<T> * v[4];
|
|
for (int i=0; i<4; ++i)
|
|
v[i] = f->GetVertex( (i+f->_adaptiveFlags.rots)%4 );
|
|
|
|
HbrHalfedge<T> * e;
|
|
|
|
e = v[0]->GetIncidentEdge()->GetPrev()->GetOpposite()->GetPrev();
|
|
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
|
|
|
|
e = v[1]->GetIncidentEdge();
|
|
result[remap[idx++ % ringsize]] = _remapTable[e->GetDestVertex()->GetID()];
|
|
|
|
e = v[2]->GetNextEdge( v[2]->GetEdge(v[1]) );
|
|
for (int i=0; i<3; ++i) {
|
|
e = e->GetNext();
|
|
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
|
|
}
|
|
|
|
e = v[3]->GetNextEdge( v[3]->GetEdge(v[2]) );
|
|
for (int i=0; i<3; ++i) {
|
|
e = e->GetNext();
|
|
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
|
|
}
|
|
} else if (ringsize==9) {
|
|
|
|
// Corner case
|
|
//
|
|
// 0 1 4
|
|
// o ---- o ---- o ----
|
|
// | | |
|
|
// | 3 | 2 | 5
|
|
// o ---- o ---- o ----
|
|
// | | |
|
|
// | 8 | 7 | 6
|
|
// o ---- o ---- o ----
|
|
// | | |
|
|
// | | |
|
|
|
|
HbrVertex<T> * v0 = f->GetVertex( (0+f->_adaptiveFlags.rots)%4 ),
|
|
* v2 = f->GetVertex( (2+f->_adaptiveFlags.rots)%4 ),
|
|
* v3 = f->GetVertex( (3+f->_adaptiveFlags.rots)%4 );
|
|
|
|
HbrHalfedge<T> * e;
|
|
|
|
e = v0->GetIncidentEdge()->GetPrev()->GetOpposite()->GetPrev();
|
|
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
|
|
|
|
e = v2->GetIncidentEdge();
|
|
result[remap[idx++ % ringsize]] = _remapTable[e->GetDestVertex()->GetID()];
|
|
|
|
e = v3->GetNextEdge( v3->GetEdge(v2) );
|
|
for (int i=0; i<3; ++i) {
|
|
e = e->GetNext();
|
|
result[remap[idx++ % ringsize]] = _remapTable[e->GetOrgVertex()->GetID()];
|
|
}
|
|
}
|
|
assert(idx==ringsize);
|
|
}
|
|
|
|
// Populate the quad-offsets table used by Gregory patches
|
|
template <class T> void
|
|
FarPatchTablesFactory<T>::getQuadOffsets( HbrFace<T> * f, unsigned int * result ) {
|
|
|
|
assert( f and f->GetNumVertices()==4 );
|
|
|
|
// Builds a table of value pairs for each vertex of the patch.
|
|
//
|
|
// o
|
|
// N0 |
|
|
// |
|
|
// |
|
|
// o ------ o ------ o
|
|
// N1 V | .... M3
|
|
// | .......
|
|
// | .......
|
|
// o .......
|
|
// N2
|
|
//
|
|
// [...] [N2 - N3] [...]
|
|
//
|
|
// Each value pair is composed of 2 index values in range [0-4[ pointing
|
|
// to the 2 neighbor vertices to the vertex that belong to the Gregory patch.
|
|
// Neighbor ordering is valence counter-clockwise and must match the winding
|
|
// used to build the vertexValenceTable.
|
|
//
|
|
|
|
class GatherOffsetsOperator : public HbrVertexOperator<T> {
|
|
public:
|
|
HbrVertex<T> ** verts; int offsets[2]; int index; int count;
|
|
|
|
GatherOffsetsOperator(HbrVertex<T> ** iverts) : verts(iverts) { }
|
|
|
|
void reset() {
|
|
index=count=offsets[0]=offsets[1]=0;
|
|
}
|
|
|
|
virtual void operator() (HbrVertex<T> &v) {
|
|
// Resolve which 2 neighbor vertices of v belong to the Gregory patch
|
|
for (unsigned char i=0; i<4; ++i)
|
|
if (&v==verts[i]) {
|
|
assert(count<3);
|
|
offsets[count++]=index;
|
|
break;
|
|
}
|
|
++index;
|
|
}
|
|
};
|
|
|
|
// 4 central CVs of the Gregory patch
|
|
HbrVertex<T> * fvs[4] = { f->GetVertex(0),
|
|
f->GetVertex(1),
|
|
f->GetVertex(2),
|
|
f->GetVertex(3) };
|
|
|
|
|
|
// Hbr vertex operator that iterates over neighbor vertices
|
|
GatherOffsetsOperator op( fvs );
|
|
|
|
for (unsigned char i=0; i<4; ++i) {
|
|
|
|
op.reset();
|
|
|
|
fvs[i]->ApplyOperatorSurroundingVertices( op );
|
|
|
|
if (op.offsets[1] - op.offsets[0] != 1)
|
|
std::swap(op.offsets[0], op.offsets[1]);
|
|
|
|
// Pack the 2 indices in 16 bits
|
|
result[i] = (op.offsets[0] | (op.offsets[1] << 8));
|
|
}
|
|
}
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
using namespace OPENSUBDIV_VERSION;
|
|
|
|
} // end namespace OpenSubdiv
|
|
|
|
#endif /* FAR_PATCH_TABLES_FACTORY_H */
|