Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,73 @@
#
# Copyright 2013 Pixar
#
# Licensed under the terms set forth in the LICENSE.txt file available at
# https://opensubdiv.org/license.
#
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# source & headers
set(SOURCE_FILES
fvarLevel.cpp
fvarRefinement.cpp
level.cpp
quadRefinement.cpp
refinement.cpp
sparseSelector.cpp
triRefinement.cpp
)
set(PRIVATE_HEADER_FILES
quadRefinement.h
triRefinement.h
)
set(PUBLIC_HEADER_FILES
array.h
componentInterfaces.h
fvarLevel.h
fvarRefinement.h
level.h
refinement.h
sparseSelector.h
stackBuffer.h
types.h
)
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
include_directories("${CMAKE_CURRENT_BINARY_DIR}")
if (NOT NO_LIB)
# Compile objs first for both the CPU and GPU libs -----
add_library(vtr_obj
OBJECT
${SOURCE_FILES}
${PRIVATE_HEADER_FILES}
${PUBLIC_HEADER_FILES}
)
set_target_properties(vtr_obj
PROPERTIES
FOLDER "opensubdiv"
)
endif()
#-------------------------------------------------------------------------------
osd_add_doxy_headers( "${DOXY_HEADER_FILES}" )
install(
FILES
${PUBLIC_HEADER_FILES}
DESTINATION
"${CMAKE_INCDIR_BASE}/vtr"
PERMISSIONS
OWNER_READ
GROUP_READ
WORLD_READ )
#-------------------------------------------------------------------------------

View File

@@ -0,0 +1,131 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_ARRAY_INTERFACE_H
#define OPENSUBDIV3_VTR_ARRAY_INTERFACE_H
#include "../version.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
//
// This class provides a simple array-like interface -- a subset std::vector's interface -- for
// a sequence of elements stored in contiguous memory. It provides a unified representation for
// referencing data on the stack, all or a subset of std::vector<>, or anywhere else in memory.
//
// Note that its members are head/size rather than begin/end as in std::vector -- we frequently
// need only the size for many queries, and that is most often what is stored elsewhere in other
// classes, so we hope to reduce unnecessary address arithmetic constructing the interface and
// accessing the size. The size type is also specifically 32-bit (rather than size_t) to match
// internal usage and avoid unnecessary conversion to/from 64-bit.
//
// Question:
// Naming is at issue here... formerly called ArrayInterface until that was shot down it has
// been simplified to Array but needs to be distanced from std::array as it DOES NOT store its
// own memory and is simply an interface to memory stored elsewhere.
//
template <typename TYPE>
class ConstArray {
public:
typedef TYPE value_type;
typedef int size_type;
typedef TYPE const& const_reference;
typedef TYPE const* const_iterator;
typedef TYPE& reference;
typedef TYPE* iterator;
public:
ConstArray() : _begin(0), _size(0) { }
ConstArray(value_type const * ptr, size_type sizeArg) :
_begin(ptr), _size(sizeArg) { }
size_type size() const { return _size; }
bool empty() const { return _size==0; }
const_reference operator[](int index) const { return _begin[index]; }
const_iterator begin() const { return _begin; }
const_iterator end() const { return _begin + _size; }
size_type FindIndexIn4Tuple(value_type value) const {
assert(_size>=4);
if (value == _begin[0]) return 0;
if (value == _begin[1]) return 1;
if (value == _begin[2]) return 2;
if (value == _begin[3]) return 3;
assert("FindIndexIn4Tuple() did not find expected value!" == 0);
return -1;
}
size_type FindIndex(value_type value) const {
for (size_type i=0; i<size(); ++i) {
if (value==_begin[i]) {
return i;
}
}
return -1;
}
protected:
value_type const * _begin;
size_type _size;
};
template <typename TYPE>
class Array : public ConstArray<TYPE> {
public:
typedef TYPE value_type;
typedef int size_type;
typedef TYPE const& const_reference;
typedef TYPE& reference;
typedef TYPE* iterator;
public:
Array() : ConstArray<TYPE>() { }
Array(value_type * ptr, size_type sizeArg) : ConstArray<TYPE>(ptr, sizeArg) { }
public:
const_reference operator[](int index) const {
return ConstArray<TYPE>::_begin[index];
}
reference operator[](int index) {
return const_cast<reference>(ConstArray<TYPE>::_begin[index]);
}
iterator begin() {
return const_cast<iterator>(ConstArray<TYPE>::_begin);
}
iterator end() {
return const_cast<iterator>(ConstArray<TYPE>::_begin +
ConstArray<TYPE>::_size);
}
};
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_ARRAY_INTERFACE_H */

View File

@@ -0,0 +1,141 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_COMPONENT_INTERFACES_H
#define OPENSUBDIV3_VTR_COMPONENT_INTERFACES_H
#include "../version.h"
#include "../sdc/types.h"
#include "../sdc/crease.h"
#include "../vtr/types.h"
#include "../vtr/stackBuffer.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// Simple classes supporting the interfaces required of generic topological
// types in the Scheme mask queries, e.g. <typename FACE, VERTEX, etc.>
//
// These are not used with Vtr but arguably belong with it as the details to
// write these efficiently depends very much on intimate details of Vtr's
// implementation, e.g. the use of tag bits, subdivision Rules, etc.
//
//
// For <typename FACE>, which provides information in the neighborhood of a face:
//
class FaceInterface {
public:
FaceInterface() { }
FaceInterface(int vertCount) : _vertCount(vertCount) { }
~FaceInterface() { }
public: // Generic interface expected of <typename FACE>:
int GetNumVertices() const { return _vertCount; }
private:
int _vertCount;
};
//
// For <typename EDGE>, which provides information in the neighborhood of an edge:
//
class EdgeInterface {
public:
EdgeInterface() { }
EdgeInterface(Level const& level) : _level(&level) { }
~EdgeInterface() { }
void SetIndex(int edgeIndex) { _eIndex = edgeIndex; }
public: // Generic interface expected of <typename EDGE>:
int GetNumFaces() const { return _level->getEdgeFaces(_eIndex).size(); }
float GetSharpness() const { return _level->getEdgeSharpness(_eIndex); }
void GetChildSharpnesses(Sdc::Crease const&, float s[2]) const {
// Need to use the Refinement here to identify the two child edges:
s[0] = s[1] = GetSharpness() - 1.0f;
}
void GetNumVerticesPerFace(int vertsPerFace[]) const {
ConstIndexArray eFaces = _level->getEdgeFaces(_eIndex);
for (int i = 0; i < eFaces.size(); ++i) {
vertsPerFace[i] = _level->getFaceVertices(eFaces[i]).size();
}
}
private:
const Level* _level;
int _eIndex;
};
//
// For <typename VERTEX>, which provides information in the neighborhood of a vertex:
//
class VertexInterface {
public:
VertexInterface() { }
VertexInterface(Level const& parent, Level const& child) : _parent(&parent), _child(&child) { }
~VertexInterface() { }
void SetIndex(int parentIndex, int childIndex) {
_pIndex = parentIndex;
_cIndex = childIndex;
_eCount = _parent->getVertexEdges(_pIndex).size();
_fCount = _parent->getVertexFaces(_pIndex).size();
}
public: // Generic interface expected of <typename VERT>:
int GetNumEdges() const { return _eCount; }
int GetNumFaces() const { return _fCount; }
float GetSharpness() const { return _parent->getVertexSharpness(_pIndex); }
float* GetSharpnessPerEdge(float pSharpness[]) const {
ConstIndexArray pEdges = _parent->getVertexEdges(_pIndex);
for (int i = 0; i < _eCount; ++i) {
pSharpness[i] = _parent->getEdgeSharpness(pEdges[i]);
}
return pSharpness;
}
float GetChildSharpness(Sdc::Crease const&) const { return _child->getVertexSharpness(_cIndex); }
float* GetChildSharpnessPerEdge(Sdc::Crease const& crease, float cSharpness[]) const {
internal::StackBuffer<float,16> pSharpness(_eCount);
GetSharpnessPerEdge(pSharpness);
crease.SubdivideEdgeSharpnessesAroundVertex(_eCount, pSharpness, cSharpness);
return cSharpness;
}
private:
const Level* _parent;
const Level* _child;
int _pIndex;
int _cIndex;
int _eCount;
int _fCount;
};
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_COMPONENT_INTERFACES_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,422 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_FVAR_LEVEL_H
#define OPENSUBDIV3_VTR_FVAR_LEVEL_H
#include "../version.h"
#include "../sdc/types.h"
#include "../sdc/crease.h"
#include "../sdc/options.h"
#include "../vtr/types.h"
#include "../vtr/level.h"
#include <vector>
#include <cassert>
#include <cstring>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// FVarLevel:
// A "face-varying channel" includes the topology for a set of face-varying
// data, relative to the topology of the Level with which it is associated.
//
// Analogous to a set of vertices and face-vertices that define the topology for
// the geometry, a channel requires a set of "values" and "face-values". The
// "values" are indices of entries in a set of face-varying data, just as vertices
// are indices into a set of vertex data. The face-values identify a value for
// each vertex of the face, and so define topology for the values that may be
// unique to each channel.
//
// In addition to the value size and the vector of face-values (which matches the
// size of the geometry's face-vertices), tags are associated with each component
// to identify deviations of the face-varying topology from the vertex topology.
// And since there may be a one-to-many mapping between vertices and face-varying
// values, that mapping is also allocated.
//
// It turns out that the mapping used is able to completely encode the set of
// face-values and is more amenable to refinement. Currently the face-values
// take up almost half the memory of this representation, so if memory does
// become a concern, we do not need to store them. The only reason we do so now
// is that the face-value interface for specifying base topology and inspecting
// subsequent levels is very familiar to that of face-vertices for clients. So
// having them available for such access is convenient.
//
// Regarding scope and access...
// Unclear at this early state, but leaning towards nesting this class within
// Level, given the intimate dependency between the two.
// Everything is being declared public for now to facilitate access until it's
// clearer how this functionality will be provided.
//
class FVarLevel {
public:
//
// Component tags -- trying to minimize the types needed here:
//
// Tag per Edge:
// - facilitates topological analysis around each vertex
// - required during refinement to spawn one or more edge-values
//
struct ETag {
ETag() { }
void clear() { std::memset(this, 0, sizeof(ETag)); }
typedef unsigned char ETagSize;
ETagSize _mismatch : 1; // local FVar topology does not match
ETagSize _disctsV0 : 1; // discontinuous at vertex 0
ETagSize _disctsV1 : 1; // discontinuous at vertex 1
ETagSize _linear : 1; // linear boundary constraints
Level::ETag combineWithLevelETag(Level::ETag) const;
};
//
// Tag per Value:
// - informs both refinement and interpolation
// - every value spawns a child value in refinement
// - includes a subset of Level::VTag to be later combined with a VTag
//
struct ValueTag {
ValueTag() { }
void clear() { std::memset(this, 0, sizeof(ValueTag)); }
bool isMismatch() const { return _mismatch; }
bool isCrease() const { return _crease; }
bool isCorner() const { return !_crease; }
bool isSemiSharp() const { return _semiSharp; }
bool isInfSharp() const { return !_semiSharp && !_crease; }
bool isDepSharp() const { return _depSharp; }
bool hasCreaseEnds() const { return _crease || _semiSharp; }
bool hasInfSharpEdges() const { return _infSharpEdges; }
bool hasInfIrregularity() const { return _infIrregular; }
typedef unsigned char ValueTagSize;
// If there is no mismatch, no other members should be inspected
ValueTagSize _mismatch : 1; // local FVar topology does not match
ValueTagSize _xordinary : 1; // local FVar topology is extra-ordinary
ValueTagSize _nonManifold : 1; // local FVar topology is non-manifold
ValueTagSize _crease : 1; // value is a crease, otherwise a corner
ValueTagSize _semiSharp : 1; // value is a corner decaying to crease
ValueTagSize _depSharp : 1; // value is a corner by dependency on another
ValueTagSize _infSharpEdges : 1; // value is a corner by inf-sharp features
ValueTagSize _infIrregular : 1; // value span includes inf-sharp irregularity
Level::VTag combineWithLevelVTag(Level::VTag) const;
// Alternate constructor and accessor for dealing with integer bits directly:
explicit ValueTag(ValueTagSize bits) {
std::memcpy(this, &bits, sizeof(bits));
}
ValueTagSize getBits() const {
ValueTagSize bits;
std::memcpy(&bits, this, sizeof(bits));
return bits;
}
};
typedef Vtr::ConstArray<ValueTag> ConstValueTagArray;
typedef Vtr::Array<ValueTag> ValueTagArray;
//
// Simple struct containing the "end faces" of a crease, i.e. the faces which
// contain the FVar values to be used when interpolating the crease. (Prefer
// the struct over std::pair for its member names)
//
struct CreaseEndPair {
LocalIndex _startFace;
LocalIndex _endFace;
};
typedef Vtr::ConstArray<CreaseEndPair> ConstCreaseEndPairArray;
typedef Vtr::Array<CreaseEndPair> CreaseEndPairArray;
typedef LocalIndex Sibling;
typedef ConstLocalIndexArray ConstSiblingArray;
typedef LocalIndexArray SiblingArray;
public:
FVarLevel(Level const& level);
~FVarLevel();
// Queries for the entire channel:
Level const& getLevel() const { return _level; }
int getNumValues() const { return _valueCount; }
int getNumFaceValuesTotal() const { return (int) _faceVertValues.size(); }
bool isLinear() const { return _isLinear; }
bool hasLinearBoundaries() const { return _hasLinearBoundaries; }
bool hasSmoothBoundaries() const { return ! _hasLinearBoundaries; }
bool hasCreaseEnds() const { return hasSmoothBoundaries(); }
Sdc::Options getOptions() const { return _options; }
// Queries per face:
ConstIndexArray getFaceValues(Index fIndex) const;
IndexArray getFaceValues(Index fIndex);
// Queries per edge:
ETag getEdgeTag(Index eIndex) const { return _edgeTags[eIndex]; }
bool edgeTopologyMatches(Index eIndex) const { return !getEdgeTag(eIndex)._mismatch; }
// Queries per vertex (and its potential sibling values):
int getNumVertexValues(Index v) const { return _vertSiblingCounts[v]; }
Index getVertexValueOffset(Index v, Sibling i = 0) const { return _vertSiblingOffsets[v] + i; }
Index getVertexValue(Index v, Sibling i = 0) const { return _vertValueIndices[getVertexValueOffset(v,i)]; }
Index findVertexValueIndex(Index vertexIndex, Index valueIndex) const;
// Methods to access/modify array properties per vertex:
ConstIndexArray getVertexValues(Index vIndex) const;
IndexArray getVertexValues(Index vIndex);
ConstValueTagArray getVertexValueTags(Index vIndex) const;
ValueTagArray getVertexValueTags(Index vIndex);
ConstCreaseEndPairArray getVertexValueCreaseEnds(Index vIndex) const;
CreaseEndPairArray getVertexValueCreaseEnds(Index vIndex);
ConstSiblingArray getVertexFaceSiblings(Index vIndex) const;
SiblingArray getVertexFaceSiblings(Index vIndex);
// Queries per value:
ValueTag getValueTag(Index valueIndex) const { return _vertValueTags[valueIndex]; }
bool valueTopologyMatches(Index valueIndex) const { return !getValueTag(valueIndex)._mismatch; }
CreaseEndPair getValueCreaseEndPair(Index valueIndex) const { return _vertValueCreaseEnds[valueIndex]; }
// Tag queries related to faces (use Level methods for those returning Level::VTag/ETag)
void getFaceValueTags(Index faceIndex, ValueTag valueTags[]) const;
ValueTag getFaceCompositeValueTag(Index faceIndex) const;
// Higher-level topological queries, i.e. values in a neighborhood:
void getEdgeFaceValues(Index eIndex, int fIncToEdge, Index valuesPerVert[2]) const;
void getVertexEdgeValues(Index vIndex, Index valuesPerEdge[]) const;
void getVertexCreaseEndValues(Index vIndex, Sibling sibling, Index endValues[2]) const;
// Initialization and allocation helpers:
void setOptions(Sdc::Options const& options);
void resizeVertexValues(int numVertexValues);
void resizeValues(int numValues);
void resizeComponents();
// Topological analysis methods -- tagging and face-value population:
void completeTopologyFromFaceValues(int regBoundaryValence);
void initializeFaceValuesFromFaceVertices();
void initializeFaceValuesFromVertexFaceSiblings();
struct ValueSpan;
void gatherValueSpans(Index vIndex, ValueSpan * vValueSpans) const;
// Debugging methods:
bool validate() const;
void print() const;
void buildFaceVertexSiblingsFromVertexFaceSiblings(std::vector<Sibling>& fvSiblings) const;
private:
// Just as Refinements build Levels, FVarRefinements build FVarLevels...
friend class FVarRefinement;
Level const & _level;
// Linear interpolation options vary between channels:
Sdc::Options _options;
bool _isLinear;
bool _hasLinearBoundaries;
bool _hasDependentSharpness;
int _valueCount;
//
// Vectors recording face-varying topology including tags that help propagate
// data through the refinement hierarchy. Vectors are not sparse but most use
// 8-bit values relative to the local topology.
//
// The vector of face-values is actually redundant here, but is constructed as
// it is most convenient for clients. It represents almost half the memory of
// the topology (4 32-bit integers per face) and not surprisingly, populating
// it takes a considerable amount of the refinement time (1/3). We can reduce
// both if we are willing to compute these on demand for clients.
//
// Per-face (matches face-verts of corresponding level):
std::vector<Index> _faceVertValues;
// Per-edge:
std::vector<ETag> _edgeTags;
// Per-vertex:
std::vector<Sibling> _vertSiblingCounts;
std::vector<int> _vertSiblingOffsets;
std::vector<Sibling> _vertFaceSiblings;
// Per-value:
std::vector<Index> _vertValueIndices;
std::vector<ValueTag> _vertValueTags;
std::vector<CreaseEndPair> _vertValueCreaseEnds;
};
//
// Access/modify the values associated with each face:
//
inline ConstIndexArray
FVarLevel::getFaceValues(Index fIndex) const {
int vCount = _level.getNumFaceVertices(fIndex);
int vOffset = _level.getOffsetOfFaceVertices(fIndex);
return ConstIndexArray(&_faceVertValues[vOffset], vCount);
}
inline IndexArray
FVarLevel::getFaceValues(Index fIndex) {
int vCount = _level.getNumFaceVertices(fIndex);
int vOffset = _level.getOffsetOfFaceVertices(fIndex);
return IndexArray(&_faceVertValues[vOffset], vCount);
}
inline FVarLevel::ConstSiblingArray
FVarLevel::getVertexFaceSiblings(Index vIndex) const {
int vCount = _level.getNumVertexFaces(vIndex);
int vOffset = _level.getOffsetOfVertexFaces(vIndex);
return ConstSiblingArray(&_vertFaceSiblings[vOffset], vCount);
}
inline FVarLevel::SiblingArray
FVarLevel::getVertexFaceSiblings(Index vIndex) {
int vCount = _level.getNumVertexFaces(vIndex);
int vOffset = _level.getOffsetOfVertexFaces(vIndex);
return SiblingArray(&_vertFaceSiblings[vOffset], vCount);
}
inline ConstIndexArray
FVarLevel::getVertexValues(Index vIndex) const
{
int vCount = getNumVertexValues(vIndex);
int vOffset = getVertexValueOffset(vIndex);
return ConstIndexArray(&_vertValueIndices[vOffset], vCount);
}
inline IndexArray
FVarLevel::getVertexValues(Index vIndex)
{
int vCount = getNumVertexValues(vIndex);
int vOffset = getVertexValueOffset(vIndex);
return IndexArray(&_vertValueIndices[vOffset], vCount);
}
inline FVarLevel::ConstValueTagArray
FVarLevel::getVertexValueTags(Index vIndex) const
{
int vCount = getNumVertexValues(vIndex);
int vOffset = getVertexValueOffset(vIndex);
return ConstValueTagArray(&_vertValueTags[vOffset], vCount);
}
inline FVarLevel::ValueTagArray
FVarLevel::getVertexValueTags(Index vIndex)
{
int vCount = getNumVertexValues(vIndex);
int vOffset = getVertexValueOffset(vIndex);
return ValueTagArray(&_vertValueTags[vOffset], vCount);
}
inline FVarLevel::ConstCreaseEndPairArray
FVarLevel::getVertexValueCreaseEnds(Index vIndex) const
{
int vCount = getNumVertexValues(vIndex);
int vOffset = getVertexValueOffset(vIndex);
return ConstCreaseEndPairArray(&_vertValueCreaseEnds[vOffset], vCount);
}
inline FVarLevel::CreaseEndPairArray
FVarLevel::getVertexValueCreaseEnds(Index vIndex)
{
int vCount = getNumVertexValues(vIndex);
int vOffset = getVertexValueOffset(vIndex);
return CreaseEndPairArray(&_vertValueCreaseEnds[vOffset], vCount);
}
inline Index
FVarLevel::findVertexValueIndex(Index vertexIndex, Index valueIndex) const {
if (_level.getDepth() > 0) return valueIndex;
Index vvIndex = getVertexValueOffset(vertexIndex);
while (_vertValueIndices[vvIndex] != valueIndex) {
++ vvIndex;
}
return vvIndex;
}
//
// Methods related to tagging:
//
inline Level::ETag
FVarLevel::ETag::combineWithLevelETag(Level::ETag levelTag) const
{
if (this->_mismatch) {
levelTag._boundary = true;
levelTag._infSharp = true;
}
return levelTag;
}
inline Level::VTag
FVarLevel::ValueTag::combineWithLevelVTag(Level::VTag levelTag) const
{
if (this->_mismatch) {
//
// Semi-sharp FVar values are always tagged and treated as corners
// (at least three sharp edges (two boundary edges and one interior
// semi-sharp) and/or vertex is semi-sharp) until the sharpness has
// decayed, but they ultimately lie on the inf-sharp crease of the
// FVar boundary. Consider this when tagging inf-sharp features.
//
if (this->isCorner()) {
levelTag._rule = (Level::VTag::VTagSize) Sdc::Crease::RULE_CORNER;
} else {
levelTag._rule = (Level::VTag::VTagSize) Sdc::Crease::RULE_CREASE;
}
if (this->isCrease() || this->isSemiSharp()) {
levelTag._infSharp = false;
levelTag._infSharpCrease = true;
levelTag._corner = false;
} else {
levelTag._infSharp = true;
levelTag._infSharpCrease = false;
levelTag._corner = !this->_infIrregular && !this->_infSharpEdges;
}
levelTag._infSharpEdges = true;
levelTag._infIrregular = this->_infIrregular;
levelTag._boundary = true;
levelTag._xordinary = this->_xordinary;
levelTag._nonManifold |= this->_nonManifold;
}
return levelTag;
}
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_FVAR_LEVEL_H */

View File

@@ -0,0 +1,675 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../sdc/types.h"
#include "../sdc/crease.h"
#include "../vtr/array.h"
#include "../vtr/stackBuffer.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 {
namespace internal {
//
// Simple (for now) constructor and destructor:
//
FVarRefinement::FVarRefinement(Refinement const& refinement,
FVarLevel& parentFVarLevel,
FVarLevel& childFVarLevel) :
_refinement(refinement),
_parentLevel(refinement.parent()),
_parentFVar(parentFVarLevel),
_childLevel(refinement.child()),
_childFVar(childFVarLevel) {
}
FVarRefinement::~FVarRefinement() {
}
//
// Methods supporting the refinement of face-varying data that has previously
// been applied to the Refinement member. So these methods already have access
// to fully refined child components.
//
void
FVarRefinement::applyRefinement() {
//
// Transfer basic properties from the parent to child level:
//
_childFVar._options = _parentFVar._options;
_childFVar._isLinear = _parentFVar._isLinear;
_childFVar._hasLinearBoundaries = _parentFVar._hasLinearBoundaries;
_childFVar._hasDependentSharpness = _parentFVar._hasDependentSharpness;
//
// 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();
if (_childFVar.hasSmoothBoundaries()) {
propagateValueCreases();
reclassifySemisharpValues();
}
//
// The refined face-values are technically redundant as they can be constructed
// from the face-vertex siblings -- do so here as a post-process
//
if (_childFVar.getNumValues() > _childLevel.getNumVertices()) {
_childFVar.initializeFaceValuesFromVertexFaceSiblings();
} else {
_childFVar.initializeFaceValuesFromFaceVertices();
}
//printf("FVar refinement to level %d:\n", _childLevel.getDepth());
//_childFVar.print();
//printf("Validating refinement to level %d:\n", _childLevel.getDepth());
//_childFVar.validate();
//assert(_childFVar.validate());
}
//
// 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() {
int maxVertexValueCount = _refinement.getNumChildVerticesFromFaces();
Index cVert = _refinement.getFirstChildVertexFromEdges();
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromEdges();
for ( ; cVert < cVertEnd; ++cVert) {
Index pEdge = _refinement.getChildVertexParentIndex(cVert);
maxVertexValueCount += _parentFVar.edgeTopologyMatches(pEdge)
? 1 : _parentLevel.getEdgeFaces(pEdge).size();
}
cVert = _refinement.getFirstChildVertexFromVertices();
cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
for ( ; cVert < cVertEnd; ++cVert) {
assert(_refinement.isChildVertexComplete(cVert));
Index pVert = _refinement.getChildVertexParentIndex(cVert);
maxVertexValueCount += _parentFVar.getNumVertexValues(pVert);
}
//
// 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:
_childFVar.resizeComponents();
// Resize the vertex-value tags in the child level:
_childFVar._vertValueTags.resize(maxVertexValueCount);
// Resize the vertex-value "parent source" mapping in the refinement:
_childValueParentSource.resize(maxVertexValueCount, 0);
}
void
FVarRefinement::trimAndFinalizeChildValues() {
_childFVar._vertValueTags.resize(_childFVar._valueCount);
if (_childFVar.hasSmoothBoundaries()) {
_childFVar._vertValueCreaseEnds.resize(_childFVar._valueCount);
}
_childValueParentSource.resize(_childFVar._valueCount);
// 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;
}
}
inline int
FVarRefinement::populateChildValuesForEdgeVertex(Index cVert, Index pEdge) {
//
// Determine the number of sibling values for the child vertex of this discts
// edge and populate their related topological data (e.g. source face).
//
// This turns out to be very simple. For FVar refinement to handle all cases
// of non-manifold edges, when an edge is discts we generate a FVar value for
// each face incident the edge. So in the uniform refinement case we will
// have as many child values as parent faces incident the edge. But even when
// refinement is sparse, if this edge-vertex is not complete, we will still be
// guaranteed that a child face exists for each parent face since one of the
// edge's end vertices must be complete and therefore include all child faces.
//
ConstIndexArray pEdgeFaces = _parentLevel.getEdgeFaces(pEdge);
if (pEdgeFaces.size() == 1) {
// No sibling so the first face (0) guaranteed to be a source and all
// sibling indices per incident face will also be 0 -- all of which was
// done on initialization, so nothing further to do.
return 1;
}
//
// Update the parent-source of all child values:
//
int cValueCount = pEdgeFaces.size();
Index cValueOffset = _childFVar.getVertexValueOffset(cVert);
for (int i = 0; i < cValueCount; ++i) {
_childValueParentSource[cValueOffset + i] = (LocalIndex) i;
}
//
// Update the vertex-face siblings for the faces incident the child vertex:
//
ConstIndexArray cVertFaces = _childLevel.getVertexFaces(cVert);
FVarLevel::SiblingArray cVertFaceSiblings = _childFVar.getVertexFaceSiblings(cVert);
assert(cVertFaces.size() == cVertFaceSiblings.size());
assert(cVertFaces.size() >= cValueCount);
for (int i = 0; i < cVertFaceSiblings.size(); ++i) {
Index pFaceI = _refinement.getChildFaceParentFace(cVertFaces[i]);
if (pEdgeFaces.size() == 2) {
// Only two parent faces and all siblings previously initialized to 0:
if (pFaceI == pEdgeFaces[1]) {
cVertFaceSiblings[i] = (LocalIndex) 1;
}
} else {
// Non-manifold case with > 2 parent faces -- match child faces to parent:
for (int j = 0; j < pEdgeFaces.size(); ++j) {
if (pFaceI == pEdgeFaces[j]) {
cVertFaceSiblings[i] = (LocalIndex) j;
}
}
}
}
return cValueCount;
}
inline int
FVarRefinement::populateChildValuesForVertexVertex(Index cVert, Index pVert) {
//
// 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.isChildVertexComplete(cVert));
// 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);
// Update the parent source for all child values:
for (int j = 1; j < cValueCount; ++j) {
_childValueParentSource[cValueIndex + j] = (LocalIndex) j;
}
// Update the vertex-face siblings:
FVarLevel::ConstSiblingArray pVertFaceSiblings = _parentFVar.getVertexFaceSiblings(pVert);
FVarLevel::SiblingArray cVertFaceSiblings = _childFVar.getVertexFaceSiblings(cVert);
for (int j = 0; j < cVertFaceSiblings.size(); ++j) {
cVertFaceSiblings[j] = pVertFaceSiblings[j];
}
}
return cValueCount;
}
void
FVarRefinement::populateChildValues() {
//
// 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, and it just makes sense
// to be consistent (e.g. if there is a 1-to-1 correspondence between vertices
// and their FVar-values, their children will correspond).
//
_childFVar._valueCount = 0;
if (_refinement.hasFaceVerticesFirst()) {
populateChildValuesFromFaceVertices();
populateChildValuesFromEdgeVertices();
populateChildValuesFromVertexVertices();
} else {
populateChildValuesFromVertexVertices();
populateChildValuesFromFaceVertices();
populateChildValuesFromEdgeVertices();
}
}
void
FVarRefinement::populateChildValuesFromFaceVertices() {
Index cVert = _refinement.getFirstChildVertexFromFaces();
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromFaces();
for ( ; cVert < cVertEnd; ++cVert) {
_childFVar._vertSiblingOffsets[cVert] = _childFVar._valueCount;
_childFVar._vertSiblingCounts[cVert] = 1;
_childFVar._valueCount ++;
}
}
void
FVarRefinement::populateChildValuesFromEdgeVertices() {
Index cVert = _refinement.getFirstChildVertexFromEdges();
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromEdges();
for ( ; cVert < cVertEnd; ++cVert) {
Index pEdge = _refinement.getChildVertexParentIndex(cVert);
_childFVar._vertSiblingOffsets[cVert] = _childFVar._valueCount;
if (_parentFVar.edgeTopologyMatches(pEdge)) {
_childFVar._vertSiblingCounts[cVert] = 1;
_childFVar._valueCount ++;
} else {
int cValueCount = populateChildValuesForEdgeVertex(cVert, pEdge);
_childFVar._vertSiblingCounts[cVert] = (LocalIndex)cValueCount;
_childFVar._valueCount += cValueCount;
}
}
}
void
FVarRefinement::populateChildValuesFromVertexVertices() {
Index cVert = _refinement.getFirstChildVertexFromVertices();
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
for ( ; cVert < cVertEnd; ++cVert) {
Index pVert = _refinement.getChildVertexParentIndex(cVert);
_childFVar._vertSiblingOffsets[cVert] = _childFVar._valueCount;
if (_parentFVar.valueTopologyMatches(_parentFVar.getVertexValueOffset(pVert))) {
_childFVar._vertSiblingCounts[cVert] = 1;
_childFVar._valueCount ++;
} else {
int cValueCount = populateChildValuesForVertexVertex(cVert, pVert);
_childFVar._vertSiblingCounts[cVert] = (LocalIndex)cValueCount;
_childFVar._valueCount += cValueCount;
}
}
}
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
//
FVarLevel::ETag eTagMatch;
eTagMatch.clear();
eTagMatch._mismatch = false;
for (int eIndex = 0; eIndex < _refinement.getNumChildEdgesFromFaces(); ++eIndex) {
_childFVar._edgeTags[eIndex] = eTagMatch;
}
for (int eIndex = _refinement.getNumChildEdgesFromFaces(); eIndex < _childLevel.getNumEdges(); ++eIndex) {
Index pEdge = _refinement.getChildEdgeParentIndex(eIndex);
_childFVar._edgeTags[eIndex] = _parentFVar._edgeTags[pEdge];
}
}
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
//
//
// Values from face-vertices -- all match and are sequential:
//
FVarLevel::ValueTag valTagMatch;
valTagMatch.clear();
Index cVert = _refinement.getFirstChildVertexFromFaces();
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromFaces();
Index cVertValue = _childFVar.getVertexValueOffset(cVert);
for ( ; cVert < cVertEnd; ++cVert, ++cVertValue) {
_childFVar._vertValueTags[cVertValue] = valTagMatch;
}
//
// 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:
//
FVarLevel::ValueTag valTagMismatch = valTagMatch;
valTagMismatch._mismatch = true;
FVarLevel::ValueTag valTagCrease = valTagMismatch;
valTagCrease._crease = true;
FVarLevel::ValueTag& valTagSplitEdge = _parentFVar.hasSmoothBoundaries() ? valTagCrease : valTagMismatch;
cVert = _refinement.getFirstChildVertexFromEdges();
cVertEnd = cVert + _refinement.getNumChildVerticesFromEdges();
for ( ; cVert < cVertEnd; ++cVert) {
Index pEdge = _refinement.getChildVertexParentIndex(cVert);
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
FVarLevel::ETag pEdgeTag = _parentFVar._edgeTags[pEdge];
if (pEdgeTag._mismatch || pEdgeTag._linear) {
std::fill(cValueTags.begin(), cValueTags.end(), valTagSplitEdge);
} else {
std::fill(cValueTags.begin(), cValueTags.end(), valTagMatch);
}
}
//
// Values from vertex-vertices -- inherit tags from parent values when complete
// otherwise (not yet supported) need to identify the parent value for each child:
//
cVert = _refinement.getFirstChildVertexFromVertices();
cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
for ( ; cVert < cVertEnd; ++cVert) {
Index pVert = _refinement.getChildVertexParentIndex(cVert);
assert(_refinement.isChildVertexComplete(cVert));
FVarLevel::ConstValueTagArray pValueTags = _parentFVar.getVertexValueTags(pVert);
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
memcpy(cValueTags.begin(), pValueTags.begin(),
pValueTags.size()*sizeof(FVarLevel::ValueTag));
}
}
void
FVarRefinement::propagateValueCreases() {
assert(_childFVar.hasSmoothBoundaries());
// Skip child vertices from faces:
//
// For each child vertex from an edge that has FVar values and is complete, initialize
// the crease-ends for those values tagged as smooth boundaries
//
// 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:
//
int incChildFacesPerEdge = (_refinement.getRegularFaceSize() == 4) ? 2 : 3;
Index cVert = _refinement.getFirstChildVertexFromEdges();
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromEdges();
for ( ; cVert < cVertEnd; ++cVert) {
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
if (!cValueTags[0].isMismatch()) continue;
if (!_refinement.isChildVertexComplete(cVert)) continue;
FVarLevel::CreaseEndPairArray cValueCreaseEnds = _childFVar.getVertexValueCreaseEnds(cVert);
int creaseStartFace = 0;
int creaseEndFace = creaseStartFace + incChildFacesPerEdge - 1;
for (int i = 0; i < cValueTags.size(); ++i) {
if (!cValueTags[i].isInfSharp()) {
cValueCreaseEnds[i]._startFace = (LocalIndex) creaseStartFace;
cValueCreaseEnds[i]._endFace = (LocalIndex) creaseEndFace;
}
creaseStartFace += incChildFacesPerEdge;
creaseEndFace += incChildFacesPerEdge;
}
}
//
// For each child vertex from a vertex that has FVar values and is complete, initialize
// the crease-ends for those values tagged as smooth or semi-sharp (to become smooth
// eventually):
//
cVert = _refinement.getFirstChildVertexFromVertices();
cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
for ( ; cVert < cVertEnd; ++cVert) {
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
if (!cValueTags[0].isMismatch()) continue;
if (!_refinement.isChildVertexComplete(cVert)) continue;
Index pVert = _refinement.getChildVertexParentIndex(cVert);
FVarLevel::ConstCreaseEndPairArray pCreaseEnds = _parentFVar.getVertexValueCreaseEnds(pVert);
FVarLevel::CreaseEndPairArray cCreaseEnds = _childFVar.getVertexValueCreaseEnds(cVert);
for (int j = 0; j < cValueTags.size(); ++j) {
if (!cValueTags[j].isInfSharp()) {
cCreaseEnds[j] = pCreaseEnds[j];
}
}
}
}
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:
//
bool hasDependentSharpness = _parentFVar._hasDependentSharpness;
internal::StackBuffer<Index,16> cVertEdgeBuffer(_childLevel.getMaxValence());
Index cVert = _refinement.getFirstChildVertexFromVertices();
Index cVertEnd = cVert + _refinement.getNumChildVerticesFromVertices();
for ( ; cVert < cVertEnd; ++cVert) {
FVarLevel::ValueTagArray cValueTags = _childFVar.getVertexValueTags(cVert);
if (!cValueTags[0].isMismatch()) continue;
if (!_refinement.isChildVertexComplete(cVert)) continue;
// If the parent vertex wasn't semi-sharp, the child vertex and values can't be:
Index pVert = _refinement.getChildVertexParentIndex(cVert);
Level::VTag pVertTags = _parentLevel.getVertexTag(pVert);
if (!pVertTags._semiSharp && !pVertTags._semiSharpEdges) continue;
// If the child vertex is still sharp, all values remain unaffected:
Level::VTag cVertTags = _childLevel.getVertexTag(cVert);
if (cVertTags._semiSharp || cVertTags._infSharp) continue;
// 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:
//
if (!cVertTags._semiSharp && !cVertTags._semiSharpEdges) {
for (int j = 0; j < cValueTags.size(); ++j) {
if (cValueTags[j]._semiSharp) {
cValueTags[j]._semiSharp = false;
cValueTags[j]._depSharp = false;
cValueTags[j]._crease = true;
}
}
continue;
}
// There are some semi-sharp edges left -- for those values tagged as semi-sharp,
// see if they are still semi-sharp and clear those that are not:
//
FVarLevel::CreaseEndPairArray const cValueCreaseEnds = _childFVar.getVertexValueCreaseEnds(cVert);
// Beware accessing the child's vert-edges -- full topology may not be enabled:
ConstIndexArray cVertEdges;
if (_childLevel.getNumVertexEdgesTotal()) {
cVertEdges = _childLevel.getVertexEdges(cVert);
} else {
ConstIndexArray pVertEdges = _parentLevel.getVertexEdges(pVert);
ConstLocalIndexArray pVertInEdge = _parentLevel.getVertexEdgeLocalIndices(pVert);
for (int i = 0; i < pVertEdges.size(); ++i) {
cVertEdgeBuffer[i] = _refinement.getEdgeChildEdges(pVertEdges[i])[pVertInEdge[i]];
}
cVertEdges = IndexArray(cVertEdgeBuffer, pVertEdges.size());
}
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;
bool isStillSemiSharp = false;
if (vEndFace > vStartFace) {
for (int k = vStartFace + 1; !isStillSemiSharp && (k <= vEndFace); ++k) {
isStillSemiSharp = _childLevel.getEdgeTag(cVertEdges[k])._semiSharp;
}
} else if (vStartFace > vEndFace) {
for (int k = vStartFace + 1; !isStillSemiSharp && (k < cVertEdges.size()); ++k) {
isStillSemiSharp = _childLevel.getEdgeTag(cVertEdges[k])._semiSharp;
}
for (int k = 0; !isStillSemiSharp && (k <= vEndFace); ++k) {
isStillSemiSharp = _childLevel.getEdgeTag(cVertEdges[k])._semiSharp;
}
}
if (!isStillSemiSharp) {
cValueTags[j]._semiSharp = false;
cValueTags[j]._depSharp = false;
cValueTags[j]._crease = true;
}
}
}
//
// Now account for "dependent sharpness" (only matters when we have two values) --
// if one value was dependent/sharpened based on the other, clear the dependency
// tag if it is no longer sharp:
//
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;
}
}
}
}
float
FVarRefinement::getFractionalWeight(Index pVert, LocalIndex pSibling,
Index cVert, LocalIndex /* cSibling */) const {
//
// Need to identify sharpness values for edges within the spans for both the
// parent and child...
//
// 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...
//
internal::StackBuffer<Index,16> cVertEdgeBuffer;
ConstIndexArray pVertEdges = _parentLevel.getVertexEdges(pVert);
ConstIndexArray cVertEdges;
// Beware accessing the child's vert-edges -- full topology may not be enabled:
if (_childLevel.getNumVertexEdgesTotal()) {
cVertEdges = _childLevel.getVertexEdges(cVert);
} else {
cVertEdgeBuffer.SetSize(pVertEdges.size());
ConstLocalIndexArray pVertInEdge = _parentLevel.getVertexEdgeLocalIndices(pVert);
for (int i = 0; i < pVertEdges.size(); ++i) {
cVertEdgeBuffer[i] = _refinement.getEdgeChildEdges(pVertEdges[i])[pVertInEdge[i]];
}
cVertEdges = IndexArray(cVertEdgeBuffer, pVertEdges.size());
}
internal::StackBuffer<float,32> sharpnessBuffer(2 * pVertEdges.size());
float * pEdgeSharpness = sharpnessBuffer;
float * cEdgeSharpness = sharpnessBuffer + pVertEdges.size();
FVarLevel::CreaseEndPair pValueCreaseEnds = _parentFVar.getVertexValueCreaseEnds(pVert)[pSibling];
LocalIndex pStartFace = pValueCreaseEnds._startFace;
LocalIndex pEndFace = pValueCreaseEnds._endFace;
int interiorEdgeCount = 0;
if (pEndFace > pStartFace) {
for (int i = pStartFace + 1; i <= pEndFace; ++i, ++interiorEdgeCount) {
pEdgeSharpness[interiorEdgeCount] = _parentLevel.getEdgeSharpness(pVertEdges[i]);
cEdgeSharpness[interiorEdgeCount] = _childLevel.getEdgeSharpness(cVertEdges[i]);
}
} else if (pStartFace > pEndFace) {
for (int i = pStartFace + 1; i < pVertEdges.size(); ++i, ++interiorEdgeCount) {
pEdgeSharpness[interiorEdgeCount] = _parentLevel.getEdgeSharpness(pVertEdges[i]);
cEdgeSharpness[interiorEdgeCount] = _childLevel.getEdgeSharpness(cVertEdges[i]);
}
for (int i = 0; i <= pEndFace; ++i, ++interiorEdgeCount) {
pEdgeSharpness[interiorEdgeCount] = _parentLevel.getEdgeSharpness(pVertEdges[i]);
cEdgeSharpness[interiorEdgeCount] = _childLevel.getEdgeSharpness(cVertEdges[i]);
}
}
return Sdc::Crease(_refinement.getOptions()).ComputeFractionalWeightAtVertex(
_parentLevel.getVertexSharpness(pVert), _childLevel.getVertexSharpness(cVert),
interiorEdgeCount, pEdgeSharpness, cEdgeSharpness);
}
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,100 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_FVAR_REFINEMENT_H
#define OPENSUBDIV3_VTR_FVAR_REFINEMENT_H
#include "../version.h"
#include "../sdc/types.h"
#include "../sdc/crease.h"
#include "../vtr/types.h"
#include "../vtr/refinement.h"
#include "../vtr/fvarLevel.h"
#include <vector>
#include <cassert>
#include <cstring>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// FVarRefinement:
// A face-varying refinement contains data to support the refinement of a
// particular face-varying "channel". Just as Refinement maintains a mapping
// between the components of a parent Level and its child, the face-varying
// analog maintains a mapping between the face-varying values of a parent
// FVarLevel and its child.
//
// It turns out there is little data necessary here, so the class consists
// mainly of methods that populate the child FVarLevel. The mapping data in
// the refinement between Levels serves most purposes and all that is required
// in addition is a mapping from values in the child FVarLevel to the parent.
//
class FVarRefinement {
public:
FVarRefinement(Refinement const& refinement, FVarLevel& parent, FVarLevel& child);
~FVarRefinement();
int getChildValueParentSource(Index vIndex, int sibling) const {
return _childValueParentSource[_childFVar.getVertexValueOffset(vIndex, (LocalIndex)sibling)];
}
float getFractionalWeight(Index pVert, LocalIndex pSibling,
Index cVert, LocalIndex cSibling) const;
// Modifiers supporting application of the refinement:
void applyRefinement();
void estimateAndAllocateChildValues();
void populateChildValues();
void populateChildValuesFromFaceVertices();
void populateChildValuesFromEdgeVertices();
int populateChildValuesForEdgeVertex(Index cVert, Index pEdge);
void populateChildValuesFromVertexVertices();
int populateChildValuesForVertexVertex(Index cVert, Index pVert);
void trimAndFinalizeChildValues();
void propagateEdgeTags();
void propagateValueTags();
void propagateValueCreases();
void reclassifySemisharpValues();
private:
//
// Identify the Refinement, its Levels and assigned FVarLevels for more
// immediate access -- child FVarLevel is non-const as it is to be assigned:
//
Refinement const & _refinement;
Level const & _parentLevel;
FVarLevel const & _parentFVar;
Level const & _childLevel;
FVarLevel & _childFVar;
// When refinement is sparse, we need a mapping between siblings of a vertex
// value in the parent and child -- and for some child values, there will not
// be a parent value, in which case the source of the parent component will
// be stored. So we refer to the parent "source" rather than "sibling":
//
std::vector<LocalIndex> _childValueParentSource;
};
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_FVAR_REFINEMENT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,863 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_LEVEL_H
#define OPENSUBDIV3_VTR_LEVEL_H
#include "../version.h"
#include "../sdc/types.h"
#include "../sdc/crease.h"
#include "../sdc/options.h"
#include "../vtr/types.h"
#include <algorithm>
#include <vector>
#include <cassert>
#include <cstring>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
class Refinement;
class TriRefinement;
class QuadRefinement;
class FVarRefinement;
class FVarLevel;
//
// Level:
// A refinement level includes a vectorized representation of the topology
// for a particular subdivision level. The topology is "complete" in that any
// level can be used as the base level of another subdivision hierarchy and can
// be considered a complete mesh independent of its ancestors. It currently
// does contain a "depth" member -- as some inferences can then be made about
// the topology (i.e. all quads or all tris if not level 0).
//
// This class is intended for private use within the library. There are still
// opportunities to specialize levels -- e.g. those supporting N-sided faces vs
// those that are purely quads or tris -- so we prefer to insulate it from public
// access.
//
// The representation of topology here is to store six topological relationships
// in tables of integers. Each is stored in its own array(s) so the result is
// a SOA representation of the topology. The six relations are:
//
// - face-verts: vertices incident/comprising a face
// - face-edges: edges incident a face
// - edge-verts: vertices incident/comprising an edge
// - edge-faces: faces incident an edge
// - vert-faces: faces incident a vertex
// - vert-edges: edges incident a vertex
//
// There is some redundancy here but the intent is not that this be a minimal
// representation, the intent is that it be amenable to refinement. Classes in
// the Far layer essentially store 5 of these 6 in a permuted form -- we add
// the face-edges here to simplify refinement.
//
class Level {
public:
//
// Simple nested types to hold the tags for each component type -- some of
// which are user-specified features (e.g. whether a face is a hole or not)
// while others indicate the topological nature of the component, how it
// is affected by creasing in its neighborhood, etc.
//
// Most of these properties are passed down to child components during
// refinement, but some -- notably the designation of a component as semi-
// sharp -- require re-determination as sharpness values are reduced at each
// level.
//
struct VTag {
VTag() { }
// When cleared, the VTag ALMOST represents a smooth, regular, interior
// vertex -- the Type enum requires a bit be explicitly set for Smooth,
// so that must be done explicitly if desired on initialization.
void clear() { std::memset((void*) this, 0, sizeof(VTag)); }
typedef unsigned short VTagSize;
VTagSize _nonManifold : 1; // fixed
VTagSize _xordinary : 1; // fixed
VTagSize _boundary : 1; // fixed
VTagSize _corner : 1; // fixed
VTagSize _infSharp : 1; // fixed
VTagSize _semiSharp : 1; // variable
VTagSize _semiSharpEdges : 1; // variable
VTagSize _rule : 4; // variable when _semiSharp
// These next to tags are complementary -- the "incomplete" tag is only
// relevant for refined levels while the "incident an irregular face" tag
// is only relevant for the base level. They could be combined as both
// indicate "no full regular ring" around a vertex
VTagSize _incomplete : 1; // variable only set in refined levels
VTagSize _incidIrregFace : 1; // variable only set in base level
// Tags indicating incident infinitely-sharp (permanent) features
VTagSize _infSharpEdges : 1; // fixed
VTagSize _infSharpCrease : 1; // fixed
VTagSize _infIrregular : 1; // fixed
// Alternate constructor and accessor for dealing with integer bits directly:
explicit VTag(VTagSize bits) {
std::memcpy(this, &bits, sizeof(bits));
}
VTagSize getBits() const {
VTagSize bits;
std::memcpy(&bits, this, sizeof(bits));
return bits;
}
static VTag BitwiseOr(VTag const vTags[], int size = 4);
};
struct ETag {
ETag() { }
// When cleared, the ETag represents a smooth, manifold, interior edge
void clear() { std::memset((void*) this, 0, sizeof(ETag)); }
typedef unsigned char ETagSize;
ETagSize _nonManifold : 1; // fixed
ETagSize _boundary : 1; // fixed
ETagSize _infSharp : 1; // fixed
ETagSize _semiSharp : 1; // variable
// Alternate constructor and accessor for dealing with integer bits directly:
explicit ETag(ETagSize bits) {
std::memcpy(this, &bits, sizeof(bits));
}
ETagSize getBits() const {
ETagSize bits;
std::memcpy(&bits, this, sizeof(bits));
return bits;
}
static ETag BitwiseOr(ETag const eTags[], int size = 4);
};
struct FTag {
FTag() { }
void clear() { std::memset((void*) this, 0, sizeof(FTag)); }
typedef unsigned char FTagSize;
FTagSize _hole : 1; // fixed
// On deck -- coming soon...
//FTagSize _hasEdits : 1; // variable
};
// Additional simple struct to identify a "span" around a vertex, i.e. a
// subset of the faces around a vertex delimited by some property (e.g. a
// face-varying discontinuity, an inf-sharp edge, etc.)
//
// The span requires an "origin" and a "size" to fully define its extent.
// Use of the size is required over a leading/trailing pair as the valence
// around a non-manifold vertex cannot be trivially determined from two
// extremeties. Similarly a start face is chosen over an edge as starting
// with a manifold edge is ambiguous. Additional tags also support
// non-manifold cases, e.g. periodic spans at the apex of a double cone.
//
// Currently setting the size to 0 or leaving the span "unassigned" is an
// indication to use the full neighborhood rather than a subset -- prefer
// use of the const method here to direct inspection of the member.
//
struct VSpan {
VSpan() { std::memset((void*) this, 0, sizeof(VSpan)); }
void clear() { std::memset((void*) this, 0, sizeof(VSpan)); }
bool isAssigned() const { return _numFaces > 0; }
LocalIndex _numFaces;
LocalIndex _startFace;
LocalIndex _cornerInSpan;
unsigned short _periodic : 1;
unsigned short _sharp : 1;
};
public:
Level();
~Level();
// Simple accessors:
int getDepth() const { return _depth; }
int getNumVertices() const { return _vertCount; }
int getNumFaces() const { return _faceCount; }
int getNumEdges() const { return _edgeCount; }
// More global sizes may prove useful...
int getNumFaceVerticesTotal() const { return (int) _faceVertIndices.size(); }
int getNumFaceEdgesTotal() const { return (int) _faceEdgeIndices.size(); }
int getNumEdgeVerticesTotal() const { return (int) _edgeVertIndices.size(); }
int getNumEdgeFacesTotal() const { return (int) _edgeFaceIndices.size(); }
int getNumVertexFacesTotal() const { return (int) _vertFaceIndices.size(); }
int getNumVertexEdgesTotal() const { return (int) _vertEdgeIndices.size(); }
int getMaxValence() const { return _maxValence; }
int getMaxEdgeFaces() const { return _maxEdgeFaces; }
// Methods to access the relation tables/indices -- note that for some relations
// (i.e. those where a component is "contained by" a neighbor, or more generally
// when the neighbor is a simplex of higher dimension) we store an additional
// "local index", e.g. for the case of vert-faces if one of the faces F[i] is
// incident a vertex V, then L[i] is the "local index" in F[i] of vertex V.
// Once have only quads (or tris), this local index need only occupy two bits
// and could conceivably be packed into the same integer as the face index, but
// for now, given the need to support faces of potentially high valence we'll
// use an 8- or 16-bit integer.
//
// Methods to access the six topological relations:
ConstIndexArray getFaceVertices(Index faceIndex) const;
ConstIndexArray getFaceEdges(Index faceIndex) const;
ConstIndexArray getEdgeVertices(Index edgeIndex) const;
ConstIndexArray getEdgeFaces(Index edgeIndex) const;
ConstIndexArray getVertexFaces(Index vertIndex) const;
ConstIndexArray getVertexEdges(Index vertIndex) const;
ConstLocalIndexArray getEdgeFaceLocalIndices(Index edgeIndex) const;
ConstLocalIndexArray getVertexFaceLocalIndices(Index vertIndex) const;
ConstLocalIndexArray getVertexEdgeLocalIndices(Index vertIndex) const;
// Replace these with access to sharpness buffers/arrays rather than elements:
float getEdgeSharpness(Index edgeIndex) const;
float getVertexSharpness(Index vertIndex) const;
Sdc::Crease::Rule getVertexRule(Index vertIndex) const;
Index findEdge(Index v0Index, Index v1Index) const;
// Holes
void setFaceHole(Index faceIndex, bool b);
bool isFaceHole(Index faceIndex) const;
// Face-varying
Sdc::Options getFVarOptions(int channel) const;
int getNumFVarChannels() const { return (int) _fvarChannels.size(); }
int getNumFVarValues(int channel) const;
ConstIndexArray getFaceFVarValues(Index faceIndex, int channel) const;
FVarLevel & getFVarLevel(int channel) { return *_fvarChannels[channel]; }
FVarLevel const & getFVarLevel(int channel) const { return *_fvarChannels[channel]; }
// Manifold/non-manifold tags:
void setEdgeNonManifold(Index edgeIndex, bool b);
bool isEdgeNonManifold(Index edgeIndex) const;
void setVertexNonManifold(Index vertIndex, bool b);
bool isVertexNonManifold(Index vertIndex) const;
// General access to all component tags:
VTag const & getVertexTag(Index vertIndex) const { return _vertTags[vertIndex]; }
ETag const & getEdgeTag(Index edgeIndex) const { return _edgeTags[edgeIndex]; }
FTag const & getFaceTag(Index faceIndex) const { return _faceTags[faceIndex]; }
VTag & getVertexTag(Index vertIndex) { return _vertTags[vertIndex]; }
ETag & getEdgeTag(Index edgeIndex) { return _edgeTags[edgeIndex]; }
FTag & getFaceTag(Index faceIndex) { return _faceTags[faceIndex]; }
public:
// Debugging aides:
enum TopologyError {
TOPOLOGY_MISSING_EDGE_FACES=0,
TOPOLOGY_MISSING_EDGE_VERTS,
TOPOLOGY_MISSING_FACE_EDGES,
TOPOLOGY_MISSING_FACE_VERTS,
TOPOLOGY_MISSING_VERT_FACES,
TOPOLOGY_MISSING_VERT_EDGES,
TOPOLOGY_FAILED_CORRELATION_EDGE_FACE,
TOPOLOGY_FAILED_CORRELATION_FACE_VERT,
TOPOLOGY_FAILED_CORRELATION_FACE_EDGE,
TOPOLOGY_FAILED_ORIENTATION_INCIDENT_EDGE,
TOPOLOGY_FAILED_ORIENTATION_INCIDENT_FACE,
TOPOLOGY_FAILED_ORIENTATION_INCIDENT_FACES_EDGES,
TOPOLOGY_DEGENERATE_EDGE,
TOPOLOGY_NON_MANIFOLD_EDGE,
TOPOLOGY_INVALID_CREASE_EDGE,
TOPOLOGY_INVALID_CREASE_VERT
};
static char const * getTopologyErrorString(TopologyError errCode);
typedef void (* ValidationCallback)(TopologyError errCode, char const * msg, void const * clientData);
bool validateTopology(ValidationCallback callback=0, void const * clientData=0) const;
void print(const Refinement* parentRefinement = 0) const;
public:
// High-level topology queries -- these may be moved elsewhere:
bool isSingleCreasePatch(Index face, float* sharpnessOut=NULL, int* rotationOut=NULL) const;
//
// When inspecting topology, the component tags -- particularly VTag and ETag -- are most
// often inspected in groups for the face to which they belong. They are designed to be
// bitwise OR'd (the result then referred to as a "composite" tag) to make quick decisions
// about the face as a whole to avoid tedious topological inspection.
//
// The same logic can be applied to topology in a FVar channel when tags specific to that
// channel are used. Note that the VTags apply to the FVar values assigned to the corners
// of the face and not the vertex as a whole. The "composite" face-varying VTag for a
// vertex is the union of VTags of all distinct FVar values for that vertex.
//
bool doesVertexFVarTopologyMatch(Index vIndex, int fvarChannel) const;
bool doesFaceFVarTopologyMatch( Index fIndex, int fvarChannel) const;
bool doesEdgeFVarTopologyMatch( Index eIndex, int fvarChannel) const;
void getFaceVTags(Index fIndex, VTag vTags[], int fvarChannel = -1) const;
void getFaceETags(Index fIndex, ETag eTags[], int fvarChannel = -1) const;
VTag getFaceCompositeVTag(Index fIndex, int fvarChannel = -1) const;
VTag getFaceCompositeVTag(ConstIndexArray & fVerts) const;
VTag getVertexCompositeFVarVTag(Index vIndex, int fvarChannel) const;
//
// When gathering "patch points" we may want the indices of the vertices or the corresponding
// FVar values for a particular channel. Both are represented and equally accessible within
// the faces, so we allow all to be returned through these methods. Setting the optional FVar
// channel to -1 will retrieve indices of vertices instead of FVar values:
//
int gatherQuadLinearPatchPoints(Index fIndex, Index patchPoints[], int rotation = 0,
int fvarChannel = -1) const;
int gatherQuadRegularInteriorPatchPoints(Index fIndex, Index patchPoints[], int rotation = 0,
int fvarChannel = -1) const;
int gatherQuadRegularBoundaryPatchPoints(Index fIndex, Index patchPoints[], int boundaryEdgeInFace,
int fvarChannel = -1) const;
int gatherQuadRegularCornerPatchPoints( Index fIndex, Index patchPoints[], int cornerVertInFace,
int fvarChannel = -1) const;
int gatherQuadRegularRingAroundVertex(Index vIndex, Index ringPoints[],
int fvarChannel = -1) const;
int gatherQuadRegularPartialRingAroundVertex(Index vIndex, VSpan const & span, Index ringPoints[],
int fvarChannel = -1) const;
// WIP -- for future use, need to extend for face-varying...
int gatherTriRegularInteriorPatchPoints( Index fIndex, Index patchVerts[], int rotation = 0) const;
int gatherTriRegularBoundaryVertexPatchPoints(Index fIndex, Index patchVerts[], int boundaryVertInFace) const;
int gatherTriRegularBoundaryEdgePatchPoints( Index fIndex, Index patchVerts[], int boundaryEdgeInFace) const;
int gatherTriRegularCornerVertexPatchPoints( Index fIndex, Index patchVerts[], int cornerVertInFace) const;
int gatherTriRegularCornerEdgePatchPoints( Index fIndex, Index patchVerts[], int cornerEdgeInFace) const;
public:
// Sizing methods used to construct a level to populate:
void resizeFaces( int numFaces);
void resizeFaceVertices(int numFaceVertsTotal);
void resizeFaceEdges( int numFaceEdgesTotal);
void resizeEdges( int numEdges);
void resizeEdgeVertices(); // always 2*edgeCount
void resizeEdgeFaces(int numEdgeFacesTotal);
void resizeVertices( int numVertices);
void resizeVertexFaces(int numVertexFacesTotal);
void resizeVertexEdges(int numVertexEdgesTotal);
void setMaxValence(int maxValence);
// Modifiers to populate the relations for each component:
IndexArray getFaceVertices(Index faceIndex);
IndexArray getFaceEdges(Index faceIndex);
IndexArray getEdgeVertices(Index edgeIndex);
IndexArray getEdgeFaces(Index edgeIndex);
IndexArray getVertexFaces(Index vertIndex);
IndexArray getVertexEdges(Index vertIndex);
LocalIndexArray getEdgeFaceLocalIndices(Index edgeIndex);
LocalIndexArray getVertexFaceLocalIndices(Index vertIndex);
LocalIndexArray getVertexEdgeLocalIndices(Index vertIndex);
// Replace these with access to sharpness buffers/arrays rather than elements:
float& getEdgeSharpness(Index edgeIndex);
float& getVertexSharpness(Index vertIndex);
// Create, destroy and populate face-varying channels:
int createFVarChannel(int fvarValueCount, Sdc::Options const& options);
void destroyFVarChannel(int channel);
IndexArray getFaceFVarValues(Index faceIndex, int channel);
void completeFVarChannelTopology(int channel, int regBoundaryValence);
// Counts and offsets for all relation types:
// - these may be unwarranted if we let Refinement access members directly...
int getNumFaceVertices( Index faceIndex) const { return _faceVertCountsAndOffsets[2*faceIndex]; }
int getOffsetOfFaceVertices(Index faceIndex) const { return _faceVertCountsAndOffsets[2*faceIndex + 1]; }
int getNumFaceEdges( Index faceIndex) const { return getNumFaceVertices(faceIndex); }
int getOffsetOfFaceEdges(Index faceIndex) const { return getOffsetOfFaceVertices(faceIndex); }
int getNumEdgeVertices( Index ) const { return 2; }
int getOffsetOfEdgeVertices(Index edgeIndex) const { return 2 * edgeIndex; }
int getNumEdgeFaces( Index edgeIndex) const { return _edgeFaceCountsAndOffsets[2*edgeIndex]; }
int getOffsetOfEdgeFaces(Index edgeIndex) const { return _edgeFaceCountsAndOffsets[2*edgeIndex + 1]; }
int getNumVertexFaces( Index vertIndex) const { return _vertFaceCountsAndOffsets[2*vertIndex]; }
int getOffsetOfVertexFaces(Index vertIndex) const { return _vertFaceCountsAndOffsets[2*vertIndex + 1]; }
int getNumVertexEdges( Index vertIndex) const { return _vertEdgeCountsAndOffsets[2*vertIndex]; }
int getOffsetOfVertexEdges(Index vertIndex) const { return _vertEdgeCountsAndOffsets[2*vertIndex + 1]; }
ConstIndexArray getFaceVertices() const;
//
// Note that for some relations, the size of the relations for a child component
// can vary radically from its parent due to the sparsity of the refinement. So
// in these cases a few additional utilities are provided to help define the set
// of incident components. Assuming adequate memory has been allocated, the
// "resize" methods here initialize the set of incident components by setting
// both the size and the appropriate offset, while "trim" is use to quickly lower
// the size from an upper bound and nothing else.
//
void resizeFaceVertices(Index FaceIndex, int count);
void resizeEdgeFaces(Index edgeIndex, int count);
void trimEdgeFaces( Index edgeIndex, int count);
void resizeVertexFaces(Index vertIndex, int count);
void trimVertexFaces( Index vertIndex, int count);
void resizeVertexEdges(Index vertIndex, int count);
void trimVertexEdges( Index vertIndex, int count);
public:
//
// Initial plans were to have a few specific classes properly construct the
// topology from scratch, e.g. the Refinement class and a Factory class for
// the base level, by populating all topological relations. The need to have
// a class construct full topology given only a simple face-vertex list, made
// it necessary to write code to define and orient all relations -- and most
// of that seemed best placed here.
//
bool completeTopologyFromFaceVertices();
Index findEdge(Index v0, Index v1, ConstIndexArray v0Edges) const;
// Methods supporting the above:
void orientIncidentComponents();
bool orderVertexFacesAndEdges(Index vIndex, Index* vFaces, Index* vEdges) const;
bool orderVertexFacesAndEdges(Index vIndex);
bool testVertexNonManifoldCrease(Index vIndex) const;
void populateLocalIndices();
IndexArray shareFaceVertCountsAndOffsets() const;
private:
// Refinement classes (including all subclasses) build a Level:
friend class Refinement;
friend class TriRefinement;
friend class QuadRefinement;
//
// A Level is independent of subdivision scheme or options. While it may have been
// affected by them in its construction, they are not associated with it -- a Level
// is pure topology and any subdivision parameters are external.
//
// Simple members for inventory, etc.
int _faceCount;
int _edgeCount;
int _vertCount;
// The "depth" member is clearly useful in both the topological splitting and the
// stencil queries, but arguably it ties the Level to a hierarchy which counters
// the idea of it being independent.
int _depth;
// Maxima to help clients manage sizing of data buffers. Given "max valence",
// the "max edge faces" is strictly redundant as it will always be less, but
// since it will typically be so much less (i.e. 2) it is kept for now.
int _maxEdgeFaces;
int _maxValence;
//
// Topology vectors:
// Note that of all of these, only data for the face-edge relation is not
// stored in the osd::FarTables in any form. The FarTable vectors combine
// the edge-vert and edge-face relations. The eventual goal is that this
// data be part of the osd::Far classes and be a superset of the FarTable
// vectors, i.e. no data duplication or conversion. The fact that FarTable
// already stores 5 of the 6 possible relations should make the topology
// storage as a whole a non-issue.
//
// The vert-face-child and vert-edge-child indices are also arguably not
// a topology relation but more one for parent/child relations. But it is
// a topological relationship, and if named differently would not likely
// raise this. It has been named with "child" in the name as it does play
// a more significant role during subdivision in mapping between parent
// and child components, and so has been named to reflect that more clearly.
//
// Per-face:
std::vector<Index> _faceVertCountsAndOffsets; // 2 per face, redundant after level 0
std::vector<Index> _faceVertIndices; // 3 or 4 per face, variable at level 0
std::vector<Index> _faceEdgeIndices; // matches face-vert indices
std::vector<FTag> _faceTags; // 1 per face: includes "hole" tag
// Per-edge:
std::vector<Index> _edgeVertIndices; // 2 per edge
std::vector<Index> _edgeFaceCountsAndOffsets; // 2 per edge
std::vector<Index> _edgeFaceIndices; // varies with faces per edge
std::vector<LocalIndex> _edgeFaceLocalIndices; // varies with faces per edge
std::vector<float> _edgeSharpness; // 1 per edge
std::vector<ETag> _edgeTags; // 1 per edge: manifold, boundary, etc.
// Per-vertex:
std::vector<Index> _vertFaceCountsAndOffsets; // 2 per vertex
std::vector<Index> _vertFaceIndices; // varies with valence
std::vector<LocalIndex> _vertFaceLocalIndices; // varies with valence, 8-bit for now
std::vector<Index> _vertEdgeCountsAndOffsets; // 2 per vertex
std::vector<Index> _vertEdgeIndices; // varies with valence
std::vector<LocalIndex> _vertEdgeLocalIndices; // varies with valence, 8-bit for now
std::vector<float> _vertSharpness; // 1 per vertex
std::vector<VTag> _vertTags; // 1 per vertex: manifold, Sdc::Rule, etc.
// Face-varying channels:
std::vector<FVarLevel*> _fvarChannels;
};
//
// Access/modify the vertices incident a given face:
//
inline ConstIndexArray
Level::getFaceVertices(Index faceIndex) const {
return ConstIndexArray(&_faceVertIndices[_faceVertCountsAndOffsets[faceIndex*2+1]],
_faceVertCountsAndOffsets[faceIndex*2]);
}
inline IndexArray
Level::getFaceVertices(Index faceIndex) {
return IndexArray(&_faceVertIndices[_faceVertCountsAndOffsets[faceIndex*2+1]],
_faceVertCountsAndOffsets[faceIndex*2]);
}
inline void
Level::resizeFaceVertices(Index faceIndex, int count) {
int* countOffsetPair = &_faceVertCountsAndOffsets[faceIndex*2];
countOffsetPair[0] = count;
countOffsetPair[1] = (faceIndex == 0) ? 0 : (countOffsetPair[-2] + countOffsetPair[-1]);
_maxValence = std::max(_maxValence, count);
}
inline ConstIndexArray
Level::getFaceVertices() const {
return ConstIndexArray(&_faceVertIndices[0], (int)_faceVertIndices.size());
}
//
// Access/modify the edges incident a given face:
//
inline ConstIndexArray
Level::getFaceEdges(Index faceIndex) const {
return ConstIndexArray(&_faceEdgeIndices[_faceVertCountsAndOffsets[faceIndex*2+1]],
_faceVertCountsAndOffsets[faceIndex*2]);
}
inline IndexArray
Level::getFaceEdges(Index faceIndex) {
return IndexArray(&_faceEdgeIndices[_faceVertCountsAndOffsets[faceIndex*2+1]],
_faceVertCountsAndOffsets[faceIndex*2]);
}
//
// Access/modify the faces incident a given vertex:
//
inline ConstIndexArray
Level::getVertexFaces(Index vertIndex) const {
return ConstIndexArray( (&_vertFaceIndices[0]) + _vertFaceCountsAndOffsets[vertIndex*2+1],
_vertFaceCountsAndOffsets[vertIndex*2]);
}
inline IndexArray
Level::getVertexFaces(Index vertIndex) {
return IndexArray( (&_vertFaceIndices[0]) + _vertFaceCountsAndOffsets[vertIndex*2+1],
_vertFaceCountsAndOffsets[vertIndex*2]);
}
inline ConstLocalIndexArray
Level::getVertexFaceLocalIndices(Index vertIndex) const {
return ConstLocalIndexArray( (&_vertFaceLocalIndices[0]) + _vertFaceCountsAndOffsets[vertIndex*2+1],
_vertFaceCountsAndOffsets[vertIndex*2]);
}
inline LocalIndexArray
Level::getVertexFaceLocalIndices(Index vertIndex) {
return LocalIndexArray( (&_vertFaceLocalIndices[0]) + _vertFaceCountsAndOffsets[vertIndex*2+1],
_vertFaceCountsAndOffsets[vertIndex*2]);
}
inline void
Level::resizeVertexFaces(Index vertIndex, int count) {
int* countOffsetPair = &_vertFaceCountsAndOffsets[vertIndex*2];
countOffsetPair[0] = count;
countOffsetPair[1] = (vertIndex == 0) ? 0 : (countOffsetPair[-2] + countOffsetPair[-1]);
}
inline void
Level::trimVertexFaces(Index vertIndex, int count) {
_vertFaceCountsAndOffsets[vertIndex*2] = count;
}
//
// Access/modify the edges incident a given vertex:
//
inline ConstIndexArray
Level::getVertexEdges(Index vertIndex) const {
return ConstIndexArray( (&_vertEdgeIndices[0]) +_vertEdgeCountsAndOffsets[vertIndex*2+1],
_vertEdgeCountsAndOffsets[vertIndex*2]);
}
inline IndexArray
Level::getVertexEdges(Index vertIndex) {
return IndexArray( (&_vertEdgeIndices[0]) +_vertEdgeCountsAndOffsets[vertIndex*2+1],
_vertEdgeCountsAndOffsets[vertIndex*2]);
}
inline ConstLocalIndexArray
Level::getVertexEdgeLocalIndices(Index vertIndex) const {
return ConstLocalIndexArray( (&_vertEdgeLocalIndices[0]) + _vertEdgeCountsAndOffsets[vertIndex*2+1],
_vertEdgeCountsAndOffsets[vertIndex*2]);
}
inline LocalIndexArray
Level::getVertexEdgeLocalIndices(Index vertIndex) {
return LocalIndexArray( (&_vertEdgeLocalIndices[0]) + _vertEdgeCountsAndOffsets[vertIndex*2+1],
_vertEdgeCountsAndOffsets[vertIndex*2]);
}
inline void
Level::resizeVertexEdges(Index vertIndex, int count) {
int* countOffsetPair = &_vertEdgeCountsAndOffsets[vertIndex*2];
countOffsetPair[0] = count;
countOffsetPair[1] = (vertIndex == 0) ? 0 : (countOffsetPair[-2] + countOffsetPair[-1]);
_maxValence = std::max(_maxValence, count);
}
inline void
Level::trimVertexEdges(Index vertIndex, int count) {
_vertEdgeCountsAndOffsets[vertIndex*2] = count;
}
inline void
Level::setMaxValence(int valence) {
_maxValence = valence;
}
//
// Access/modify the vertices incident a given edge:
//
inline ConstIndexArray
Level::getEdgeVertices(Index edgeIndex) const {
return ConstIndexArray(&_edgeVertIndices[edgeIndex*2], 2);
}
inline IndexArray
Level::getEdgeVertices(Index edgeIndex) {
return IndexArray(&_edgeVertIndices[edgeIndex*2], 2);
}
//
// Access/modify the faces incident a given edge:
//
inline ConstIndexArray
Level::getEdgeFaces(Index edgeIndex) const {
return ConstIndexArray(&_edgeFaceIndices[0] +
_edgeFaceCountsAndOffsets[edgeIndex*2+1],
_edgeFaceCountsAndOffsets[edgeIndex*2]);
}
inline IndexArray
Level::getEdgeFaces(Index edgeIndex) {
return IndexArray(&_edgeFaceIndices[0] +
_edgeFaceCountsAndOffsets[edgeIndex*2+1],
_edgeFaceCountsAndOffsets[edgeIndex*2]);
}
inline ConstLocalIndexArray
Level::getEdgeFaceLocalIndices(Index edgeIndex) const {
return ConstLocalIndexArray(&_edgeFaceLocalIndices[0] +
_edgeFaceCountsAndOffsets[edgeIndex*2+1],
_edgeFaceCountsAndOffsets[edgeIndex*2]);
}
inline LocalIndexArray
Level::getEdgeFaceLocalIndices(Index edgeIndex) {
return LocalIndexArray(&_edgeFaceLocalIndices[0] +
_edgeFaceCountsAndOffsets[edgeIndex*2+1],
_edgeFaceCountsAndOffsets[edgeIndex*2]);
}
inline void
Level::resizeEdgeFaces(Index edgeIndex, int count) {
int* countOffsetPair = &_edgeFaceCountsAndOffsets[edgeIndex*2];
countOffsetPair[0] = count;
countOffsetPair[1] = (edgeIndex == 0) ? 0 : (countOffsetPair[-2] + countOffsetPair[-1]);
_maxEdgeFaces = std::max(_maxEdgeFaces, count);
}
inline void
Level::trimEdgeFaces(Index edgeIndex, int count) {
_edgeFaceCountsAndOffsets[edgeIndex*2] = count;
}
//
// Access/modify sharpness values:
//
inline float
Level::getEdgeSharpness(Index edgeIndex) const {
return _edgeSharpness[edgeIndex];
}
inline float&
Level::getEdgeSharpness(Index edgeIndex) {
return _edgeSharpness[edgeIndex];
}
inline float
Level::getVertexSharpness(Index vertIndex) const {
return _vertSharpness[vertIndex];
}
inline float&
Level::getVertexSharpness(Index vertIndex) {
return _vertSharpness[vertIndex];
}
inline Sdc::Crease::Rule
Level::getVertexRule(Index vertIndex) const {
return (Sdc::Crease::Rule) _vertTags[vertIndex]._rule;
}
//
// Access/modify hole tag:
//
inline void
Level::setFaceHole(Index faceIndex, bool b) {
_faceTags[faceIndex]._hole = b;
}
inline bool
Level::isFaceHole(Index faceIndex) const {
return _faceTags[faceIndex]._hole;
}
//
// Access/modify non-manifold tags:
//
inline void
Level::setEdgeNonManifold(Index edgeIndex, bool b) {
_edgeTags[edgeIndex]._nonManifold = b;
}
inline bool
Level::isEdgeNonManifold(Index edgeIndex) const {
return _edgeTags[edgeIndex]._nonManifold;
}
inline void
Level::setVertexNonManifold(Index vertIndex, bool b) {
_vertTags[vertIndex]._nonManifold = b;
}
inline bool
Level::isVertexNonManifold(Index vertIndex) const {
return _vertTags[vertIndex]._nonManifold;
}
//
// Sizing methods to allocate space:
//
inline void
Level::resizeFaces(int faceCount) {
_faceCount = faceCount;
_faceVertCountsAndOffsets.resize(2 * faceCount);
_faceTags.resize(faceCount);
std::memset((void*) &_faceTags[0], 0, _faceCount * sizeof(FTag));
}
inline void
Level::resizeFaceVertices(int totalFaceVertCount) {
_faceVertIndices.resize(totalFaceVertCount);
}
inline void
Level::resizeFaceEdges(int totalFaceEdgeCount) {
_faceEdgeIndices.resize(totalFaceEdgeCount);
}
inline void
Level::resizeEdges(int edgeCount) {
_edgeCount = edgeCount;
_edgeFaceCountsAndOffsets.resize(2 * edgeCount);
_edgeSharpness.resize(edgeCount);
_edgeTags.resize(edgeCount);
if (edgeCount>0) {
std::memset((void*) &_edgeTags[0], 0, _edgeCount * sizeof(ETag));
}
}
inline void
Level::resizeEdgeVertices() {
_edgeVertIndices.resize(2 * _edgeCount);
}
inline void
Level::resizeEdgeFaces(int totalEdgeFaceCount) {
_edgeFaceIndices.resize(totalEdgeFaceCount);
_edgeFaceLocalIndices.resize(totalEdgeFaceCount);
}
inline void
Level::resizeVertices(int vertCount) {
_vertCount = vertCount;
_vertFaceCountsAndOffsets.resize(2 * vertCount);
_vertEdgeCountsAndOffsets.resize(2 * vertCount);
_vertSharpness.resize(vertCount);
_vertTags.resize(vertCount);
std::memset((void*) &_vertTags[0], 0, _vertCount * sizeof(VTag));
}
inline void
Level::resizeVertexFaces(int totalVertFaceCount) {
_vertFaceIndices.resize(totalVertFaceCount);
_vertFaceLocalIndices.resize(totalVertFaceCount);
}
inline void
Level::resizeVertexEdges(int totalVertEdgeCount) {
_vertEdgeIndices.resize(totalVertEdgeCount);
_vertEdgeLocalIndices.resize(totalVertEdgeCount);
}
inline IndexArray
Level::shareFaceVertCountsAndOffsets() const {
// XXXX manuelk we have to force const casting here (classes don't 'share'
// members usually...)
return IndexArray(const_cast<Index *>(&_faceVertCountsAndOffsets[0]),
(int)_faceVertCountsAndOffsets.size());
}
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_LEVEL_H */

View File

@@ -0,0 +1,993 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../sdc/crease.h"
#include "../vtr/types.h"
#include "../vtr/level.h"
#include "../vtr/quadRefinement.h"
#include <cassert>
#include <cstdio>
#include <utility>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// Simple constructor, destructor and basic initializers:
//
QuadRefinement::QuadRefinement(Level const & parentArg, Level & childArg, Sdc::Options const & optionsArg) :
Refinement(parentArg, childArg, optionsArg) {
_splitType = Sdc::SPLIT_TO_QUADS;
_regFaceSize = 4;
}
QuadRefinement::~QuadRefinement() {
}
//
// Methods to construct the parent-to-child mapping
//
void
QuadRefinement::allocateParentChildIndices() {
//
// Initialize the vectors of indices mapping parent components to those child components
// that will originate from each.
//
int faceChildFaceCount = (int) _parent->_faceVertIndices.size();
int faceChildEdgeCount = (int) _parent->_faceEdgeIndices.size();
int edgeChildEdgeCount = (int) _parent->_edgeVertIndices.size();
int faceChildVertCount = _parent->getNumFaces();
int edgeChildVertCount = _parent->getNumEdges();
int vertChildVertCount = _parent->getNumVertices();
//
// First reference the parent Level's face-vertex counts/offsets -- they can be used
// here for both the face-child-faces and face-child-edges as they both have one per
// face-vertex.
//
// Given we will be ignoring initial values with uniform refinement and assigning all
// directly, initializing here is a waste...
//
Index initValue = 0;
_faceChildFaceCountsAndOffsets = _parent->shareFaceVertCountsAndOffsets();
_faceChildEdgeCountsAndOffsets = _parent->shareFaceVertCountsAndOffsets();
_faceChildFaceIndices.resize(faceChildFaceCount, initValue);
_faceChildEdgeIndices.resize(faceChildEdgeCount, initValue);
_edgeChildEdgeIndices.resize(edgeChildEdgeCount, initValue);
_faceChildVertIndex.resize(faceChildVertCount, initValue);
_edgeChildVertIndex.resize(edgeChildVertCount, initValue);
_vertChildVertIndex.resize(vertChildVertCount, initValue);
}
//
// Methods to populate the face-vertex relation of the child Level:
// - child faces only originate from parent faces
//
void
QuadRefinement::populateFaceVertexRelation() {
// Both face-vertex and face-edge share the face-vertex counts/offsets within a
// Level, so be sure not to re-initialize it if already done:
//
if (_child->_faceVertCountsAndOffsets.size() == 0) {
populateFaceVertexCountsAndOffsets();
}
_child->_faceVertIndices.resize(_child->getNumFaces() * 4);
populateFaceVerticesFromParentFaces();
}
void
QuadRefinement::populateFaceVertexCountsAndOffsets() {
_child->_faceVertCountsAndOffsets.resize(_child->getNumFaces() * 2);
for (int i = 0; i < _child->getNumFaces(); ++i) {
_child->_faceVertCountsAndOffsets[i*2 + 0] = 4;
_child->_faceVertCountsAndOffsets[i*2 + 1] = i << 2;
}
}
void
QuadRefinement::populateFaceVerticesFromParentFaces() {
//
// This is pretty straightforward, but is a good example for the case of
// iterating through the parent faces rather than the child faces, as the
// same topology information for the parent faces is required for each of
// the child faces.
//
// For each of the child faces of a parent face, identify the child vertices
// for its face-verts from the child vertices of the parent face, its edges
// and its vertices.
//
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
ConstIndexArray pFaceVerts = _parent->getFaceVertices(pFace),
pFaceEdges = _parent->getFaceEdges(pFace),
pFaceChildren = getFaceChildFaces(pFace);
int pFaceSize = pFaceVerts.size();
for (int j = 0; j < pFaceSize; ++j) {
Index cFace = pFaceChildren[j];
if (IndexIsValid(cFace)) {
int jPrev = j ? (j - 1) : (pFaceSize - 1);
Index cVertOfFace = _faceChildVertIndex[pFace];
Index cVertOfEPrev = _edgeChildVertIndex[pFaceEdges[jPrev]];
Index cVertOfVert = _vertChildVertIndex[pFaceVerts[j]];
Index cVertOfENext = _edgeChildVertIndex[pFaceEdges[j]];
IndexArray cFaceVerts = _child->getFaceVertices(cFace);
// Note orientation wrt parent face -- quad vs non-quad...
if (pFaceSize == 4) {
int jOpp = jPrev ? (jPrev - 1) : 3;
int jNext = jOpp ? (jOpp - 1) : 3;
cFaceVerts[j] = cVertOfVert;
cFaceVerts[jNext] = cVertOfENext;
cFaceVerts[jOpp] = cVertOfFace;
cFaceVerts[jPrev] = cVertOfEPrev;
} else {
cFaceVerts[0] = cVertOfVert;
cFaceVerts[1] = cVertOfENext;
cFaceVerts[2] = cVertOfFace;
cFaceVerts[3] = cVertOfEPrev;
}
}
}
}
}
//
// Methods to populate the face-vertex relation of the child Level:
// - child faces only originate from parent faces
//
void
QuadRefinement::populateFaceEdgeRelation() {
// Both face-vertex and face-edge share the face-vertex counts/offsets, so be sure
// not to re-initialize it if already done:
//
if (_child->_faceVertCountsAndOffsets.size() == 0) {
populateFaceVertexCountsAndOffsets();
}
_child->_faceEdgeIndices.resize(_child->getNumFaces() * 4);
populateFaceEdgesFromParentFaces();
}
void
QuadRefinement::populateFaceEdgesFromParentFaces() {
//
// This is fairly straightforward, but since we are dealing with edges here, we
// occasionally have to deal with the limitation of them being undirected. Since
// child faces from the same parent face share much in common, we iterate through
// the parent faces.
//
// Each child face of the parent is based on a corner vertex from which we denote
// a "previous" and "next" edge, which are child edges of the parent face's edges.
// The two remaining edges per child faces are perpendicular to these prev/next
// edges and share the child vertex of the parent face.
//
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
ConstIndexArray pFaceVerts = _parent->getFaceVertices(pFace),
pFaceEdges = _parent->getFaceEdges(pFace),
pFaceChildFaces = getFaceChildFaces(pFace),
pFaceChildEdges = getFaceChildEdges(pFace);
int pFaceSize = pFaceVerts.size();
for (int j = 0; j < pFaceSize; ++j) {
Index cFace = pFaceChildFaces[j];
if (IndexIsValid(cFace)) {
//
// Identify the vertex pairs for the prev/next parent edges -- from
// which we will determine the prev/next child edges:
//
int jPrev = j ? (j - 1) : (pFaceSize - 1);
Index pPrevEdge = pFaceEdges[jPrev];
ConstIndexArray pPrevEdgeVerts = _parent->getEdgeVertices(pPrevEdge);
Index pNextEdge = pFaceEdges[j];
ConstIndexArray pNextEdgeVerts = _parent->getEdgeVertices(pNextEdge);
//
// Now identify the two prev/next child edges (beware of degenerate
// edges here) and the two remaining perpendicular child edges:
//
Index pCornerVert = pFaceVerts[j];
int cornerInPrevEdge = (pPrevEdgeVerts[0] != pPrevEdgeVerts[1])
? (pPrevEdgeVerts[0] != pCornerVert) : 1;
int cornerInNextEdge = (pNextEdgeVerts[0] != pNextEdgeVerts[1])
? (pNextEdgeVerts[0] != pCornerVert) : 0;
Index cEdgeOfEdgePrev = getEdgeChildEdges(pPrevEdge)[cornerInPrevEdge];
Index cEdgeOfEdgeNext = getEdgeChildEdges(pNextEdge)[cornerInNextEdge];
Index cEdgePerpEdgePrev = pFaceChildEdges[jPrev];
Index cEdgePerpEdgeNext = pFaceChildEdges[j];
//
// Assign the identified child edges to the child face's face-edges:
//
IndexArray cFaceEdges = _child->getFaceEdges(cFace);
// Note orientation wrt parent face -- quad vs non-quad...
if (pFaceSize == 4) {
int jOpp = jPrev ? (jPrev - 1) : 3;
int jNext = jOpp ? (jOpp - 1) : 3;
cFaceEdges[j] = cEdgeOfEdgeNext;
cFaceEdges[jNext] = cEdgePerpEdgeNext;
cFaceEdges[jOpp] = cEdgePerpEdgePrev;
cFaceEdges[jPrev] = cEdgeOfEdgePrev;
} else {
cFaceEdges[0] = cEdgeOfEdgeNext;
cFaceEdges[1] = cEdgePerpEdgeNext;
cFaceEdges[2] = cEdgePerpEdgePrev;
cFaceEdges[3] = cEdgeOfEdgePrev;
}
}
}
}
}
//
// Methods to populate the edge-vertex relation of the child Level:
// - child edges originate from parent faces and edges
//
void
QuadRefinement::populateEdgeVertexRelation() {
_child->_edgeVertIndices.resize(_child->getNumEdges() * 2);
populateEdgeVerticesFromParentFaces();
populateEdgeVerticesFromParentEdges();
}
void
QuadRefinement::populateEdgeVerticesFromParentFaces() {
//
// This is straightforward. All child edges of parent faces are assigned
// their first vertex from the child vertex of the face -- so it is common
// to all. The second vertex is the child vertex of the parent edge to
// which the new child edge is perpendicular.
//
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
ConstIndexArray pFaceEdges = _parent->getFaceEdges(pFace),
pFaceChildEdges = getFaceChildEdges(pFace);
for (int j = 0; j < pFaceEdges.size(); ++j) {
Index cEdge = pFaceChildEdges[j];
if (IndexIsValid(cEdge)) {
IndexArray cEdgeVerts = _child->getEdgeVertices(cEdge);
cEdgeVerts[0] = _faceChildVertIndex[pFace];
cEdgeVerts[1] = _edgeChildVertIndex[pFaceEdges[j]];
}
}
}
}
void
QuadRefinement::populateEdgeVerticesFromParentEdges() {
//
// This is straightforward. All child edges of parent edges are assigned
// their first vertex from the child vertex of the edge -- so it is common
// to both. The second vertex is the child vertex of the vertex at the
// end of the parent edge.
//
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
ConstIndexArray pEdgeVerts = _parent->getEdgeVertices(pEdge),
pEdgeChildren = getEdgeChildEdges(pEdge);
// May want to unroll this trivial loop of 2...
for (int j = 0; j < 2; ++j) {
Index cEdge = pEdgeChildren[j];
if (IndexIsValid(cEdge)) {
IndexArray cEdgeVerts = _child->getEdgeVertices(cEdge);
cEdgeVerts[0] = _edgeChildVertIndex[pEdge];
cEdgeVerts[1] = _vertChildVertIndex[pEdgeVerts[j]];
}
}
}
}
//
// Methods to populate the edge-face relation of the child Level:
// - child edges originate from parent faces and edges
// - sparse refinement poses challenges with allocation here
// - we need to update the counts/offsets as we populate
//
void
QuadRefinement::populateEdgeFaceRelation() {
//
// Notes on allocating/initializing the edge-face counts/offsets vector:
//
// Be aware of scheme-specific decisions here, e.g.:
// - inspection of sparse child faces for edges from faces
// - no guaranteed "neighborhood" around Bilinear verts from verts
//
// If uniform subdivision, face count of a child edge will be:
// - 2 for new interior edges from parent faces
// == 2 * number of parent face verts for both quad- and tri-split
// - same as parent edge for edges from parent edges
// If sparse subdivision, face count of a child edge will be:
// - 1 or 2 for new interior edge depending on child faces in parent face
// - requires inspection if not all child faces present
// ? same as parent edge for edges from parent edges
// - given end vertex must have its full set of child faces
// - not for Bilinear -- only if neighborhood is non-zero
// - could at least make a quick traversal of components and use the above
// two points to get much closer estimate than what is used for uniform
//
int childEdgeFaceIndexSizeEstimate = (int)_parent->_faceVertIndices.size() * 2 +
(int)_parent->_edgeFaceIndices.size() * 2;
_child->_edgeFaceCountsAndOffsets.resize(_child->getNumEdges() * 2);
_child->_edgeFaceIndices.resize( childEdgeFaceIndexSizeEstimate);
_child->_edgeFaceLocalIndices.resize(childEdgeFaceIndexSizeEstimate);
// Update _maxEdgeFaces from the parent level before calling the
// populateEdgeFacesFromParent methods below, as these may further
// update _maxEdgeFaces.
_child->_maxEdgeFaces = _parent->_maxEdgeFaces;
populateEdgeFacesFromParentFaces();
populateEdgeFacesFromParentEdges();
// Revise the over-allocated estimate based on what is used (as indicated in the
// count/offset for the last vertex) and trim the index vector accordingly:
childEdgeFaceIndexSizeEstimate = _child->getNumEdgeFaces(_child->getNumEdges()-1) +
_child->getOffsetOfEdgeFaces(_child->getNumEdges()-1);
_child->_edgeFaceIndices.resize( childEdgeFaceIndexSizeEstimate);
_child->_edgeFaceLocalIndices.resize(childEdgeFaceIndexSizeEstimate);
}
void
QuadRefinement::populateEdgeFacesFromParentFaces() {
//
// This is straightforward topologically, but when refinement is sparse the
// contents of the counts/offsets vector is not certain and is populated
// incrementally. So there will be some resizing/trimming here.
//
// Topologically, the child edges from within a parent face will typically
// have two incident child faces (only one or none if sparse). These child
// edges and faces are interleaved within the parent and easily identified.
// Note that the edge-face "local indices" are also needed here and that
// orientation of child faces within their parent depends on it being a quad
// or not.
//
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
ConstIndexArray pFaceChildFaces = getFaceChildFaces(pFace),
pFaceChildEdges = getFaceChildEdges(pFace);
int pFaceSize = pFaceChildFaces.size();
for (int j = 0; j < pFaceSize; ++j) {
Index cEdge = pFaceChildEdges[j];
if (IndexIsValid(cEdge)) {
//
// Reserve enough edge-faces, populate and trim as needed:
//
_child->resizeEdgeFaces(cEdge, 2);
IndexArray cEdgeFaces = _child->getEdgeFaces(cEdge);
LocalIndexArray cEdgeInFace = _child->getEdgeFaceLocalIndices(cEdge);
// One or two child faces may be assigned:
int jNext = ((j + 1) < pFaceSize) ? (j + 1) : 0;
int cEdgeFaceCount = 0;
if (IndexIsValid(pFaceChildFaces[j])) {
// Note orientation wrt incident parent faces -- quad vs non-quad...
cEdgeFaces[cEdgeFaceCount] = pFaceChildFaces[j];
cEdgeInFace[cEdgeFaceCount] = (LocalIndex)((pFaceSize == 4) ? jNext : 1);
cEdgeFaceCount++;
}
if (IndexIsValid(pFaceChildFaces[jNext])) {
// Note orientation wrt incident parent faces -- quad vs non-quad...
cEdgeFaces[cEdgeFaceCount] = pFaceChildFaces[jNext];
cEdgeInFace[cEdgeFaceCount] = (LocalIndex)((pFaceSize == 4) ? ((jNext + 2) & 3) : 2);
cEdgeFaceCount++;
}
_child->trimEdgeFaces(cEdge, cEdgeFaceCount);
}
}
}
}
void
QuadRefinement::populateEdgeFacesFromParentEdges() {
//
// Note -- the edge-face counts/offsets vector is not known
// ahead of time and is populated incrementally, so we cannot
// thread this yet...
//
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
ConstIndexArray pEdgeChildEdges = getEdgeChildEdges(pEdge);
if (!IndexIsValid(pEdgeChildEdges[0]) && !IndexIsValid(pEdgeChildEdges[1])) continue;
ConstIndexArray pEdgeFaces = _parent->getEdgeFaces(pEdge);
ConstLocalIndexArray pEdgeInFace = _parent->getEdgeFaceLocalIndices(pEdge);
ConstIndexArray pEdgeVerts = _parent->getEdgeVertices(pEdge);
for (int j = 0; j < 2; ++j) {
Index cEdge = pEdgeChildEdges[j];
if (!IndexIsValid(cEdge)) continue;
// Reserve enough edge-faces, populate and trim as needed:
_child->resizeEdgeFaces(cEdge, pEdgeFaces.size());
IndexArray cEdgeFaces = _child->getEdgeFaces(cEdge);
LocalIndexArray cEdgeInFace = _child->getEdgeFaceLocalIndices(cEdge);
//
// Each parent face may contribute an incident child face:
//
int cEdgeFaceCount = 0;
for (int i = 0; i < pEdgeFaces.size(); ++i) {
Index pFace = pEdgeFaces[i];
int edgeInFace = pEdgeInFace[i];
ConstIndexArray pFaceVerts = _parent->getFaceVertices(pFace),
pFaceChildren = getFaceChildFaces(pFace);
//
// We need to first identify the potentially incident child-face and see
// if it exists before we can assign it. Beware a degenerate edge here
// when inspecting the undirected edge.
//
int childOfEdge = (pEdgeVerts[0] == pEdgeVerts[1]) ? j : (pFaceVerts[edgeInFace] != pEdgeVerts[j]);
int childInFace = edgeInFace + childOfEdge;
if (childInFace == pFaceChildren.size()) childInFace = 0;
if (IndexIsValid(pFaceChildren[childInFace])) {
// Note orientation wrt incident parent faces -- quad vs non-quad...
cEdgeFaces[cEdgeFaceCount] = pFaceChildren[childInFace];
cEdgeInFace[cEdgeFaceCount] = (LocalIndex)
((pFaceVerts.size() == 4) ? edgeInFace : (childOfEdge ? 3 : 0));
cEdgeFaceCount++;
}
}
_child->trimEdgeFaces(cEdge, cEdgeFaceCount);
}
}
}
//
// Methods to populate the vertex-face relation of the child Level:
// - child vertices originate from parent faces, edges and vertices
// - sparse refinement poses challenges with allocation here:
// - we need to update the counts/offsets as we populate
// - note this imposes ordering constraints and inhibits concurrency
//
void
QuadRefinement::populateVertexFaceRelation() {
//
// Notes on allocating/initializing the vertex-face counts/offsets vector:
//
// Be aware of scheme-specific decisions here, e.g.:
// - no verts from parent faces for Loop (unless N-gons supported)
// - more interior edges and faces for verts from parent edges for Loop
// - no guaranteed "neighborhood" around Bilinear verts from verts
//
// If uniform subdivision, vert-face count will be (catmark or loop):
// - 4 or 0 for verts from parent faces (for catmark)
// - 2x or 3x number in parent edge for verts from parent edges
// - same as parent vert for verts from parent verts
// If sparse subdivision, vert-face count will be:
// - the number of child faces in parent face
// - 1 or 2x number in parent edge for verts from parent edges
// - where the 1 or 2 is number of child edges of parent edge
// - same as parent vert for verts from parent verts (catmark)
//
int childVertFaceIndexSizeEstimate = (int)_parent->_faceVertIndices.size()
+ (int)_parent->_edgeFaceIndices.size() * 2
+ (int)_parent->_vertFaceIndices.size();
_child->_vertFaceCountsAndOffsets.resize(_child->getNumVertices() * 2);
_child->_vertFaceIndices.resize( childVertFaceIndexSizeEstimate);
_child->_vertFaceLocalIndices.resize( childVertFaceIndexSizeEstimate);
if (getFirstChildVertexFromVertices() == 0) {
populateVertexFacesFromParentVertices();
populateVertexFacesFromParentFaces();
populateVertexFacesFromParentEdges();
} else {
populateVertexFacesFromParentFaces();
populateVertexFacesFromParentEdges();
populateVertexFacesFromParentVertices();
}
// Revise the over-allocated estimate based on what is used (as indicated in the
// count/offset for the last vertex) and trim the index vectors accordingly:
childVertFaceIndexSizeEstimate = _child->getNumVertexFaces(_child->getNumVertices()-1) +
_child->getOffsetOfVertexFaces(_child->getNumVertices()-1);
_child->_vertFaceIndices.resize( childVertFaceIndexSizeEstimate);
_child->_vertFaceLocalIndices.resize(childVertFaceIndexSizeEstimate);
}
void
QuadRefinement::populateVertexFacesFromParentFaces() {
for (int pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
int cVert = _faceChildVertIndex[pFace];
if (!IndexIsValid(cVert)) continue;
ConstIndexArray pFaceChildren = getFaceChildFaces(pFace);
int pFaceSize = pFaceChildren.size();
//
// Reserve enough vert-faces, populate and trim to the actual size:
//
_child->resizeVertexFaces(cVert, pFaceSize);
IndexArray cVertFaces = _child->getVertexFaces(cVert);
LocalIndexArray cVertInFace = _child->getVertexFaceLocalIndices(cVert);
//
// Inspect each of the child faces of this parent face and add those that
// exist as incident the child vertex of this face:
//
int cVertFaceCount = 0;
for (int j = 0; j < pFaceSize; ++j) {
if (IndexIsValid(pFaceChildren[j])) {
// Note orientation wrt parent face -- quad vs non-quad...
cVertFaces[cVertFaceCount] = pFaceChildren[j];
cVertInFace[cVertFaceCount] = (LocalIndex)((pFaceSize == 4) ? ((j+2) & 3) : 2);
cVertFaceCount++;
}
}
_child->trimVertexFaces(cVert, cVertFaceCount);
}
}
void
QuadRefinement::populateVertexFacesFromParentEdges() {
for (int pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
int cVert = _edgeChildVertIndex[pEdge];
if (!IndexIsValid(cVert)) continue;
ConstIndexArray pEdgeFaces = _parent->getEdgeFaces(pEdge);
ConstLocalIndexArray pEdgeInFace = _parent->getEdgeFaceLocalIndices(pEdge);
//
// Reserve enough vert-faces, populate and trim to the actual size:
//
_child->resizeVertexFaces(cVert, 2 * pEdgeFaces.size());
IndexArray cVertFaces = _child->getVertexFaces(cVert);
LocalIndexArray cVertInFace = _child->getVertexFaceLocalIndices(cVert);
//
// For each face incident the parent edge, identify its corresponding two child faces
// and assign those of the two that exist. The second face is considered and added
// first to preserve CC-wise ordering of faces wrt the vertex.
//
int cVertFaceCount = 0;
for (int i = 0; i < pEdgeFaces.size(); ++i) {
Index pFace = pEdgeFaces[i];
int edgeInFace = pEdgeInFace[i];
ConstIndexArray pFaceChildren = getFaceChildFaces(pFace);
int pFaceSize = pFaceChildren.size();
int faceChild0 = edgeInFace;
int faceChild1 = edgeInFace + 1;
if (faceChild1 == pFaceChildren.size()) faceChild1 = 0;
if (IndexIsValid(pFaceChildren[faceChild1])) {
// Note orientation wrt incident parent faces -- quad vs non-quad...
cVertFaces[cVertFaceCount] = pFaceChildren[faceChild1];
cVertInFace[cVertFaceCount] = (LocalIndex)((pFaceSize == 4) ? faceChild0 : 3);
cVertFaceCount++;
}
if (IndexIsValid(pFaceChildren[faceChild0])) {
// Note orientation wrt incident parent faces -- quad vs non-quad...
cVertFaces[cVertFaceCount] = pFaceChildren[faceChild0];
cVertInFace[cVertFaceCount] = (LocalIndex)((pFaceSize == 4) ? faceChild1 : 1);
cVertFaceCount++;
}
}
_child->trimVertexFaces(cVert, cVertFaceCount);
}
}
void
QuadRefinement::populateVertexFacesFromParentVertices() {
for (int pVert = 0; pVert < _parent->getNumVertices(); ++pVert) {
int cVert = _vertChildVertIndex[pVert];
if (!IndexIsValid(cVert)) continue;
ConstIndexArray pVertFaces = _parent->getVertexFaces(pVert);
ConstLocalIndexArray pVertInFace = _parent->getVertexFaceLocalIndices(pVert);
//
// Reserve enough vert-faces, populate and trim to the actual size:
//
_child->resizeVertexFaces(cVert, pVertFaces.size());
IndexArray cVertFaces = _child->getVertexFaces(cVert);
LocalIndexArray cVertInFace = _child->getVertexFaceLocalIndices(cVert);
//
// Inspect each of the faces incident the parent vertex and add those that
// spawned a child face corresponding to (and so incident) this child vertex:
//
int cVertFaceCount = 0;
for (int i = 0; i < pVertFaces.size(); ++i) {
Index pFace = pVertFaces[i];
LocalIndex vertInFace = pVertInFace[i];
ConstIndexArray pFaceChildren = getFaceChildFaces(pFace);
if (IndexIsValid(pFaceChildren[vertInFace])) {
int pFaceSize = pFaceChildren.size();
// Note orientation wrt incident parent faces -- quad vs non-quad...
cVertFaces[cVertFaceCount] = pFaceChildren[vertInFace];
cVertInFace[cVertFaceCount] = (LocalIndex)((pFaceSize == 4) ? vertInFace : 0);
cVertFaceCount++;
}
}
_child->trimVertexFaces(cVert, cVertFaceCount);
}
}
//
// Methods to populate the vertex-edge relation of the child Level:
// - child vertices originate from parent faces, edges and vertices
// - sparse refinement poses challenges with allocation here:
// - we need to update the counts/offsets as we populate
// - note this imposes ordering constraints and inhibits concurrency
//
void
QuadRefinement::populateVertexEdgeRelation() {
//
// Notes on allocating/initializing the vertex-edge counts/offsets vector:
//
// Be aware of scheme-specific decisions here, e.g.:
// - no verts from parent faces for Loop
// - more interior edges and faces for verts from parent edges for Loop
// - no guaranteed "neighborhood" around Bilinear verts from verts
//
// If uniform subdivision, vert-edge count will be:
// - 4 or 0 for verts from parent faces (for catmark)
// - 2 + N or 2 + 2*N faces incident parent edge for verts from parent edges
// - same as parent vert for verts from parent verts
// If sparse subdivision, vert-edge count will be:
// - non-trivial function of child faces in parent face
// - 1 child face will always result in 2 child edges
// * 2 child faces can mean 3 or 4 child edges
// - 3 child faces will always result in 4 child edges
// - 1 or 2 + N faces incident parent edge for verts from parent edges
// - where the 1 or 2 is number of child edges of parent edge
// - any end vertex will require all N child faces (catmark)
// - same as parent vert for verts from parent verts (catmark)
//
int childVertEdgeIndexSizeEstimate = (int)_parent->_faceVertIndices.size()
+ (int)_parent->_edgeFaceIndices.size() + _parent->getNumEdges() * 2
+ (int)_parent->_vertEdgeIndices.size();
_child->_vertEdgeCountsAndOffsets.resize(_child->getNumVertices() * 2);
_child->_vertEdgeIndices.resize( childVertEdgeIndexSizeEstimate);
_child->_vertEdgeLocalIndices.resize( childVertEdgeIndexSizeEstimate);
if (getFirstChildVertexFromVertices() == 0) {
populateVertexEdgesFromParentVertices();
populateVertexEdgesFromParentFaces();
populateVertexEdgesFromParentEdges();
} else {
populateVertexEdgesFromParentFaces();
populateVertexEdgesFromParentEdges();
populateVertexEdgesFromParentVertices();
}
// Revise the over-allocated estimate based on what is used (as indicated in the
// count/offset for the last vertex) and trim the index vectors accordingly:
childVertEdgeIndexSizeEstimate = _child->getNumVertexEdges(_child->getNumVertices()-1) +
_child->getOffsetOfVertexEdges(_child->getNumVertices()-1);
_child->_vertEdgeIndices.resize( childVertEdgeIndexSizeEstimate);
_child->_vertEdgeLocalIndices.resize(childVertEdgeIndexSizeEstimate);
}
void
QuadRefinement::populateVertexEdgesFromParentFaces() {
for (int pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
int cVert = _faceChildVertIndex[pFace];
if (!IndexIsValid(cVert)) continue;
ConstIndexArray pFaceVerts = _parent->getFaceVertices(pFace),
pFaceChildEdges = getFaceChildEdges(pFace);
//
// Reserve enough vert-edges, populate and trim to the actual size:
//
_child->resizeVertexEdges(cVert, pFaceVerts.size());
IndexArray cVertEdges = _child->getVertexEdges(cVert);
LocalIndexArray cVertInEdge = _child->getVertexEdgeLocalIndices(cVert);
//
// Need to ensure correct ordering here when complete -- we want the "leading"
// edge of each child face first. The child vert is in the center of a new
// face so new "boundaries" will only occur when the vertex is incomplete.
//
int cVertEdgeCount = 0;
for (int j = 0; j < pFaceVerts.size(); ++j) {
int jLeadingEdge = j ? (j - 1) : (pFaceVerts.size() - 1);
if (IndexIsValid(pFaceChildEdges[jLeadingEdge])) {
cVertEdges[cVertEdgeCount] = pFaceChildEdges[jLeadingEdge];
cVertInEdge[cVertEdgeCount] = 0;
cVertEdgeCount++;
}
}
_child->trimVertexEdges(cVert, cVertEdgeCount);
}
}
void
QuadRefinement::populateVertexEdgesFromParentEdges() {
//
// This relation turns out to be awkward to populate given the mixed parentage
// of the incident edges of the child vertex of an edge -- two child edges
// originate from the parent edge while one or more will originate from the
// faces incident the parent edge. The need to interleave these for proper
// CC-wise orientation is what really complicates this.
//
// Unlike other relations, we generate the results and then re-order them as
// needed. In this case we assign the first two incident edges as the child
// edges of the parent edge, followed then by those originating from a parent
// face. We then swap the second and third (and possibly the first two) so
// that we have the desired origin sequence beginning [edge, face, edge, ...]
//
for (int pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
int cVert = _edgeChildVertIndex[pEdge];
if (!IndexIsValid(cVert)) continue;
ConstIndexArray pEdgeFaces = _parent->getEdgeFaces(pEdge);
ConstLocalIndexArray pEdgeInFace = _parent->getEdgeFaceLocalIndices(pEdge);
ConstIndexArray pEdgeVerts = _parent->getEdgeVertices(pEdge),
pEdgeChildEdges = getEdgeChildEdges(pEdge);
//
// Reserve enough vert-edges, populate and trim to the actual size:
//
_child->resizeVertexEdges(cVert, pEdgeFaces.size() + 2);
IndexArray cVertEdges = _child->getVertexEdges(cVert);
LocalIndexArray cVertInEdge = _child->getVertexEdgeLocalIndices(cVert);
//
// Identify and assign the first two child edges of the parent edge -- until
// we look more closely at the orientation of the parent edge in the first
// face we don't know what order these two should be in, so just assign them
// for now and swap them later if necessary:
//
int cVertEdgeCount = 0;
if (IndexIsValid(pEdgeChildEdges[0])) {
cVertEdges[cVertEdgeCount] = pEdgeChildEdges[0];
cVertInEdge[cVertEdgeCount] = 0;
cVertEdgeCount++;
}
if (IndexIsValid(pEdgeChildEdges[1])) {
cVertEdges[cVertEdgeCount] = pEdgeChildEdges[1];
cVertInEdge[cVertEdgeCount] = 0;
cVertEdgeCount++;
}
//
// Append the interior edge of each incident parent face -- swapping the
// first face-edge with the second edge-edge just added to get the desired
// sequence of child edges originating from (edge, face0, edge, ...)
//
for (int i = 0; i < pEdgeFaces.size(); ++i) {
Index pFace = pEdgeFaces[i];
int edgeInFace = pEdgeInFace[i];
Index cEdgeOfFace = getFaceChildEdges(pFace)[edgeInFace];
if (IndexIsValid(cEdgeOfFace)) {
cVertEdges[cVertEdgeCount] = cEdgeOfFace;
cVertInEdge[cVertEdgeCount] = 1;
cVertEdgeCount++;
// Check if swapping this first face-edge with the last edge-edge
// is necessary:
if ((i == 0) && (cVertEdgeCount == 3)) {
// Remember to order the first of the two child edges according
// to the parent edge's orientation in this first face:
if ((pEdgeVerts[0] != pEdgeVerts[1]) &&
(_parent->getFaceVertices(pFace)[edgeInFace] == pEdgeVerts[0])) {
std::swap(cVertEdges[0], cVertEdges[1]);
std::swap(cVertInEdge[0], cVertInEdge[1]);
}
std::swap(cVertEdges[1], cVertEdges[2]);
std::swap(cVertInEdge[1], cVertInEdge[2]);
}
}
}
_child->trimVertexEdges(cVert, cVertEdgeCount);
}
}
void
QuadRefinement::populateVertexEdgesFromParentVertices() {
for (int pVert = 0; pVert < _parent->getNumVertices(); ++pVert) {
int cVert = _vertChildVertIndex[pVert];
if (!IndexIsValid(cVert)) continue;
ConstIndexArray pVertEdges = _parent->getVertexEdges(pVert);
ConstLocalIndexArray pVertInEdge = _parent->getVertexEdgeLocalIndices(pVert);
//
// Reserve enough vert-edges, populate and trim to the actual size:
//
_child->resizeVertexEdges(cVert, pVertEdges.size());
IndexArray cVertEdges = _child->getVertexEdges(cVert);
LocalIndexArray cVertInEdge = _child->getVertexEdgeLocalIndices(cVert);
int cVertEdgeCount = 0;
for (int i = 0; i < pVertEdges.size(); ++i) {
Index pEdgeIndex = pVertEdges[i];
LocalIndex pEdgeVert = pVertInEdge[i];
Index pEdgeChildIndex = getEdgeChildEdges(pEdgeIndex)[pEdgeVert];
if (IndexIsValid(pEdgeChildIndex)) {
cVertEdges[cVertEdgeCount] = pEdgeChildIndex;
cVertInEdge[cVertEdgeCount] = 1;
cVertEdgeCount++;
}
}
_child->trimVertexEdges(cVert, cVertEdgeCount);
}
}
//
// Methods to populate child-component indices for sparse selection:
//
// Need to find a better place for these anon helper methods now that they are required
// both in the base class and the two subclasses for quad- and tri-splitting...
//
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
QuadRefinement::markSparseFaceChildren() {
assert(_parentFaceTag.size() > 0);
//
// For each parent face:
// All boundary edges will be adequately marked as a result of the pass over the
// edges above and boundary vertices marked by selection. So all that remains is to
// identify the child faces and interior child edges for a face requiring neighboring
// child faces.
// For each corner vertex selected, we need to mark the corresponding child face,
// the two interior child edges and shared child vertex in the middle.
//
assert(_splitType == Sdc::SPLIT_TO_QUADS);
for (Index pFace = 0; pFace < parent().getNumFaces(); ++pFace) {
//
// Mark all descending child components of a selected face. Otherwise inspect
// its incident vertices to see if anything neighboring has been selected --
// requiring partial refinement of this face.
//
// Remember that a selected face cannot be transitional, and that only a
// transitional face will be partially refined.
//
IndexArray fChildFaces = getFaceChildFaces(pFace);
IndexArray fChildEdges = getFaceChildEdges(pFace);
ConstIndexArray fVerts = parent().getFaceVertices(pFace);
SparseTag& pFaceTag = _parentFaceTag[pFace];
if (pFaceTag._selected) {
for (int i = 0; i < fVerts.size(); ++i) {
markSparseIndexSelected(fChildFaces[i]);
markSparseIndexSelected(fChildEdges[i]);
}
markSparseIndexSelected(_faceChildVertIndex[pFace]);
pFaceTag._transitional = 0;
} else {
int marked = false;
for (int i = 0; i < fVerts.size(); ++i) {
if (_parentVertexTag[fVerts[i]]._selected) {
int iPrev = i ? (i - 1) : (fVerts.size() - 1);
markSparseIndexNeighbor(fChildFaces[i]);
markSparseIndexNeighbor(fChildEdges[i]);
markSparseIndexNeighbor(fChildEdges[iPrev]);
marked = true;
}
}
if (marked) {
markSparseIndexNeighbor(_faceChildVertIndex[pFace]);
//
// Assign selection and transitional tags to faces when required:
//
// Only non-selected faces may be "transitional", and we need to inspect
// all tags on its boundary edges to be sure. Since we're inspecting each
// now (and may need to later) retain the transitional state of each in a
// 4-bit mask that reflects the full transitional topology for later.
//
ConstIndexArray fEdges = parent().getFaceEdges(pFace);
if (fEdges.size() == 4) {
pFaceTag._transitional = (unsigned char)
((_parentEdgeTag[fEdges[0]]._transitional << 0) |
(_parentEdgeTag[fEdges[1]]._transitional << 1) |
(_parentEdgeTag[fEdges[2]]._transitional << 2) |
(_parentEdgeTag[fEdges[3]]._transitional << 3));
} else if (fEdges.size() == 3) {
pFaceTag._transitional = (unsigned char)
((_parentEdgeTag[fEdges[0]]._transitional << 0) |
(_parentEdgeTag[fEdges[1]]._transitional << 1) |
(_parentEdgeTag[fEdges[2]]._transitional << 2));
} else {
pFaceTag._transitional = 0;
for (int i = 0; i < fEdges.size(); ++i) {
pFaceTag._transitional |= _parentEdgeTag[fEdges[i]]._transitional;
}
}
}
}
}
}
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,86 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_QUAD_REFINEMENT_H
#define OPENSUBDIV3_VTR_QUAD_REFINEMENT_H
#include "../version.h"
#include "../vtr/refinement.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// QuadRefinement:
// A QuadRefinement is a subclass of Refinement that splits all faces into quads.
// It provides the configuration of parent-to-child components and the population of
// all required topological relations in order to complete a valid Refinement.
//
class QuadRefinement : public Refinement {
public:
QuadRefinement(Level const & parent, Level & child, Sdc::Options const & options);
~QuadRefinement();
protected:
//
// Virtual methods to complete the configuration of the parent-to-child mapping:
//
virtual void allocateParentChildIndices();
virtual void markSparseFaceChildren();
//
// Virtual methods to populate the six topological relations:
//
virtual void populateFaceVertexRelation();
virtual void populateFaceEdgeRelation();
virtual void populateEdgeVertexRelation();
virtual void populateEdgeFaceRelation();
virtual void populateVertexFaceRelation();
virtual void populateVertexEdgeRelation();
//
// Internal helper methods for populating the topology:
//
void populateFaceVertexCountsAndOffsets();
void populateFaceVerticesFromParentFaces();
void populateFaceEdgesFromParentFaces();
void populateEdgeVerticesFromParentFaces();
void populateEdgeVerticesFromParentEdges();
void populateEdgeFacesFromParentFaces();
void populateEdgeFacesFromParentEdges();
void populateVertexFacesFromParentFaces();
void populateVertexFacesFromParentEdges();
void populateVertexFacesFromParentVertices();
void populateVertexEdgesFromParentFaces();
void populateVertexEdgesFromParentEdges();
void populateVertexEdgesFromParentVertices();
private:
//
// Data members -- currently none
//
};
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_REFINEMENT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,439 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_REFINEMENT_H
#define OPENSUBDIV3_VTR_REFINEMENT_H
#include "../version.h"
#include "../sdc/types.h"
#include "../sdc/options.h"
#include "../vtr/types.h"
#include "../vtr/level.h"
#include <vector>
//
// Declaration for the main refinement class (Refinement) and its pre-requisites:
//
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
class FVarRefinement;
//
// Refinement:
// A refinement is a mapping between two levels -- relating the components in the original
// (parent) level to the one refined (child). The refinement may be complete (uniform) or sparse
// (adaptive or otherwise selective), so not all components in the parent level will spawn
// components in the child level.
//
// Refinement is an abstract class and expects subclasses corresponding to the different types
// of topological splits that the supported subdivision schemes collectively require, i.e. those
// listed in Sdc::SplitType. Note the virtual requirements expected of the subclasses in the list
// of protected methods -- they differ mainly in the topology that is created in the child Level
// and not the propagation of tags through refinement, subdivision of sharpness values or the
// treatment of face-varying data. The primary subclasses are QuadRefinement and TriRefinement.
//
// At a high level, all that is necessary in terms of interface is to construct, initialize
// (linking the two levels), optionally select components for sparse refinement (via use of the
// SparseSelector) and call the refine() method. This usage is expected of Far::TopologyRefiner.
//
// Since we really want this class to be restricted from public access eventually, all methods
// begin with lower case (as is the convention for protected methods) and the list of friends
// will be maintained more strictly.
//
class Refinement {
public:
Refinement(Level const & parent, Level & child, Sdc::Options const& schemeOptions);
virtual ~Refinement();
Level const& parent() const { return *_parent; }
Level const& child() const { return *_child; }
Level& child() { return *_child; }
Sdc::Split getSplitType() const { return _splitType; }
int getRegularFaceSize() const { return _regFaceSize; }
Sdc::Options getOptions() const { return _options; }
// Face-varying:
int getNumFVarChannels() const { return (int) _fvarChannels.size(); }
FVarRefinement const & getFVarRefinement(int c) const { return *_fvarChannels[c]; }
//
// Options associated with the actual refinement operation, which may end up
// quite involved if we want to allow for the refinement of data that is not
// of interest to be suppressed. For now we have:
//
// "sparse": the alternative to uniform refinement, which requires that
// components be previously selected/marked to be included.
//
// "minimal topology": this is one that may get broken down into a finer
// set of options. It suppresses "full topology" in the child level
// and only generates what is minimally necessary for interpolation --
// which requires at least the face-vertices for faces, but also the
// vertex-faces for any face-varying channels present. So it will
// generate one or two of the six possible topological relations.
//
// These are strictly controlled right now, e.g. for sparse refinement, we
// currently enforce full topology at the finest level to allow for subsequent
// patch construction.
//
struct Options {
Options() : _sparse(false),
_faceVertsFirst(false),
_minimalTopology(false)
{ }
unsigned int _sparse : 1;
unsigned int _faceVertsFirst : 1;
unsigned int _minimalTopology : 1;
// Still under consideration:
//unsigned int _childToParentMap : 1;
};
void refine(Options options = Options());
bool hasFaceVerticesFirst() const { return _faceVertsFirst; }
public:
//
// Access to members -- some testing classes (involving vertex interpolation)
// currently make use of these:
//
int getNumChildFacesFromFaces() const { return _childFaceFromFaceCount; }
int getNumChildEdgesFromFaces() const { return _childEdgeFromFaceCount; }
int getNumChildEdgesFromEdges() const { return _childEdgeFromEdgeCount; }
int getNumChildVerticesFromFaces() const { return _childVertFromFaceCount; }
int getNumChildVerticesFromEdges() const { return _childVertFromEdgeCount; }
int getNumChildVerticesFromVertices() const { return _childVertFromVertCount; }
Index getFirstChildFaceFromFaces() const { return _firstChildFaceFromFace; }
Index getFirstChildEdgeFromFaces() const { return _firstChildEdgeFromFace; }
Index getFirstChildEdgeFromEdges() const { return _firstChildEdgeFromEdge; }
Index getFirstChildVertexFromFaces() const { return _firstChildVertFromFace; }
Index getFirstChildVertexFromEdges() const { return _firstChildVertFromEdge; }
Index getFirstChildVertexFromVertices() const { return _firstChildVertFromVert; }
Index getFaceChildVertex(Index f) const { return _faceChildVertIndex[f]; }
Index getEdgeChildVertex(Index e) const { return _edgeChildVertIndex[e]; }
Index getVertexChildVertex(Index v) const { return _vertChildVertIndex[v]; }
ConstIndexArray getFaceChildFaces(Index parentFace) const;
ConstIndexArray getFaceChildEdges(Index parentFace) const;
ConstIndexArray getEdgeChildEdges(Index parentEdge) const;
// Child-to-parent relationships
bool isChildVertexComplete(Index v) const { return ! _childVertexTag[v]._incomplete; }
Index getChildFaceParentFace(Index f) const { return _childFaceParentIndex[f]; }
int getChildFaceInParentFace(Index f) const { return _childFaceTag[f]._indexInParent; }
Index getChildEdgeParentIndex(Index e) const { return _childEdgeParentIndex[e]; }
Index getChildVertexParentIndex(Index v) const { return _childVertexParentIndex[v]; }
//
// Modifiers intended for internal/protected use:
//
public:
IndexArray getFaceChildFaces(Index parentFace);
IndexArray getFaceChildEdges(Index parentFace);
IndexArray getEdgeChildEdges(Index parentEdge);
public:
//
// Tags have now been added per-component in Level, but there is additional need to tag
// components within Refinement -- we can't tag the parent level components for any
// refinement (in order to keep it const) and tags associated with children that are
// specific to the child-to-parent mapping may not be warranted in the child level.
//
// Parent tags are only required for sparse refinement. The main property to tag is
// whether a component was selected, and so a single SparseTag is used for all three
// component types. Tagging if a component is "transitional" is also useful. This may
// only be necessary for edges but is currently packed into a mask per-edge for faces,
// which could be deferred, in which case "transitional" could be a single bit.
//
// Child tags are part of the child-to-parent mapping, which consists of the parent
// component index for each child component, plus a tag for the child indicating more
// about its relationship to its parent, e.g. is it completely defined, what the parent
// component type is, what is the index of the child within its parent, etc.
//
struct SparseTag {
SparseTag() : _selected(0), _transitional(0) { }
unsigned char _selected : 1; // component specifically selected for refinement
unsigned char _transitional : 4; // adjacent to a refined component (4-bits for face)
};
struct ChildTag {
ChildTag() { }
unsigned char _incomplete : 1; // incomplete neighborhood to represent limit of parent
unsigned char _parentType : 2; // type of parent component: vertex, edge or face
unsigned char _indexInParent : 2; // index of child wrt parent: 0-3, or iterative if N > 4
};
// Methods to access and modify tags:
SparseTag const & getParentFaceSparseTag( Index f) const { return _parentFaceTag[f]; }
SparseTag const & getParentEdgeSparseTag( Index e) const { return _parentEdgeTag[e]; }
SparseTag const & getParentVertexSparseTag(Index v) const { return _parentVertexTag[v]; }
SparseTag & getParentFaceSparseTag( Index f) { return _parentFaceTag[f]; }
SparseTag & getParentEdgeSparseTag( Index e) { return _parentEdgeTag[e]; }
SparseTag & getParentVertexSparseTag(Index v) { return _parentVertexTag[v]; }
ChildTag const & getChildFaceTag( Index f) const { return _childFaceTag[f]; }
ChildTag const & getChildEdgeTag( Index e) const { return _childEdgeTag[e]; }
ChildTag const & getChildVertexTag(Index v) const { return _childVertexTag[v]; }
ChildTag & getChildFaceTag( Index f) { return _childFaceTag[f]; }
ChildTag & getChildEdgeTag( Index e) { return _childEdgeTag[e]; }
ChildTag & getChildVertexTag(Index v) { return _childVertexTag[v]; }
// Remaining methods should really be protected -- for use by subclasses...
public:
//
// Methods involved in constructing the parent-to-child mapping -- when the
// refinement is sparse, additional methods are needed to identify the selection:
//
void populateParentToChildMapping();
void populateParentChildIndices();
void printParentToChildMapping() const;
virtual void allocateParentChildIndices() = 0;
// Supporting method for sparse refinement:
void initializeSparseSelectionTags();
void markSparseChildComponentIndices();
void markSparseVertexChildren();
void markSparseEdgeChildren();
virtual void markSparseFaceChildren() = 0;
void initializeChildComponentCounts();
//
// Methods involved in constructing the child-to-parent mapping:
//
void populateChildToParentMapping();
void populateFaceParentVectors(ChildTag const initialChildTags[2][4]);
void populateFaceParentFromParentFaces(ChildTag const initialChildTags[2][4]);
void populateEdgeParentVectors(ChildTag const initialChildTags[2][4]);
void populateEdgeParentFromParentFaces(ChildTag const initialChildTags[2][4]);
void populateEdgeParentFromParentEdges(ChildTag const initialChildTags[2][4]);
void populateVertexParentVectors(ChildTag const initialChildTags[2][4]);
void populateVertexParentFromParentFaces(ChildTag const initialChildTags[2][4]);
void populateVertexParentFromParentEdges(ChildTag const initialChildTags[2][4]);
void populateVertexParentFromParentVertices(ChildTag const initialChildTags[2][4]);
//
// Methods involved in propagating component tags from parent to child:
//
void propagateComponentTags();
void populateFaceTagVectors();
void populateFaceTagsFromParentFaces();
void populateEdgeTagVectors();
void populateEdgeTagsFromParentFaces();
void populateEdgeTagsFromParentEdges();
void populateVertexTagVectors();
void populateVertexTagsFromParentFaces();
void populateVertexTagsFromParentEdges();
void populateVertexTagsFromParentVertices();
//
// Methods (and types) involved in subdividing the topology -- though not
// fully exploited, any subset of the 6 relations can be generated:
//
struct Relations {
unsigned int _faceVertices : 1;
unsigned int _faceEdges : 1;
unsigned int _edgeVertices : 1;
unsigned int _edgeFaces : 1;
unsigned int _vertexFaces : 1;
unsigned int _vertexEdges : 1;
void setAll(bool enable) {
_faceVertices = enable;
_faceEdges = enable;
_edgeVertices = enable;
_edgeFaces = enable;
_vertexFaces = enable;
_vertexEdges = enable;
}
};
void subdivideTopology(Relations const& relationsToSubdivide);
virtual void populateFaceVertexRelation() = 0;
virtual void populateFaceEdgeRelation() = 0;
virtual void populateEdgeVertexRelation() = 0;
virtual void populateEdgeFaceRelation() = 0;
virtual void populateVertexFaceRelation() = 0;
virtual void populateVertexEdgeRelation() = 0;
//
// Methods involved in subdividing and inspecting sharpness values:
//
void subdivideSharpnessValues();
void subdivideVertexSharpness();
void subdivideEdgeSharpness();
void reclassifySemisharpVertices();
//
// Methods involved in subdividing face-varying topology:
//
void subdivideFVarChannels();
protected:
// A debug method of Level prints a Refinement (should really change this)
friend void Level::print(const Refinement *) const;
//
// Data members -- the logical grouping of some of these (and methods that make use
// of them) may lead to grouping them into a few utility classes or structs...
//
// Defined on construction:
Level const * _parent;
Level * _child;
Sdc::Options _options;
// Defined by the subclass:
Sdc::Split _splitType;
int _regFaceSize;
// Determined by the refinement options:
bool _uniform;
bool _faceVertsFirst;
//
// Inventory and ordering of the types of child components:
//
int _childFaceFromFaceCount; // arguably redundant (all faces originate from faces)
int _childEdgeFromFaceCount;
int _childEdgeFromEdgeCount;
int _childVertFromFaceCount;
int _childVertFromEdgeCount;
int _childVertFromVertCount;
int _firstChildFaceFromFace; // arguably redundant (all faces originate from faces)
int _firstChildEdgeFromFace;
int _firstChildEdgeFromEdge;
int _firstChildVertFromFace;
int _firstChildVertFromEdge;
int _firstChildVertFromVert;
//
// The parent-to-child mapping:
// These are vectors sized according to the number of parent components (and
// their topology) that contain references/indices to the child components that
// result from them by refinement. When refinement is sparse, parent components
// that have not spawned all child components will have their missing children
// marked as invalid.
//
// NOTE the "Array" members here. Often vectors within the Level can be shared
// with the Refinement, and an Array instance is used to do so. If not shared
// the subclass just initializes the Array members after allocating its own local
// vector members.
//
IndexArray _faceChildFaceCountsAndOffsets;
IndexArray _faceChildEdgeCountsAndOffsets;
IndexVector _faceChildFaceIndices; // *cannot* always use face-vert counts/offsets
IndexVector _faceChildEdgeIndices; // can use face-vert counts/offsets
IndexVector _faceChildVertIndex;
IndexVector _edgeChildEdgeIndices; // trivial/corresponding pair for each
IndexVector _edgeChildVertIndex;
IndexVector _vertChildVertIndex;
//
// The child-to-parent mapping:
//
IndexVector _childFaceParentIndex;
IndexVector _childEdgeParentIndex;
IndexVector _childVertexParentIndex;
std::vector<ChildTag> _childFaceTag;
std::vector<ChildTag> _childEdgeTag;
std::vector<ChildTag> _childVertexTag;
//
// Tags for sparse selection of components:
//
std::vector<SparseTag> _parentFaceTag;
std::vector<SparseTag> _parentEdgeTag;
std::vector<SparseTag> _parentVertexTag;
//
// Refinement data for face-varying channels present in the Levels being refined:
//
std::vector<FVarRefinement*> _fvarChannels;
};
inline ConstIndexArray
Refinement::getFaceChildFaces(Index parentFace) const {
return ConstIndexArray(&_faceChildFaceIndices[_faceChildFaceCountsAndOffsets[2*parentFace+1]],
_faceChildFaceCountsAndOffsets[2*parentFace]);
}
inline IndexArray
Refinement::getFaceChildFaces(Index parentFace) {
return IndexArray(&_faceChildFaceIndices[_faceChildFaceCountsAndOffsets[2*parentFace+1]],
_faceChildFaceCountsAndOffsets[2*parentFace]);
}
inline ConstIndexArray
Refinement::getFaceChildEdges(Index parentFace) const {
return ConstIndexArray(&_faceChildEdgeIndices[_faceChildEdgeCountsAndOffsets[2*parentFace+1]],
_faceChildEdgeCountsAndOffsets[2*parentFace]);
}
inline IndexArray
Refinement::getFaceChildEdges(Index parentFace) {
return IndexArray(&_faceChildEdgeIndices[_faceChildEdgeCountsAndOffsets[2*parentFace+1]],
_faceChildEdgeCountsAndOffsets[2*parentFace]);
}
inline ConstIndexArray
Refinement::getEdgeChildEdges(Index parentEdge) const {
return ConstIndexArray(&_edgeChildEdgeIndices[parentEdge*2], 2);
}
inline IndexArray
Refinement::getEdgeChildEdges(Index parentEdge) {
return IndexArray(&_edgeChildEdgeIndices[parentEdge*2], 2);
}
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_REFINEMENT_H */

View File

@@ -0,0 +1,84 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../vtr/sparseSelector.h"
#include "../vtr/level.h"
#include "../vtr/refinement.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// Component selection methods:
// Marking of selection is retained in the SparseTags of the Refinement. The
// selection simply marks the parent components -- not any child components that may
// be derived from them. That is done later when we need to additionally identify
// all of the "neighboring" child components that must exist at the next subdivision
// level in order to fully define supported further refinement of selected components.
//
inline void
SparseSelector::initializeSelection() {
if (!_selected) {
_refine->initializeSparseSelectionTags();
_selected = true;
}
}
void
SparseSelector::selectVertex(Index parentVertex) {
initializeSelection();
// Don't bother to test-and-set here, just set
markVertexSelected(parentVertex);
}
void
SparseSelector::selectEdge(Index parentEdge) {
initializeSelection();
if (!wasEdgeSelected(parentEdge)) {
markEdgeSelected(parentEdge);
// Mark the two end vertices:
ConstIndexArray eVerts = _refine->parent().getEdgeVertices(parentEdge);
markVertexSelected(eVerts[0]);
markVertexSelected(eVerts[1]);
}
}
void
SparseSelector::selectFace(Index parentFace) {
initializeSelection();
if (!wasFaceSelected(parentFace)) {
markFaceSelected(parentFace);
// Mark the face's incident verts and edges as selected:
ConstIndexArray fEdges = _refine->parent().getFaceEdges(parentFace),
fVerts = _refine->parent().getFaceVertices(parentFace);
for (int i = 0; i < fVerts.size(); ++i) {
markEdgeSelected(fEdges[i]);
markVertexSelected(fVerts[i]);
}
}
}
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,84 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_SPARSE_SELECTOR_H
#define OPENSUBDIV3_VTR_SPARSE_SELECTOR_H
#include "../version.h"
#include "../vtr/types.h"
#include "../vtr/refinement.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// SparseSelector:
// Class supporting "selection" of components in a Level for sparse Refinement.
// The term "selection" here implies interest in the limit for that component, i.e.
// the limit point for a selected vertex, the limit patch for a face, etc. So this
// class is responsible for ensuring that all neighboring components required to
// support the limit of those selected are included in the refinement.
//
// This class is associated with (and constructed given) a Refinement and its role
// is to initialize that Refinement instance for eventual sparse refinement. So it
// is a friend of and expected to modify the Refinement as part of the selection.
// Given its simplicity and scope it may be worth nesting it in Vtr::Refinement.
//
// While all three component types -- vertices, edges and faces -- can be selected,
// only selection of faces is currently used and actively supported as part of the
// feature-adaptive refinement.
//
class SparseSelector {
public:
SparseSelector(Refinement& refine) : _refine(&refine), _selected(false) { }
~SparseSelector() { }
void setRefinement(Refinement& refine) { _refine = &refine; }
Refinement& getRefinement() const { return *_refine; }
bool isSelectionEmpty() const { return !_selected; }
//
// Methods for selecting (and marking) components for refinement. All component indices
// refer to components in the parent:
//
void selectVertex(Index pVertex);
void selectEdge( Index pEdge);
void selectFace( Index pFace);
private:
SparseSelector() : _refine(0), _selected(false) { }
bool wasVertexSelected(Index pVertex) const { return _refine->getParentVertexSparseTag(pVertex)._selected; }
bool wasEdgeSelected( Index pEdge) const { return _refine->getParentEdgeSparseTag(pEdge)._selected; }
bool wasFaceSelected( Index pFace) const { return _refine->getParentFaceSparseTag(pFace)._selected; }
void markVertexSelected(Index pVertex) const { _refine->getParentVertexSparseTag(pVertex)._selected = true; }
void markEdgeSelected( Index pEdge) const { _refine->getParentEdgeSparseTag(pEdge)._selected = true; }
void markFaceSelected( Index pFace) const { _refine->getParentFaceSparseTag(pFace)._selected = true; }
void initializeSelection();
private:
Refinement* _refine;
bool _selected;
};
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_SPARSE_SELECTOR_H */

View File

@@ -0,0 +1,210 @@
//
// Copyright 2015 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_STACK_BUFFER_H
#define OPENSUBDIV3_VTR_STACK_BUFFER_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// The StackBuffer class is intended solely to take the place of VLAs (Variable
// Length Arrays) which most compilers support, but are not strictly standard C++.
// Portability concerns forces us to make use of either alloca() or some other
// mechanism to create small arrays on the stack that are typically based on the
// valence of a vertex -- small in general, but occasionally large.
//
// Note also that since the intent of this is to replace VLAs -- not general
// std::vectors -- support for std::vector functionality is intentionally limited
// and STL-like naming is avoided. Like a VLA there is no incremental growth.
// Support for resizing is available to reuse an instance at the beginning of a
// loop with a new size, but resizing in this case reinitializes all elements.
//
template <typename TYPE, unsigned int SIZE, bool POD_TYPE = false>
class StackBuffer
{
public:
typedef unsigned int size_type;
public:
// Constructors and destructor -- declared inline below:
StackBuffer();
StackBuffer(size_type size);
~StackBuffer();
public:
// Note the reliance on implicit casting so that it can be used similar to
// a VLA. This removes the need for operator[] as the resulting TYPE* will
// natively support []. (The presence of both TYPE* and operator[] also
// causes an ambiguous overloading error with 32-bit MSVC builds.)
operator TYPE const * () const { return _data; }
operator TYPE * () { return _data; }
size_type GetSize() const { return _size; }
void SetSize(size_type size);
void Reserve(size_type capacity);
private:
// Non-copyable:
StackBuffer(const StackBuffer<TYPE,SIZE,POD_TYPE> &) { }
StackBuffer& operator=(const StackBuffer<TYPE,SIZE,POD_TYPE> &) { return *this; }
void allocate(size_type capacity);
void deallocate();
void construct();
void destruct();
private:
TYPE * _data;
size_type _size;
size_type _capacity;
// Is alignment an issue here? The staticData arena will at least be double-word
// aligned within this struct, which meets current and most anticipated needs.
char _staticData[SIZE * sizeof(TYPE)];
char * _dynamicData;
};
//
// Core allocation/deallocation methods:
//
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline void
StackBuffer<TYPE,SIZE,POD_TYPE>::allocate(size_type capacity) {
// Again, is alignment an issue here? C++ spec says new will return pointer
// "suitably aligned" for conversion to pointers of other types, which implies
// at least an alignment of 16.
_dynamicData = static_cast<char*>(::operator new(capacity * sizeof(TYPE)));
_data = reinterpret_cast<TYPE*>(_dynamicData);
_capacity = capacity;
}
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline void
StackBuffer<TYPE,SIZE,POD_TYPE>::deallocate() {
::operator delete(_dynamicData);
_data = reinterpret_cast<TYPE*>(_staticData);
_capacity = SIZE;
}
//
// Explicit element-wise construction and destruction within allocated memory.
// Compilers do not always optimize out the iteration here even when there is
// no construction or destruction, so the POD_TYPE arguement can be used to
// force this when/if it becomes an issue (and it has been in some cases).
//
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline void
StackBuffer<TYPE,SIZE,POD_TYPE>::construct() {
for (size_type i = 0; i < _size; ++i) {
(void) new (&_data[i]) TYPE;
}
}
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline void
StackBuffer<TYPE,SIZE,POD_TYPE>::destruct() {
for (size_type i = 0; i < _size; ++i) {
_data[i].~TYPE();
}
}
//
// Inline constructors and destructor:
//
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline
StackBuffer<TYPE,SIZE,POD_TYPE>::StackBuffer() :
_data(reinterpret_cast<TYPE*>(_staticData)),
_size(0),
_capacity(SIZE),
_dynamicData(0) {
}
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline
StackBuffer<TYPE,SIZE,POD_TYPE>::StackBuffer(size_type size) :
_data(reinterpret_cast<TYPE*>(_staticData)),
_size(size),
_capacity(SIZE),
_dynamicData(0) {
if (size > SIZE) {
allocate(size);
}
if (!POD_TYPE) {
construct();
}
}
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline
StackBuffer<TYPE,SIZE,POD_TYPE>::~StackBuffer() {
if (!POD_TYPE) {
destruct();
}
deallocate();
}
//
// Inline sizing methods:
//
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline void
StackBuffer<TYPE,SIZE,POD_TYPE>::Reserve(size_type capacity) {
if (capacity > _capacity) {
if (!POD_TYPE) {
destruct();
}
deallocate();
allocate(capacity);
}
}
template <typename TYPE, unsigned int SIZE, bool POD_TYPE>
inline void
StackBuffer<TYPE,SIZE,POD_TYPE>::SetSize(size_type size)
{
if (!POD_TYPE) {
destruct();
}
if (size == 0) {
deallocate();
} else if (size > _capacity) {
deallocate();
allocate(size);
}
_size = size;
if (!POD_TYPE) {
construct();
}
}
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_STACK_BUFFER_H */

View File

@@ -0,0 +1,913 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../sdc/crease.h"
#include "../vtr/types.h"
#include "../vtr/level.h"
#include "../vtr/triRefinement.h"
#include <cassert>
#include <cstdio>
#include <utility>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// Simple constructor, destructor and basic initializers:
//
TriRefinement::TriRefinement(Level const & parentArg, Level & childArg, Sdc::Options const & optionsArg) :
Refinement(parentArg, childArg, optionsArg) {
_splitType = Sdc::SPLIT_TO_TRIS;
_regFaceSize = 3;
}
TriRefinement::~TriRefinement() {
}
//
// Methods to construct the parent-to-child mapping
//
void
TriRefinement::allocateParentChildIndices() {
//
// Initialize the vectors of indices mapping parent components to those child components
// that will originate from each.
//
//
// Beware these child-counts when Loop subdivision supports N-sided faces in the cage
// - there will 2*(N-2) additional face-child-faces for each N-sided face
// - there will 2*(N-2)+1 additional face-child-edges for each N-sided face
// - there will 1 face-child-vertex for each N-sided face
// Can consider these reasonable estimates and grow as needed later -- but be clear
// about it if so.
//
int faceChildFaceCount = _parent->getNumFaces() * 4;
int faceChildEdgeCount = (int) _parent->_faceEdgeIndices.size();
int edgeChildEdgeCount = (int) _parent->_edgeVertIndices.size();
int faceChildVertCount = 0;
int edgeChildVertCount = _parent->getNumEdges();
int vertChildVertCount = _parent->getNumVertices();
//
// First initialize the count/offset vectors for the child-faces and child-edges of
// parent faces. For now we can use the parent's face-vert counts for the child-edges
// of faces, but we must use a local vector for the child-faces.
//
// This will be more necessary (and need adjustment) when N-sided faces are supported.
//
_localFaceChildFaceCountsAndOffsets.resize(_parent->getNumFaces() * 2, 4);
for (int i = 0; i < _parent->getNumFaces(); ++i) {
_localFaceChildFaceCountsAndOffsets[i*2 + 1] = 4 * i;
}
_faceChildFaceCountsAndOffsets = IndexArray(&_localFaceChildFaceCountsAndOffsets[0],
(int)_localFaceChildFaceCountsAndOffsets.size());
_faceChildEdgeCountsAndOffsets = _parent->shareFaceVertCountsAndOffsets();
//
// Given we will be ignoring initial values with uniform refinement and assigning all
// directly, initializing here is a waste...
//
Index initValue = 0;
_faceChildFaceIndices.resize(faceChildFaceCount, initValue);
_faceChildEdgeIndices.resize(faceChildEdgeCount, initValue);
_edgeChildEdgeIndices.resize(edgeChildEdgeCount, initValue);
_faceChildVertIndex.resize(faceChildVertCount, initValue);
_edgeChildVertIndex.resize(edgeChildVertCount, initValue);
_vertChildVertIndex.resize(vertChildVertCount, initValue);
}
//
// Methods to populate the face-vertex relation of the child Level:
// - child faces only originate from parent faces
//
void
TriRefinement::populateFaceVertexRelation() {
// Both face-vertex and face-edge share the face-vertex counts/offsets within a
// Level, so be sure not to re-initialize it if already done:
//
if (_child->_faceVertCountsAndOffsets.size() == 0) {
populateFaceVertexCountsAndOffsets();
}
_child->_faceVertIndices.resize(_child->getNumFaces() * 3);
populateFaceVerticesFromParentFaces();
}
void
TriRefinement::populateFaceVertexCountsAndOffsets() {
_child->_faceVertCountsAndOffsets.resize(_child->getNumFaces() * 2, 3);
for (int i = 0; i < _child->getNumFaces(); ++i) {
_child->_faceVertCountsAndOffsets[i*2 + 1] = i * 3;
}
}
void
TriRefinement::populateFaceVerticesFromParentFaces() {
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
ConstIndexArray pFaceVerts = _parent->getFaceVertices(pFace),
pFaceEdges = _parent->getFaceEdges(pFace),
pFaceChildren = getFaceChildFaces(pFace);
assert(pFaceVerts.size() == 3);
assert(pFaceChildren.size() == 4);
Index cVertsOfPEdges[3];
cVertsOfPEdges[0] = _edgeChildVertIndex[pFaceEdges[0]];
cVertsOfPEdges[1] = _edgeChildVertIndex[pFaceEdges[1]];
cVertsOfPEdges[2] = _edgeChildVertIndex[pFaceEdges[2]];
//
// For the child face at vertex I (where I is 0..2), the child vertex
// of vertex I becomes the I'th vertex of its child face. This matches
// the pattern for quads of irregular faces for Catmark.
//
// The orientation for the 4th "interior" face is unclear -- it begins
// with the child vertex of the 2nd edge of the triangle. According
// to the notes with the Hbr implementation "the ordering of vertices
// here is done to preserve parametric space as best we can."
//
if (IndexIsValid(pFaceChildren[0])) {
IndexArray cFaceVerts = _child->getFaceVertices(pFaceChildren[0]);
cFaceVerts[0] = _vertChildVertIndex[pFaceVerts[0]];
cFaceVerts[1] = cVertsOfPEdges[0];
cFaceVerts[2] = cVertsOfPEdges[2];
}
if (IndexIsValid(pFaceChildren[1])) {
IndexArray cFaceVerts = _child->getFaceVertices(pFaceChildren[1]);
cFaceVerts[0] = cVertsOfPEdges[0];
cFaceVerts[1] = _vertChildVertIndex[pFaceVerts[1]];
cFaceVerts[2] = cVertsOfPEdges[1];
}
if (IndexIsValid(pFaceChildren[2])) {
IndexArray cFaceVerts = _child->getFaceVertices(pFaceChildren[2]);
cFaceVerts[0] = cVertsOfPEdges[2];
cFaceVerts[1] = cVertsOfPEdges[1];
cFaceVerts[2] = _vertChildVertIndex[pFaceVerts[2]];
}
if (IndexIsValid(pFaceChildren[3])) {
IndexArray cFaceVerts = _child->getFaceVertices(pFaceChildren[3]);
cFaceVerts[0] = cVertsOfPEdges[1];
cFaceVerts[1] = cVertsOfPEdges[2];
cFaceVerts[2] = cVertsOfPEdges[0];
}
}
}
//
// Methods to populate the face-vertex relation of the child Level:
// - child faces only originate from parent faces
//
void
TriRefinement::populateFaceEdgeRelation() {
// Both face-vertex and face-edge share the face-vertex counts/offsets, so be sure
// not to re-initialize it if already done:
//
if (_child->_faceVertCountsAndOffsets.size() == 0) {
populateFaceVertexCountsAndOffsets();
}
_child->_faceEdgeIndices.resize(_child->getNumFaces() * 3);
populateFaceEdgesFromParentFaces();
}
void
TriRefinement::populateFaceEdgesFromParentFaces() {
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
ConstIndexArray pFaceVerts = _parent->getFaceVertices(pFace),
pFaceEdges = _parent->getFaceEdges(pFace),
pFaceChildFaces = getFaceChildFaces(pFace),
pFaceChildEdges = getFaceChildEdges(pFace);
assert(pFaceChildFaces.size() == 4);
assert(pFaceChildEdges.size() == 3);
Index pEdgeChildEdges[3][2];
for (int i = 0; i < 3; ++i) {
Index pEdge = pFaceEdges[i];
ConstIndexArray cEdges = getEdgeChildEdges(pEdge);
ConstIndexArray pEdgeVerts = _parent->getEdgeVertices(pEdge);
// Be careful to consider degenerate edge when orienting here:
bool edgeReversedWrtFace = (pEdgeVerts[0] != pEdgeVerts[1]) &&
(pFaceVerts[i] != pEdgeVerts[0]);
pEdgeChildEdges[i][0] = cEdges[edgeReversedWrtFace];
pEdgeChildEdges[i][1] = cEdges[!edgeReversedWrtFace];
}
if (IndexIsValid(pFaceChildFaces[0])) {
IndexArray cFaceEdges = _child->getFaceEdges(pFaceChildFaces[0]);
cFaceEdges[0] = pEdgeChildEdges[0][0];
cFaceEdges[1] = pFaceChildEdges[0];
cFaceEdges[2] = pEdgeChildEdges[2][1];
}
if (IndexIsValid(pFaceChildFaces[1])) {
IndexArray cFaceEdges = _child->getFaceEdges(pFaceChildFaces[1]);
cFaceEdges[0] = pEdgeChildEdges[0][1];
cFaceEdges[1] = pEdgeChildEdges[1][0];
cFaceEdges[2] = pFaceChildEdges[1];
}
if (IndexIsValid(pFaceChildFaces[2])) {
IndexArray cFaceEdges = _child->getFaceEdges(pFaceChildFaces[2]);
cFaceEdges[0] = pFaceChildEdges[2];
cFaceEdges[1] = pEdgeChildEdges[1][1];
cFaceEdges[2] = pEdgeChildEdges[2][0];
}
if (IndexIsValid(pFaceChildFaces[3])) {
IndexArray cFaceEdges = _child->getFaceEdges(pFaceChildFaces[3]);
cFaceEdges[0] = pFaceChildEdges[2];
cFaceEdges[1] = pFaceChildEdges[0];
cFaceEdges[2] = pFaceChildEdges[1];
}
}
}
//
// Methods to populate the edge-vertex relation of the child Level:
// - child edges originate from parent faces and edges
//
void
TriRefinement::populateEdgeVertexRelation() {
_child->_edgeVertIndices.resize(_child->getNumEdges() * 2);
populateEdgeVerticesFromParentFaces();
populateEdgeVerticesFromParentEdges();
}
void
TriRefinement::populateEdgeVerticesFromParentFaces() {
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
ConstIndexArray pFaceEdges = _parent->getFaceEdges(pFace),
pFaceChildEdges = getFaceChildEdges(pFace);
assert(pFaceEdges.size() == 3);
assert(pFaceChildEdges.size() == 3);
Index pEdgeChildVerts[3];
pEdgeChildVerts[0] = _edgeChildVertIndex[pFaceEdges[0]];
pEdgeChildVerts[1] = _edgeChildVertIndex[pFaceEdges[1]];
pEdgeChildVerts[2] = _edgeChildVertIndex[pFaceEdges[2]];
if (IndexIsValid(pFaceChildEdges[0])) {
IndexArray cEdgeVerts = _child->getEdgeVertices(pFaceChildEdges[0]);
cEdgeVerts[0] = pEdgeChildVerts[0];
cEdgeVerts[1] = pEdgeChildVerts[2];
}
if (IndexIsValid(pFaceChildEdges[1])) {
IndexArray cEdgeVerts = _child->getEdgeVertices(pFaceChildEdges[1]);
cEdgeVerts[0] = pEdgeChildVerts[1];
cEdgeVerts[1] = pEdgeChildVerts[0];
}
if (IndexIsValid(pFaceChildEdges[2])) {
IndexArray cEdgeVerts = _child->getEdgeVertices(pFaceChildEdges[2]);
cEdgeVerts[0] = pEdgeChildVerts[2];
cEdgeVerts[1] = pEdgeChildVerts[1];
}
}
}
void
TriRefinement::populateEdgeVerticesFromParentEdges() {
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
ConstIndexArray pEdgeVerts = _parent->getEdgeVertices(pEdge),
pEdgeChildEdges = getEdgeChildEdges(pEdge);
if (IndexIsValid(pEdgeChildEdges[0])) {
IndexArray cEdgeVerts = _child->getEdgeVertices(pEdgeChildEdges[0]);
cEdgeVerts[0] = _edgeChildVertIndex[pEdge];
cEdgeVerts[1] = _vertChildVertIndex[pEdgeVerts[0]];
}
if (IndexIsValid(pEdgeChildEdges[1])) {
IndexArray cEdgeVerts = _child->getEdgeVertices(pEdgeChildEdges[1]);
cEdgeVerts[0] = _edgeChildVertIndex[pEdge];
cEdgeVerts[1] = _vertChildVertIndex[pEdgeVerts[1]];
}
}
}
//
// Methods to populate the edge-face relation of the child Level:
// - child edges originate from parent faces and edges
// - sparse refinement poses challenges with allocation here
// - we need to update the counts/offsets as we populate
//
void
TriRefinement::populateEdgeFaceRelation() {
//
// This is essentially the same as the quad-split version except for the
// sizing estimates:
// - every child-edge within a face will have 2 incident faces
// - every child-edge from a edge may have N incident faces
// - use the parents edge-face count for this
//
int childEdgeFaceIndexSizeEstimate = (int)_faceChildEdgeIndices.size() * 2 +
(int)_parent->_edgeFaceIndices.size() * 2;
_child->_edgeFaceCountsAndOffsets.resize(_child->getNumEdges() * 2);
_child->_edgeFaceIndices.resize(childEdgeFaceIndexSizeEstimate);
_child->_edgeFaceLocalIndices.resize(childEdgeFaceIndexSizeEstimate);
// Update _maxEdgeFaces from the parent level before calling the
// populateEdgeFacesFromParent methods below, as these may further
// update _maxEdgeFaces.
_child->_maxEdgeFaces = _parent->_maxEdgeFaces;
populateEdgeFacesFromParentFaces();
populateEdgeFacesFromParentEdges();
// Revise the over-allocated estimate based on what is used (as indicated in the
// count/offset for the last vertex) and trim the index vector accordingly:
childEdgeFaceIndexSizeEstimate = _child->getNumEdgeFaces(_child->getNumEdges()-1) +
_child->getOffsetOfEdgeFaces(_child->getNumEdges()-1);
_child->_edgeFaceIndices.resize(childEdgeFaceIndexSizeEstimate);
_child->_edgeFaceLocalIndices.resize(childEdgeFaceIndexSizeEstimate);
}
void
TriRefinement::populateEdgeFacesFromParentFaces() {
for (Index pFace = 0; pFace < _parent->getNumFaces(); ++pFace) {
ConstIndexArray pFaceChildFaces = getFaceChildFaces(pFace),
pFaceChildEdges = getFaceChildEdges(pFace);
assert(pFaceChildFaces.size() == 4);
assert(pFaceChildEdges.size() == 3);
// Every child-edge of a face potentially shares the middle child face:
Index cFaceMiddle = pFaceChildFaces[3];
bool isFaceMiddleValid = IndexIsValid(cFaceMiddle);
for (int j = 0; j < pFaceChildEdges.size(); ++j) {
Index cEdge = pFaceChildEdges[j];
if (IndexIsValid(cEdge)) {
// Reserve enough edge-faces, populate and trim as needed:
_child->resizeEdgeFaces(cEdge, 2);
IndexArray cEdgeFaces = _child->getEdgeFaces(cEdge);
LocalIndexArray cEdgeInFace = _child->getEdgeFaceLocalIndices(cEdge);
int cEdgeFaceCount = 0;
if (IndexIsValid(pFaceChildFaces[j])) {
cEdgeFaces[cEdgeFaceCount] = pFaceChildFaces[j];
cEdgeInFace[cEdgeFaceCount] = (LocalIndex) ((j + 1) % 3);
cEdgeFaceCount++;
}
if (isFaceMiddleValid) {
cEdgeFaces[cEdgeFaceCount] = cFaceMiddle;
cEdgeInFace[cEdgeFaceCount] = (LocalIndex) ((j + 1) % 3);
cEdgeFaceCount++;
}
_child->trimEdgeFaces(cEdge, cEdgeFaceCount);
}
}
}
}
void
TriRefinement::populateEdgeFacesFromParentEdges() {
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
ConstIndexArray pEdgeChildEdges = getEdgeChildEdges(pEdge);
if (!IndexIsValid(pEdgeChildEdges[0]) && !IndexIsValid(pEdgeChildEdges[1])) continue;
ConstIndexArray pEdgeFaces = _parent->getEdgeFaces(pEdge);
ConstLocalIndexArray pEdgeInFace = _parent->getEdgeFaceLocalIndices(pEdge);
ConstIndexArray pEdgeVerts = _parent->getEdgeVertices(pEdge);
for (int j = 0; j < 2; ++j) {
Index cEdge = pEdgeChildEdges[j];
if (!IndexIsValid(cEdge)) continue;
//
// Reserve enough edge-faces, populate and trim as needed:
//
_child->resizeEdgeFaces(cEdge, pEdgeFaces.size());
IndexArray cEdgeFaces = _child->getEdgeFaces(cEdge);
LocalIndexArray cEdgeInFace = _child->getEdgeFaceLocalIndices(cEdge);
//
// Each parent face may contribute an incident child face:
//
// For each incident face and local-index, we immediately know
// the two child faces that are associated with the two child edges.
// We just need to identify how to pair them based on edge direction.
//
// Note also here, that we could identify the pairs of child faces
// once for the parent before dealing with each child edge (we do the
// "find edge in face search" twice here as a result). We will
// generally have 2 or 1 incident face to the parent edge so we
// can put the child-pairs on the stack.
//
// Here's a more promising alternative -- instead of iterating
// through the child edges to "pull" data from the parent, iterate
// through the parent edges' faces and apply valid child faces to
// the appropriate child edge. We should be able to use end-verts
// of the parent edge to get the corresponding child face for each,
// but we can't avoid a vert-in-face search and a subsequent parity
// test of the end-vert.
//
int cEdgeFaceCount = 0;
for (int i = 0; i < pEdgeFaces.size(); ++i) {
Index pFace = pEdgeFaces[i];
int edgeInFace = pEdgeInFace[i];
ConstIndexArray pFaceVerts = _parent->getFaceVertices(pFace),
pFaceChildren = getFaceChildFaces(pFace);
// Inspect either this child of the face or the next -- be careful
// to consider degenerate edge when orienting here:
int childOfEdge = (pEdgeVerts[0] == pEdgeVerts[1]) ? j :
(pFaceVerts[edgeInFace] != pEdgeVerts[j]);
int childInFace = edgeInFace + childOfEdge;
if (childInFace == pFaceVerts.size()) childInFace = 0;
if (IndexIsValid(pFaceChildren[childInFace])) {
cEdgeFaces[cEdgeFaceCount] = pFaceChildren[childInFace];
cEdgeInFace[cEdgeFaceCount] = (LocalIndex) edgeInFace;
cEdgeFaceCount++;
}
}
_child->trimEdgeFaces(cEdge, cEdgeFaceCount);
}
}
}
//
// Methods to populate the vertex-face relation of the child Level:
// - child vertices originate from parent faces, edges and vertices
// - sparse refinement poses challenges with allocation here:
// - we need to update the counts/offsets as we populate
// - note this imposes ordering constraints and inhibits concurrency
//
void
TriRefinement::populateVertexFaceRelation() {
//
// Unlike quad-splitting, we don't have to consider vertices originating from
// faces. We also have to consider 3 faces for every incident face for vertices
// originating from edges.
//
int childVertFaceIndexSizeEstimate = (int)_parent->_edgeFaceIndices.size() * 3
+ (int)_parent->_vertFaceIndices.size();
_child->_vertFaceCountsAndOffsets.resize(_child->getNumVertices() * 2);
_child->_vertFaceIndices.resize( childVertFaceIndexSizeEstimate);
_child->_vertFaceLocalIndices.resize( childVertFaceIndexSizeEstimate);
// Remember -- no vertices-from-faces to consider here (until N-gon support)
if (getFirstChildVertexFromVertices() == 0) {
populateVertexFacesFromParentVertices();
populateVertexFacesFromParentEdges();
} else {
populateVertexFacesFromParentEdges();
populateVertexFacesFromParentVertices();
}
// Revise the over-allocated estimate based on what is used (as indicated in the
// count/offset for the last vertex) and trim the index vectors accordingly:
childVertFaceIndexSizeEstimate = _child->getNumVertexFaces(_child->getNumVertices()-1) +
_child->getOffsetOfVertexFaces(_child->getNumVertices()-1);
_child->_vertFaceIndices.resize( childVertFaceIndexSizeEstimate);
_child->_vertFaceLocalIndices.resize(childVertFaceIndexSizeEstimate);
}
void
TriRefinement::populateVertexFacesFromParentEdges() {
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
Index cVert = _edgeChildVertIndex[pEdge];
if (!IndexIsValid(cVert)) continue;
ConstIndexArray pEdgeFaces = _parent->getEdgeFaces(pEdge);
ConstLocalIndexArray pEdgeInFace = _parent->getEdgeFaceLocalIndices(pEdge);
//
// Reserve enough vert-faces, populate and trim to the actual size:
//
_child->resizeVertexFaces(cVert, 2 * pEdgeFaces.size());
IndexArray cVertFaces = _child->getVertexFaces(cVert);
LocalIndexArray cVertInFace = _child->getVertexFaceLocalIndices(cVert);
int cVertFaceCount = 0;
for (int i = 0; i < pEdgeFaces.size(); ++i) {
Index pFace = pEdgeFaces[i];
int edgeInFace = pEdgeInFace[i];
//
// Identify the corresponding three child faces for this parent face and
// their orientation wrt the child vertex to which they are incident --
// since we have the desired ordering of child faces from the parent face,
// we don't care about the orientation of the parent edge.
//
LocalIndex leadingFace = (LocalIndex) ((edgeInFace + 1) % 3);
LocalIndex middleFace = (LocalIndex) 3;
LocalIndex trailingFace = (LocalIndex) edgeInFace;
LocalIndex leadingLocalIndex = (LocalIndex) edgeInFace;
LocalIndex middleLocalIndex = (LocalIndex) ((edgeInFace + 2) % 3);
LocalIndex trailingLocalIndex = (LocalIndex) ((edgeInFace + 1) % 3);
//
// Now simply assign those of the three child faces that are valid:
//
ConstIndexArray pFaceChildFaces = getFaceChildFaces(pFace);
assert(pFaceChildFaces.size() == 4);
Index cFace = pFaceChildFaces[leadingFace];
if (IndexIsValid(cFace)) {
cVertFaces[cVertFaceCount] = cFace;
cVertInFace[cVertFaceCount] = leadingLocalIndex;
cVertFaceCount++;
}
cFace = pFaceChildFaces[middleFace];
if (IndexIsValid(cFace)) {
cVertFaces[cVertFaceCount] = cFace;
cVertInFace[cVertFaceCount] = middleLocalIndex;
cVertFaceCount++;
}
cFace = pFaceChildFaces[trailingFace];
if (IndexIsValid(cFace)) {
cVertFaces[cVertFaceCount] = cFace;
cVertInFace[cVertFaceCount] = trailingLocalIndex;
cVertFaceCount++;
}
}
_child->trimVertexFaces(cVert, cVertFaceCount);
}
}
void
TriRefinement::populateVertexFacesFromParentVertices() {
for (Index pVert = 0; pVert < _parent->getNumVertices(); ++pVert) {
Index cVert = _vertChildVertIndex[pVert];
if (!IndexIsValid(cVert)) continue;
//
// Inspect the parent vert's faces:
//
ConstIndexArray pVertFaces = _parent->getVertexFaces(pVert);
ConstLocalIndexArray pVertInFace = _parent->getVertexFaceLocalIndices(pVert);
//
// Reserve enough vert-faces, populate and trim to the actual size:
//
_child->resizeVertexFaces(cVert, pVertFaces.size());
IndexArray cVertFaces = _child->getVertexFaces(cVert);
LocalIndexArray cVertInFace = _child->getVertexFaceLocalIndices(cVert);
int cVertFaceCount = 0;
for (int i = 0; i < pVertFaces.size(); ++i) {
Index pFace = pVertFaces[i];
LocalIndex pFaceChild = pVertInFace[i];
Index cFace = getFaceChildFaces(pFace)[pFaceChild];
if (IndexIsValid(cFace)) {
cVertFaces[cVertFaceCount] = cFace;
cVertInFace[cVertFaceCount] = pFaceChild;
cVertFaceCount++;
}
}
_child->trimVertexFaces(cVert, cVertFaceCount);
}
}
//
// Methods to populate the vertex-edge relation of the child Level:
// - child vertices originate from parent faces, edges and vertices
// - sparse refinement poses challenges with allocation here:
// - we need to update the counts/offsets as we populate
// - note this imposes ordering constraints and inhibits concurrency
//
void
TriRefinement::populateVertexEdgeRelation() {
//
// Notes on allocating/initializing the vertex-edge counts/offsets vector:
//
// Be aware of scheme-specific decisions here, e.g.:
// - no verts from parent faces for Loop
// - more interior edges and faces for verts from parent edges for Loop
// - no guaranteed "neighborhood" around Bilinear verts from verts
//
// If uniform subdivision, vert-edge count will be:
// - 2 + 2*N faces incident parent edge for verts from parent edges
// - same as parent vert for verts from parent verts
// If sparse subdivision, vert-edge count will be:
// - non-trivial function of child faces in parent face
// - 1 child face will always result in 2 child edges
// * 2 child faces can mean 3 or 4 child edges
// - 3 child faces will always result in 4 child edges
// - 1 or 2 + N faces incident parent edge for verts from parent edges
// - where the 1 or 2 is number of child edges of parent edge
// - any end vertex will require all N child faces (catmark)
// - same as parent vert for verts from parent verts (catmark)
//
int childVertEdgeIndexSizeEstimate = (int)_parent->_edgeFaceIndices.size() * 2 + _parent->getNumEdges() * 2
+ (int)_parent->_vertEdgeIndices.size();
_child->_vertEdgeCountsAndOffsets.resize(_child->getNumVertices() * 2);
_child->_vertEdgeIndices.resize( childVertEdgeIndexSizeEstimate);
_child->_vertEdgeLocalIndices.resize( childVertEdgeIndexSizeEstimate);
if (getFirstChildVertexFromVertices() == 0) {
populateVertexEdgesFromParentVertices();
populateVertexEdgesFromParentEdges();
} else {
populateVertexEdgesFromParentEdges();
populateVertexEdgesFromParentVertices();
}
// Revise the over-allocated estimate based on what is used (as indicated in the
// count/offset for the last vertex) and trim the index vectors accordingly:
childVertEdgeIndexSizeEstimate = _child->getNumVertexEdges(_child->getNumVertices()-1) +
_child->getOffsetOfVertexEdges(_child->getNumVertices()-1);
_child->_vertEdgeIndices.resize( childVertEdgeIndexSizeEstimate);
_child->_vertEdgeLocalIndices.resize(childVertEdgeIndexSizeEstimate);
}
void
TriRefinement::populateVertexEdgesFromParentEdges() {
for (Index pEdge = 0; pEdge < _parent->getNumEdges(); ++pEdge) {
Index cVert = _edgeChildVertIndex[pEdge];
if (!IndexIsValid(cVert)) continue;
//
// First inspect the parent edge -- its parent faces then its child edges:
//
ConstIndexArray pEdgeFaces = _parent->getEdgeFaces(pEdge);
ConstLocalIndexArray pEdgeInFace = _parent->getEdgeFaceLocalIndices(pEdge);
ConstIndexArray pEdgeVerts = _parent->getEdgeVertices(pEdge),
pEdgeChildEdges = getEdgeChildEdges(pEdge);
//
// Reserve enough vert-edges, populate and trim to the actual size:
//
_child->resizeVertexEdges(cVert, pEdgeFaces.size() + 2);
IndexArray cVertEdges = _child->getVertexEdges(cVert);
LocalIndexArray cVertInEdge = _child->getVertexEdgeLocalIndices(cVert);
//
// We need to order the incident edges around the vertex appropriately:
// - one child edge of the parent edge ("leading" in face 0)
// - two child edges interior to face 0
// - one other child edge of the parent edge ("trailing" in face 0)
// - child edges of all remaining faces
// Be careful to place the leading/trailing child edges of the parent edge
// correctly -- edges are not directed their orientation may vary. The
// interior child edges are appropriately oriented wrt their parent face.
//
// Also need to consider no faces at all, in which case we just want the
// child edges of the parent edge.
//
int cVertEdgeCount = 0;
// We only care about edge reversal in the first iteration -- in which
// the child edges of the parent edges are assigned. Other iterations
// only assign the child edges from the incident parent face:
bool pEdgeReversed = false;
Index cEdgeOfEdge0 = INDEX_INVALID,
cEdgeOfEdge1 = INDEX_INVALID;
for (int i = 0; i < pEdgeFaces.size(); ++i) {
Index pFace = pEdgeFaces[i];
int edgeInFace = pEdgeInFace[i];
ConstIndexArray pFaceChildEdges = getFaceChildEdges(pFace);
// Test the orientation of a non-degenerate edge in the first face:
if (i == 0) {
if (pEdgeVerts[0] != pEdgeVerts[1]) {
pEdgeReversed = (_parent->getFaceVertices(pFace)[edgeInFace] != pEdgeVerts[0]);
}
cEdgeOfEdge0 = pEdgeChildEdges[!pEdgeReversed];
cEdgeOfEdge1 = pEdgeChildEdges[pEdgeReversed];
}
//
// Identify the two interior and incident child edges within the face --
// bracketed by the child edges of the parent edge when dealing with the
// first face:
//
Index cEdgeOfFace0 = pFaceChildEdges[(edgeInFace + 1) % 3];
Index cEdgeOfFace1 = pFaceChildEdges[edgeInFace];
if ((i == 0) && IndexIsValid(cEdgeOfEdge0)) {
cVertEdges[cVertEdgeCount] = cEdgeOfEdge0;
cVertInEdge[cVertEdgeCount] = 0;
cVertEdgeCount++;
}
if (IndexIsValid(cEdgeOfFace0)) {
cVertEdges[cVertEdgeCount] = cEdgeOfFace0;
cVertInEdge[cVertEdgeCount] = 1;
cVertEdgeCount++;
}
if (IndexIsValid(cEdgeOfFace1)) {
cVertEdges[cVertEdgeCount] = cEdgeOfFace1;
cVertInEdge[cVertEdgeCount] = 0;
cVertEdgeCount++;
}
if ((i == 0) && IndexIsValid(cEdgeOfEdge1)) {
cVertEdges[cVertEdgeCount] = cEdgeOfEdge1;
cVertInEdge[cVertEdgeCount] = 0;
cVertEdgeCount++;
}
}
_child->trimVertexEdges(cVert, cVertEdgeCount);
}
}
void
TriRefinement::populateVertexEdgesFromParentVertices() {
for (Index pVert = 0; pVert < _parent->getNumVertices(); ++pVert) {
Index cVert = _vertChildVertIndex[pVert];
if (!IndexIsValid(cVert)) continue;
//
// Inspect the parent vert's edges first:
//
ConstIndexArray pVertEdges = _parent->getVertexEdges(pVert);
ConstLocalIndexArray pVertInEdge = _parent->getVertexEdgeLocalIndices(pVert);
//
// Reserve enough vert-edges, populate and trim to the actual size:
//
_child->resizeVertexEdges(cVert, pVertEdges.size());
IndexArray cVertEdges = _child->getVertexEdges(cVert);
LocalIndexArray cVertInEdge = _child->getVertexEdgeLocalIndices(cVert);
int cVertEdgeCount = 0;
for (int i = 0; i < pVertEdges.size(); ++i) {
Index cEdge = getEdgeChildEdges(pVertEdges[i])[pVertInEdge[i]];
if (IndexIsValid(cEdge)) {
cVertEdges[cVertEdgeCount] = cEdge;
cVertInEdge[cVertEdgeCount] = 1;
cVertEdgeCount++;
}
}
_child->trimVertexEdges(cVert, cVertEdgeCount);
}
}
//
// Methods to populate child-component indices for sparse selection:
//
// Need to find a better place for these anon helper methods now that they are required
// both in the base class and the two subclasses for quad- and tri-splitting...
//
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
TriRefinement::markSparseFaceChildren() {
assert(_parentFaceTag.size() > 0);
//
// For each parent face:
// All boundary edges will be adequately marked as a result of the pass over the
// edges above and boundary vertices marked by selection. So all that remains is to
// identify the child faces and interior child edges for a face requiring neighboring
// child faces.
// For each corner vertex selected, we need to mark the corresponding child face,
// the two interior child edges and shared child vertex in the middle.
//
for (Index pFace = 0; pFace < parent().getNumFaces(); ++pFace) {
//
// Mark all descending child components of a selected face. Otherwise inspect
// its incident vertices to see if anything neighboring has been selected --
// requiring partial refinement of this face.
//
// Remember that a selected face cannot be transitional, and that only a
// transitional face will be partially refined.
//
IndexArray fChildFaces = getFaceChildFaces(pFace);
IndexArray fChildEdges = getFaceChildEdges(pFace);
assert(fChildFaces.size() == 4);
assert(fChildEdges.size() == 3);
ConstIndexArray fVerts = parent().getFaceVertices(pFace);
SparseTag& pFaceTag = _parentFaceTag[pFace];
if (pFaceTag._selected) {
markSparseIndexSelected(fChildFaces[0]);
markSparseIndexSelected(fChildFaces[1]);
markSparseIndexSelected(fChildFaces[2]);
markSparseIndexSelected(fChildFaces[3]);
markSparseIndexSelected(fChildEdges[0]);
markSparseIndexSelected(fChildEdges[1]);
markSparseIndexSelected(fChildEdges[2]);
pFaceTag._transitional = 0;
} else {
int marked = _parentVertexTag[fVerts[0]]._selected
+ _parentVertexTag[fVerts[1]]._selected
+ _parentVertexTag[fVerts[2]]._selected;
if (marked) {
//
// If marked, see if we have any transitional edges, in which case we
// need to include the middle face:
//
ConstIndexArray fEdges = parent().getFaceEdges(pFace);
pFaceTag._transitional = (unsigned char)
((_parentEdgeTag[fEdges[0]]._transitional << 0) |
(_parentEdgeTag[fEdges[1]]._transitional << 1) |
(_parentEdgeTag[fEdges[2]]._transitional << 2));
// Now mark the child faces and their associated edges:
//
if (pFaceTag._transitional) {
markSparseIndexNeighbor(fChildFaces[3]);
markSparseIndexNeighbor(fChildEdges[0]);
markSparseIndexNeighbor(fChildEdges[1]);
markSparseIndexNeighbor(fChildEdges[2]);
}
if (_parentVertexTag[fVerts[0]]._selected) {
markSparseIndexNeighbor(fChildFaces[0]);
markSparseIndexNeighbor(fChildEdges[0]);
}
if (_parentVertexTag[fVerts[1]]._selected) {
markSparseIndexNeighbor(fChildFaces[1]);
markSparseIndexNeighbor(fChildEdges[1]);
}
if (_parentVertexTag[fVerts[2]]._selected) {
markSparseIndexNeighbor(fChildFaces[2]);
markSparseIndexNeighbor(fChildEdges[2]);
}
}
}
}
}
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,90 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_TRI_REFINEMENT_H
#define OPENSUBDIV3_VTR_TRI_REFINEMENT_H
#include "../version.h"
#include "../vtr/refinement.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
namespace internal {
//
// TriRefinement:
// A TriRefinement is a subclass of Refinement that splits all faces into tris.
// It provides the configuration of parent-to-child components and the population of
// all required topological relations in order to complete a valid Refinement.
//
class TriRefinement : public Refinement {
public:
TriRefinement(Level const & parent, Level & child, Sdc::Options const & options);
~TriRefinement();
protected:
//
// Virtual methods to complete the configuration of the parent-to-child mapping:
//
virtual void allocateParentChildIndices();
virtual void markSparseFaceChildren();
//
// Virtual methods to populate the six topological relations:
//
virtual void populateFaceVertexRelation();
virtual void populateFaceEdgeRelation();
virtual void populateEdgeVertexRelation();
virtual void populateEdgeFaceRelation();
virtual void populateVertexFaceRelation();
virtual void populateVertexEdgeRelation();
//
// Internal helper methods for populating the topology -- a few of these are
// identical to what is used for quad-splitting, so we may move them to the
// base class...
//
void populateFaceVertexCountsAndOffsets();
void populateFaceVerticesFromParentFaces();
void populateFaceEdgesFromParentFaces();
void populateEdgeVerticesFromParentFaces();
void populateEdgeVerticesFromParentEdges();
void populateEdgeFacesFromParentFaces();
void populateEdgeFacesFromParentEdges();
void populateVertexFacesFromParentEdges();
void populateVertexFacesFromParentVertices();
void populateVertexEdgesFromParentEdges();
void populateVertexEdgesFromParentVertices();
private:
//
// Unlike the quad-split, which can share some vectors with the parent Level since
// child components correspond to face-vertices, the tri-split must define its
// own local vectors to identify the children for each parent component -- to
// be referenced within the base class for more immediate/inline access:
//
IndexVector _localFaceChildFaceCountsAndOffsets;
};
} // end namespace internal
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_REFINEMENT_H */

View File

@@ -0,0 +1,75 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_VTR_TYPES_H
#define OPENSUBDIV3_VTR_TYPES_H
#include "../version.h"
#include "../vtr/array.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr {
//
// A few types (and constants) for use within Vtr and potentially by its
// clients (appropriately exported and retyped)
//
//
// Integer type and constants to index the vectors of components. Note that we
// can't use specific width integer types like uint32_t, etc. as use of stdint
// is not portable.
//
// The convention throughout the OpenSubdiv code is to use "int" in most places,
// with "unsigned int" being limited to a few cases (why?). So we continue that
// trend here and use "int" for topological indices (with -1 indicating "invalid")
// despite the fact that we lose half the range compared to using "uint" (with ~0
// as invalid).
//
typedef int Index;
static const Index INDEX_INVALID = -1;
inline bool IndexIsValid(Index index) { return (index != INDEX_INVALID); }
//
// Integer type and constants used to index one component within another. Ideally
// this is just 2 bits once refinement reduces faces to tris or quads -- and so
// could potentially be combined with an Index -- but we need something larger for
// the N-sided face.
//
typedef unsigned short LocalIndex;
// Declared as "int" since it's intended for more general use
static const int VALENCE_LIMIT = ((1 << 16) - 1); // std::numeric_limits<LocalIndex>::max()
//
// Collections of integer types in variable or fixed sized arrays. Note that the use
// of "vector" in the name indicates a class that wraps an std::vector (typically a
// member variable) which is fully resizable and owns its own storage, whereas "array"
// wraps a vtr::Array which uses a fixed block of pre-allocated memory.
//
typedef std::vector<Index> IndexVector;
typedef Array<Index> IndexArray;
typedef ConstArray<Index> ConstIndexArray;
typedef Array<LocalIndex> LocalIndexArray;
typedef ConstArray<LocalIndex> ConstLocalIndexArray;
} // end namespace Vtr
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_VTR_TYPES_H */