2014-09-05 22:07:46 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
//
|
2015-01-06 22:26:20 +00:00
|
|
|
#include "../sdc/types.h"
|
2014-09-05 22:07:46 +00:00
|
|
|
#include "../sdc/crease.h"
|
|
|
|
#include "../vtr/array.h"
|
|
|
|
#include "../vtr/refinement.h"
|
|
|
|
#include "../vtr/fvarLevel.h"
|
|
|
|
|
|
|
|
#include "../vtr/fvarRefinement.h"
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
#include <cstdio>
|
|
|
|
#include <cstring>
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// FVarRefinement:
|
|
|
|
// Analogous to Refinement -- retains data to facilitate refinement and
|
|
|
|
// population of refined face-varying data channels.
|
|
|
|
//
|
|
|
|
namespace OpenSubdiv {
|
|
|
|
namespace OPENSUBDIV_VERSION {
|
|
|
|
|
|
|
|
namespace Vtr {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Simple (for now) constructor and destructor:
|
|
|
|
//
|
|
|
|
FVarRefinement::FVarRefinement(Refinement const& refinement,
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarLevel& parentFVarLevel,
|
|
|
|
FVarLevel& childFVarLevel) :
|
2014-09-05 22:07:46 +00:00
|
|
|
_refinement(refinement),
|
2014-11-04 01:31:24 +00:00
|
|
|
_parentLevel(*refinement._parent),
|
|
|
|
_parentFVar(parentFVarLevel),
|
|
|
|
_childLevel(*refinement._child),
|
|
|
|
_childFVar(childFVarLevel) {
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FVarRefinement::~FVarRefinement() {
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// Methods supporting the refinement of face-varying data that has previously
|
|
|
|
// been applied to the Refinment member. So these methods already have access
|
|
|
|
// to fully refined child components.
|
|
|
|
//
|
|
|
|
|
|
|
|
void
|
|
|
|
FVarRefinement::applyRefinement() {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Transfer basic properties from the parent to child level:
|
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._options = _parentFVar._options;
|
|
|
|
|
|
|
|
_childFVar._isLinear = _parentFVar._isLinear;
|
2015-02-12 04:51:00 +00:00
|
|
|
_childFVar._hasLinearBoundaries = _parentFVar._hasLinearBoundaries;
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._hasDependentSharpness = _parentFVar._hasDependentSharpness;
|
2014-09-05 22:07:46 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// It's difficult to know immediately how many child values arise from the
|
|
|
|
// refinement -- particularly when sparse, so we get a close upper bound,
|
|
|
|
// resize for that number and trim when finished:
|
|
|
|
//
|
|
|
|
estimateAndAllocateChildValues();
|
|
|
|
populateChildValues();
|
|
|
|
trimAndFinalizeChildValues();
|
|
|
|
|
|
|
|
propagateEdgeTags();
|
|
|
|
propagateValueTags();
|
2015-02-12 04:51:00 +00:00
|
|
|
if (_childFVar.hasSmoothBoundaries()) {
|
2014-09-30 01:46:33 +00:00
|
|
|
propagateValueCreases();
|
2014-10-06 19:07:44 +00:00
|
|
|
reclassifySemisharpValues();
|
2014-09-30 01:46:33 +00:00
|
|
|
}
|
2014-09-05 22:07:46 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// The refined face-values are technically redundant as they can be constructed
|
|
|
|
// from the face-vertex siblings -- do so here as a post-process
|
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
if (_childFVar.getNumValues() > _childLevel.getNumVertices()) {
|
|
|
|
_childFVar.initializeFaceValuesFromVertexFaceSiblings();
|
2014-09-05 22:07:46 +00:00
|
|
|
} else {
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar.initializeFaceValuesFromFaceVertices();
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
//printf("FVar refinement to level %d:\n", _childLevel.getDepth());
|
|
|
|
//_childFVar.print();
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
//printf("Validating refinement to level %d:\n", _childLevel.getDepth());
|
|
|
|
//_childFVar.validate();
|
|
|
|
//assert(_childFVar.validate());
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Quickly estimate the memory required for face-varying vertex-values in the child
|
|
|
|
// and allocate them. For uniform refinement this estimate should exactly match the
|
|
|
|
// desired result. For sparse refinement the excess should generally be low as the
|
|
|
|
// sparse boundary components generally occur where face-varying data is continuous.
|
|
|
|
//
|
|
|
|
void
|
|
|
|
FVarRefinement::estimateAndAllocateChildValues() {
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
int maxVertexValueCount = _refinement.getNumChildVerticesFromFaces();
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
Index cVert = _refinement.getFirstChildVertexFromEdges();
|
|
|
|
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromEdges();
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
2014-09-05 22:07:46 +00:00
|
|
|
Index pEdge = _refinement.getChildVertexParentIndex(cVert);
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
maxVertexValueCount += _parentFVar.edgeTopologyMatches(pEdge)
|
|
|
|
? 1 : _parentLevel.getEdgeFaces(pEdge).size();
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
|
|
|
|
cVert = _refinement.getFirstChildVertexFromVertices();
|
|
|
|
cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
2014-09-05 22:07:46 +00:00
|
|
|
assert(!_refinement._childVertexTag[cVert]._incomplete);
|
|
|
|
Index pVert = _refinement.getChildVertexParentIndex(cVert);
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
maxVertexValueCount += _parentFVar.getNumVertexValues(pVert);
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Now allocate/initialize for the maximum -- use resize() and trim the size later
|
|
|
|
// to avoid the constant growing with reserve() and incremental sizing. We know
|
|
|
|
// the estimate should be close and memory wasted should be small, so initialize
|
|
|
|
// all to zero as well to avoid writing in all but affected areas:
|
|
|
|
//
|
|
|
|
// Resize vectors that mirror the component counts:
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar.resizeComponents();
|
2014-09-05 22:07:46 +00:00
|
|
|
|
|
|
|
// Resize the vertex-value tags in the child level:
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._vertValueTags.resize(maxVertexValueCount);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
|
|
|
// Resize the vertex-value "parent source" mapping in the refinement:
|
|
|
|
_childValueParentSource.resize(maxVertexValueCount, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FVarRefinement::trimAndFinalizeChildValues() {
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._vertValueTags.resize(_childFVar._valueCount);
|
2015-02-12 04:51:00 +00:00
|
|
|
if (_childFVar.hasSmoothBoundaries()) {
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._vertValueCreaseEnds.resize(_childFVar._valueCount);
|
2014-09-30 01:46:33 +00:00
|
|
|
}
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
_childValueParentSource.resize(_childFVar._valueCount);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
// Allocate and initialize the vector of indices (redundant after level 0):
|
|
|
|
_childFVar._vertValueIndices.resize(_childFVar._valueCount);
|
|
|
|
for (int i = 0; i < _childFVar._valueCount; ++i) {
|
|
|
|
_childFVar._vertValueIndices[i] = i;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarRefinement::populateChildValuesForEdgeVertex(Index cVert, Index pEdge) {
|
2014-09-05 22:07:46 +00:00
|
|
|
|
|
|
|
// If we have a boundary edge with a mismatched end vertex, we only have one
|
|
|
|
// value and such cases were already initialized on construction, so return:
|
|
|
|
//
|
2014-12-15 18:23:13 +00:00
|
|
|
ConstIndexArray pEdgeFaces = _parentLevel.getEdgeFaces(pEdge);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
if (pEdgeFaces.size() == 1) return 1;
|
2014-09-05 22:07:46 +00:00
|
|
|
assert(pEdgeFaces.size() == 2);
|
|
|
|
|
|
|
|
// Determine the number of sibling values for the child vertex:
|
|
|
|
//
|
2014-12-15 18:23:13 +00:00
|
|
|
ConstIndexArray cVertFaces = _childLevel.getVertexFaces(cVert);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
int cValueCount = 1;
|
2014-09-05 22:07:46 +00:00
|
|
|
if (cVertFaces.size() > 2) {
|
2014-11-04 01:31:24 +00:00
|
|
|
cValueCount = 2;
|
2014-09-05 22:07:46 +00:00
|
|
|
} else if (cVertFaces.size() == 2) {
|
2014-11-04 01:31:24 +00:00
|
|
|
cValueCount = 1 + (cVertFaces[0] != cVertFaces[1]);
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// We may have no sibling values here when the edge was discts, but only in the
|
|
|
|
// case where the refinement was sparse (and so the child vertex "incomplete").
|
|
|
|
// In such a case the source of the single value needs to be identified:
|
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
Index cValueIndex = _childFVar.getVertexValueOffset(cVert);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
if (cValueCount == 1) {
|
|
|
|
_childValueParentSource[cValueIndex] = (cVertFaces[0] != pEdgeFaces[0]);
|
|
|
|
} else {
|
|
|
|
assert(cValueCount == 2);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
|
|
|
// Update the parent-source of any siblings (typically one):
|
2014-11-04 01:31:24 +00:00
|
|
|
for (int j = 1; j < cValueCount; ++j) {
|
|
|
|
_childValueParentSource[cValueIndex + j] = (LocalIndex) j;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update the vertex-face siblings:
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarLevel::SiblingArray cVertFaceSiblings = _childFVar.getVertexFaceSiblings(cVert);
|
2014-09-05 22:07:46 +00:00
|
|
|
for (int j = 0; j < cVertFaceSiblings.size(); ++j) {
|
|
|
|
if (_refinement.getChildFaceParentFace(cVertFaces[j]) == pEdgeFaces[1]) {
|
|
|
|
cVertFaceSiblings[j] = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
return cValueCount;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline int
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarRefinement::populateChildValuesForVertexVertex(Index cVert, Index pVert) {
|
2014-09-05 22:07:46 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// We should not be getting incomplete vertex-vertices from feature-adaptive
|
|
|
|
// refinement (as neighboring vertices will be face-vertices or edge-vertices).
|
|
|
|
// This will get messy when we do (i.e. sparse refinement of Bilinear or more
|
|
|
|
// flexible and specific sparse refinement of Catmark) but for now assume 1-to-1.
|
|
|
|
//
|
|
|
|
assert(!_refinement._childVertexTag[cVert]._incomplete);
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
// Number of child values is same as number of parent values since complete:
|
|
|
|
int cValueCount = _parentFVar.getNumVertexValues(pVert);
|
|
|
|
|
|
|
|
if (cValueCount > 1) {
|
|
|
|
Index cValueIndex = _childFVar.getVertexValueOffset(cVert);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
// Update the parent source for all child values:
|
|
|
|
for (int j = 1; j < cValueCount; ++j) {
|
|
|
|
_childValueParentSource[cValueIndex + j] = (LocalIndex) j;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update the vertex-face siblings:
|
2014-12-15 18:23:13 +00:00
|
|
|
FVarLevel::ConstSiblingArray pVertFaceSiblings = _parentFVar.getVertexFaceSiblings(pVert);
|
|
|
|
FVarLevel::SiblingArray cVertFaceSiblings = _childFVar.getVertexFaceSiblings(cVert);
|
2014-09-05 22:07:46 +00:00
|
|
|
for (int j = 0; j < cVertFaceSiblings.size(); ++j) {
|
|
|
|
cVertFaceSiblings[j] = pVertFaceSiblings[j];
|
|
|
|
}
|
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
return cValueCount;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FVarRefinement::populateChildValues() {
|
|
|
|
|
|
|
|
//
|
2014-11-09 21:25:09 +00:00
|
|
|
// Be sure to match the same vertex ordering as Refinement, i.e. face-vertices
|
|
|
|
// first vs vertex-vertices first, etc. A few optimizations within the use of
|
|
|
|
// face-varying data take advantage of this assumption.
|
2014-09-05 22:07:46 +00:00
|
|
|
//
|
2014-11-09 21:25:09 +00:00
|
|
|
// Right now there are only two orderings under consideration, and its unclear
|
|
|
|
// whether only one will be supported or both. Until that's determined, assert
|
|
|
|
// the conditions we expect for these two.
|
2014-09-05 22:07:46 +00:00
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._valueCount = 0;
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-09 21:25:09 +00:00
|
|
|
if (_refinement.getFirstChildVertexFromVertices() > 0) {
|
|
|
|
assert((_refinement.getFirstChildVertexFromFaces() <=
|
|
|
|
_refinement.getFirstChildVertexFromEdges()) &&
|
|
|
|
(_refinement.getFirstChildVertexFromEdges() <
|
|
|
|
_refinement.getFirstChildVertexFromVertices()));
|
|
|
|
|
|
|
|
populateChildValuesFromFaceVertices();
|
|
|
|
populateChildValuesFromEdgeVertices();
|
|
|
|
populateChildValuesFromVertexVertices();
|
|
|
|
} else {
|
|
|
|
assert((_refinement.getFirstChildVertexFromVertices() <
|
|
|
|
_refinement.getFirstChildVertexFromFaces()) &&
|
|
|
|
(_refinement.getFirstChildVertexFromFaces() <=
|
|
|
|
_refinement.getFirstChildVertexFromEdges()));
|
|
|
|
|
|
|
|
populateChildValuesFromVertexVertices();
|
|
|
|
populateChildValuesFromFaceVertices();
|
|
|
|
populateChildValuesFromEdgeVertices();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FVarRefinement::populateChildValuesFromFaceVertices() {
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
Index cVert = _refinement.getFirstChildVertexFromFaces();
|
|
|
|
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromFaces();
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
|
|
|
_childFVar._vertSiblingOffsets[cVert] = _childFVar._valueCount;
|
|
|
|
_childFVar._vertSiblingCounts[cVert] = 1;
|
|
|
|
_childFVar._valueCount ++;
|
|
|
|
}
|
2014-11-09 21:25:09 +00:00
|
|
|
}
|
|
|
|
void
|
|
|
|
FVarRefinement::populateChildValuesFromEdgeVertices() {
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-09 21:25:09 +00:00
|
|
|
Index cVert = _refinement.getFirstChildVertexFromEdges();
|
|
|
|
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromEdges();
|
2014-11-04 01:31:24 +00:00
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
2014-09-05 22:07:46 +00:00
|
|
|
Index pEdge = _refinement.getChildVertexParentIndex(cVert);
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._vertSiblingOffsets[cVert] = _childFVar._valueCount;
|
|
|
|
if (_parentFVar.edgeTopologyMatches(pEdge)) {
|
|
|
|
_childFVar._vertSiblingCounts[cVert] = 1;
|
|
|
|
_childFVar._valueCount ++;
|
|
|
|
} else {
|
|
|
|
int cValueCount = populateChildValuesForEdgeVertex(cVert, pEdge);
|
2015-01-06 18:56:29 +00:00
|
|
|
_childFVar._vertSiblingCounts[cVert] = (LocalIndex)cValueCount;
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._valueCount += cValueCount;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
}
|
2014-11-09 21:25:09 +00:00
|
|
|
}
|
|
|
|
void
|
|
|
|
FVarRefinement::populateChildValuesFromVertexVertices() {
|
2014-11-04 01:31:24 +00:00
|
|
|
|
2014-11-09 21:25:09 +00:00
|
|
|
Index cVert = _refinement.getFirstChildVertexFromVertices();
|
|
|
|
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
|
2014-11-04 01:31:24 +00:00
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
2014-09-05 22:07:46 +00:00
|
|
|
Index pVert = _refinement.getChildVertexParentIndex(cVert);
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._vertSiblingOffsets[cVert] = _childFVar._valueCount;
|
|
|
|
if (_parentFVar.valueTopologyMatches(_parentFVar.getVertexValueOffset(pVert))) {
|
|
|
|
_childFVar._vertSiblingCounts[cVert] = 1;
|
|
|
|
_childFVar._valueCount ++;
|
|
|
|
} else {
|
|
|
|
int cValueCount = populateChildValuesForVertexVertex(cVert, pVert);
|
2015-01-06 18:56:29 +00:00
|
|
|
_childFVar._vertSiblingCounts[cVert] = (LocalIndex)cValueCount;
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._valueCount += cValueCount;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FVarRefinement::propagateEdgeTags() {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Edge tags correspond to child edges and originate from faces or edges:
|
|
|
|
// Face-edges:
|
|
|
|
// - tag can be initialized as cts (*)
|
|
|
|
// * what was this comment: "discts based on parent face-edges at ends"
|
|
|
|
// Edge-edges:
|
|
|
|
// - tag propagated from parent edge
|
|
|
|
// - need to modify if parent edge was discts at one end
|
|
|
|
// - child edge for the matching end inherits tag
|
|
|
|
// - child edge at the other end is doubly discts
|
|
|
|
//
|
2014-10-13 19:17:25 +00:00
|
|
|
FVarLevel::ETag eTagMatch;
|
|
|
|
eTagMatch.clear();
|
|
|
|
eTagMatch._mismatch = false;
|
2014-09-05 22:07:46 +00:00
|
|
|
|
|
|
|
for (int eIndex = 0; eIndex < _refinement._childEdgeFromFaceCount; ++eIndex) {
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._edgeTags[eIndex] = eTagMatch;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
for (int eIndex = _refinement._childEdgeFromFaceCount; eIndex < _childLevel.getNumEdges(); ++eIndex) {
|
2014-09-05 22:07:46 +00:00
|
|
|
Index pEdge = _refinement.getChildEdgeParentIndex(eIndex);
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
_childFVar._edgeTags[eIndex] = _parentFVar._edgeTags[pEdge];
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FVarRefinement::propagateValueTags() {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Value tags correspond to vertex-values and originate from all three sources:
|
|
|
|
// Face-values:
|
|
|
|
// - trivially initialized as matching
|
|
|
|
// Edge-values:
|
|
|
|
// - conditionally initialized based on parent edge continuity
|
|
|
|
// - should be trivial though (unlike edge-tags for the child edges)
|
|
|
|
// Vertex-values:
|
|
|
|
// - if complete, trivially propagated/inherited
|
|
|
|
// - if incomplete, need to map to child subset
|
|
|
|
//
|
2014-09-30 01:46:33 +00:00
|
|
|
|
|
|
|
//
|
2014-11-09 21:25:09 +00:00
|
|
|
// Values from face-vertices -- all match and are sequential:
|
2014-09-30 01:46:33 +00:00
|
|
|
//
|
2014-10-13 19:17:25 +00:00
|
|
|
FVarLevel::ValueTag valTagMatch;
|
|
|
|
valTagMatch.clear();
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-09 21:25:09 +00:00
|
|
|
Index cVert = _refinement.getFirstChildVertexFromFaces();
|
|
|
|
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromFaces();
|
|
|
|
Index cVertValue = _childFVar.getVertexValueOffset(cVert);
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert, ++cVertValue) {
|
|
|
|
_childFVar._vertValueTags[cVertValue] = valTagMatch;
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
2014-09-30 01:46:33 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Values from edge-vertices -- for edges that are split, tag as mismatched and tag
|
|
|
|
// as corner or crease depending on the presence of creases in the parent:
|
|
|
|
//
|
2014-10-13 19:17:25 +00:00
|
|
|
FVarLevel::ValueTag valTagMismatch = valTagMatch;
|
|
|
|
valTagMismatch._mismatch = true;
|
2014-10-06 19:07:44 +00:00
|
|
|
|
2014-10-13 19:17:25 +00:00
|
|
|
FVarLevel::ValueTag valTagCrease = valTagMismatch;
|
|
|
|
valTagCrease._crease = true;
|
2014-09-30 01:46:33 +00:00
|
|
|
|
2015-02-12 04:51:00 +00:00
|
|
|
FVarLevel::ValueTag& valTagSplitEdge = _parentFVar.hasSmoothBoundaries() ? valTagCrease : valTagMismatch;
|
2014-09-30 01:46:33 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
cVert = _refinement.getFirstChildVertexFromEdges();
|
|
|
|
cVertEnd = cVert + _refinement.getNumChildVerticesFromEdges();
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
2014-09-05 22:07:46 +00:00
|
|
|
Index pEdge = _refinement.getChildVertexParentIndex(cVert);
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2015-02-12 04:51:00 +00:00
|
|
|
FVarLevel::ETag pEdgeTag = _parentFVar._edgeTags[pEdge];
|
|
|
|
if (pEdgeTag._mismatch || pEdgeTag._linear) {
|
2014-11-04 01:31:24 +00:00
|
|
|
std::fill(cValueTags.begin(), cValueTags.end(), valTagSplitEdge);
|
2015-02-12 04:51:00 +00:00
|
|
|
} else {
|
|
|
|
std::fill(cValueTags.begin(), cValueTags.end(), valTagMatch);
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-30 01:46:33 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Values from vertex-vertices -- inherit tags from parent values when complete
|
|
|
|
// otherwise (not yet supported) need to identify the parent value for each child:
|
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
cVert = _refinement.getFirstChildVertexFromVertices();
|
|
|
|
cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
2014-09-05 22:07:46 +00:00
|
|
|
Index pVert = _refinement.getChildVertexParentIndex(cVert);
|
2014-11-04 01:31:24 +00:00
|
|
|
assert(!_refinement._childVertexTag[cVert]._incomplete);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-12-15 18:23:13 +00:00
|
|
|
FVarLevel::ConstValueTagArray pValueTags = _parentFVar.getVertexValueTags(pVert);
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
|
2014-09-05 22:07:46 +00:00
|
|
|
|
2014-11-25 22:14:59 +00:00
|
|
|
memcpy(cValueTags.begin(), pValueTags.begin(),
|
|
|
|
pValueTags.size()*sizeof(FVarLevel::ValueTag));
|
2014-09-05 22:07:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-30 01:46:33 +00:00
|
|
|
void
|
|
|
|
FVarRefinement::propagateValueCreases() {
|
|
|
|
|
2015-02-12 04:51:00 +00:00
|
|
|
assert(_childFVar.hasSmoothBoundaries());
|
2014-09-30 01:46:33 +00:00
|
|
|
|
|
|
|
// Skip child vertices from faces:
|
|
|
|
|
|
|
|
//
|
|
|
|
// For each child vertex from an edge that has FVar values and is complete, initialize
|
2014-11-18 01:19:30 +00:00
|
|
|
// the crease-ends for those values tagged as smooth boundaries
|
2014-09-30 01:46:33 +00:00
|
|
|
//
|
2014-11-18 01:19:30 +00:00
|
|
|
// Note that this does depend on the nature of the topological split, i.e. how many
|
|
|
|
// child faces are incident the new child vertex for each face that becomes a crease,
|
|
|
|
// so identify constants to be used in each iteration first:
|
|
|
|
//
|
|
|
|
LocalIndex crease0StartFace = 0;
|
|
|
|
LocalIndex crease0EndFace = 1;
|
|
|
|
LocalIndex crease1StartFace = 2;
|
|
|
|
LocalIndex crease1EndFace = 3;
|
|
|
|
|
|
|
|
if (_refinement._splitType == Sdc::SPLIT_TO_TRIS) {
|
|
|
|
crease0EndFace = 2;
|
|
|
|
crease1StartFace = 3;
|
|
|
|
crease1EndFace = 5;
|
|
|
|
}
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
Index cVert = _refinement.getFirstChildVertexFromEdges();
|
|
|
|
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromEdges();
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
|
|
|
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
|
|
|
|
|
|
|
|
if (!cValueTags[0].isMismatch()) continue;
|
2014-09-30 01:46:33 +00:00
|
|
|
if (_refinement._childVertexTag[cVert]._incomplete) continue;
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarLevel::CreaseEndPairArray cValueCreaseEnds = _childFVar.getVertexValueCreaseEnds(cVert);
|
|
|
|
|
|
|
|
if (!cValueTags[0].isInfSharp()) {
|
2014-11-18 01:19:30 +00:00
|
|
|
cValueCreaseEnds[0]._startFace = crease0StartFace;
|
|
|
|
cValueCreaseEnds[0]._endFace = crease0EndFace;
|
2014-09-30 01:46:33 +00:00
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
if ((cValueTags.size() > 1) && !cValueTags[1].isInfSharp()) {
|
2014-11-18 01:19:30 +00:00
|
|
|
cValueCreaseEnds[1]._startFace = crease1StartFace;
|
|
|
|
cValueCreaseEnds[1]._endFace = crease1EndFace;
|
2014-09-30 01:46:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// For each child vertex from a vertex that has FVar values and is complete, initialize
|
2014-10-06 19:07:44 +00:00
|
|
|
// the crease-ends for those values tagged as smooth or semi-sharp (to become smooth
|
|
|
|
// eventually):
|
2014-09-30 01:46:33 +00:00
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
cVert = _refinement.getFirstChildVertexFromVertices();
|
|
|
|
cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
|
|
|
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
|
|
|
|
|
|
|
|
if (!cValueTags[0].isMismatch()) continue;
|
2014-09-30 01:46:33 +00:00
|
|
|
if (_refinement._childVertexTag[cVert]._incomplete) continue;
|
|
|
|
|
|
|
|
Index pVert = _refinement.getChildVertexParentIndex(cVert);
|
|
|
|
|
2014-12-15 18:23:13 +00:00
|
|
|
FVarLevel::ConstCreaseEndPairArray pCreaseEnds = _parentFVar.getVertexValueCreaseEnds(pVert);
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarLevel::CreaseEndPairArray cCreaseEnds = _childFVar.getVertexValueCreaseEnds(cVert);
|
2014-09-30 01:46:33 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
for (int j = 0; j < cValueTags.size(); ++j) {
|
|
|
|
if (!cValueTags[j].isInfSharp()) {
|
|
|
|
cCreaseEnds[j] = pCreaseEnds[j];
|
2014-09-30 01:46:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-06 19:07:44 +00:00
|
|
|
void
|
|
|
|
FVarRefinement::reclassifySemisharpValues() {
|
|
|
|
|
|
|
|
//
|
|
|
|
// Reclassify the tags of semi-sharp vertex values to smooth creases according to
|
|
|
|
// changes in sharpness:
|
|
|
|
//
|
|
|
|
// Vertex values introduced on edge-verts can never be semi-sharp as they will be
|
|
|
|
// introduced on discts edges, which are implicitly infinitely sharp, so we can
|
|
|
|
// skip them entirely.
|
|
|
|
//
|
|
|
|
// So we just need to deal with those values descended from parent vertices that
|
|
|
|
// were semi-sharp. The child values will have inherited the semi-sharp tag from
|
|
|
|
// their parent values -- we will be able to clear it in many simple cases but
|
|
|
|
// ultimately will need to inspect each value:
|
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
bool hasDependentSharpness = _parentFVar._hasDependentSharpness;
|
2014-10-24 23:15:22 +00:00
|
|
|
|
2014-10-13 19:17:25 +00:00
|
|
|
FVarLevel::ValueTag valTagCrease;
|
|
|
|
valTagCrease.clear();
|
|
|
|
valTagCrease._mismatch = true;
|
|
|
|
valTagCrease._crease = true;
|
2014-10-06 19:07:44 +00:00
|
|
|
|
2014-10-13 19:17:25 +00:00
|
|
|
FVarLevel::ValueTag valTagSemiSharp;
|
|
|
|
valTagSemiSharp.clear();
|
|
|
|
valTagSemiSharp._mismatch = true;
|
2014-10-06 19:07:44 +00:00
|
|
|
valTagSemiSharp._semiSharp = true;
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
Index cVert = _refinement.getFirstChildVertexFromVertices();
|
|
|
|
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
|
|
|
|
|
|
|
|
for ( ; cVert < cVertEnd; ++cVert) {
|
|
|
|
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
|
2014-10-06 19:07:44 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
if (!cValueTags[0].isMismatch()) continue;
|
2014-10-06 19:07:44 +00:00
|
|
|
if (_refinement._childVertexTag[cVert]._incomplete) continue;
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
// If the parent vertex wasn't semi-sharp, the child vertex and values can't be:
|
2014-10-06 19:07:44 +00:00
|
|
|
Index pVert = _refinement.getChildVertexParentIndex(cVert);
|
2014-11-04 01:31:24 +00:00
|
|
|
if (!_parentLevel._vertTags[pVert]._semiSharp) continue;
|
2014-10-06 19:07:44 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
// If the child vertex is still sharp, all values remain unaffected:
|
|
|
|
if (!Sdc::Crease::IsSmooth(_childLevel._vertSharpness[cVert])) continue;
|
2014-10-06 19:07:44 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
// If the child is no longer semi-sharp, we can just clear those values marked
|
|
|
|
// (i.e. make them creases, others may remain corners) and continue:
|
2014-10-06 19:07:44 +00:00
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
if (!_childLevel._vertTags[cVert]._semiSharp) {
|
|
|
|
for (int j = 0; j < cValueTags.size(); ++j) {
|
|
|
|
if (cValueTags[j]._semiSharp) {
|
2015-02-10 01:41:19 +00:00
|
|
|
FVarLevel::ValueTag cValueTagOld = cValueTags[j];
|
2014-11-04 01:31:24 +00:00
|
|
|
cValueTags[j] = valTagCrease;
|
2015-02-10 01:41:19 +00:00
|
|
|
cValueTags[j]._xordinary = cValueTagOld._xordinary;
|
2014-10-24 23:15:22 +00:00
|
|
|
}
|
2014-10-06 19:07:44 +00:00
|
|
|
}
|
2014-10-24 23:15:22 +00:00
|
|
|
continue;
|
2014-10-06 19:07:44 +00:00
|
|
|
}
|
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
// There are some semi-sharp edges left -- for those values tagged as semi-sharp,
|
2014-10-24 23:15:22 +00:00
|
|
|
// see if they are still semi-sharp and clear those that are not:
|
2014-10-06 19:07:44 +00:00
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarLevel::CreaseEndPairArray const cValueCreaseEnds = _childFVar.getVertexValueCreaseEnds(cVert);
|
2014-10-24 23:15:22 +00:00
|
|
|
|
2014-12-15 18:23:13 +00:00
|
|
|
ConstIndexArray cVertEdges = _childLevel.getVertexEdges(cVert);
|
2014-10-24 23:15:22 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
for (int j = 0; j < cValueTags.size(); ++j) {
|
|
|
|
if (cValueTags[j]._semiSharp && !cValueTags[j]._depSharp) {
|
|
|
|
LocalIndex vStartFace = cValueCreaseEnds[j]._startFace;
|
|
|
|
LocalIndex vEndFace = cValueCreaseEnds[j]._endFace;
|
2014-10-24 23:15:22 +00:00
|
|
|
|
|
|
|
bool isStillSemiSharp = false;
|
2014-11-04 01:31:24 +00:00
|
|
|
if (vEndFace > vStartFace) {
|
|
|
|
for (int k = vStartFace + 1; !isStillSemiSharp && (k <= vEndFace); ++k) {
|
|
|
|
isStillSemiSharp = _childLevel._edgeTags[cVertEdges[k]]._semiSharp;
|
2014-10-24 23:15:22 +00:00
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
} else if (vStartFace > vEndFace) {
|
|
|
|
for (int k = vStartFace + 1; !isStillSemiSharp && (k < cVertEdges.size()); ++k) {
|
|
|
|
isStillSemiSharp = _childLevel._edgeTags[cVertEdges[k]]._semiSharp;
|
2014-10-24 23:15:22 +00:00
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
for (int k = 0; !isStillSemiSharp && (k <= vEndFace); ++k) {
|
|
|
|
isStillSemiSharp = _childLevel._edgeTags[cVertEdges[k]]._semiSharp;
|
2014-10-24 23:15:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!isStillSemiSharp) {
|
2015-02-10 01:41:19 +00:00
|
|
|
FVarLevel::ValueTag cValueTagOld = cValueTags[j];
|
2014-11-04 01:31:24 +00:00
|
|
|
cValueTags[j] = valTagCrease;
|
2015-02-10 01:41:19 +00:00
|
|
|
cValueTags[j]._xordinary = cValueTagOld._xordinary;
|
2014-10-06 19:07:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-10-24 23:15:22 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Now account for "dependent sharpness" (only matters when we have two values) --
|
2014-11-04 01:31:24 +00:00
|
|
|
// if one value was dependent/sharpened based on the other, clear the dependency
|
2014-10-24 23:15:22 +00:00
|
|
|
// tag if it is no longer sharp:
|
|
|
|
//
|
2014-11-04 01:31:24 +00:00
|
|
|
if ((cValueTags.size() == 2) && hasDependentSharpness) {
|
|
|
|
if (cValueTags[0]._depSharp && !cValueTags[1]._semiSharp) {
|
|
|
|
cValueTags[0]._depSharp = false;
|
|
|
|
} else if (cValueTags[1]._depSharp && !cValueTags[0]._semiSharp) {
|
|
|
|
cValueTags[1]._depSharp = false;
|
2014-10-24 23:15:22 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-06 19:07:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
float
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarRefinement::getFractionalWeight(Index pVert, LocalIndex pSibling,
|
|
|
|
Index cVert, LocalIndex /* cSibling */) const {
|
2014-10-13 19:17:25 +00:00
|
|
|
|
2014-10-06 19:07:44 +00:00
|
|
|
// Should only be called when the parent was semi-sharp but this child vertex
|
|
|
|
// value (not necessarily the child vertex as a whole) is no longer semi-sharp:
|
2014-11-04 01:31:24 +00:00
|
|
|
assert(_parentLevel._vertTags[pVert]._semiSharp);
|
|
|
|
assert(!_childLevel._vertTags[cVert]._incomplete);
|
2014-10-06 19:07:44 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
// Need to identify sharpness values for edges within the spans for both the
|
|
|
|
// parent and child...
|
|
|
|
//
|
2014-10-13 19:17:25 +00:00
|
|
|
// Consider gathering the complete parent and child sharpness vectors outside
|
|
|
|
// this method and re-using them for each sibling, i.e. passing them to this
|
|
|
|
// method somehow. We may also need them there for mask-related purposes...
|
|
|
|
//
|
2014-12-15 18:23:13 +00:00
|
|
|
ConstIndexArray pVertEdges = _parentLevel.getVertexEdges(pVert);
|
|
|
|
ConstIndexArray cVertEdges = _childLevel.getVertexEdges(cVert);
|
2014-10-13 19:17:25 +00:00
|
|
|
|
|
|
|
float * pEdgeSharpness = (float*) alloca(2 * pVertEdges.size() * sizeof(float));
|
|
|
|
float * cEdgeSharpness = pEdgeSharpness + pVertEdges.size();
|
2014-10-06 19:07:44 +00:00
|
|
|
|
2014-11-04 01:31:24 +00:00
|
|
|
FVarLevel::CreaseEndPair pValueCreaseEnds = _parentFVar.getVertexValueCreaseEnds(pVert)[pSibling];
|
|
|
|
|
|
|
|
LocalIndex pStartFace = pValueCreaseEnds._startFace;
|
|
|
|
LocalIndex pEndFace = pValueCreaseEnds._endFace;
|
2014-10-13 19:17:25 +00:00
|
|
|
|
|
|
|
int interiorEdgeCount = 0;
|
2014-11-04 01:31:24 +00:00
|
|
|
if (pEndFace > pStartFace) {
|
|
|
|
for (int i = pStartFace + 1; i <= pEndFace; ++i, ++interiorEdgeCount) {
|
|
|
|
pEdgeSharpness[interiorEdgeCount] = _parentLevel._edgeSharpness[pVertEdges[i]];
|
|
|
|
cEdgeSharpness[interiorEdgeCount] = _childLevel._edgeSharpness[cVertEdges[i]];
|
2014-10-13 19:17:25 +00:00
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
} else if (pStartFace > pEndFace) {
|
|
|
|
for (int i = pStartFace + 1; i < pVertEdges.size(); ++i, ++interiorEdgeCount) {
|
|
|
|
pEdgeSharpness[interiorEdgeCount] = _parentLevel._edgeSharpness[pVertEdges[i]];
|
|
|
|
cEdgeSharpness[interiorEdgeCount] = _childLevel._edgeSharpness[cVertEdges[i]];
|
2014-10-13 19:17:25 +00:00
|
|
|
}
|
2014-11-04 01:31:24 +00:00
|
|
|
for (int i = 0; i <= pEndFace; ++i, ++interiorEdgeCount) {
|
|
|
|
pEdgeSharpness[interiorEdgeCount] = _parentLevel._edgeSharpness[pVertEdges[i]];
|
|
|
|
cEdgeSharpness[interiorEdgeCount] = _childLevel._edgeSharpness[cVertEdges[i]];
|
2014-10-13 19:17:25 +00:00
|
|
|
}
|
2014-10-06 19:07:44 +00:00
|
|
|
}
|
2014-11-18 01:19:30 +00:00
|
|
|
return Sdc::Crease(_refinement._options).ComputeFractionalWeightAtVertex(
|
2014-11-04 01:31:24 +00:00
|
|
|
_parentLevel._vertSharpness[pVert], _childLevel._vertSharpness[cVert],
|
2014-10-13 19:17:25 +00:00
|
|
|
interiorEdgeCount, pEdgeSharpness, cEdgeSharpness);
|
2014-10-06 19:07:44 +00:00
|
|
|
}
|
|
|
|
|
2014-09-05 22:07:46 +00:00
|
|
|
} // end namespace Vtr
|
|
|
|
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
|
|
} // end namespace OpenSubdiv
|