mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-30 23:30:07 +00:00
5aa10c8f55
- consider the vertex from the edge with the maximum number of faces
1252 lines
47 KiB
C++
1252 lines
47 KiB
C++
//
|
|
// Copyright 2014 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.
|
|
//
|
|
#include "../sdc/crease.h"
|
|
#include "../sdc/catmarkScheme.h"
|
|
#include "../sdc/bilinearScheme.h"
|
|
#include "../vtr/types.h"
|
|
#include "../vtr/level.h"
|
|
#include "../vtr/refinement.h"
|
|
#include "../vtr/fvarLevel.h"
|
|
#include "../vtr/fvarRefinement.h"
|
|
#include "../vtr/stackBuffer.h"
|
|
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
#include <utility>
|
|
|
|
|
|
namespace OpenSubdiv {
|
|
namespace OPENSUBDIV_VERSION {
|
|
|
|
namespace Vtr {
|
|
namespace internal {
|
|
|
|
//
|
|
// Simple constructor, destructor and basic initializers:
|
|
//
|
|
Refinement::Refinement(Level const & parentArg, Level & childArg, Sdc::Options const& options) :
|
|
_parent(&parentArg),
|
|
_child(&childArg),
|
|
_options(options),
|
|
_regFaceSize(-1),
|
|
_uniform(false),
|
|
_faceVertsFirst(false),
|
|
_childFaceFromFaceCount(0),
|
|
_childEdgeFromFaceCount(0),
|
|
_childEdgeFromEdgeCount(0),
|
|
_childVertFromFaceCount(0),
|
|
_childVertFromEdgeCount(0),
|
|
_childVertFromVertCount(0),
|
|
_firstChildFaceFromFace(0),
|
|
_firstChildEdgeFromFace(0),
|
|
_firstChildEdgeFromEdge(0),
|
|
_firstChildVertFromFace(0),
|
|
_firstChildVertFromEdge(0),
|
|
_firstChildVertFromVert(0) {
|
|
|
|
assert((childArg.getDepth() == 0) && (childArg.getNumVertices() == 0));
|
|
childArg._depth = 1 + parentArg.getDepth();
|
|
}
|
|
|
|
Refinement::~Refinement() {
|
|
|
|
for (int i = 0; i < (int)_fvarChannels.size(); ++i) {
|
|
delete _fvarChannels[i];
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::initializeChildComponentCounts() {
|
|
|
|
//
|
|
// Assign the child's component counts/inventory based on the child components identified:
|
|
//
|
|
_child->_faceCount = _childFaceFromFaceCount;
|
|
_child->_edgeCount = _childEdgeFromFaceCount + _childEdgeFromEdgeCount;
|
|
_child->_vertCount = _childVertFromFaceCount + _childVertFromEdgeCount + _childVertFromVertCount;
|
|
}
|
|
|
|
void
|
|
Refinement::initializeSparseSelectionTags() {
|
|
|
|
_parentFaceTag.resize(_parent->getNumFaces());
|
|
_parentEdgeTag.resize(_parent->getNumEdges());
|
|
_parentVertexTag.resize(_parent->getNumVertices());
|
|
}
|
|
|
|
|
|
//
|
|
// The main refinement method -- provides a high-level overview of refinement:
|
|
//
|
|
// The refinement process is as follows:
|
|
// - determine a mapping from parent components to their potential child components
|
|
// - for sparse refinement this mapping will be partial
|
|
// - determine the reverse mapping from chosen child components back to their parents
|
|
// - previously this was optional -- not strictly necessary and comes at added cost
|
|
// - does simplify iteration of child components when refinement is sparse
|
|
// - propagate/initialize component Tags from parents to their children
|
|
// - knowing these Tags for a child component simplifies dealing with it later
|
|
// - subdivide the topology, i.e. populate all topology relations for the child Level
|
|
// - any subset of the 6 relations in a Level can be created
|
|
// - using the minimum required in the last Level is very advantageous
|
|
// - subdivide the sharpness values in the child Level
|
|
// - subdivide face-varying channels in the child Level
|
|
//
|
|
void
|
|
Refinement::refine(Options refineOptions) {
|
|
|
|
// This will become redundant when/if assigned on construction:
|
|
assert(_parent && _child);
|
|
|
|
_uniform = !refineOptions._sparse;
|
|
_faceVertsFirst = refineOptions._faceVertsFirst;
|
|
|
|
// We may soon have an option here to suppress refinement of FVar channels...
|
|
bool refineOptions_ignoreFVarChannels = false;
|
|
|
|
bool optionallyRefineFVar = (_parent->getNumFVarChannels() > 0) && !refineOptions_ignoreFVarChannels;
|
|
|
|
//
|
|
// Initialize the parent-to-child and reverse child-to-parent mappings and propagate
|
|
// component tags to the new child components:
|
|
//
|
|
populateParentToChildMapping();
|
|
|
|
initializeChildComponentCounts();
|
|
|
|
populateChildToParentMapping();
|
|
|
|
propagateComponentTags();
|
|
|
|
//
|
|
// Subdivide the topology -- populating only those of the 6 relations specified
|
|
// (though we do require the vertex-face relation for refining FVar channels):
|
|
//
|
|
Relations relationsToPopulate;
|
|
if (refineOptions._minimalTopology) {
|
|
relationsToPopulate.setAll(false);
|
|
relationsToPopulate._faceVertices = true;
|
|
} else {
|
|
relationsToPopulate.setAll(true);
|
|
}
|
|
if (optionallyRefineFVar) {
|
|
relationsToPopulate._vertexFaces = true;
|
|
}
|
|
|
|
subdivideTopology(relationsToPopulate);
|
|
|
|
//
|
|
// Subdivide the sharpness values and face-varying channels:
|
|
// - note there is some dependency of the vertex tag/Rule for semi-sharp vertices
|
|
//
|
|
subdivideSharpnessValues();
|
|
|
|
if (optionallyRefineFVar) {
|
|
subdivideFVarChannels();
|
|
}
|
|
|
|
// Various debugging support:
|
|
//
|
|
//printf("Vertex refinement to level %d completed...\n", _child->getDepth());
|
|
//_child->print();
|
|
//printf(" validating refinement to level %d...\n", _child->getDepth());
|
|
//_child->validateTopology();
|
|
//assert(_child->validateTopology());
|
|
}
|
|
|
|
|
|
//
|
|
// Methods to construct the parent-to-child mapping
|
|
//
|
|
void
|
|
Refinement::populateParentToChildMapping() {
|
|
|
|
allocateParentChildIndices();
|
|
|
|
//
|
|
// If sparse refinement, mark indices of any components in addition to those selected
|
|
// so that we have the full neighborhood for selected components:
|
|
//
|
|
if (!_uniform) {
|
|
// Make sure the selection was non-empty -- currently unsupported...
|
|
if (_parentVertexTag.size() == 0) {
|
|
assert("Unsupported empty sparse refinement detected in Refinement" == 0);
|
|
}
|
|
markSparseChildComponentIndices();
|
|
}
|
|
|
|
populateParentChildIndices();
|
|
}
|
|
|
|
namespace {
|
|
inline bool isSparseIndexMarked(Index index) { return index != 0; }
|
|
|
|
inline int
|
|
sequenceSparseIndexVector(IndexVector& indexVector, int baseValue = 0) {
|
|
int validCount = 0;
|
|
for (int i = 0; i < (int) indexVector.size(); ++i) {
|
|
indexVector[i] = isSparseIndexMarked(indexVector[i])
|
|
? (baseValue + validCount++) : INDEX_INVALID;
|
|
}
|
|
return validCount;
|
|
}
|
|
|
|
inline int
|
|
sequenceFullIndexVector(IndexVector& indexVector, int baseValue = 0) {
|
|
int indexCount = (int) indexVector.size();
|
|
for (int i = 0; i < indexCount; ++i) {
|
|
indexVector[i] = baseValue++;
|
|
}
|
|
return indexCount;
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::populateParentChildIndices() {
|
|
|
|
//
|
|
// Two vertex orderings are currently supported -- ordering vertices refined
|
|
// from vertices first, or those refined from faces first. It's possible this
|
|
// may be extended to more possibilities. Once the ordering is defined here,
|
|
// other than analogous initialization in FVarRefinement, the treatment of
|
|
// vertices in blocks based on origin should make the rest of the code
|
|
// invariant to ordering changes.
|
|
//
|
|
// These two blocks now differ only in the utility function that assigns the
|
|
// sequential values to the index vectors -- so parameterization/simplification
|
|
// is now possible...
|
|
//
|
|
if (_uniform) {
|
|
// child faces:
|
|
_firstChildFaceFromFace = 0;
|
|
_childFaceFromFaceCount = sequenceFullIndexVector(_faceChildFaceIndices, _firstChildFaceFromFace);
|
|
|
|
// child edges:
|
|
_firstChildEdgeFromFace = 0;
|
|
_childEdgeFromFaceCount = sequenceFullIndexVector(_faceChildEdgeIndices, _firstChildEdgeFromFace);
|
|
|
|
_firstChildEdgeFromEdge = _childEdgeFromFaceCount;
|
|
_childEdgeFromEdgeCount = sequenceFullIndexVector(_edgeChildEdgeIndices, _firstChildEdgeFromEdge);
|
|
|
|
// child vertices:
|
|
if (_faceVertsFirst) {
|
|
_firstChildVertFromFace = 0;
|
|
_childVertFromFaceCount = sequenceFullIndexVector(_faceChildVertIndex, _firstChildVertFromFace);
|
|
|
|
_firstChildVertFromEdge = _firstChildVertFromFace + _childVertFromFaceCount;
|
|
_childVertFromEdgeCount = sequenceFullIndexVector(_edgeChildVertIndex, _firstChildVertFromEdge);
|
|
|
|
_firstChildVertFromVert = _firstChildVertFromEdge + _childVertFromEdgeCount;
|
|
_childVertFromVertCount = sequenceFullIndexVector(_vertChildVertIndex, _firstChildVertFromVert);
|
|
} else {
|
|
_firstChildVertFromVert = 0;
|
|
_childVertFromVertCount = sequenceFullIndexVector(_vertChildVertIndex, _firstChildVertFromVert);
|
|
|
|
_firstChildVertFromFace = _firstChildVertFromVert + _childVertFromVertCount;
|
|
_childVertFromFaceCount = sequenceFullIndexVector(_faceChildVertIndex, _firstChildVertFromFace);
|
|
|
|
_firstChildVertFromEdge = _firstChildVertFromFace + _childVertFromFaceCount;
|
|
_childVertFromEdgeCount = sequenceFullIndexVector(_edgeChildVertIndex, _firstChildVertFromEdge);
|
|
}
|
|
} else {
|
|
// child faces:
|
|
_firstChildFaceFromFace = 0;
|
|
_childFaceFromFaceCount = sequenceSparseIndexVector(_faceChildFaceIndices, _firstChildFaceFromFace);
|
|
|
|
// child edges:
|
|
_firstChildEdgeFromFace = 0;
|
|
_childEdgeFromFaceCount = sequenceSparseIndexVector(_faceChildEdgeIndices, _firstChildEdgeFromFace);
|
|
|
|
_firstChildEdgeFromEdge = _childEdgeFromFaceCount;
|
|
_childEdgeFromEdgeCount = sequenceSparseIndexVector(_edgeChildEdgeIndices, _firstChildEdgeFromEdge);
|
|
|
|
// child vertices:
|
|
if (_faceVertsFirst) {
|
|
_firstChildVertFromFace = 0;
|
|
_childVertFromFaceCount = sequenceSparseIndexVector(_faceChildVertIndex, _firstChildVertFromFace);
|
|
|
|
_firstChildVertFromEdge = _firstChildVertFromFace + _childVertFromFaceCount;
|
|
_childVertFromEdgeCount = sequenceSparseIndexVector(_edgeChildVertIndex, _firstChildVertFromEdge);
|
|
|
|
_firstChildVertFromVert = _firstChildVertFromEdge + _childVertFromEdgeCount;
|
|
_childVertFromVertCount = sequenceSparseIndexVector(_vertChildVertIndex, _firstChildVertFromVert);
|
|
} else {
|
|
_firstChildVertFromVert = 0;
|
|
_childVertFromVertCount = sequenceSparseIndexVector(_vertChildVertIndex, _firstChildVertFromVert);
|
|
|
|
_firstChildVertFromFace = _firstChildVertFromVert + _childVertFromVertCount;
|
|
_childVertFromFaceCount = sequenceSparseIndexVector(_faceChildVertIndex, _firstChildVertFromFace);
|
|
|
|
_firstChildVertFromEdge = _firstChildVertFromFace + _childVertFromFaceCount;
|
|
_childVertFromEdgeCount = sequenceSparseIndexVector(_edgeChildVertIndex, _firstChildVertFromEdge);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::printParentToChildMapping() const {
|
|
|
|
printf("Parent-to-child component mapping:\n");
|
|
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
|
|
printf(" Face %d:\n", pFace);
|
|
printf(" Child vert: %d\n", _faceChildVertIndex[pFace]);
|
|
|
|
printf(" Child faces: ");
|
|
ConstIndexArray childFaces = getFaceChildFaces(pFace);
|
|
for (int i = 0; i < childFaces.size(); ++i) {
|
|
printf(" %d", childFaces[i]);
|
|
}
|
|
printf("\n");
|
|
|
|
printf(" Child edges: ");
|
|
ConstIndexArray childEdges = getFaceChildEdges(pFace);
|
|
for (int i = 0; i < childEdges.size(); ++i) {
|
|
printf(" %d", childEdges[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
|
|
printf(" Edge %d:\n", pEdge);
|
|
printf(" Child vert: %d\n", _edgeChildVertIndex[pEdge]);
|
|
|
|
ConstIndexArray childEdges = getEdgeChildEdges(pEdge);
|
|
printf(" Child edges: %d %d\n", childEdges[0], childEdges[1]);
|
|
}
|
|
for (Index pVert = 0; pVert < _parent->getNumVertices(); ++pVert) {
|
|
printf(" Vert %d:\n", pVert);
|
|
printf(" Child vert: %d\n", _vertChildVertIndex[pVert]);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Methods to construct the child-to-parent mapping:
|
|
//
|
|
void
|
|
Refinement::populateChildToParentMapping() {
|
|
|
|
ChildTag initialChildTags[2][4];
|
|
for (int i = 0; i < 2; ++i) {
|
|
for (int j = 0; j < 4; ++j) {
|
|
ChildTag & tag = initialChildTags[i][j];
|
|
|
|
tag._incomplete = (unsigned char)i;
|
|
tag._parentType = 0;
|
|
tag._indexInParent = (unsigned char)j;
|
|
}
|
|
}
|
|
|
|
populateFaceParentVectors(initialChildTags);
|
|
populateEdgeParentVectors(initialChildTags);
|
|
populateVertexParentVectors(initialChildTags);
|
|
}
|
|
|
|
void
|
|
Refinement::populateFaceParentVectors(ChildTag const initialChildTags[2][4]) {
|
|
|
|
_childFaceTag.resize(_child->getNumFaces());
|
|
_childFaceParentIndex.resize(_child->getNumFaces());
|
|
|
|
populateFaceParentFromParentFaces(initialChildTags);
|
|
}
|
|
void
|
|
Refinement::populateFaceParentFromParentFaces(ChildTag const initialChildTags[2][4]) {
|
|
|
|
if (_uniform) {
|
|
Index cFace = getFirstChildFaceFromFaces();
|
|
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
|
|
ConstIndexArray cFaces = getFaceChildFaces(pFace);
|
|
if (cFaces.size() == 4) {
|
|
_childFaceTag[cFace + 0] = initialChildTags[0][0];
|
|
_childFaceTag[cFace + 1] = initialChildTags[0][1];
|
|
_childFaceTag[cFace + 2] = initialChildTags[0][2];
|
|
_childFaceTag[cFace + 3] = initialChildTags[0][3];
|
|
|
|
_childFaceParentIndex[cFace + 0] = pFace;
|
|
_childFaceParentIndex[cFace + 1] = pFace;
|
|
_childFaceParentIndex[cFace + 2] = pFace;
|
|
_childFaceParentIndex[cFace + 3] = pFace;
|
|
|
|
cFace += 4;
|
|
} else {
|
|
bool childTooLarge = (cFaces.size() > 4);
|
|
for (int i = 0; i < cFaces.size(); ++i, ++cFace) {
|
|
_childFaceTag[cFace] = initialChildTags[0][childTooLarge ? 0 : i];
|
|
_childFaceParentIndex[cFace] = pFace;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Child faces of faces:
|
|
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
|
|
bool incomplete = !_parentFaceTag[pFace]._selected;
|
|
|
|
IndexArray cFaces = getFaceChildFaces(pFace);
|
|
if (!incomplete && (cFaces.size() == 4)) {
|
|
_childFaceTag[cFaces[0]] = initialChildTags[0][0];
|
|
_childFaceTag[cFaces[1]] = initialChildTags[0][1];
|
|
_childFaceTag[cFaces[2]] = initialChildTags[0][2];
|
|
_childFaceTag[cFaces[3]] = initialChildTags[0][3];
|
|
|
|
_childFaceParentIndex[cFaces[0]] = pFace;
|
|
_childFaceParentIndex[cFaces[1]] = pFace;
|
|
_childFaceParentIndex[cFaces[2]] = pFace;
|
|
_childFaceParentIndex[cFaces[3]] = pFace;
|
|
} else {
|
|
bool childTooLarge = (cFaces.size() > 4);
|
|
for (int i = 0; i < cFaces.size(); ++i) {
|
|
if (IndexIsValid(cFaces[i])) {
|
|
_childFaceTag[cFaces[i]] = initialChildTags[incomplete][childTooLarge ? 0 : i];
|
|
_childFaceParentIndex[cFaces[i]] = pFace;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::populateEdgeParentVectors(ChildTag const initialChildTags[2][4]) {
|
|
|
|
_childEdgeTag.resize(_child->getNumEdges());
|
|
_childEdgeParentIndex.resize(_child->getNumEdges());
|
|
|
|
populateEdgeParentFromParentFaces(initialChildTags);
|
|
populateEdgeParentFromParentEdges(initialChildTags);
|
|
}
|
|
void
|
|
Refinement::populateEdgeParentFromParentFaces(ChildTag const initialChildTags[2][4]) {
|
|
|
|
if (_uniform) {
|
|
Index cEdge = getFirstChildEdgeFromFaces();
|
|
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
|
|
ConstIndexArray cEdges = getFaceChildEdges(pFace);
|
|
if (cEdges.size() == 4) {
|
|
_childEdgeTag[cEdge + 0] = initialChildTags[0][0];
|
|
_childEdgeTag[cEdge + 1] = initialChildTags[0][1];
|
|
_childEdgeTag[cEdge + 2] = initialChildTags[0][2];
|
|
_childEdgeTag[cEdge + 3] = initialChildTags[0][3];
|
|
|
|
_childEdgeParentIndex[cEdge + 0] = pFace;
|
|
_childEdgeParentIndex[cEdge + 1] = pFace;
|
|
_childEdgeParentIndex[cEdge + 2] = pFace;
|
|
_childEdgeParentIndex[cEdge + 3] = pFace;
|
|
|
|
cEdge += 4;
|
|
} else {
|
|
bool childTooLarge = (cEdges.size() > 4);
|
|
for (int i = 0; i < cEdges.size(); ++i, ++cEdge) {
|
|
_childEdgeTag[cEdge] = initialChildTags[0][childTooLarge ? 0 : i];
|
|
_childEdgeParentIndex[cEdge] = pFace;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
|
|
bool incomplete = !_parentFaceTag[pFace]._selected;
|
|
|
|
IndexArray cEdges = getFaceChildEdges(pFace);
|
|
if (!incomplete && (cEdges.size() == 4)) {
|
|
_childEdgeTag[cEdges[0]] = initialChildTags[0][0];
|
|
_childEdgeTag[cEdges[1]] = initialChildTags[0][1];
|
|
_childEdgeTag[cEdges[2]] = initialChildTags[0][2];
|
|
_childEdgeTag[cEdges[3]] = initialChildTags[0][3];
|
|
|
|
_childEdgeParentIndex[cEdges[0]] = pFace;
|
|
_childEdgeParentIndex[cEdges[1]] = pFace;
|
|
_childEdgeParentIndex[cEdges[2]] = pFace;
|
|
_childEdgeParentIndex[cEdges[3]] = pFace;
|
|
} else {
|
|
bool childTooLarge = (cEdges.size() > 4);
|
|
for (int i = 0; i < cEdges.size(); ++i) {
|
|
if (IndexIsValid(cEdges[i])) {
|
|
_childEdgeTag[cEdges[i]] = initialChildTags[incomplete][childTooLarge ? 0 : i];
|
|
_childEdgeParentIndex[cEdges[i]] = pFace;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void
|
|
Refinement::populateEdgeParentFromParentEdges(ChildTag const initialChildTags[2][4]) {
|
|
|
|
if (_uniform) {
|
|
Index cEdge = getFirstChildEdgeFromEdges();
|
|
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge, cEdge += 2) {
|
|
_childEdgeTag[cEdge + 0] = initialChildTags[0][0];
|
|
_childEdgeTag[cEdge + 1] = initialChildTags[0][1];
|
|
|
|
_childEdgeParentIndex[cEdge + 0] = pEdge;
|
|
_childEdgeParentIndex[cEdge + 1] = pEdge;
|
|
}
|
|
} else {
|
|
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
|
|
bool incomplete = !_parentEdgeTag[pEdge]._selected;
|
|
|
|
IndexArray cEdges = getEdgeChildEdges(pEdge);
|
|
if (!incomplete) {
|
|
_childEdgeTag[cEdges[0]] = initialChildTags[0][0];
|
|
_childEdgeTag[cEdges[1]] = initialChildTags[0][1];
|
|
|
|
_childEdgeParentIndex[cEdges[0]] = pEdge;
|
|
_childEdgeParentIndex[cEdges[1]] = pEdge;
|
|
} else {
|
|
for (int i = 0; i < 2; ++i) {
|
|
if (IndexIsValid(cEdges[i])) {
|
|
_childEdgeTag[cEdges[i]] = initialChildTags[incomplete][i];
|
|
_childEdgeParentIndex[cEdges[i]] = pEdge;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::populateVertexParentVectors(ChildTag const initialChildTags[2][4]) {
|
|
|
|
if (_uniform) {
|
|
_childVertexTag.resize(_child->getNumVertices(), initialChildTags[0][0]);
|
|
} else {
|
|
_childVertexTag.resize(_child->getNumVertices(), initialChildTags[1][0]);
|
|
}
|
|
_childVertexParentIndex.resize(_child->getNumVertices());
|
|
|
|
populateVertexParentFromParentFaces(initialChildTags);
|
|
populateVertexParentFromParentEdges(initialChildTags);
|
|
populateVertexParentFromParentVertices(initialChildTags);
|
|
}
|
|
void
|
|
Refinement::populateVertexParentFromParentFaces(ChildTag const initialChildTags[2][4]) {
|
|
|
|
if (getNumChildVerticesFromFaces() == 0) return;
|
|
|
|
if (_uniform) {
|
|
Index cVert = getFirstChildVertexFromFaces();
|
|
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace, ++cVert) {
|
|
// Child tag was initialized as the complete and only child when allocated
|
|
|
|
_childVertexParentIndex[cVert] = pFace;
|
|
}
|
|
} else {
|
|
ChildTag const & completeChildTag = initialChildTags[0][0];
|
|
|
|
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
|
|
Index cVert = _faceChildVertIndex[pFace];
|
|
if (IndexIsValid(cVert)) {
|
|
// Child tag was initialized as incomplete -- reset if complete:
|
|
if (_parentFaceTag[pFace]._selected) {
|
|
_childVertexTag[cVert] = completeChildTag;
|
|
}
|
|
_childVertexParentIndex[cVert] = pFace;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void
|
|
Refinement::populateVertexParentFromParentEdges(ChildTag const initialChildTags[2][4]) {
|
|
|
|
if (_uniform) {
|
|
Index cVert = getFirstChildVertexFromEdges();
|
|
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge, ++cVert) {
|
|
// Child tag was initialized as the complete and only child when allocated
|
|
|
|
_childVertexParentIndex[cVert] = pEdge;
|
|
}
|
|
} else {
|
|
ChildTag const & completeChildTag = initialChildTags[0][0];
|
|
|
|
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
|
|
Index cVert = _edgeChildVertIndex[pEdge];
|
|
if (IndexIsValid(cVert)) {
|
|
// Child tag was initialized as incomplete -- reset if complete:
|
|
if (_parentEdgeTag[pEdge]._selected) {
|
|
_childVertexTag[cVert] = completeChildTag;
|
|
}
|
|
_childVertexParentIndex[cVert] = pEdge;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void
|
|
Refinement::populateVertexParentFromParentVertices(ChildTag const initialChildTags[2][4]) {
|
|
|
|
if (_uniform) {
|
|
Index cVert = getFirstChildVertexFromVertices();
|
|
for (Index pVert = 0; pVert < _parent->getNumVertices(); ++pVert, ++cVert) {
|
|
// Child tag was initialized as the complete and only child when allocated
|
|
|
|
_childVertexParentIndex[cVert] = pVert;
|
|
}
|
|
} else {
|
|
ChildTag const & completeChildTag = initialChildTags[0][0];
|
|
|
|
for (Index pVert = 0; pVert < _parent->getNumVertices(); ++pVert) {
|
|
Index cVert = _vertChildVertIndex[pVert];
|
|
if (IndexIsValid(cVert)) {
|
|
// Child tag was initialized as incomplete but these should be complete:
|
|
if (_parentVertexTag[pVert]._selected) {
|
|
_childVertexTag[cVert] = completeChildTag;
|
|
}
|
|
_childVertexParentIndex[cVert] = pVert;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Methods to propagate/initialize child component tags from their parent component:
|
|
//
|
|
void
|
|
Refinement::propagateComponentTags() {
|
|
|
|
populateFaceTagVectors();
|
|
populateEdgeTagVectors();
|
|
populateVertexTagVectors();
|
|
}
|
|
|
|
void
|
|
Refinement::populateFaceTagVectors() {
|
|
|
|
_child->_faceTags.resize(_child->getNumFaces());
|
|
|
|
populateFaceTagsFromParentFaces();
|
|
}
|
|
void
|
|
Refinement::populateFaceTagsFromParentFaces() {
|
|
|
|
//
|
|
// Tags for faces originating from faces are inherited from the parent face:
|
|
//
|
|
Index cFace = getFirstChildFaceFromFaces();
|
|
Index cFaceEnd = cFace + getNumChildFacesFromFaces();
|
|
for ( ; cFace < cFaceEnd; ++cFace) {
|
|
_child->_faceTags[cFace] = _parent->_faceTags[_childFaceParentIndex[cFace]];
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::populateEdgeTagVectors() {
|
|
|
|
_child->_edgeTags.resize(_child->getNumEdges());
|
|
|
|
populateEdgeTagsFromParentFaces();
|
|
populateEdgeTagsFromParentEdges();
|
|
}
|
|
void
|
|
Refinement::populateEdgeTagsFromParentFaces() {
|
|
|
|
//
|
|
// Tags for edges originating from faces are all constant:
|
|
//
|
|
Level::ETag eTag;
|
|
eTag.clear();
|
|
|
|
Index cEdge = getFirstChildEdgeFromFaces();
|
|
Index cEdgeEnd = cEdge + getNumChildEdgesFromFaces();
|
|
for ( ; cEdge < cEdgeEnd; ++cEdge) {
|
|
_child->_edgeTags[cEdge] = eTag;
|
|
}
|
|
}
|
|
void
|
|
Refinement::populateEdgeTagsFromParentEdges() {
|
|
|
|
//
|
|
// Tags for edges originating from edges are inherited from the parent edge:
|
|
//
|
|
Index cEdge = getFirstChildEdgeFromEdges();
|
|
Index cEdgeEnd = cEdge + getNumChildEdgesFromEdges();
|
|
for ( ; cEdge < cEdgeEnd; ++cEdge) {
|
|
_child->_edgeTags[cEdge] = _parent->_edgeTags[_childEdgeParentIndex[cEdge]];
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::populateVertexTagVectors() {
|
|
|
|
_child->_vertTags.resize(_child->getNumVertices());
|
|
|
|
populateVertexTagsFromParentFaces();
|
|
populateVertexTagsFromParentEdges();
|
|
populateVertexTagsFromParentVertices();
|
|
|
|
if (!_uniform) {
|
|
for (Index cVert = 0; cVert < _child->getNumVertices(); ++cVert) {
|
|
if (_childVertexTag[cVert]._incomplete) {
|
|
_child->_vertTags[cVert]._incomplete = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void
|
|
Refinement::populateVertexTagsFromParentFaces() {
|
|
|
|
//
|
|
// Similarly, tags for vertices originating from faces are all constant -- with the
|
|
// unfortunate exception of refining level 0, where the faces may be N-sided and so
|
|
// introduce new vertices that need to be tagged as extra-ordinary:
|
|
//
|
|
if (getNumChildVerticesFromFaces() == 0) return;
|
|
|
|
Level::VTag vTag;
|
|
vTag.clear();
|
|
vTag._rule = Sdc::Crease::RULE_SMOOTH;
|
|
|
|
Index cVert = getFirstChildVertexFromFaces();
|
|
Index cVertEnd = cVert + getNumChildVerticesFromFaces();
|
|
|
|
if (_parent->_depth > 0) {
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
|
_child->_vertTags[cVert] = vTag;
|
|
}
|
|
} else {
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
|
_child->_vertTags[cVert] = vTag;
|
|
|
|
if (_parent->getNumFaceVertices(_childVertexParentIndex[cVert]) != _regFaceSize) {
|
|
_child->_vertTags[cVert]._xordinary = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void
|
|
Refinement::populateVertexTagsFromParentEdges() {
|
|
|
|
//
|
|
// Tags for vertices originating from edges are initialized according to the tags
|
|
// of the parent edge:
|
|
//
|
|
Level::VTag vTag;
|
|
vTag.clear();
|
|
|
|
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
|
|
Index cVert = _edgeChildVertIndex[pEdge];
|
|
if (!IndexIsValid(cVert)) continue;
|
|
|
|
// From the cleared local VTag, we just need to assign properties dependent
|
|
// on the parent edge:
|
|
Level::ETag const& pEdgeTag = _parent->_edgeTags[pEdge];
|
|
|
|
vTag._nonManifold = pEdgeTag._nonManifold;
|
|
vTag._boundary = pEdgeTag._boundary;
|
|
vTag._semiSharpEdges = pEdgeTag._semiSharp;
|
|
vTag._infSharpEdges = pEdgeTag._infSharp;
|
|
vTag._infSharpCrease = pEdgeTag._infSharp;
|
|
vTag._infIrregular = pEdgeTag._infSharp && pEdgeTag._nonManifold;
|
|
|
|
vTag._rule = (Level::VTag::VTagSize)((pEdgeTag._semiSharp || pEdgeTag._infSharp)
|
|
? Sdc::Crease::RULE_CREASE : Sdc::Crease::RULE_SMOOTH);
|
|
|
|
_child->_vertTags[cVert] = vTag;
|
|
}
|
|
}
|
|
void
|
|
Refinement::populateVertexTagsFromParentVertices() {
|
|
|
|
//
|
|
// Tags for vertices originating from vertices are inherited from the parent vertex:
|
|
//
|
|
Index cVert = getFirstChildVertexFromVertices();
|
|
Index cVertEnd = cVert + getNumChildVerticesFromVertices();
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
|
_child->_vertTags[cVert] = _parent->_vertTags[_childVertexParentIndex[cVert]];
|
|
_child->_vertTags[cVert]._incidIrregFace = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Methods to subdivide the topology:
|
|
//
|
|
// The main method to subdivide topology is fairly simple -- given a set of relations
|
|
// to populate it simply tests and populates each relation separately. The method for
|
|
// each relation is responsible for appropriate allocation and initialization of all
|
|
// data involved, and these are virtual -- provided by a quad- or tri-split subclass.
|
|
//
|
|
void
|
|
Refinement::subdivideTopology(Relations const& applyTo) {
|
|
|
|
if (applyTo._faceVertices) {
|
|
populateFaceVertexRelation();
|
|
}
|
|
if (applyTo._faceEdges) {
|
|
populateFaceEdgeRelation();
|
|
}
|
|
if (applyTo._edgeVertices) {
|
|
populateEdgeVertexRelation();
|
|
}
|
|
if (applyTo._edgeFaces) {
|
|
populateEdgeFaceRelation();
|
|
}
|
|
if (applyTo._vertexFaces) {
|
|
populateVertexFaceRelation();
|
|
}
|
|
if (applyTo._vertexEdges) {
|
|
populateVertexEdgeRelation();
|
|
}
|
|
|
|
//
|
|
// Additional members of the child Level not specific to any relation...
|
|
// - note in the case of max-valence, the child's max-valence may be less
|
|
// than the parent if that maximal parent vertex was not included in the sparse
|
|
// refinement (possible when sparse refinement is more general).
|
|
// - it may also be more if the base level was fairly trivial, i.e. less
|
|
// than the regular valence, or contains non-manifold edges with many faces.
|
|
// - NOTE that when/if we support N-gons for tri-splitting, that the valence
|
|
// of edge-vertices introduced on the N-gon may be 7 rather than 6, while N may
|
|
// be less than both.
|
|
//
|
|
// In general, we need a better way to deal with max-valence. The fact that
|
|
// each topology relation is independent/optional complicates the issue of
|
|
// where to keep track of it...
|
|
//
|
|
if (_splitType == Sdc::SPLIT_TO_QUADS) {
|
|
_child->_maxValence = std::max(_parent->_maxValence, 4);
|
|
_child->_maxValence = std::max(_child->_maxValence, 2 + _parent->_maxEdgeFaces);
|
|
} else {
|
|
_child->_maxValence = std::max(_parent->_maxValence, 6);
|
|
_child->_maxValence = std::max(_child->_maxValence, 2 + _parent->_maxEdgeFaces * 2);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Methods to subdivide sharpness values:
|
|
//
|
|
void
|
|
Refinement::subdivideSharpnessValues() {
|
|
|
|
//
|
|
// Subdividing edge and vertex sharpness values are independent, but in order
|
|
// to maintain proper classification/tagging of components as semi-sharp, both
|
|
// must be computed and the neighborhood inspected to properly update the
|
|
// status.
|
|
//
|
|
// It is possible to clear the semi-sharp status when propagating the tags and
|
|
// to reset it (potentially multiple times) when updating the sharpness values.
|
|
// The vertex subdivision Rule is also affected by this, which complicates the
|
|
// process. So for now we apply a post-process to explicitly handle all
|
|
// semi-sharp vertices.
|
|
//
|
|
|
|
// These methods will update sharpness tags local to the edges and vertices:
|
|
subdivideEdgeSharpness();
|
|
subdivideVertexSharpness();
|
|
|
|
// This method uses local sharpness tags (set above) to update vertex tags that
|
|
// reflect the neighborhood of the vertex (e.g. its rule):
|
|
reclassifySemisharpVertices();
|
|
}
|
|
|
|
void
|
|
Refinement::subdivideEdgeSharpness() {
|
|
|
|
Sdc::Crease creasing(_options);
|
|
|
|
_child->_edgeSharpness.clear();
|
|
_child->_edgeSharpness.resize(_child->getNumEdges(), Sdc::Crease::SHARPNESS_SMOOTH);
|
|
|
|
//
|
|
// Edge sharpness is passed to child-edges using the parent edge and the
|
|
// parent vertex for which the child corresponds. Child-edges are created
|
|
// from both parent faces and parent edges, but those child-edges created
|
|
// from a parent face should be within the face's interior and so smooth
|
|
// (and so previously initialized).
|
|
//
|
|
// The presence/validity of each parent edges child vert indicates one or
|
|
// more child edges.
|
|
//
|
|
// NOTE -- It is also useful at this time to classify the child vert of
|
|
// this edge based on the creasing information here, particularly when a
|
|
// non-trivial creasing method like Chaikin is used. This is not being
|
|
// done now but is worth considering...
|
|
//
|
|
internal::StackBuffer<float,16> pVertEdgeSharpness;
|
|
if (!creasing.IsUniform()) {
|
|
pVertEdgeSharpness.Reserve(_parent->getMaxValence());
|
|
}
|
|
|
|
Index cEdge = getFirstChildEdgeFromEdges();
|
|
Index cEdgeEnd = cEdge + getNumChildEdgesFromEdges();
|
|
for ( ; cEdge < cEdgeEnd; ++cEdge) {
|
|
float& cSharpness = _child->_edgeSharpness[cEdge];
|
|
Level::ETag& cEdgeTag = _child->_edgeTags[cEdge];
|
|
|
|
if (cEdgeTag._infSharp) {
|
|
cSharpness = Sdc::Crease::SHARPNESS_INFINITE;
|
|
} else if (cEdgeTag._semiSharp) {
|
|
Index pEdge = _childEdgeParentIndex[cEdge];
|
|
float pSharpness = _parent->_edgeSharpness[pEdge];
|
|
|
|
if (creasing.IsUniform()) {
|
|
cSharpness = creasing.SubdivideUniformSharpness(pSharpness);
|
|
} else {
|
|
ConstIndexArray pEdgeVerts = _parent->getEdgeVertices(pEdge);
|
|
Index pVert = pEdgeVerts[_childEdgeTag[cEdge]._indexInParent];
|
|
ConstIndexArray pVertEdges = _parent->getVertexEdges(pVert);
|
|
|
|
for (int i = 0; i < pVertEdges.size(); ++i) {
|
|
pVertEdgeSharpness[i] = _parent->_edgeSharpness[pVertEdges[i]];
|
|
}
|
|
cSharpness = creasing.SubdivideEdgeSharpnessAtVertex(pSharpness, pVertEdges.size(),
|
|
pVertEdgeSharpness);
|
|
}
|
|
if (! Sdc::Crease::IsSharp(cSharpness)) {
|
|
cEdgeTag._semiSharp = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::subdivideVertexSharpness() {
|
|
|
|
Sdc::Crease creasing(_options);
|
|
|
|
_child->_vertSharpness.clear();
|
|
_child->_vertSharpness.resize(_child->getNumVertices(), Sdc::Crease::SHARPNESS_SMOOTH);
|
|
|
|
//
|
|
// All child-verts originating from faces or edges are initialized as smooth
|
|
// above. Only those originating from vertices require "subdivided" values:
|
|
//
|
|
// Only deal with the subrange of vertices originating from vertices:
|
|
Index cVertBegin = getFirstChildVertexFromVertices();
|
|
Index cVertEnd = cVertBegin + getNumChildVerticesFromVertices();
|
|
|
|
for (Index cVert = cVertBegin; cVert < cVertEnd; ++cVert) {
|
|
float& cSharpness = _child->_vertSharpness[cVert];
|
|
Level::VTag& cVertTag = _child->_vertTags[cVert];
|
|
|
|
if (cVertTag._infSharp) {
|
|
cSharpness = Sdc::Crease::SHARPNESS_INFINITE;
|
|
} else if (cVertTag._semiSharp) {
|
|
Index pVert = _childVertexParentIndex[cVert];
|
|
float pSharpness = _parent->_vertSharpness[pVert];
|
|
|
|
cSharpness = creasing.SubdivideVertexSharpness(pSharpness);
|
|
if (! Sdc::Crease::IsSharp(cSharpness)) {
|
|
cVertTag._semiSharp = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::reclassifySemisharpVertices() {
|
|
|
|
typedef Level::VTag::VTagSize VTagSize;
|
|
|
|
Sdc::Crease creasing(_options);
|
|
|
|
//
|
|
// Inspect all vertices derived from edges -- for those whose parent edges were semisharp,
|
|
// reset the semisharp tag and the associated Rule according to the sharpness pair for the
|
|
// subdivided edges (note this may be better handled when the edge sharpness is computed):
|
|
//
|
|
Index vertFromEdgeBegin = getFirstChildVertexFromEdges();
|
|
Index vertFromEdgeEnd = vertFromEdgeBegin + getNumChildVerticesFromEdges();
|
|
|
|
for (Index cVert = vertFromEdgeBegin; cVert < vertFromEdgeEnd; ++cVert) {
|
|
Level::VTag& cVertTag = _child->_vertTags[cVert];
|
|
if (!cVertTag._semiSharpEdges) continue;
|
|
|
|
Index pEdge = _childVertexParentIndex[cVert];
|
|
|
|
ConstIndexArray cEdges = getEdgeChildEdges(pEdge);
|
|
|
|
if (_childVertexTag[cVert]._incomplete) {
|
|
// One child edge likely missing -- assume Crease if remaining edge semi-sharp:
|
|
cVertTag._semiSharpEdges = (IndexIsValid(cEdges[0]) && _child->_edgeTags[cEdges[0]]._semiSharp) ||
|
|
(IndexIsValid(cEdges[1]) && _child->_edgeTags[cEdges[1]]._semiSharp);
|
|
cVertTag._rule = (VTagSize)(cVertTag._semiSharpEdges ? Sdc::Crease::RULE_CREASE : Sdc::Crease::RULE_SMOOTH);
|
|
} else {
|
|
int sharpEdgeCount = _child->_edgeTags[cEdges[0]]._semiSharp + _child->_edgeTags[cEdges[1]]._semiSharp;
|
|
|
|
cVertTag._semiSharpEdges = (sharpEdgeCount > 0);
|
|
cVertTag._rule = (VTagSize)(creasing.DetermineVertexVertexRule(0.0, sharpEdgeCount));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Inspect all vertices derived from vertices -- for those whose parent vertices were
|
|
// semisharp (inherited in the child vert's tag), inspect and reset the semisharp tag
|
|
// and the associated Rule (based on neighboring child edges around the child vertex).
|
|
//
|
|
// We should never find such a vertex "incomplete" in a sparse refinement as a parent
|
|
// vertex is either selected or not, but never neighboring. So the only complication
|
|
// here is whether the local topology of child edges exists -- it may have been pruned
|
|
// from the last level to reduce memory. If so, we use the parent to identify the
|
|
// child edges.
|
|
//
|
|
// In both cases, we count the number of sharp and semisharp child edges incident the
|
|
// child vertex and adjust the "semisharp" and "rule" tags accordingly.
|
|
//
|
|
Index vertFromVertBegin = getFirstChildVertexFromVertices();
|
|
Index vertFromVertEnd = vertFromVertBegin + getNumChildVerticesFromVertices();
|
|
|
|
for (Index cVert = vertFromVertBegin; cVert < vertFromVertEnd; ++cVert) {
|
|
Index pVert = _childVertexParentIndex[cVert];
|
|
Level::VTag const& pVertTag = _parent->_vertTags[pVert];
|
|
|
|
// Skip if parent not semi-sharp:
|
|
if (!pVertTag._semiSharp && !pVertTag._semiSharpEdges) continue;
|
|
|
|
//
|
|
// We need to inspect the child neighborhood's sharpness when either semi-sharp
|
|
// edges were present around the parent vertex, or the parent vertex sharpness
|
|
// decayed:
|
|
//
|
|
Level::VTag& cVertTag = _child->_vertTags[cVert];
|
|
|
|
bool sharpVertexDecayed = pVertTag._semiSharp && !cVertTag._semiSharp;
|
|
|
|
if (pVertTag._semiSharpEdges || sharpVertexDecayed) {
|
|
int infSharpEdgeCount = 0;
|
|
int semiSharpEdgeCount = 0;
|
|
|
|
bool cVertEdgesPresent = (_child->getNumVertexEdgesTotal() > 0);
|
|
if (cVertEdgesPresent) {
|
|
ConstIndexArray cEdges = _child->getVertexEdges(cVert);
|
|
|
|
for (int i = 0; i < cEdges.size(); ++i) {
|
|
Level::ETag cEdgeTag = _child->_edgeTags[cEdges[i]];
|
|
|
|
infSharpEdgeCount += cEdgeTag._infSharp;
|
|
semiSharpEdgeCount += cEdgeTag._semiSharp;
|
|
}
|
|
} else {
|
|
ConstIndexArray pEdges = _parent->getVertexEdges(pVert);
|
|
ConstLocalIndexArray pVertInEdge = _parent->getVertexEdgeLocalIndices(pVert);
|
|
|
|
for (int i = 0; i < pEdges.size(); ++i) {
|
|
ConstIndexArray cEdgePair = getEdgeChildEdges(pEdges[i]);
|
|
|
|
Index cEdge = cEdgePair[pVertInEdge[i]];
|
|
Level::ETag cEdgeTag = _child->_edgeTags[cEdge];
|
|
|
|
infSharpEdgeCount += cEdgeTag._infSharp;
|
|
semiSharpEdgeCount += cEdgeTag._semiSharp;
|
|
}
|
|
}
|
|
cVertTag._semiSharpEdges = (semiSharpEdgeCount > 0);
|
|
|
|
if (!cVertTag._semiSharp && !cVertTag._infSharp) {
|
|
cVertTag._rule = (VTagSize)(creasing.DetermineVertexVertexRule(0.0,
|
|
infSharpEdgeCount + semiSharpEdgeCount));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Methods to subdivide face-varying channels:
|
|
//
|
|
void
|
|
Refinement::subdivideFVarChannels() {
|
|
|
|
assert(_child->_fvarChannels.size() == 0);
|
|
assert(this->_fvarChannels.size() == 0);
|
|
|
|
int channelCount = _parent->getNumFVarChannels();
|
|
|
|
for (int channel = 0; channel < channelCount; ++channel) {
|
|
FVarLevel* parentFVar = _parent->_fvarChannels[channel];
|
|
|
|
FVarLevel* childFVar = new FVarLevel(*_child);
|
|
FVarRefinement* refineFVar = new FVarRefinement(*this, *parentFVar, *childFVar);
|
|
|
|
refineFVar->applyRefinement();
|
|
|
|
_child->_fvarChannels.push_back(childFVar);
|
|
this->_fvarChannels.push_back(refineFVar);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Marking of sparse child components -- including those selected and those neighboring...
|
|
//
|
|
// For schemes requiring neighboring support, this is the equivalent of the "guarantee
|
|
// neighbors" in Hbr -- it ensures that all components required to define the limit of
|
|
// those "selected" are also generated in the refinement.
|
|
//
|
|
// The difference with Hbr is that we do this in a single pass for all components once
|
|
// "selection" of components of interest has been completed.
|
|
//
|
|
// Considering two approaches:
|
|
// 1) By Vertex neighborhoods:
|
|
// - for each base vertex
|
|
// - for each incident face
|
|
// - test and mark components for its child face
|
|
// or
|
|
// 2) By Edge and Face contents:
|
|
// - for each base edge
|
|
// - test and mark local components
|
|
// - for each base face
|
|
// - test and mark local components
|
|
//
|
|
// Given a typical quad mesh with N verts, N faces and 2*N edges, determine which is more
|
|
// efficient...
|
|
//
|
|
// Going with (2) initially for simplicity -- certain aspects of (1) are awkward, i.e. the
|
|
// identification of child-edges to be marked (trivial in (2). We are also guaranteed with
|
|
// (2) that we only visit each component once, i.e. each edge and each face.
|
|
//
|
|
// Revising the above assessment... (2) has gotten WAY more complicated once the ability to
|
|
// select child faces is provided. Given that feature is important to Manuel for support
|
|
// of the FarStencilTables we have to assume it will be needed. So we'll try (1) out as it
|
|
// will be simpler to get it correct -- we can work on improving performance later.
|
|
//
|
|
// Complexity added by child component selection:
|
|
// - the child vertex of the component can now be selected as part of a child face or
|
|
// edge, and so the parent face or edge is not fully selected. So we've had to add another
|
|
// bit to the marking masks to indicate when a parent component is "fully selected".
|
|
// - selecting a child face creates the situation where child edges of parent edges do
|
|
// not have any selected vertex at their ends -- both can be neighboring. This complicated
|
|
// the marking of neighboring child edges, which was otherwise trivial -- if any end vertex
|
|
// of a child edge (of a parent edge) was selected, the child edge was at least neighboring.
|
|
//
|
|
// Final note on the marking technique:
|
|
// There are currently two values to the marking of child components, which are no
|
|
// longer that useful. It is now sufficient, and not likely to be necessary, to distinguish
|
|
// between what was selected or added to support it. Ultimately that will be determined by
|
|
// inspecting the selected flag on the parent component once the child-to-parent map is in
|
|
// place.
|
|
//
|
|
namespace {
|
|
Index const IndexSparseMaskNeighboring = (1 << 0);
|
|
Index const IndexSparseMaskSelected = (1 << 1);
|
|
|
|
inline void markSparseIndexNeighbor(Index& index) { index = IndexSparseMaskNeighboring; }
|
|
inline void markSparseIndexSelected(Index& index) { index = IndexSparseMaskSelected; }
|
|
}
|
|
|
|
void
|
|
Refinement::markSparseChildComponentIndices() {
|
|
|
|
//
|
|
// There is an explicit ordering here as the work done for vertices is a subset
|
|
// of what is required for edges, which in turn is a subset of what is required
|
|
// for faces. This ordering and their related implementations tries to avoid
|
|
// doing redundant work and accomplishing everything necessary in a single
|
|
// iteration through each component type.
|
|
//
|
|
markSparseVertexChildren();
|
|
markSparseEdgeChildren();
|
|
markSparseFaceChildren();
|
|
}
|
|
|
|
|
|
void
|
|
Refinement::markSparseVertexChildren() {
|
|
|
|
assert(_parentVertexTag.size() > 0);
|
|
|
|
//
|
|
// For each parent vertex:
|
|
// - mark the descending child vertex for each selected vertex
|
|
//
|
|
for (Index pVert = 0; pVert < parent().getNumVertices(); ++pVert) {
|
|
if (_parentVertexTag[pVert]._selected) {
|
|
markSparseIndexSelected(_vertChildVertIndex[pVert]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
Refinement::markSparseEdgeChildren() {
|
|
|
|
assert(_parentEdgeTag.size() > 0);
|
|
|
|
//
|
|
// For each parent edge:
|
|
// - mark the descending child edges and vertex for each selected edge
|
|
// - test each end vertex of unselected edges to see if selected:
|
|
// - mark both the child edge and the middle child vertex if so
|
|
// - set transitional bit for all edges based on selection of incident faces
|
|
//
|
|
// Note that no edges have been marked "fully selected" -- only their vertices have
|
|
// been marked and marking of their child edges deferred to visiting each edge only
|
|
// once here.
|
|
//
|
|
for (Index pEdge = 0; pEdge < parent().getNumEdges(); ++pEdge) {
|
|
IndexArray eChildEdges = getEdgeChildEdges(pEdge);
|
|
ConstIndexArray eVerts = parent().getEdgeVertices(pEdge);
|
|
|
|
SparseTag& pEdgeTag = _parentEdgeTag[pEdge];
|
|
|
|
if (pEdgeTag._selected) {
|
|
markSparseIndexSelected(eChildEdges[0]);
|
|
markSparseIndexSelected(eChildEdges[1]);
|
|
markSparseIndexSelected(_edgeChildVertIndex[pEdge]);
|
|
} else {
|
|
if (_parentVertexTag[eVerts[0]]._selected) {
|
|
markSparseIndexNeighbor(eChildEdges[0]);
|
|
markSparseIndexNeighbor(_edgeChildVertIndex[pEdge]);
|
|
}
|
|
if (_parentVertexTag[eVerts[1]]._selected) {
|
|
markSparseIndexNeighbor(eChildEdges[1]);
|
|
markSparseIndexNeighbor(_edgeChildVertIndex[pEdge]);
|
|
}
|
|
}
|
|
|
|
//
|
|
// TAG the parent edges as "transitional" here if only one was selected (or in
|
|
// the more general non-manifold case, they are not all selected the same way).
|
|
// We use the transitional tags on the edges to TAG the parent face below.
|
|
//
|
|
// Note -- this is best done now rather than as a post-process as we have more
|
|
// explicit information about the selected components. Unless we also tag the
|
|
// parent faces as selected, we can't easily tell from the child-faces of the
|
|
// edge's incident faces which were generated by selection or neighboring...
|
|
//
|
|
ConstIndexArray eFaces = parent().getEdgeFaces(pEdge);
|
|
if (eFaces.size() == 2) {
|
|
pEdgeTag._transitional = (_parentFaceTag[eFaces[0]]._selected !=
|
|
_parentFaceTag[eFaces[1]]._selected);
|
|
} else if (eFaces.size() < 2) {
|
|
pEdgeTag._transitional = false;
|
|
} else {
|
|
bool isFace0Selected = _parentFaceTag[eFaces[0]]._selected;
|
|
|
|
pEdgeTag._transitional = false;
|
|
for (int i = 1; i < eFaces.size(); ++i) {
|
|
if (_parentFaceTag[eFaces[i]]._selected != isFace0Selected) {
|
|
pEdgeTag._transitional = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // end namespace internal
|
|
} // end namespace Vtr
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
} // end namespace OpenSubdiv
|