Merge pull request #990 from barfowl/sparse_patch_tables

Minor extensions to Far classes to help manage large PatchTables
This commit is contained in:
David G Yu 2018-09-13 10:53:20 -07:00 committed by GitHub
commit 46553497c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1041 additions and 89 deletions

View File

@ -172,6 +172,10 @@ public:
template <class T> void
ComputeLocalPointValues(T const *src, T *dst) const;
template <class T> void
ComputeLocalPointValues(T const *srcBase, int numBase,
T const *srcRefined, T *dst) const;
/// \brief Returns the stencil table to compute local point vertex values
StencilTable const *GetLocalPointStencilTable() const;
@ -198,6 +202,10 @@ public:
template <class T> void
ComputeLocalPointValuesVarying(T const *src, T *dst) const;
template <class T> void
ComputeLocalPointValuesVarying(T const *srcBase, int numBase,
T const *srcRefined, T *dst) const;
/// \brief Returns the stencil table to compute local point varying values
StencilTable const *GetLocalPointVaryingStencilTable() const;
@ -226,6 +234,10 @@ public:
template <class T> void
ComputeLocalPointValuesFaceVarying(T const *src, T *dst, int channel = 0) const;
template <class T> void
ComputeLocalPointValuesFaceVarying(T const *srcBase, int numBase,
T const *srcRefined, T *dst, int channel = 0) const;
/// \brief Returns the stencil table to compute local point face-varying values
StencilTable const *GetLocalPointFaceVaryingStencilTable(int channel = 0) const;
@ -781,6 +793,20 @@ PatchTable::ComputeLocalPointValues(T const *src, T *dst) const {
}
}
}
template <class T>
inline void
PatchTable::ComputeLocalPointValues(T const *srcBase, int numBase,
T const *srcRefined, T *dst) const {
if (_localPointStencils.IsSet()) {
if (_vertexPrecisionIsDouble) {
_localPointStencils.Get<double>()->UpdateValues(
srcBase, numBase, srcRefined, dst);
} else {
_localPointStencils.Get<float>()->UpdateValues(
srcBase, numBase, srcRefined, dst);
}
}
}
template <class T>
inline void
@ -793,6 +819,20 @@ PatchTable::ComputeLocalPointValuesVarying(T const *src, T *dst) const {
}
}
}
template <class T>
inline void
PatchTable::ComputeLocalPointValuesVarying(T const *srcBase, int numBase,
T const *srcRefined, T *dst) const {
if (_localPointVaryingStencils.IsSet()) {
if (_varyingPrecisionIsDouble) {
_localPointVaryingStencils.Get<double>()->UpdateValues(
srcBase, numBase, srcRefined, dst);
} else {
_localPointVaryingStencils.Get<float>()->UpdateValues(
srcBase, numBase, srcRefined, dst);
}
}
}
template <class T>
inline void
@ -807,6 +847,22 @@ PatchTable::ComputeLocalPointValuesFaceVarying(T const *src, T *dst, int channel
}
}
}
template <class T>
inline void
PatchTable::ComputeLocalPointValuesFaceVarying(T const *srcBase, int numBase,
T const *srcRefined, T *dst, int channel) const {
if (channel >= 0 && channel < (int)_localPointFaceVaryingStencils.size()) {
if (_localPointFaceVaryingStencils[channel].IsSet()) {
if (_faceVaryingPrecisionIsDouble) {
_localPointFaceVaryingStencils[channel].Get<double>()->UpdateValues(
srcBase, numBase, srcRefined, dst);
} else {
_localPointFaceVaryingStencils[channel].Get<float>()->UpdateValues(
srcBase, numBase, srcRefined, dst);
}
}
}
}
//

View File

@ -103,7 +103,8 @@ public:
//
typedef PatchTableFactory::Options Options;
PatchTableBuilder(TopologyRefiner const & refiner, Options options);
PatchTableBuilder(TopologyRefiner const & refiner, Options options,
ConstIndexArray selectedFaces);
~PatchTableBuilder();
void BuildUniform();
@ -313,6 +314,8 @@ private:
// High level methods for assembling the table:
void identifyAdaptivePatches();
void appendAdaptivePatch(int levelIndex, Index faceIndex);
void testAdaptivePatchRecursive(int levelIndex, Index faceIndex);
void populateAdaptivePatches();
void allocateVertexTables();
@ -325,6 +328,7 @@ private:
// Refiner and Options passed on construction:
TopologyRefiner const & _refiner;
Options const _options;
ConstIndexArray _selectedFaces;
// Flags indicating the need for processing based on provided options
unsigned int _requiresLocalPoints : 1;
@ -361,8 +365,8 @@ private:
// Constructor
PatchTableBuilder::PatchTableBuilder(
TopologyRefiner const & refiner, Options opts) :
_refiner(refiner), _options(opts),
TopologyRefiner const & refiner, Options opts, ConstIndexArray faces) :
_refiner(refiner), _options(opts), _selectedFaces(faces),
_table(0), _patchBuilder(0), _ptexIndices(refiner),
_numRegularPatches(0), _numIrregularPatches(0),
_legacyGregoryHelper(0) {
@ -898,16 +902,48 @@ PatchTableBuilder::BuildAdaptive() {
// <level,face> pairs to identify each patch for later construction, while
// accumulating the number of regular vs irregular patches to size tables.
//
inline void
PatchTableBuilder::appendAdaptivePatch(int levelIndex, Index faceIndex) {
_patches.push_back(PatchTuple(faceIndex, levelIndex));
// Count the patches here to simplify subsequent allocation.
if (_patchBuilder->IsPatchRegular(levelIndex, faceIndex)) {
++_numRegularPatches;
} else {
++_numIrregularPatches;
// LegacyGregory needs to distinguish boundary vs interior
if (_requiresLegacyGregoryTables) {
_legacyGregoryHelper->AddPatchFace(levelIndex, faceIndex);
}
}
}
inline void
PatchTableBuilder::testAdaptivePatchRecursive(int levelIndex, Index faceIndex) {
if (_patchBuilder->IsFaceALeaf(levelIndex, faceIndex)) {
if (_patchBuilder->IsFaceAPatch(levelIndex, faceIndex)) {
appendAdaptivePatch(levelIndex, faceIndex);
}
} else {
TopologyLevel const & level = _refiner.GetLevel(levelIndex);
ConstIndexArray childFaces = level.GetFaceChildFaces(faceIndex);
for (int i = 0; i < childFaces.size(); ++i) {
if (Vtr::IndexIsValid(childFaces[i])) {
testAdaptivePatchRecursive(levelIndex + 1, childFaces[i]);
}
}
}
}
void
PatchTableBuilder::identifyAdaptivePatches() {
//
// Iterate through the levels of refinement. Accumulate index offsets
// for each level while inspecting faces for patches:
// First initialize the offsets for all levels
//
int reservePatches = _refiner.GetNumFacesTotal();
_patches.reserve(reservePatches);
_levelVertOffsets.push_back(0);
_levelFVarValueOffsets.resize(_fvarChannelIndices.size());
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
@ -926,24 +962,33 @@ PatchTableBuilder::identifyAdaptivePatches() {
_levelFVarValueOffsets[fvc].back()
+ level.getNumFVarValues(refinerChannel));
}
}
for (int faceIndex = 0; faceIndex < level.getNumFaces(); ++faceIndex) {
//
// If a set of selected base faces is present, identify the patches
// depth first. Otherwise search breadth first through the levels:
//
_patches.reserve(_refiner.GetNumFacesTotal());
if (_patchBuilder->IsFaceAPatch(levelIndex, faceIndex) &&
_patchBuilder->IsFaceALeaf(levelIndex, faceIndex)) {
// This depth-first-all test is intended for development testing only
bool depthFirstTestForAll = false;
if (_selectedFaces.size()) {
for (int i = 0; i < (int)_selectedFaces.size(); ++i) {
testAdaptivePatchRecursive(0, _selectedFaces[i]);
}
} else if (depthFirstTestForAll) {
for (int baseFace = 0; baseFace < _refiner.getLevel(0).getNumFaces(); ++baseFace) {
testAdaptivePatchRecursive(0, baseFace);
}
} else {
for (int levelIndex=0; levelIndex<_refiner.GetNumLevels(); ++levelIndex) {
int numFaces = _refiner.getLevel(levelIndex).getNumFaces();
_patches.push_back(PatchTuple(faceIndex, levelIndex));
for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
// Count the patches here to simplify subsequent allocation.
if (_patchBuilder->IsPatchRegular(levelIndex, faceIndex)) {
++_numRegularPatches;
} else {
++_numIrregularPatches;
// LegacyGregory needs to distinguish boundary vs interior
if (_requiresLegacyGregoryTables) {
_legacyGregoryHelper->AddPatchFace(levelIndex, faceIndex);
}
if (_patchBuilder->IsFaceAPatch(levelIndex, faceIndex) &&
_patchBuilder->IsFaceALeaf(levelIndex, faceIndex)) {
appendAdaptivePatch(levelIndex, faceIndex);
}
}
}
@ -1889,9 +1934,11 @@ PatchTableBuilder::LegacyGregoryHelper::FinalizeVertexValence(
// to the PatchTableBuilder implementation
//
PatchTable *
PatchTableFactory::Create(TopologyRefiner const & refiner, Options options) {
PatchTableFactory::Create(TopologyRefiner const & refiner,
Options options,
ConstIndexArray selectedFaces) {
PatchTableBuilder builder(refiner, options);
PatchTableBuilder builder(refiner, options, selectedFaces);
if (refiner.IsUniform()) {
builder.BuildUniform();

View File

@ -129,7 +129,11 @@ public:
///
/// For adaptively refined patches, patches are defined at different levels,
/// including the base level, so the indices of patch vertices include
/// vertices from all levels.
/// vertices from all levels. A sparse set of patches can be created by
/// restricting the patches generated to those descending from a given set
/// of faces at the base level. This sparse set of base faces is expected
/// to be a subset of the faces that were adaptively refined in the given
/// TopologyRefiner, otherwise results are undefined.
///
/// For uniformly refined patches, all patches are completely defined within
/// the last level. There is often no use for intermediate levels and they
@ -140,17 +144,21 @@ public:
/// the base level in addition to the last level while indices for face-varying
/// patches include only the last level.
///
/// @param refiner TopologyRefiner from which to generate patches
/// @param refiner TopologyRefiner from which to generate patches
///
/// @param options Options controlling the creation of the table
/// @param options Options controlling the creation of the table
///
/// @return A new instance of PatchTable
/// @param selectedFaces Only create patches for the given set of base faces.
///
/// @return A new instance of PatchTable
///
static PatchTable * Create(TopologyRefiner const & refiner,
Options options=Options());
Options options = Options(),
ConstIndexArray selectedFaces = ConstIndexArray());
public:
// PatchFaceTag
//
// This simple struct was previously used within the factory to take inventory of
// various kinds of patches to fully allocate buffers prior to populating them. It
// was not intended to be exposed as part of the public interface.
@ -158,6 +166,9 @@ public:
// It is no longer used internally and is being kept here to respect preservation
// of the public interface, but it will be deprecated at the earliest opportunity.
//
/// \brief Obsolete internal struct accidentally exposed for public use -- due to
/// be deprecated.
//
struct PatchFaceTag {
public:
unsigned int _hasPatch : 1;

View File

@ -193,18 +193,24 @@ public:
/// \note The destination buffers are assumed to have allocated at least
/// \c GetNumStencils() elements.
///
/// @param controlValues Buffer with primvar data for the control vertices
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param values Destination buffer for the interpolated primvar
/// data
/// @param dstValues Destination buffer for the interpolated primvar data
///
/// @param start index of first value to update
/// @param start Index of first destination value to update
///
/// @param end Index of last value to update
/// @param end Index of last destination value to update
///
template <class T>
void UpdateValues(T const *controlValues, T *values, Index start=-1, Index end=-1) const {
this->update(controlValues, values, _weights, start, end);
void UpdateValues(T const *srcValues, T *dstValues, Index start=-1, Index end=-1) const {
this->update(srcValues, dstValues, _weights, start, end);
}
template <class T>
void UpdateValues(T const *srcBaseValues, int numBaseValues, T const *srcNonBaseValues,
T *dstValues, Index start=-1, Index end=-1) const {
this->update(srcBaseValues, numBaseValues, srcNonBaseValues,
dstValues, _weights, start, end);
}
/// \brief Clears the stencils from the table
@ -214,7 +220,11 @@ protected:
// Update values by applying cached stencil weights to new control values
template <class T>
void update( T const *controlValues, T *values,
void update( T const *srcValues, T *dstValues,
std::vector<REAL> const & valueWeights, Index start, Index end) const;
template <class T>
void update( T const *srcBaseValues, int numBaseValues,
T const *srcNonBaseValues, T *dstalues,
std::vector<REAL> const & valueWeights, Index start, Index end) const;
// Populate the offsets table from the stencil sizes in _sizes (factory helper)
@ -441,24 +451,35 @@ public:
/// \note The destination buffers ('uderivs' & 'vderivs') are assumed to
/// have allocated at least \c GetNumStencils() elements.
///
/// @param controlValues Buffer with primvar data for the control vertices
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param uderivs Destination buffer for the interpolated 'u'
/// derivative primvar data
/// @param uderivs Destination buffer for the interpolated 'u'
/// derivative primvar data
///
/// @param vderivs Destination buffer for the interpolated 'v'
/// derivative primvar data
/// @param vderivs Destination buffer for the interpolated 'v'
/// derivative primvar data
///
/// @param start index of first value to update
/// @param start Index of first destination derivative to update
///
/// @param end Index of last value to update
/// @param end Index of last destination derivative to update
///
template <class T>
void UpdateDerivs(T const *controlValues, T *uderivs, T *vderivs,
void UpdateDerivs(T const *srcValues, T *uderivs, T *vderivs,
int start=-1, int end=-1) const {
this->update(controlValues, uderivs, _duWeights, start, end);
this->update(controlValues, vderivs, _dvWeights, start, end);
this->update(srcValues, uderivs, _duWeights, start, end);
this->update(srcValues, vderivs, _dvWeights, start, end);
}
template <class T>
void UpdateDerivs(T const *srcBaseValues, int numBaseValues,
T const *srcNonBaseValues, T *uderivs, T *vderivs,
int start=-1, int end=-1) const {
this->update(srcBaseValues, numBaseValues, srcNonBaseValues,
uderivs, _duWeights, start, end);
this->update(srcBaseValues, numBaseValues, srcNonBaseValues,
vderivs, _dvWeights, start, end);
}
/// \brief Updates 2nd derivative values based on the control values
@ -466,28 +487,41 @@ public:
/// \note The destination buffers ('uuderivs', 'uvderivs', & 'vderivs') are
/// assumed to have allocated at least \c GetNumStencils() elements.
///
/// @param controlValues Buffer with primvar data for the control vertices
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param uuderivs Destination buffer for the interpolated 'uu'
/// derivative primvar data
/// @param uuderivs Destination buffer for the interpolated 'uu'
/// derivative primvar data
///
/// @param uvderivs Destination buffer for the interpolated 'uv'
/// derivative primvar data
/// @param uvderivs Destination buffer for the interpolated 'uv'
/// derivative primvar data
///
/// @param vvderivs Destination buffer for the interpolated 'vv'
/// derivative primvar data
/// @param vvderivs Destination buffer for the interpolated 'vv'
/// derivative primvar data
///
/// @param start index of first value to update
/// @param start Index of first destination derivative to update
///
/// @param end Index of last value to update
/// @param end Index of last destination derivative to update
///
template <class T>
void Update2ndDerivs(T const *controlValues, T *uuderivs, T *uvderivs, T *vvderivs,
void Update2ndDerivs(T const *srcValues, T *uuderivs, T *uvderivs, T *vvderivs,
int start=-1, int end=-1) const {
this->update(controlValues, uuderivs, _duuWeights, start, end);
this->update(controlValues, uvderivs, _duvWeights, start, end);
this->update(controlValues, vvderivs, _dvvWeights, start, end);
this->update(srcValues, uuderivs, _duuWeights, start, end);
this->update(srcValues, uvderivs, _duvWeights, start, end);
this->update(srcValues, vvderivs, _dvvWeights, start, end);
}
template <class T>
void Update2ndDerivs(T const *srcBaseValues, int numBaseValues,
T const *srcOtherValues, T *uuderivs, T *uvderivs, T *vvderivs,
int start=-1, int end=-1) const {
this->update(srcBaseValues, numBaseValues, srcOtherValues,
uuderivs, _duuWeights, start, end);
this->update(srcBaseValues, numBaseValues, srcOtherValues,
uvderivs, _duvWeights, start, end);
this->update(srcBaseValues, numBaseValues, srcOtherValues,
vvderivs, _dvvWeights, start, end);
}
/// \brief Clears the stencils from the table
@ -544,7 +578,8 @@ protected:
// Update values by applying cached stencil weights to new control values
template <typename REAL>
template <class T> void
StencilTableReal<REAL>::update(T const *controlValues, T *values,
StencilTableReal<REAL>::update(T const *srcBaseValues, int numBaseValues,
T const *srcNonBaseValues, T *dstValues,
std::vector<REAL> const &valueWeights, Index start, Index end) const {
int const * sizes = &_sizes.at(0);
@ -556,7 +591,7 @@ StencilTableReal<REAL>::update(T const *controlValues, T *values,
sizes += start;
indices += _offsets[start];
weights += _offsets[start];
values += start;
dstValues += start;
}
if (end<start || end<0) {
@ -564,17 +599,34 @@ StencilTableReal<REAL>::update(T const *controlValues, T *values,
}
int nstencils = end - std::max(0, start);
for (int i=0; i<nstencils; ++i, ++sizes) {
// Zero out the result accumulators
values[i].Clear();
// For each element in the array, add the coef's contribution
for (int j=0; j<*sizes; ++j, ++indices, ++weights) {
values[i].AddWithWeight( controlValues[*indices], *weights );
// Use separate loops for single and split buffers
if (srcNonBaseValues == 0) {
for (int i=0; i<nstencils; ++i, ++sizes) {
dstValues[i].Clear();
for (int j=0; j<*sizes; ++j, ++indices, ++weights) {
dstValues[i].AddWithWeight( srcBaseValues[*indices], *weights );
}
}
} else {
for (int i=0; i<nstencils; ++i, ++sizes) {
dstValues[i].Clear();
for (int j=0; j<*sizes; ++j, ++indices, ++weights) {
T const & srcValue = (*indices < numBaseValues)
? srcBaseValues[*indices]
: srcNonBaseValues[*indices - numBaseValues];
dstValues[i].AddWithWeight( srcValue, *weights );
}
}
}
}
template <typename REAL>
template <class T> void
StencilTableReal<REAL>::update(T const *srcValues, T *dstValues,
std::vector<REAL> const &valueWeights, Index start, Index end) const {
this->update(srcValues, 0, (T const *)0, dstValues, valueWeights, start, end);
}
template <typename REAL>
inline void

View File

@ -53,7 +53,8 @@ TopologyRefiner::TopologyRefiner(Sdc::SchemeType schemeType, Sdc::Options scheme
_totalEdges(0),
_totalFaces(0),
_totalFaceVertices(0),
_maxValence(0) {
_maxValence(0),
_baseLevelOwned(true) {
// Need to revisit allocation scheme here -- want to use smart-ptrs for these
// but will probably have to settle for explicit new/delete...
@ -63,10 +64,35 @@ TopologyRefiner::TopologyRefiner(Sdc::SchemeType schemeType, Sdc::Options scheme
assembleFarLevels();
}
//
// The copy constructor is protected and used by the factory to create a new instance
// from only the base level of the given instance -- it does not create a full copy.
// So members reflecting any refinement are default-initialized while those dependent
// on the base level are copied or explicitly initialized after its assignment.
//
TopologyRefiner::TopologyRefiner(TopologyRefiner const & source) :
_subdivType(source._subdivType),
_subdivOptions(source._subdivOptions),
_isUniform(true),
_hasHoles(source._hasHoles),
_maxLevel(0),
_uniformOptions(0),
_adaptiveOptions(0),
_baseLevelOwned(false) {
_levels.reserve(10);
_levels.push_back(source._levels[0]);
initializeInventory();
_farLevels.reserve(10);
assembleFarLevels();
}
TopologyRefiner::~TopologyRefiner() {
for (int i=0; i<(int)_levels.size(); ++i) {
delete _levels[i];
if ((i > 0) || _baseLevelOwned) delete _levels[i];
}
for (int i=0; i<(int)_refinements.size(); ++i) {
@ -345,7 +371,8 @@ namespace internal {
} // end namespace internal
void
TopologyRefiner::RefineAdaptive(AdaptiveOptions options) {
TopologyRefiner::RefineAdaptive(AdaptiveOptions options,
ConstIndexArray baseFacesToRefine) {
if (_levels[0]->getNumVertices() == 0) {
Error(FAR_RUNTIME_ERROR,
@ -436,7 +463,15 @@ TopologyRefiner::RefineAdaptive(AdaptiveOptions options) {
//
Vtr::internal::SparseSelector selector(*refinement);
selectFeatureAdaptiveComponents(selector, (i <= shallowLevel) ? moreFeaturesMask : lessFeaturesMask);
internal::FeatureMask const & levelFeatures = (i <= shallowLevel) ? moreFeaturesMask
: lessFeaturesMask;
if (i == 1) {
selectFeatureAdaptiveComponents(selector, levelFeatures, baseFacesToRefine);
} else {
selectFeatureAdaptiveComponents(selector, levelFeatures, ConstIndexArray());
}
if (selector.isSelectionEmpty()) {
delete refinement;
delete &childLevel;
@ -667,9 +702,32 @@ namespace {
// and will select all relevant topological features for inclusion in the subsequent sparse
// refinement.
//
namespace {
bool
faceNextToIrregFace(Vtr::internal::Level const& level, Index faceIndex, int regFaceSize) {
// Wish there was a better way to determine this -- we must inspect the sizes
// of all incident faces of all corners of the face...
//
Vtr::ConstIndexArray faceVerts = level.getFaceVertices(faceIndex);
for (int i = 0; i < faceVerts.size(); ++i) {
ConstIndexArray vertFaces = level.getVertexFaces(faceVerts[i]);
for (int j = 0; j < vertFaces.size(); ++j) {
if (level.getFaceVertices(vertFaces[j]).size() != regFaceSize) {
return true;
}
}
}
return false;
}
}
void
TopologyRefiner::selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector& selector,
internal::FeatureMask const & featureMask) {
internal::FeatureMask const & featureMask,
ConstIndexArray facesToRefine) {
Vtr::internal::Level const& level = selector.getRefinement().parent();
int levelDepth = level.getDepth();
@ -684,7 +742,11 @@ TopologyRefiner::selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector&
//
// Inspect each face and the properties tagged at all of its corners:
//
for (Vtr::Index face = 0; face < level.getNumFaces(); ++face) {
int numFacesToRefine = facesToRefine.size() ? facesToRefine.size() : level.getNumFaces();
for (int fIndex = 0; fIndex < numFacesToRefine; ++fIndex) {
Vtr::Index face = facesToRefine.size() ? facesToRefine[fIndex] : (Index) fIndex;
if (level.isFaceHole(face)) {
continue;
@ -694,18 +756,18 @@ TopologyRefiner::selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector&
// Testing irregular faces is only necessary at level 0, and potentially warrants
// separating out as the caller can detect these.
//
// We need to also ensure that all adjacent faces to this are selected, so we
// select every face incident every vertex of the face. This is the only place
// where other faces are selected as a side effect and somewhat undermines the
// whole intent of the per-face traversal.
//
if (selectIrregularFaces) {
Vtr::ConstIndexArray faceVerts = level.getFaceVertices(face);
if (faceVerts.size() != regularFaceSize) {
if (neighborhood == 0) {
selector.selectFace(face);
} else {
selector.selectFace(face);
// For non-linear schemes, if the faces to refine were not explicitly
// specified, select all faces adjacent to this irregular face. (This
// is the only place where other faces are selected as a side effect
// and somewhat undermines the whole intent of the per-face traversal.)
//
if ((neighborhood > 0) && facesToRefine.empty()) {
for (int i = 0; i < faceVerts.size(); ++i) {
ConstIndexArray fVertFaces = level.getVertexFaces(faceVerts[i]);
for (int j = 0; j < fVertFaces.size(); ++j) {
@ -714,6 +776,18 @@ TopologyRefiner::selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector&
}
}
continue;
} else {
// For non-linear schemes, if the faces to refine were explicitly
// specified, we can't count on this regular face be selected as
// adjacent to an irregular face above, so see if any of its
// neighboring faces are irregular and select if so:
//
if ((neighborhood > 0) && !facesToRefine.empty()) {
if (faceNextToIrregFace(level, face, regularFaceSize)) {
selector.selectFace(face);
continue;
}
}
}
}

View File

@ -174,11 +174,14 @@ public:
///< instead of child vertices of vertices
};
/// \brief Feature Adaptive topology refinement (restricted to scheme Catmark)
/// \brief Feature Adaptive topology refinement
///
/// @param options Options controlling adaptive refinement
/// @param options Options controlling adaptive refinement
///
void RefineAdaptive(AdaptiveOptions options);
/// @param selectedFaces Limit adaptive refinement to the specified faces
///
void RefineAdaptive(AdaptiveOptions options,
ConstIndexArray selectedFaces = ConstIndexArray());
/// \brief Returns the options specified on refinement
AdaptiveOptions GetAdaptiveOptions() const { return _adaptiveOptions; }
@ -216,6 +219,9 @@ protected:
template <typename REAL>
friend class PrimvarRefinerReal;
// Copy constructor exposed via the factory class:
TopologyRefiner(TopologyRefiner const & source);
Vtr::internal::Level & getLevel(int l) { return *_levels[l]; }
Vtr::internal::Level const & getLevel(int l) const { return *_levels[l]; }
@ -225,11 +231,11 @@ protected:
private:
// Not default constructible or copyable:
TopologyRefiner() : _uniformOptions(0), _adaptiveOptions(0) { }
TopologyRefiner(TopologyRefiner const &) : _uniformOptions(0), _adaptiveOptions(0) { }
TopologyRefiner & operator=(TopologyRefiner const &) { return *this; }
void selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector& selector,
internal::FeatureMask const & mask);
internal::FeatureMask const & mask,
ConstIndexArray selectedFaces);
void initializeInventory();
void updateInventory(Vtr::internal::Level const & newLevel);
@ -258,7 +264,9 @@ private:
int _totalFaceVertices;
int _maxValence;
// There is some redundancy here -- to be reduced later
// Note the base level may be shared with another instance
bool _baseLevelOwned;
std::vector<Vtr::internal::Level *> _levels;
std::vector<Vtr::internal::Refinement *> _refinements;

View File

@ -112,6 +112,21 @@ public:
///
static TopologyRefiner* Create(MESH const& mesh, Options options = Options());
/// \brief Instantiates a TopologyRefiner from the base level of an
/// existing instance.
///
/// This allows lightweight copies of the same topology to be refined
/// differently for each new instance. As with other classes that refer
/// to an existing TopologyRefiner, it must generally exist for the entire
/// lifetime of the new instance. In this case, the base level of the
/// original instance must be preserved.
///
/// @param baseLevel An existing TopologyRefiner to share base level.
///
/// @return A new instance of TopologyRefiner or 0 for failure
///
static TopologyRefiner* Create(TopologyRefiner const & sourceOfBaseLevel);
protected:
typedef Vtr::internal::Level::TopologyError TopologyError;
@ -331,6 +346,13 @@ TopologyRefinerFactory<MESH>::Create(MESH const& mesh, Options options) {
return refiner;
}
template <class MESH>
TopologyRefiner*
TopologyRefinerFactory<MESH>::Create(TopologyRefiner const & source) {
return new TopologyRefiner(source);
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::populateBaseLevel(TopologyRefiner& refiner, MESH const& mesh, Options options) {

View File

@ -25,6 +25,7 @@
include_directories(
"${PROJECT_SOURCE_DIR}/"
"${OPENSUBDIV_INCLUDE_DIR}/"
)
add_subdirectory(hbr)

View File

@ -32,6 +32,7 @@ set(TUTORIALS
tutorial_6
tutorial_7
tutorial_8
tutorial_9
)
foreach(tutorial ${TUTORIALS})

View File

@ -0,0 +1,38 @@
#
# Copyright 2018 DreamWorks Animation LLC.
#
# Licensed under the Apache License, Version 2.0 (the "Apache License")
# with the following modification; you may not use this file except in
# compliance with the Apache License and the following modification to it:
# Section 6. Trademarks. is deleted and replaced with:
#
# 6. Trademarks. This License does not grant permission to use the trade
# names, trademarks, service marks, or product names of the Licensor
# and its affiliates, except as required to comply with Section 4(c) of
# the License and to reproduce the content of the NOTICE file.
#
# You may obtain a copy of the Apache License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Apache License with the above modification is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the Apache License for the specific
# language governing permissions and limitations under the Apache License.
#
set(SOURCE_FILES
far_tutorial_9.cpp
)
_add_executable(far_tutorial_9 "tutorials/far"
${SOURCE_FILES}
$<TARGET_OBJECTS:sdc_obj>
$<TARGET_OBJECTS:vtr_obj>
$<TARGET_OBJECTS:far_obj>
$<TARGET_OBJECTS:regression_common_obj>
)
install(TARGETS far_tutorial_9 DESTINATION "${CMAKE_BINDIR_BASE}/tutorials")

View File

@ -0,0 +1,642 @@
//
// Copyright 2018 DreamWorks Animation LLC.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
//
// Description:
// This tutorial shows how to manage the limit surface of a potentially
// large mesh by creating groups of patches for selected faces of the
// mesh. Familiarity with construction and evaluation of a PatchTable
// is assumed (see far/tutorial_6).
//
// When the patches for a mesh do not need to be retained for further
// use, e.g. when simply computing points for a tessellation, the time
// and space required to construct a single large PatchTable can be
// considerable. By constructing, evaluating and discarding smaller
// PatchTables for subsets of the mesh, the high transient memory cost
// can be avoided when computed serially. When computed in parallel,
// there may be little memory savings, but the construction time can
// then be distributed.
//
// This tutorial creates simple geometry (currently a lattice of cubes)
// that can be expanded in complexity with a simple multiplier. The
// collection of faces are then divided into a specified number of groups
// from which patches will be constructed and evaluated. A simple
// tessellation (a triangle fan around the midpoint of each face) is then
// written in Obj format to the standard output.
//
#include "../../../regression/common/far_utils.h"
#include <opensubdiv/far/topologyDescriptor.h>
#include <opensubdiv/far/primvarRefiner.h>
#include <opensubdiv/far/patchTableFactory.h>
#include <opensubdiv/far/patchMap.h>
#include <opensubdiv/far/ptexIndices.h>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <sstream>
using namespace OpenSubdiv;
using Far::Index;
//
// Global utilities in this namespace are not relevant to the tutorial.
// They simply serve to construct some default geometry to be processed
// in the form of a TopologyRefiner and vector of vertex positions.
//
namespace {
//
// Simple structs for (x,y,z) position and a 3-tuple for the set
// of vertices of a triangle:
//
struct Pos {
Pos() { }
Pos(float x, float y, float z) { p[0] = x, p[1] = y, p[2] = z; }
Pos operator+(Pos const & op) const {
return Pos(p[0] + op.p[0], p[1] + op.p[1], p[2] + op.p[2]);
}
// Clear() and AddWithWeight() required for interpolation:
void Clear( void * =0 ) { p[0] = p[1] = p[2] = 0.0f; }
void AddWithWeight(Pos const & src, double weight) {
p[0] += (float)weight * src.p[0];
p[1] += (float)weight * src.p[1];
p[2] += (float)weight * src.p[2];
}
float p[3];
};
typedef std::vector<Pos> PosVector;
struct Tri {
Tri() { }
Tri(int a, int b, int c) { v[0] = a, v[1] = b, v[2] = c; }
int v[3];
};
typedef std::vector<Tri> TriVector;
//
// Functions to populate the topology and geometry arrays with simple
// shapes that we can multiply to increase complexity:
//
void
appendDefaultPrimitive(Pos const & origin,
std::vector<int> & vertsPerFace,
std::vector<Index> & faceVerts,
std::vector<Pos> & positionsPerVert) {
// Local topology and position of a cube centered at origin:
static float const cubePositions[8][3] = { { -0.5f, -0.5f, -0.5f },
{ -0.5f, 0.5f, -0.5f },
{ -0.5f, 0.5f, 0.5f },
{ -0.5f, -0.5f, 0.5f },
{ 0.5f, -0.5f, -0.5f },
{ 0.5f, 0.5f, -0.5f },
{ 0.5f, 0.5f, 0.5f },
{ 0.5f, -0.5f, 0.5f } };
static int const cubeFaceVerts[6][4] = { { 0, 3, 2, 1 },
{ 4, 5, 6, 7 },
{ 0, 4, 7, 3 },
{ 1, 2, 6, 5 },
{ 0, 1, 5, 4 },
{ 3, 7, 6, 2 } };
// Identify the next vertex before appending vertex positions:
int baseVertex = (int) positionsPerVert.size();
for (int i = 0; i < 8; ++i) {
float const * p = cubePositions[i];
positionsPerVert.push_back(origin + Pos(p[0], p[1], p[2]));
}
// Append number of verts-per-face and face-vertices for each face:
for (int i = 0; i < 6; ++i) {
vertsPerFace.push_back(4);
for (int j = 0; j < 4; ++j) {
faceVerts.push_back(baseVertex + cubeFaceVerts[i][j]);
}
}
}
void
createDefaultGeometry(int multiplier,
std::vector<int> & vertsPerFace,
std::vector<Index> & faceVerts,
std::vector<Pos> & positionsPerVert) {
// Default primitive is currently a cube:
int const vertsPerPrimitive = 8;
int const facesPerPrimitive = 6;
int const faceVertsPerPrimitive = 24;
int nPrimitives = multiplier * multiplier * multiplier;
positionsPerVert.reserve(nPrimitives * vertsPerPrimitive);
vertsPerFace.reserve(nPrimitives * facesPerPrimitive);
faceVerts.reserve(nPrimitives * faceVertsPerPrimitive);
for (int x = 0; x < multiplier; ++x) {
for (int y = 0; y < multiplier; ++y) {
for (int z = 0; z < multiplier; ++z) {
appendDefaultPrimitive(Pos(x * 2.0f, y * 2.0f, z * 2.0f),
vertsPerFace, faceVerts,
positionsPerVert);
}
}
}
}
//
// Create a TopologyRefiner from default geometry created above:
//
Far::TopologyRefiner *
createTopologyRefinerDefault(int multiplier,
PosVector & posVector) {
std::vector<int> topVertsPerFace;
std::vector<Index> topFaceVerts;
createDefaultGeometry(multiplier, topVertsPerFace, topFaceVerts, posVector);
typedef Far::TopologyDescriptor Descriptor;
Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK;
Sdc::Options options;
options.SetVtxBoundaryInterpolation(Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER);
Descriptor desc;
desc.numVertices = (int) posVector.size();
desc.numFaces = (int) topVertsPerFace.size();
desc.numVertsPerFace = &topVertsPerFace[0];
desc.vertIndicesPerFace = &topFaceVerts[0];
// Instantiate a FarTopologyRefiner from the descriptor.
Far::TopologyRefiner * refiner =
Far::TopologyRefinerFactory<Descriptor>::Create(desc,
Far::TopologyRefinerFactory<Descriptor>::Options(type, options));
if (refiner == 0) {
exit(EXIT_FAILURE);
}
bool dumpDefaultGeometryToObj = false;
if (dumpDefaultGeometryToObj) {
int nVerts = (int) posVector.size();
for (int i = 0; i < nVerts; ++i) {
float const * p = posVector[i].p;
printf("v %f %f %f\n", p[0], p[1], p[2]);
}
int const * fVerts = &topFaceVerts[0];
int nFaces = (int) topVertsPerFace.size();
for (int i = 0; i < nFaces; ++i) {
printf("f");
for (int j = 0; j < topVertsPerFace[i]; ++j) {
printf(" %d", 1 + *fVerts++);
}
printf("\n");
}
exit(EXIT_SUCCESS);
}
return refiner;
}
//
// Create a TopologyRefiner from a specified Obj file:
// geometry created internally:
//
Far::TopologyRefiner *
createTopologyRefinerFromObj(std::string const & objFileName,
PosVector & posVector) {
const char * filename = objFileName.c_str();
const Shape * shape = 0;
std::ifstream ifs(filename);
if (ifs) {
std::stringstream ss;
ss << ifs.rdbuf();
ifs.close();
std::string shapeString = ss.str();
shape = Shape::parseObj(shapeString.c_str(), kCatmark, false);
if (shape == 0) {
fprintf(stderr, "Error: Cannot create Shape from .obj file '%s'\n", filename);
return 0;
}
} else {
fprintf(stderr, "Error: Cannot open .obj file '%s'\n", filename);
return 0;
}
Sdc::SchemeType sdcType = GetSdcType(*shape);
Sdc::Options sdcOptions = GetSdcOptions(*shape);
Far::TopologyRefiner * refiner = Far::TopologyRefinerFactory<Shape>::Create(*shape,
Far::TopologyRefinerFactory<Shape>::Options(sdcType, sdcOptions));
if (refiner == 0) {
fprintf(stderr, "Error: Unable to construct TopologyRefiner from .obj file '%s'\n", filename);
return 0;
}
int numVertices = refiner->GetNumVerticesTotal();
posVector.resize(numVertices);
std::memcpy(&posVector[0], &shape->verts[0], numVertices * 3 * sizeof(float));
return refiner;
}
} // end namespace
//
// The PatchGroup bundles objects used to create and evaluate a sparse set
// of patches. Its construction creates a PatchTable and all other objects
// necessary to evaluate patches associated with the specified subset of
// faces provided. A simple method to tessellate a specified face is
// provided.
//
// Note that, since the data buffers for the base level and refined levels
// are separate (we want to avoid copying primvar data for the base level
// of a potentially large mesh), that patch evaluation needs to account
// for the separation when combining control points.
//
struct PatchGroup {
PatchGroup(Far::PatchTableFactory::Options patchOptions,
Far::TopologyRefiner const & baseRefinerArg,
Far::PtexIndices const & basePtexIndicesArg,
std::vector<Pos> const & basePositionsArg,
std::vector<Index> const & baseFacesArg);
~PatchGroup();
void TessellateBaseFace(int face, PosVector & tessPoints,
TriVector & tessTris) const;
// Const reference members:
Far::TopologyRefiner const & baseRefiner;
Far::PtexIndices const & basePtexIndices;
std::vector<Pos> const & basePositions;
std::vector<Index> const & baseFaces;
// Members constructed to evaluate patches:
Far::PatchTable * patchTable;
Far::PatchMap * patchMap;
int patchFaceSize;
std::vector<Pos> localPositions;
};
PatchGroup::PatchGroup(Far::PatchTableFactory::Options patchOptions,
Far::TopologyRefiner const & baseRefinerArg,
Far::PtexIndices const & basePtexIndicesArg,
std::vector<Pos> const & basePositionsArg,
std::vector<Index> const & baseFacesArg) :
baseRefiner(baseRefinerArg),
basePtexIndices(basePtexIndicesArg),
basePositions(basePositionsArg),
baseFaces(baseFacesArg) {
// Derive adaptive refinement options from the given patch options:
//
Far::TopologyRefiner::AdaptiveOptions adaptiveOptions(0);
adaptiveOptions.isolationLevel = patchOptions.maxIsolationLevel;
adaptiveOptions.useInfSharpPatch = patchOptions.useInfSharpPatch;
adaptiveOptions.useSingleCreasePatch = patchOptions.useSingleCreasePatch;
adaptiveOptions.considerFVarChannels = patchOptions.generateFVarTables;
// Create a local refiner (sharing the base level), apply adaptive refinement
// to the given subset of base faces, and construct a patch table (and its
// associated map) for the same set of faces:
//
Far::ConstIndexArray groupFaces(&baseFaces[0], (int)baseFaces.size());
Far::TopologyRefiner *localRefiner =
Far::TopologyRefinerFactory<Far::TopologyDescriptor>::Create(baseRefiner);
localRefiner->RefineAdaptive(adaptiveOptions, groupFaces);
patchTable = Far::PatchTableFactory::Create(*localRefiner, patchOptions,
groupFaces);
patchMap = new Far::PatchMap(*patchTable);
patchFaceSize = Sdc::SchemeTypeTraits::GetRegularFaceSize(baseRefiner.GetSchemeType());
// Compute the number of refined and local points needed to evaluate the
// patches, allocate and interpolate. This varies from far/tutorial_6 in
// that the primvar buffer for the base vertices is separate from the
// refined vertices and local patch points (which must also be accounted
// for when evaluating the patches).
//
int nBaseVertices = localRefiner->GetLevel(0).GetNumVertices();
int nRefinedVertices = localRefiner->GetNumVerticesTotal() - nBaseVertices;
int nLocalPoints = patchTable->GetNumLocalPoints();
localPositions.resize(nRefinedVertices + nLocalPoints);
if (nRefinedVertices) {
Far::PrimvarRefiner primvarRefiner(*localRefiner);
Pos const * src = &basePositions[0];
Pos * dst = &localPositions[0];
for (int level = 1; level < localRefiner->GetNumLevels(); ++level) {
primvarRefiner.Interpolate(level, src, dst);
src = dst;
dst += localRefiner->GetLevel(level).GetNumVertices();
}
}
if (nLocalPoints) {
patchTable->ComputeLocalPointValues(&basePositions[0], nBaseVertices,
&localPositions[0], &localPositions[nRefinedVertices]);
}
delete localRefiner;
}
PatchGroup::~PatchGroup() {
delete patchTable;
delete patchMap;
}
void
PatchGroup::TessellateBaseFace(int face, PosVector & tessPoints,
TriVector & tessTris) const {
// Tesselate the face with points at the midpoint of the face and at
// each corner, and triangles connecting the midpoint to each edge.
// Irregular faces require an aribrary number of corners points, but
// all are at the origin of the child face of the irregular base face:
//
float const quadPoints[5][2] = { { 0.5f, 0.5f },
{ 0.0f, 0.0f },
{ 1.0f, 0.0f },
{ 1.0f, 1.0f },
{ 0.0f, 1.0f } };
float const triPoints[4][2] = { { 0.5f, 0.5f },
{ 0.0f, 0.0f },
{ 1.0f, 0.0f },
{ 0.0f, 1.0f } };
float const irregPoints[4][2] = { { 1.0f, 1.0f },
{ 0.0f, 0.0f } };
// Determine the topology of the given base face and the resulting
// tessellation points and faces to generate:
//
int baseFace = baseFaces[face];
int faceSize = baseRefiner.GetLevel(0).GetFaceVertices(baseFace).size();
bool faceIsIrregular = (faceSize != patchFaceSize);
int nTessPoints = faceSize + 1;
int nTessFaces = faceSize;
tessPoints.resize(nTessPoints);
tessTris.resize(nTessFaces);
// Compute the mid and corner points -- remember that for an irregular
// face, we must reference the individual ptex faces for each corner:
//
int ptexFace = basePtexIndices.GetFaceId(baseFace);
int numBaseVerts = (int) basePositions.size();
for (int i = 0; i < nTessPoints; ++i) {
// Choose the (s,t) coordinate from the fixed tessellation:
float const * st = faceIsIrregular ? irregPoints[i != 0]
: ((faceSize == 4) ? quadPoints[i] : triPoints[i]);
// Locate the patch corresponding to the face ptex idx and (s,t)
// and evaluate:
int patchFace = ptexFace;
if (faceIsIrregular && (i > 0)) {
patchFace += i - 1;
}
Far::PatchTable::PatchHandle const * handle =
patchMap->FindPatch(patchFace, st[0], st[1]);
assert(handle);
float pWeights[20];
patchTable->EvaluateBasis(*handle, st[0], st[1], pWeights);
// Identify the patch cvs and combine with the evaluated weights --
// remember to distinguish cvs in the base level:
Far::ConstIndexArray cvIndices = patchTable->GetPatchVertices(*handle);
Pos & pos = tessPoints[i];
pos.Clear();
for (int cv = 0; cv < cvIndices.size(); ++cv) {
int cvIndex = cvIndices[cv];
if (cvIndex < numBaseVerts) {
pos.AddWithWeight(basePositions[cvIndex], pWeights[cv]);
} else {
pos.AddWithWeight(localPositions[cvIndex - numBaseVerts], pWeights[cv]);
}
}
}
// Assign triangles connecting the midpoint of the base face to the
// points computed at the ends of each of its edges:
//
for (int i = 0; i < nTessFaces; ++i) {
tessTris[i] = Tri(0, 1 + i, 1 + ((i + 1) % faceSize));
}
}
//
// Command line arguments parsed to provide run-time options:
//
class Args {
public:
std::string inputObjFile;
Sdc::SchemeType schemeType;
int geoMultiplier;
int maxPatchDepth;
int numPatchGroups;
bool noTessFlag;
bool noOutputFlag;
public:
Args(int argc, char ** argv) :
inputObjFile(),
schemeType(Sdc::SCHEME_CATMARK),
geoMultiplier(10),
maxPatchDepth(3),
numPatchGroups(10),
noTessFlag(false),
noOutputFlag(false) {
for (int i = 1; i < argc; ++i) {
if (strstr(argv[i], ".obj")) {
if (inputObjFile.empty()) {
inputObjFile = std::string(argv[i]);
} else {
fprintf(stderr, "Warning: .obj file '%s' ignored\n", argv[i]);
}
} else if (!strcmp(argv[i], "-mult")) {
if (++i < argc) geoMultiplier = atoi(argv[i]);
} else if (!strcmp(argv[i], "-bilinear")) {
schemeType = Sdc::SCHEME_BILINEAR;
} else if (!strcmp(argv[i], "-catmark")) {
schemeType = Sdc::SCHEME_CATMARK;
} else if (!strcmp(argv[i], "-loop")) {
schemeType = Sdc::SCHEME_LOOP;
} else if (!strcmp(argv[i], "-depth")) {
if (++i < argc) maxPatchDepth = atoi(argv[i]);
} else if (!strcmp(argv[i], "-groups")) {
if (++i < argc) numPatchGroups = atoi(argv[i]);
} else if (!strcmp(argv[i], "-notess")) {
noTessFlag = true;
} else if (!strcmp(argv[i], "-nooutput")) {
noOutputFlag = true;
} else {
fprintf(stderr, "Warning: Argument '%s' ignored\n", argv[i]);
}
}
}
private:
Args() { }
};
//
// Load command line arguments and geometry, then divide the mesh into groups
// of faces from which to create and tessellate patches:
//
int
main(int argc, char **argv) {
Args args(argc, argv);
//
// Create or load the base geometry (command line arguments allow a
// .obj file to be specified). In addition to the TopologyRefiner
// and set of positions for the base vertices, a set of PtexIndices is
// also required to evaluate patches, so build it here once for use
// elsewhere:
//
std::vector<Pos> basePositions;
Far::TopologyRefiner * baseRefinerPtr = args.inputObjFile.empty() ?
createTopologyRefinerDefault(args.geoMultiplier, basePositions) :
createTopologyRefinerFromObj(args.inputObjFile, basePositions);
assert(baseRefinerPtr);
Far::TopologyRefiner & baseRefiner = *baseRefinerPtr;
Far::PtexIndices basePtexIndices(baseRefiner);
//
// Determine the sizes of the patch groups specified -- there will be
// two sizes that differ by one to account for unequal division:
//
int numBaseFaces = baseRefiner.GetNumFacesTotal();
int numPatchGroups = args.numPatchGroups;
if (numPatchGroups > numBaseFaces) {
numPatchGroups = numBaseFaces;
} else if (numPatchGroups < 1) {
numPatchGroups = 1;
}
int lesserGroupSize = numBaseFaces / numPatchGroups;
int numLargerGroups = numBaseFaces - (numPatchGroups * lesserGroupSize);
//
// Define the options used to construct the patches for each group.
// Unless suppressed, a tessellation in Obj format will also be printed
// to standard output, so keep track of the vertex indices.
//
Far::PatchTableFactory::Options patchOptions(args.maxPatchDepth);
patchOptions.generateVaryingTables = false;
patchOptions.shareEndCapPatchPoints = false;
patchOptions.endCapType =
Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS;
int objVertCount = 0;
PosVector tessPoints;
TriVector tessFaces;
for (int i = 0; i < numPatchGroups; ++i) {
//
// Initialize a vector with a group of base faces from which to
// create and evaluate patches:
//
Index minFace = i * lesserGroupSize + std::min(i, numLargerGroups);
Index maxFace = minFace + lesserGroupSize + (i < numLargerGroups);
std::vector<Far::Index> baseFaces(maxFace - minFace);
for (int face = minFace; face < maxFace; ++face) {
baseFaces[face - minFace] = face;
}
//
// Declare a PatchGroup and tessellate its base faces -- generating
// vertices and faces in Obj format to standard output:
//
PatchGroup patchGroup(patchOptions,
baseRefiner, basePtexIndices, basePositions, baseFaces);
if (args.noTessFlag) continue;
if (!args.noOutputFlag) {
printf("g patchGroup_%d\n", i);
}
for (int j = 0; j < (int) baseFaces.size(); ++j) {
patchGroup.TessellateBaseFace(j, tessPoints, tessFaces);
if (!args.noOutputFlag) {
int nVerts = (int) tessPoints.size();
for (int k = 0; k < nVerts; ++k) {
float const * p = tessPoints[k].p;
printf("v %f %f %f\n", p[0], p[1], p[2]);
}
int nTris = (int) tessFaces.size();
int vBase = 1 + objVertCount;
for (int k = 0; k < nTris; ++k) {
int const * v = tessFaces[k].v;
printf("f %d %d %d\n", vBase + v[0], vBase + v[1], vBase + v[2]);
}
objVertCount += nVerts;
}
}
}
delete baseRefinerPtr;
return EXIT_SUCCESS;
}