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,91 @@
#
# 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
bilinearPatchBuilder.cpp
catmarkPatchBuilder.cpp
error.cpp
loopPatchBuilder.cpp
patchBasis.cpp
patchBuilder.cpp
patchDescriptor.cpp
patchMap.cpp
patchTable.cpp
patchTableFactory.cpp
ptexIndices.cpp
stencilTable.cpp
stencilTableFactory.cpp
stencilBuilder.cpp
topologyDescriptor.cpp
topologyRefiner.cpp
topologyRefinerFactory.cpp
)
set(PRIVATE_HEADER_FILES
bilinearPatchBuilder.h
catmarkPatchBuilder.h
loopPatchBuilder.h
patchBasis.h
patchBuilder.h
sparseMatrix.h
stencilBuilder.h
)
set(PUBLIC_HEADER_FILES
error.h
patchDescriptor.h
patchParam.h
patchMap.h
patchTable.h
patchTableFactory.h
primvarRefiner.h
ptexIndices.h
stencilTable.h
stencilTableFactory.h
topologyDescriptor.h
topologyLevel.h
topologyRefiner.h
topologyRefinerFactory.h
types.h
)
set(DOXY_HEADER_FILES ${PUBLIC_HEADER_FILES})
#-------------------------------------------------------------------------------
if (NOT NO_LIB)
# Compile objs first for both the CPU and GPU libs -----
add_library(far_obj
OBJECT
${SOURCE_FILES}
${PRIVATE_HEADER_FILES}
${PUBLIC_HEADER_FILES}
)
set_target_properties(far_obj
PROPERTIES
FOLDER "opensubdiv"
)
endif()
#-------------------------------------------------------------------------------
osd_add_doxy_headers( "${DOXY_HEADER_FILES}" )
install(
FILES
${PUBLIC_HEADER_FILES}
DESTINATION
"${CMAKE_INCDIR_BASE}/far"
PERMISSIONS
OWNER_READ
GROUP_READ
WORLD_READ )
#-------------------------------------------------------------------------------

View File

@@ -0,0 +1,90 @@
//
// Copyright 2018 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/bilinearPatchBuilder.h"
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
using Vtr::internal::Level;
using Vtr::internal::FVarLevel;
using Vtr::internal::Refinement;
namespace Far {
namespace {
//
// The patch type associated with each basis for Bilinear -- quickly indexed
// from an array. The patch type here is essentially the quad form of each
// basis.
//
PatchDescriptor::Type patchTypeFromBasisArray[] = {
PatchDescriptor::NON_PATCH, // undefined
PatchDescriptor::QUADS, // regular
PatchDescriptor::GREGORY_BASIS, // Gregory
PatchDescriptor::QUADS, // linear
PatchDescriptor::NON_PATCH }; // Bezier -- for future use
};
BilinearPatchBuilder::BilinearPatchBuilder(
TopologyRefiner const& refiner, Options const& options) :
PatchBuilder(refiner, options) {
_regPatchType = patchTypeFromBasisArray[_options.regBasisType];
_irregPatchType = (_options.irregBasisType == BASIS_UNSPECIFIED)
? _regPatchType
: patchTypeFromBasisArray[_options.irregBasisType];
_nativePatchType = PatchDescriptor::QUADS;
_linearPatchType = PatchDescriptor::QUADS;
}
BilinearPatchBuilder::~BilinearPatchBuilder() {
}
PatchDescriptor::Type
BilinearPatchBuilder::patchTypeFromBasis(BasisType basis) const {
return patchTypeFromBasisArray[(int)basis];
}
template <typename REAL>
int
BilinearPatchBuilder::convertSourcePatch(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<REAL> & matrix) const {
assert("Conversion from Bilinear patches to other bases not yet supported" == 0);
// For suppressing warnings until implemented...
if (sourcePatch.GetNumSourcePoints() == 0) return -1;
if (patchType == PatchDescriptor::NON_PATCH) return -1;
if (matrix.GetNumRows() <= 0) return -1;
return -1;
}
int
BilinearPatchBuilder::convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const {
return convertSourcePatch(sourcePatch, patchType, matrix);
}
int
BilinearPatchBuilder::convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const {
return convertSourcePatch(sourcePatch, patchType, matrix);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,56 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_BILINEAR_PATCH_BUILDER_H
#define OPENSUBDIV3_FAR_BILINEAR_PATCH_BUILDER_H
#include "../version.h"
#include "../far/patchBuilder.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// BilinearPatchBuilder
//
// Declaration of PatchBuilder subclass supporting Sdc::SCHEME_BILINEAR.
// Required virtual methods are included, along with any customizations
// local to their implementation.
//
class BilinearPatchBuilder : public PatchBuilder {
public:
BilinearPatchBuilder(TopologyRefiner const& refiner, Options const& options);
virtual ~BilinearPatchBuilder();
protected:
virtual PatchDescriptor::Type patchTypeFromBasis(BasisType basis) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const;
private:
template <typename REAL>
int convertSourcePatch(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<REAL> & matrix) const;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_BILINEAR_PATCH_BUILDER_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_CATMARK_PATCH_BUILDER_H
#define OPENSUBDIV3_FAR_CATMARK_PATCH_BUILDER_H
#include "../version.h"
#include "../far/patchBuilder.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// CatmarkPatchBuilder
//
// Declaration of PatchBuilder subclass supporting Sdc::SCHEME_CATMARK.
// Required virtual methods are included, along with any customizations
// local to their implementation.
//
class CatmarkPatchBuilder : public PatchBuilder {
public:
CatmarkPatchBuilder(TopologyRefiner const& refiner, Options const& options);
virtual ~CatmarkPatchBuilder();
protected:
virtual PatchDescriptor::Type patchTypeFromBasis(BasisType basis) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const;
private:
typedef SparseMatrix<float> ConversionMatrix;
template <typename REAL>
int convertSourcePatch(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<REAL> & matrix) const;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_CATMARK_PATCH_BUILDER_H */

View File

@@ -0,0 +1,90 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/error.h"
#include <cassert>
#include <cstdarg>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Statics for the publicly assignable callbacks and the methods to
// assign them (disable static assignment warnings when doing so):
//
static ErrorCallbackFunc errorFunc = 0;
static WarningCallbackFunc warningFunc = 0;
#ifdef __INTEL_COMPILER
#pragma warning disable 1711
#endif
void SetErrorCallback(ErrorCallbackFunc func) {
errorFunc = func;
}
void SetWarningCallback(WarningCallbackFunc func) {
warningFunc = func;
}
#ifdef __INTEL_COMPILER
#pragma warning enable 1711
#endif
//
// The default error and warning callbacks eventually belong in the
// internal namespace:
//
void Error(ErrorType err, const char *format, ...) {
static char const * errorTypeLabel[] = {
"No Error",
"Fatal Error",
"Coding Error (internal)",
"Coding Error",
"Error"
};
assert(err!=FAR_NO_ERROR);
char message[10240];
va_list argptr;
va_start(argptr, format);
vsnprintf(message, 10240, format, argptr);
va_end(argptr);
if (errorFunc) {
errorFunc(err, message);
} else {
printf("%s: %s\n", errorTypeLabel[err], message);
}
}
void Warning(const char *format, ...) {
char message[10240];
va_list argptr;
va_start(argptr, format);
vsnprintf(message, 10240, format, argptr);
va_end(argptr);
if (warningFunc) {
warningFunc(message);
} else {
fprintf(stdout, "Warning: %s\n", message);
}
}
} // end namespace
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,78 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_ERROR_H
#define OPENSUBDIV3_FAR_ERROR_H
#include "../version.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
typedef enum {
FAR_NO_ERROR, ///< No error. Move along.
FAR_FATAL_ERROR, ///< Issue a fatal error and end the program.
FAR_INTERNAL_CODING_ERROR, ///< Issue an internal programming error, but continue execution.
FAR_CODING_ERROR, ///< Issue a generic programming error, but continue execution.
FAR_RUNTIME_ERROR ///< Issue a generic runtime error, but continue execution.
} ErrorType;
/// \brief The error callback function type (default is "printf")
typedef void (*ErrorCallbackFunc)(ErrorType err, const char *message);
/// \brief Sets the error callback function (default is "printf")
///
/// \note This function is not thread-safe !
///
/// @param func function pointer to the callback function
///
void SetErrorCallback(ErrorCallbackFunc func);
/// \brief The warning callback function type (default is "printf")
typedef void (*WarningCallbackFunc)(const char *message);
/// \brief Sets the warning callback function (default is "printf")
///
/// \note This function is not thread-safe !
///
/// @param func function pointer to the callback function
///
void SetWarningCallback(WarningCallbackFunc func);
//
// The following are intended for internal use only (and will eventually
// be moved within namespace internal)
//
/// \brief Sends an OSD error with a message (internal use only)
///
/// @param err the error type
///
/// @param format the format of the message (followed by arguments)
///
void Error(ErrorType err, const char *format, ...);
/// \brief Sends an OSD warning message (internal use only)
///
/// @param format the format of the message (followed by arguments)
///
void Warning(const char *format, ...);
} // end namespace
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_FAR_ERROR_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_LOOP_PATCH_BUILDER_H
#define OPENSUBDIV3_FAR_LOOP_PATCH_BUILDER_H
#include "../version.h"
#include "../far/patchBuilder.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// LoopPatchBuilder
//
// Declaration of PatchBuilder subclass supporting Sdc::SCHEME_LOOP.
// Required virtual methods are included, along with any customizations
// local to their implementation.
//
class LoopPatchBuilder : public PatchBuilder {
public:
LoopPatchBuilder(TopologyRefiner const& refiner, Options const& options);
virtual ~LoopPatchBuilder();
protected:
virtual PatchDescriptor::Type patchTypeFromBasis(BasisType basis) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const;
private:
template <typename REAL>
int convertSourcePatch(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<REAL> & matrix) const;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_LOOP_PATCH_BUILDER_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,93 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_PATCH_BASIS_H
#define OPENSUBDIV3_FAR_PATCH_BASIS_H
#include "../version.h"
#include "../far/patchParam.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
//
// XXXX barfowl: These functions are being kept internal while more complete
// underlying support for all patch types is being worked out. The set of
// bases supported here is actually larger than PatchDescriptor::Type -- with
// Bezier available for internal use. A new and more complete set of types
// is warranted (without the non-patch types associated with PatchDescriptor)
// along with an interface to query properties associated with them.
//
// Note that with the high-level functions here that operate on all patch
// types, it is not strictly necessary to expose the low-level methods in
// currant usage.
//
//
// Low-level basis evaluation (normalized, unscaled) for quad patch types:
//
template <typename REAL>
int EvalBasisLinear(REAL s, REAL t,
REAL wP[4], REAL wDs[4] = 0, REAL wDt[4] = 0, REAL wDss[4] = 0, REAL wDst[4] = 0, REAL wDtt[4] = 0);
template <typename REAL>
int EvalBasisBezier(REAL s, REAL t,
REAL wP[16], REAL wDs[16] = 0, REAL wDt[16] = 0, REAL wDss[16] = 0, REAL wDst[16] = 0, REAL wDtt[16] = 0);
template <typename REAL>
int EvalBasisBSpline(REAL s, REAL t,
REAL wP[16], REAL wDs[16] = 0, REAL wDt[16] = 0, REAL wDss[16] = 0, REAL wDst[16] = 0, REAL wDtt[16] = 0);
template <typename REAL>
int EvalBasisGregory(REAL s, REAL t,
REAL wP[20], REAL wDs[20] = 0, REAL wDt[20] = 0, REAL wDss[20] = 0, REAL wDst[20] = 0, REAL wDtt[20] = 0);
//
// Low-level basis evaluation (normalized, unscaled) for triangular patch types:
//
template <typename REAL>
int EvalBasisLinearTri(REAL s, REAL t,
REAL wP[3], REAL wDs[3] = 0, REAL wDt[3] = 0, REAL wDss[3] = 0, REAL wDst[3] = 0, REAL wDtt[3] = 0);
template <typename REAL>
int EvalBasisBezierTri(REAL s, REAL t,
REAL wP[15], REAL wDs[15] = 0, REAL wDt[15] = 0, REAL wDss[15] = 0, REAL wDst[15] = 0, REAL wDtt[15] = 0);
template <typename REAL>
int EvalBasisBoxSplineTri(REAL s, REAL t,
REAL wP[12], REAL wDs[12] = 0, REAL wDt[12] = 0, REAL wDss[12] = 0, REAL wDst[12] = 0, REAL wDtt[12] = 0);
template <typename REAL>
int EvalBasisGregoryTri(REAL s, REAL t,
REAL wP[18], REAL wDs[18] = 0, REAL wDt[18] = 0, REAL wDss[18] = 0, REAL wDst[18] = 0, REAL wDtt[18] = 0);
//
// High-level basis evaluation for all types using PatchParam:
//
template <typename REAL>
int EvaluatePatchBasisNormalized(int patchType, PatchParam const & param, REAL s, REAL t,
REAL wP[], REAL wDs[] = 0, REAL wDt[] = 0, REAL wDss[] = 0, REAL wDst[] = 0, REAL wDtt[] = 0);
template <typename REAL>
int EvaluatePatchBasis(int patchType, PatchParam const & param, REAL s, REAL t,
REAL wP[], REAL wDs[] = 0, REAL wDt[] = 0, REAL wDss[] = 0, REAL wDst[] = 0, REAL wDtt[] = 0);
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_BASIS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,315 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_PATCH_BUILDER_H
#define OPENSUBDIV3_FAR_PATCH_BUILDER_H
#include "../version.h"
#include "../sdc/types.h"
#include "../far/types.h"
#include "../far/topologyRefiner.h"
#include "../far/patchDescriptor.h"
#include "../far/patchParam.h"
#include "../far/ptexIndices.h"
#include "../far/sparseMatrix.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// SourcePatch
//
// This is a local utility class that captures the full local topology of an
// arbitrarily irregular patch, i.e. a patch which may have one or all corners
// irregular. Given the topology at each corner the entire collection of
// points involved is identified and oriented consistently.
//
// Note (barfowl):
// This was originally a class internal to PatchBuilder, but there is some
// redundancy between it and the Level::VSpan used more publicly to identify
// irregular corner topology. Replacing VSpan with SourcePatch is now under
// consideration, and doing so will impact its public/private interface (which
// was left public to give PatchBuilder access).
// A simpler constructor to initialize an instance given a set of Corners
// would also be preferable if made more public (i.e. public for use within
// the library, not exported to clients) -- eliminating the need for the
// explicit initialization and required call to the Finalize() method that
// the PatchBuilder currently performs internally.
//
class SourcePatch {
public:
struct Corner {
Corner() { std::memset((void*) this, 0, sizeof(Corner)); }
LocalIndex _numFaces; // valence of corner vertex
LocalIndex _patchFace; // location of patch within incident faces
unsigned short _boundary : 1;
unsigned short _sharp : 1;
unsigned short _dart : 1;
// For internal bookkeeping -- consider hiding or moving elsewhere
unsigned short _sharesWithPrev : 1;
unsigned short _sharesWithNext : 1;
unsigned short _val2Interior : 1;
unsigned short _val2Adjacent : 1;
};
public:
SourcePatch() { std::memset((void*) this, 0, sizeof(SourcePatch)); }
~SourcePatch() { }
// To be called after all Corners have been initialized (hope to
// replace this with alternative constructor at some point)
void Finalize(int size3or4);
int GetNumSourcePoints() const { return _numSourcePoints; }
int GetMaxValence() const { return _maxValence; }
int GetMaxRingSize() const { return _maxRingSize; }
int GetCornerRingSize(int corner) const { return _ringSizes[corner]; }
int GetCornerRingPoints(int corner, int points[]) const;
// public/private access needs to be reviewed when/if used more publicly
//private:
public:
// The SourcePatch is fully defined by its Corner members
Corner _corners[4];
int _numCorners;
// Additional members (derived from Corners) to help assemble corner rings:
int _numSourcePoints;
int _maxValence;
int _maxRingSize;
int _ringSizes[4];
int _localRingSizes[4];
int _localRingOffsets[4];
};
//
// PatchBuilder
//
// This is the main class to assist the identification of limit surface
// patches from faces in a TopologyRefiner for assembly into other, larger
// datatypes.
//
// The PatchBuilder takes a const reference to a refiner and supports
// arbitrarily refined hierarchies, i.e. it is not restricted to uniform or
// adaptive refinement strategies and does not include any logic relating
// to the origin of the hierarchy. It can associate a patch with any face
// in the hierarchy (subject to a few minimum requirements) -- leaving the
// decision as to which faces/patches are appropriate to its client.
//
// PatchBuilder is an abstract base class with a subclass derived to support
// each subdivision scheme -- as such, construction relies on a factory
// method to create an instance of the appropriate subclass. Only two pure
// virtual methods are required (other than the required destructor):
//
// - determine the patch type for a subdivision scheme given a more
// general basis specification (e.g. Bezier, Gregory, Linear, etc)
//
// - convert the vertices in the subdivision hierarchy into points of a
// specified patch type, using computations specific to that scheme
//
// The base class handles the more general topological analysis that
// determines the nature of a patch associated with each face -- providing
// both queries to the client, along with more involved methods to extract
// or convert data associated with the patches. There is no concrete "Patch"
// class to which all clients would be required to conform. The queries and
// data returned are provided for clients to assemble into patches or other
// aggregates as they see fit.
//
// This is intended as an internal/private class for use within the library
// for now -- possibly to be exported for use by clients when/if its
// interface is appropriate and stable.
//
class PatchBuilder {
public:
//
// A PatchBuilder is constructed given a patch "basis" rather than a
// "type" to use with the subdivision scheme involved. The relevant
// explicit patch types will be determined from the basis and scheme:
//
enum BasisType {
BASIS_UNSPECIFIED,
BASIS_REGULAR,
BASIS_GREGORY,
BASIS_LINEAR,
BASIS_BEZIER // to be supported in future
};
//
// Required Options specify a patch basis to use for both regular and
// irregular patches -- sparing the client the need to repeatedly
// specify these for each face considered. Other options are included
// to support legacy approximations:
//
struct Options {
Options() : regBasisType(BASIS_UNSPECIFIED),
irregBasisType(BASIS_UNSPECIFIED),
fillMissingBoundaryPoints(false),
approxInfSharpWithSmooth(false),
approxSmoothCornerWithSharp(false) { }
BasisType regBasisType;
BasisType irregBasisType;
bool fillMissingBoundaryPoints;
bool approxInfSharpWithSmooth;
bool approxSmoothCornerWithSharp;
};
public:
//
// Public construction (via factory method) and destruction:
//
static PatchBuilder* Create(TopologyRefiner const& refiner,
Options const& options);
virtual ~PatchBuilder();
//
// High-level queries related to the subdivision scheme of the refiner, the
// patch types associated with it and those chosen to represent its faces:
//
int GetRegularFaceSize() const { return _schemeRegFaceSize; }
BasisType GetRegularBasisType() const { return _options.regBasisType; }
BasisType GetIrregularBasisType() const { return _options.irregBasisType; }
PatchDescriptor::Type GetRegularPatchType() const { return _regPatchType; }
PatchDescriptor::Type GetIrregularPatchType() const { return _irregPatchType; }
PatchDescriptor::Type GetNativePatchType() const { return _nativePatchType; }
PatchDescriptor::Type GetLinearPatchType() const { return _linearPatchType; }
//
// Face-level queries to determine presence of patches:
//
bool IsFaceAPatch(int level, Index face) const;
bool IsFaceALeaf(int level, Index face) const;
//
// Patch-level topological queries:
//
bool IsPatchRegular(int level, Index face, int fvc = -1) const;
int GetRegularPatchBoundaryMask(int level, Index face, int fvc = -1) const;
void GetIrregularPatchCornerSpans(int level, Index face,
Vtr::internal::Level::VSpan cornerSpans[4], int fvc = -1) const;
bool DoesFaceVaryingPatchMatch(int level, Index face, int fvc) const {
return _refiner.getLevel(level).doesFaceFVarTopologyMatch(face, fvc);
}
//
// Patch-level control point retrieval and methods for converting source
// points to a set of local points in a different basis
//
int GetRegularPatchPoints(int level, Index face,
int regBoundaryMask, // compute internally when < 0
Index patchPoints[],
int fvc = -1) const;
template <typename REAL>
int GetIrregularPatchConversionMatrix(int level, Index face,
Vtr::internal::Level::VSpan const cornerSpans[],
SparseMatrix<REAL> & matrix) const;
int GetIrregularPatchSourcePoints(int level, Index face,
Vtr::internal::Level::VSpan const cornerSpans[],
Index sourcePoints[],
int fvc = -1) const;
//
// Queries related to "single-crease" patches -- currently a subset of
// regular interior patches:
//
struct SingleCreaseInfo {
int creaseEdgeInFace;
float creaseSharpness;
};
bool IsRegularSingleCreasePatch(int level, Index face,
SingleCreaseInfo & info) const;
//
// Computing the PatchParam -- note the regrettable dependency on
// PtexIndices but PatchParam is essentially tied to it indefinitely.
// Better to pass it in than have the PatchBuilder build its own
// PtexIndices.
//
// Consider creating a PatchParamFactory which can manage the PtexIndices
// along with this method. It will then be able to generate additional
// data to accelerate these computations.
//
PatchParam ComputePatchParam(int level, Index face,
PtexIndices const& ptexIndices, bool isRegular = true,
int boundaryMask = 0, bool computeTransitionMask = false) const;
protected:
PatchBuilder(TopologyRefiner const& refiner, Options const& options);
// Internal methods supporting topology queries:
int getRegularFacePoints(int level, Index face,
Index patchPoints[], int fvc) const;
int getQuadRegularPatchPoints(int level, Index face,
int regBoundaryMask, Index patchPoints[], int fvc) const;
int getTriRegularPatchPoints(int level, Index face,
int regBoundaryMask, Index patchPoints[], int fvc) const;
// Internal methods using the SourcePatch:
int assembleIrregularSourcePatch(int level, Index face,
Vtr::internal::Level::VSpan const cornerSpans[],
SourcePatch & sourcePatch) const;
int gatherIrregularSourcePoints(int level, Index face,
Vtr::internal::Level::VSpan const cornerSpans[],
SourcePatch & sourcePatch,
Index patchPoints[], int fvc) const;
protected:
//
// Virtual methods to be provided by subclass for each scheme:
//
virtual PatchDescriptor::Type patchTypeFromBasis(BasisType basis) const = 0;
// Note overloading of the conversion for SparseMatrix<REAL>:
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<float> & matrix) const = 0;
virtual int convertToPatchType(SourcePatch const & sourcePatch,
PatchDescriptor::Type patchType,
SparseMatrix<double> & matrix) const = 0;
protected:
TopologyRefiner const& _refiner;
Options const _options;
Sdc::SchemeType _schemeType;
int _schemeRegFaceSize;
bool _schemeIsLinear;
PatchDescriptor::Type _regPatchType;
PatchDescriptor::Type _irregPatchType;
PatchDescriptor::Type _nativePatchType;
PatchDescriptor::Type _linearPatchType;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_BUILDER_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.
//
#include "../far/patchDescriptor.h"
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Lists of valid patch Descriptors for each subdivision scheme
//
// Historically this has only included the non-linear patch types, though
// it is possible for linear patches to represent irregularities for both
// Catmark and Loop, and the Bilinear scheme is adaptively refined into
// linear quads (e.g. a pentagon becoming five quads).
//
ConstPatchDescriptorArray
PatchDescriptor::GetAdaptivePatchDescriptors(Sdc::SchemeType type) {
static PatchDescriptor _loopDescriptors[] = {
PatchDescriptor(LOOP),
PatchDescriptor(GREGORY_TRIANGLE),
};
static PatchDescriptor _catmarkDescriptors[] = {
PatchDescriptor(REGULAR),
PatchDescriptor(GREGORY),
PatchDescriptor(GREGORY_BOUNDARY),
PatchDescriptor(GREGORY_BASIS),
};
switch (type) {
case Sdc::SCHEME_BILINEAR :
return ConstPatchDescriptorArray(0, 0);
case Sdc::SCHEME_CATMARK :
return ConstPatchDescriptorArray(_catmarkDescriptors,
(int)(sizeof(_catmarkDescriptors)/sizeof(PatchDescriptor)));
case Sdc::SCHEME_LOOP :
return ConstPatchDescriptorArray(_loopDescriptors,
(int)(sizeof(_loopDescriptors)/sizeof(PatchDescriptor)));
default:
assert(0);
}
return ConstPatchDescriptorArray(0, 0);;
}
void
PatchDescriptor::print() const {
static char const * types[13] = {
"NON_PATCH", "POINTS", "LINES", "QUADS", "TRIANGLES", "LOOP",
"REGULAR", "GREGORY", "GREGORY_BOUNDARY", "GREGORY_BASIS",
"GREGORY_TRIANGLE"};
printf(" type %s\n",
types[_type]);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,176 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_PATCH_DESCRIPTOR_H
#define OPENSUBDIV3_FAR_PATCH_DESCRIPTOR_H
#include "../version.h"
#include "../far/types.h"
#include "../sdc/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief Describes the type of a patch
///
/// Uniquely identifies all the different types of patches
///
class PatchDescriptor {
public:
enum Type {
NON_PATCH = 0, ///< undefined
POINTS, ///< points (useful for cage drawing)
LINES, ///< lines (useful for cage drawing)
QUADS, ///< 4-sided quadrilateral (bilinear)
TRIANGLES, ///< 3-sided triangle
LOOP, ///< regular triangular patch for the Loop scheme
REGULAR, ///< regular B-Spline patch for the Catmark scheme
GREGORY,
GREGORY_BOUNDARY,
GREGORY_BASIS,
GREGORY_TRIANGLE
};
public:
/// \brief Default constructor.
PatchDescriptor() :
_type(NON_PATCH) { }
/// \brief Constructor
PatchDescriptor(int type) :
_type(type) { }
/// \brief Copy Constructor
PatchDescriptor( PatchDescriptor const & d ) :
_type(d.GetType()) { }
/// \brief Assignment operator
PatchDescriptor & operator=( PatchDescriptor const & d ) {
_type = d.GetType();
return *this;
}
/// \brief Returns the type of the patch
Type GetType() const {
return (Type)_type;
}
/// \brief Returns true if the type is an adaptive (non-linear) patch
static inline bool IsAdaptive(Type type) {
return type > TRIANGLES;
}
/// \brief Returns true if the type is an adaptive patch
bool IsAdaptive() const {
return IsAdaptive( this->GetType() );
}
/// \brief Returns the number of control vertices expected for a patch of the
/// type described
static inline short GetNumControlVertices( Type t );
/// \brief Deprecated @see PatchDescriptor#GetNumControlVertices
static inline short GetNumFVarControlVertices( Type t );
/// \brief Returns the number of control vertices expected for a patch of the
/// type described
short GetNumControlVertices() const {
return GetNumControlVertices( this->GetType() );
}
/// \brief Deprecated @see PatchDescriptor#GetNumControlVertices
short GetNumFVarControlVertices() const {
return GetNumFVarControlVertices( this->GetType() );
}
/// \brief Number of control vertices of Regular Patches in table.
static short GetRegularPatchSize() { return 16; }
/// \brief Number of control vertices of Gregory (and Gregory Boundary) Patches in table.
static short GetGregoryPatchSize() { return 4; }
/// \brief Number of control vertices of Gregory patch basis (20)
static short GetGregoryBasisPatchSize() { return 20; }
/// \brief Returns a vector of all the legal patch descriptors for the
/// given adaptive subdivision scheme
static Vtr::ConstArray<PatchDescriptor> GetAdaptivePatchDescriptors(Sdc::SchemeType type);
/// \brief Allows ordering of patches by type
inline bool operator < ( PatchDescriptor const other ) const;
/// \brief True if the descriptors are identical
inline bool operator == ( PatchDescriptor const other ) const;
// debug helper
void print() const;
private:
unsigned int _type;
};
typedef Vtr::ConstArray<PatchDescriptor> ConstPatchDescriptorArray;
// Returns the number of control vertices expected for a patch of this type
inline short
PatchDescriptor::GetNumControlVertices( Type type ) {
switch (type) {
case REGULAR : return GetRegularPatchSize();
case LOOP : return 12;
case QUADS : return 4;
case GREGORY :
case GREGORY_BOUNDARY : return GetGregoryPatchSize();
case GREGORY_BASIS : return GetGregoryBasisPatchSize();
case GREGORY_TRIANGLE : return 18;
case TRIANGLES : return 3;
case LINES : return 2;
case POINTS : return 1;
default : return -1;
}
}
// Returns the number of face-varying control vertices expected for a patch of this type
inline short
PatchDescriptor::GetNumFVarControlVertices( Type type ) {
return PatchDescriptor::GetNumControlVertices(type);
}
// Allows ordering of patches by type
inline bool
PatchDescriptor::operator < ( PatchDescriptor const other ) const {
return (_type < other._type);
}
// True if the descriptors are identical
inline bool
PatchDescriptor::operator == ( PatchDescriptor const other ) const {
return _type == other._type;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_DESCRIPTOR_H */

View File

@@ -0,0 +1,190 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/patchMap.h"
#include <algorithm>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Inline quadtree assembly methods used by the constructor:
//
// sets all the children to point to the patch of given index
inline void
PatchMap::QuadNode::SetChildren(int index) {
for (int i=0; i<4; ++i) {
children[i].isSet = true;
children[i].isLeaf = true;
children[i].index = index;
}
}
// sets the child in "quadrant" to point to the node or patch of the given index
inline void
PatchMap::QuadNode::SetChild(int quadrant, int index, bool isLeaf) {
assert(!children[quadrant].isSet);
children[quadrant].isSet = true;
children[quadrant].isLeaf = isLeaf;
children[quadrant].index = index;
}
inline void
PatchMap::assignRootNode(QuadNode * node, int index) {
// Assign the given index to all children of the node (all leaves)
node->SetChildren(index);
}
inline PatchMap::QuadNode *
PatchMap::assignLeafOrChildNode(QuadNode * node, bool isLeaf, int quadrant, int index) {
// Assign the node given if it is a leaf node, otherwise traverse
// the node -- creating/assigning a new child node if needed
if (isLeaf) {
node->SetChild(quadrant, index, true);
return node;
}
if (node->children[quadrant].isSet) {
return &_quadtree[node->children[quadrant].index];
} else {
int newChildNodeIndex = (int)_quadtree.size();
_quadtree.push_back(QuadNode());
node->SetChild(quadrant, newChildNodeIndex, false);
return &_quadtree[newChildNodeIndex];
}
}
//
// Constructor and initialization methods for the handles and quadtree:
//
PatchMap::PatchMap(PatchTable const & patchTable) :
_minPatchFace(-1), _maxPatchFace(-1), _maxDepth(0) {
_patchesAreTriangular =
patchTable.GetVaryingPatchDescriptor().GetNumControlVertices() == 3;
if (patchTable.GetNumPatchesTotal() > 0) {
initializeHandles(patchTable);
initializeQuadtree(patchTable);
}
}
void
PatchMap::initializeHandles(PatchTable const & patchTable) {
//
// Populate the vector of patch Handles. Keep track of the min and max
// face indices to allocate resources accordingly and limit queries:
//
_minPatchFace = (int) patchTable.GetPatchParamTable()[0].GetFaceId();
_maxPatchFace = _minPatchFace;
int numArrays = (int) patchTable.GetNumPatchArrays();
int numPatches = (int) patchTable.GetNumPatchesTotal();
_handles.resize(numPatches);
for (int pArray = 0, handleIndex = 0; pArray < numArrays; ++pArray) {
ConstPatchParamArray params = patchTable.GetPatchParams(pArray);
int patchSize = patchTable.GetPatchArrayDescriptor(pArray).GetNumControlVertices();
for (Index j=0; j < patchTable.GetNumPatches(pArray); ++j, ++handleIndex) {
Handle & h = _handles[handleIndex];
h.arrayIndex = pArray;
h.patchIndex = handleIndex;
h.vertIndex = j * patchSize;
int patchFaceId = params[j].GetFaceId();
_minPatchFace = std::min(_minPatchFace, patchFaceId);
_maxPatchFace = std::max(_maxPatchFace, patchFaceId);
}
}
}
void
PatchMap::initializeQuadtree(PatchTable const & patchTable) {
//
// Reserve quadtree nodes for the worst case and prune later. Set the
// initial size to accomodate the root node of each patch face:
//
int nPatchFaces = (_maxPatchFace - _minPatchFace) + 1;
int nHandles = (int)_handles.size();
_quadtree.reserve(nPatchFaces + nHandles);
_quadtree.resize(nPatchFaces);
PatchParamTable const & params = patchTable.GetPatchParamTable();
for (int handle = 0; handle < nHandles; ++handle) {
PatchParam const & param = params[handle];
int depth = param.GetDepth();
int rootDepth = param.NonQuadRoot();
_maxDepth = std::max(_maxDepth, depth);
QuadNode * node = &_quadtree[param.GetFaceId() - _minPatchFace];
if (depth == rootDepth) {
assignRootNode(node, handle);
continue;
}
if (!_patchesAreTriangular) {
// Use the UV bits of the PatchParam directly for quad patches:
int u = param.GetU();
int v = param.GetV();
for (int j = rootDepth + 1; j <= depth; ++j) {
int uBit = (u >> (depth - j)) & 1;
int vBit = (v >> (depth - j)) & 1;
int quadrant = (vBit << 1) | uBit;
node = assignLeafOrChildNode(node, (j == depth), quadrant, handle);
}
} else {
// Use an interior UV point of triangles to identify quadrants:
double u = 0.25;
double v = 0.25;
param.UnnormalizeTriangle(u, v);
double median = 0.5;
bool triRotated = false;
for (int j = rootDepth + 1; j <= depth; ++j, median *= 0.5) {
int quadrant = transformUVToTriQuadrant(median, u, v, triRotated);
node = assignLeafOrChildNode(node, (j == depth), quadrant, handle);
}
}
}
// Swap the Node vector with a copy to reduce worst case memory allocation:
QuadTree tmpTree = _quadtree;
_quadtree.swap(tmpTree);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,223 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_PATCH_MAP_H
#define OPENSUBDIV3_FAR_PATCH_MAP_H
#include "../version.h"
#include "../far/patchTable.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief An quadtree-based map connecting coarse faces to their sub-patches
///
/// PatchTable::PatchArrays contain lists of patches that represent the limit
/// surface of a mesh, sorted by their topological type. These arrays break the
/// connection between coarse faces and their sub-patches.
///
/// The PatchMap provides a quad-tree based lookup structure that, given a singular
/// parametric location, can efficiently return a handle to the sub-patch that
/// contains this location.
///
class PatchMap {
public:
typedef PatchTable::PatchHandle Handle;
/// \brief Constructor
///
/// @param patchTable A valid PatchTable
///
PatchMap( PatchTable const & patchTable );
/// \brief Returns a handle to the sub-patch of the face at the given (u,v).
/// Note that the patch face ID corresponds to potentially quadrangulated
/// face indices and not the base face indices (see Far::PtexIndices for more
/// details).
///
/// @param patchFaceId The index of the patch (Ptex) face
///
/// @param u Local u parameter
///
/// @param v Local v parameter
///
/// @return A patch handle or 0 if the face is not supported (index
/// out of bounds) or is tagged as a hole
///
Handle const * FindPatch( int patchFaceId, double u, double v ) const;
private:
void initializeHandles(PatchTable const & patchTable);
void initializeQuadtree(PatchTable const & patchTable);
private:
// Quadtree node with 4 children, tree is just a vector of nodes
struct QuadNode {
QuadNode() { std::memset(this, 0, sizeof(QuadNode)); }
struct Child {
unsigned int isSet : 1; // true if the child has been set
unsigned int isLeaf : 1; // true if the child is a QuadNode
unsigned int index : 30; // child index (either QuadNode or Handle)
};
// sets all the children to point to the patch of given index
void SetChildren(int index);
// sets the child in "quadrant" to point to the node or patch of the given index
void SetChild(int quadrant, int index, bool isLeaf);
Child children[4];
};
typedef std::vector<QuadNode> QuadTree;
// Internal methods supporting quadtree construction and queries
void assignRootNode(QuadNode * node, int index);
QuadNode * assignLeafOrChildNode(QuadNode * node, bool isLeaf, int quad, int index);
template <class T>
static int transformUVToQuadQuadrant(T const & median, T & u, T & v);
template <class T>
static int transformUVToTriQuadrant(T const & median, T & u, T & v, bool & rotated);
private:
bool _patchesAreTriangular; // tri and quad assembly and search requirements differ
int _minPatchFace; // minimum patch face index supported by the map
int _maxPatchFace; // maximum patch face index supported by the map
int _maxDepth; // maximum depth of a patch in the tree
std::vector<Handle> _handles; // all the patches in the PatchTable
std::vector<QuadNode> _quadtree; // quadtree nodes
};
//
// Given a median value for both U and V, these methods transform a (u,v) pair
// into the quadrant that contains them and returns the quadrant index.
//
// Quadrant indexing for tri and quad patches -- consistent with PatchParam's
// usage of UV bits:
//
// (0,1) o-----o-----o (1,1) (0,1) o (1,0) o-----o-----o (0,0)
// | | | |\ \ 1 |\ 0 |
// | 2 | 3 | | \ \ | \ |
// | | | | 2 \ \| 3 \|
// o-----o-----o o-----o o-----o
// | | | |\ 3 |\ \ 2 |
// | 0 | 1 | | \ | \ \ |
// | | | | 0 \| 1 \ \|
// (0,0) o-----o-----o (1,0) (0,0) o-----o-----o (1,0) o (0,1)
//
// The triangular case also takes and returns/affects the rotation of the
// quadrant being searched and identified (quadrant 3 imparts a rotation).
//
template <class T>
inline int
PatchMap::transformUVToQuadQuadrant(T const & median, T & u, T & v) {
int uHalf = (u >= median);
if (uHalf) u -= median;
int vHalf = (v >= median);
if (vHalf) v -= median;
return (vHalf << 1) | uHalf;
}
template <class T>
int inline
PatchMap::transformUVToTriQuadrant(T const & median, T & u, T & v, bool & rotated) {
if (!rotated) {
if (u >= median) {
u -= median;
return 1;
}
if (v >= median) {
v -= median;
return 2;
}
if ((u + v) >= median) {
rotated = true;
return 3;
}
return 0;
} else {
if (u < median) {
v -= median;
return 1;
}
if (v < median) {
u -= median;
return 2;
}
u -= median;
v -= median;
if ((u + v) < median) {
rotated = false;
return 3;
}
return 0;
}
}
/// Returns a handle to the sub-patch of the face at the given (u,v).
inline PatchMap::Handle const *
PatchMap::FindPatch( int faceid, double u, double v ) const {
//
// Reject patch faces not supported by this map, or those corresponding
// to holes or otherwise unassigned (the root node for a patch will
// have all or no quadrants set):
//
if ((faceid < _minPatchFace) || (faceid > _maxPatchFace)) return 0;
QuadNode const * node = &_quadtree[faceid - _minPatchFace];
if (!node->children[0].isSet) return 0;
//
// Search the tree for the sub-patch containing the given (u,v)
//
assert( (u>=0.0) && (u<=1.0) && (v>=0.0) && (v<=1.0) );
double median = 0.5;
bool triRotated = false;
for (int depth = 0; depth <= _maxDepth; ++depth, median *= 0.5) {
int quadrant = _patchesAreTriangular
? transformUVToTriQuadrant(median, u, v, triRotated)
: transformUVToQuadQuadrant(median, u, v);
// holes should have been rejected at the root node of the face
assert(node->children[quadrant].isSet);
if (node->children[quadrant].isLeaf) {
return &_handles[node->children[quadrant].index];
} else {
node = &_quadtree[node->children[quadrant].index];
}
}
assert(0);
return 0;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_PARAM */

View File

@@ -0,0 +1,315 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_PATCH_PARAM_H
#define OPENSUBDIV3_FAR_PATCH_PARAM_H
#include "../version.h"
#include "../far/types.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief Patch parameterization
///
/// Topological refinement splits coarse mesh faces into refined faces.
///
/// This patch parameterzation describes the relationship between one
/// of these refined faces and its corresponding coarse face. It is used
/// both for refined faces that are represented as full limit surface
/// parametric patches as well as for refined faces represented as simple
/// triangles or quads. This parameterization is needed to interpolate
/// primvar data across a refined face.
///
/// The U,V and refinement level parameters describe the scale and offset
/// needed to map a location on the patch between levels of refinement.
/// The encoding of these values exploits the quad-tree organization of
/// the faces produced by subdivision. We encode the U,V origin of the
/// patch using two 10-bit integer values and the refinement level as
/// a 4-bit integer. This is sufficient to represent up through 10 levels
/// of refinement.
///
/// Special consideration must be given to the refined faces resulting from
/// irregular coarse faces. We adopt a convention similar to Ptex texture
/// mapping and define the parameterization for these faces in terms of the
/// regular faces resulting from the first topological splitting of the
/// irregular coarse face.
///
/// When computing the basis functions needed to evaluate the limit surface
/// parametric patch representing a refined face, we also need to know which
/// edges of the patch are interpolated boundaries. These edges are encoded
/// as a boundary bitmask identifying the boundary edges of the patch in
/// sequential order starting from the first vertex of the refined face.
///
/// A sparse topological refinement (like feature adaptive refinement) can
/// produce refined faces that are adjacent to faces at the next level of
/// subdivision. We identify these transitional edges with a transition
/// bitmask using the same encoding as the boundary bitmask.
///
/// For triangular subdivision schemes we specify the parameterization using
/// a similar method. Alternate triangles at a given level of refinement
/// are parameterized from their opposite corners and encoded as occupying
/// the opposite diagonal of the quad-tree hierarchy. The third barycentric
/// coordinate is dependent on and can be derived from the other two
/// coordinates. This encoding also takes inspiration from the Ptex
/// texture mapping specification.
///
/// Bitfield layout :
///
/// Field0 | Bits | Content
/// -----------|:----:|------------------------------------------------------
/// faceId | 28 | the faceId of the patch
/// transition | 4 | transition edge mask encoding
///
/// Field1 | Bits | Content
/// -----------|:----:|------------------------------------------------------
/// level | 4 | the subdivision level of the patch
/// nonquad | 1 | whether patch is refined from a non-quad face
/// regular | 1 | whether patch is regular
/// unused | 1 | unused
/// boundary | 5 | boundary edge mask encoding
/// v | 10 | log2 value of u parameter at first patch corner
/// u | 10 | log2 value of v parameter at first patch corner
///
/// Note : the bitfield is not expanded in the struct due to differences in how
/// GPU & CPU compilers pack bit-fields and endian-ness.
///
/*!
\verbatim
Quad Patch Parameterization
(0,1) (1,1)
+-------+-------+---------------+
| | | |
| L2 | L2 | |
|0,3 |1,3 | |
+-------+-------+ L1 |
| | | |
| L2 | L2 | |
|0,2 |1,2 |1,1 |
+-------+-------+---------------+
| | |
| | |
| | |
| L1 | L1 |
| | |
| | |
|0,0 |1,0 |
+---------------+---------------+
(0,0) (1,0)
\endverbatim
*/
/*!
\verbatim
Triangle Patch Parameterization
(0,1) (1,1) (0,1,0)
+-------+-------+---------------+ +
| \ | \ | \ | | \
|L2 \ |L2 \ | \ | | \
|0,3 \ |1,3 \ | \ | | L2 \
+-------+-------+ \ | +-------+
| \ | \ | L1 \ | | \ L2 | \
|L2 \ |L2 \ | \ | | \ | \
|0,2 \ |1,2 \ |1,1 \ | | L2 \ | L2 \
+-------+-------+---------------+ +-------+-------+
| \ | \ | | \ | \
| \ | \ | | \ | \
| \ | \ | | \ L1 | \
| \ | \ | | \ | \
| L1 \ | L1 \ | | L1 \ | L1 \
| \ | \ | | \ | \
|0,0 \ |1,0 \ | | \ | \
+---------------+---------------+ +---------------+---------------+
(0,0) (1,0) (0,0,1) (1,0,0)
\endverbatim
*/
struct PatchParam {
/// \brief Sets the values of the bit fields
///
/// @param faceid face index
///
/// @param u value of the u parameter for the first corner of the face
/// @param v value of the v parameter for the first corner of the face
///
/// @param depth subdivision level of the patch
/// @param nonquad true if the root face is not a quad
///
/// @param boundary 5-bits identifying boundary edges (and verts for tris)
/// @param transition 4-bits identifying transition edges
///
/// @param regular whether the patch is regular
///
void Set(Index faceid, short u, short v,
unsigned short depth, bool nonquad,
unsigned short boundary, unsigned short transition,
bool regular = false);
/// \brief Resets everything to 0
void Clear() { field0 = field1 = 0; }
/// \brief Returns the faceid
Index GetFaceId() const { return Index(unpack(field0,28,0)); }
/// \brief Returns the log2 value of the u parameter at
/// the first corner of the patch
unsigned short GetU() const { return (unsigned short)unpack(field1,10,22); }
/// \brief Returns the log2 value of the v parameter at
/// the first corner of the patch
unsigned short GetV() const { return (unsigned short)unpack(field1,10,12); }
/// \brief Returns the transition edge encoding for the patch.
unsigned short GetTransition() const { return (unsigned short)unpack(field0,4,28); }
/// \brief Returns the boundary edge encoding for the patch.
unsigned short GetBoundary() const { return (unsigned short)unpack(field1,5,7); }
/// \brief True if the parent base face is a non-quad
bool NonQuadRoot() const { return (unpack(field1,1,4) != 0); }
/// \brief Returns the level of subdivision of the patch
unsigned short GetDepth() const { return (unsigned short)unpack(field1,4,0); }
/// \brief Returns the fraction of unit parametric space covered by this face.
float GetParamFraction() const;
/// \brief A (u,v) pair in the fraction of parametric space covered by this
/// face is mapped into a normalized parametric space.
///
/// @param u u parameter
/// @param v v parameter
///
template <typename REAL>
void Normalize( REAL & u, REAL & v ) const;
template <typename REAL>
void NormalizeTriangle( REAL & u, REAL & v ) const;
/// \brief A (u,v) pair in a normalized parametric space is mapped back into the
/// fraction of parametric space covered by this face.
///
/// @param u u parameter
/// @param v v parameter
///
template <typename REAL>
void Unnormalize( REAL & u, REAL & v ) const;
template <typename REAL>
void UnnormalizeTriangle( REAL & u, REAL & v ) const;
/// \brief Returns if a triangular patch is parametrically rotated 180 degrees
bool IsTriangleRotated() const;
/// \brief Returns whether the patch is regular
bool IsRegular() const { return (unpack(field1,1,5) != 0); }
unsigned int field0:32;
unsigned int field1:32;
private:
unsigned int pack(unsigned int value, int width, int offset) const {
return (unsigned int)((value & ((1<<width)-1)) << offset);
}
unsigned int unpack(unsigned int value, int width, int offset) const {
return (unsigned int)((value >> offset) & ((1<<width)-1));
}
};
typedef std::vector<PatchParam> PatchParamTable;
typedef Vtr::Array<PatchParam> PatchParamArray;
typedef Vtr::ConstArray<PatchParam> ConstPatchParamArray;
inline void
PatchParam::Set(Index faceid, short u, short v,
unsigned short depth, bool nonquad,
unsigned short boundary, unsigned short transition,
bool regular) {
field0 = pack(faceid, 28, 0) |
pack(transition, 4, 28);
field1 = pack(u, 10, 22) |
pack(v, 10, 12) |
pack(boundary, 5, 7) |
pack(regular, 1, 5) |
pack(nonquad, 1, 4) |
pack(depth, 4, 0);
}
inline float
PatchParam::GetParamFraction( ) const {
return 1.0f / (float)(1 << (GetDepth() - NonQuadRoot()));
}
template <typename REAL>
inline void
PatchParam::Normalize( REAL & u, REAL & v ) const {
REAL fracInv = (REAL)(1.0f / GetParamFraction());
u = u * fracInv - (REAL)GetU();
v = v * fracInv - (REAL)GetV();
}
template <typename REAL>
inline void
PatchParam::Unnormalize( REAL & u, REAL & v ) const {
REAL frac = (REAL)GetParamFraction();
u = (u + (REAL)GetU()) * frac;
v = (v + (REAL)GetV()) * frac;
}
inline bool
PatchParam::IsTriangleRotated() const {
return (GetU() + GetV()) >= (1 << GetDepth());
}
template <typename REAL>
inline void
PatchParam::NormalizeTriangle( REAL & u, REAL & v ) const {
if (IsTriangleRotated()) {
REAL fracInv = (REAL)(1.0f / GetParamFraction());
int depthFactor = 1 << GetDepth();
u = (REAL)(depthFactor - GetU()) - (u * fracInv);
v = (REAL)(depthFactor - GetV()) - (v * fracInv);
} else {
Normalize(u, v);
}
}
template <typename REAL>
inline void
PatchParam::UnnormalizeTriangle( REAL & u, REAL & v ) const {
if (IsTriangleRotated()) {
REAL frac = GetParamFraction();
int depthFactor = 1 << GetDepth();
u = ((REAL)(depthFactor - GetU()) - u) * frac;
v = ((REAL)(depthFactor - GetV()) - v) * frac;
} else {
Unnormalize(u, v);
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_PARAM */

View File

@@ -0,0 +1,637 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/patchTable.h"
#include "../far/patchBasis.h"
#include <algorithm>
#include <cstring>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
PatchTable::PatchTable(int maxvalence) :
_maxValence(maxvalence),
_localPointStencils(),
_localPointVaryingStencils(),
_varyingDesc(Far::PatchDescriptor::QUADS),
_isUniformLinear(false),
_vertexPrecisionIsDouble(false),
_varyingPrecisionIsDouble(false),
_faceVaryingPrecisionIsDouble(false) {
}
// Copy constructor
// XXXX manuelk we need to eliminate this constructor (C++11 smart pointers)
PatchTable::PatchTable(PatchTable const & src) :
_maxValence(src._maxValence),
_numPtexFaces(src._numPtexFaces),
_patchArrays(src._patchArrays),
_patchVerts(src._patchVerts),
_paramTable(src._paramTable),
_quadOffsetsTable(src._quadOffsetsTable),
_vertexValenceTable(src._vertexValenceTable),
_localPointStencils(src._localPointStencils),
_localPointVaryingStencils(src._localPointVaryingStencils),
_varyingDesc(src._varyingDesc),
_fvarChannels(src._fvarChannels),
_sharpnessIndices(src._sharpnessIndices),
_sharpnessValues(src._sharpnessValues),
_isUniformLinear(src._isUniformLinear),
_vertexPrecisionIsDouble(src._vertexPrecisionIsDouble),
_varyingPrecisionIsDouble(src._varyingPrecisionIsDouble),
_faceVaryingPrecisionIsDouble(src._faceVaryingPrecisionIsDouble) {
if (src._localPointStencils) {
if (src._vertexPrecisionIsDouble) {
_localPointStencils.Set(
new StencilTableReal<double>(*src._localPointStencils.Get<double>()));
} else {
_localPointStencils.Set(
new StencilTableReal<float>(*src._localPointStencils.Get<float>()));
}
}
if (src._localPointVaryingStencils) {
if (src._varyingPrecisionIsDouble) {
_localPointVaryingStencils.Set(
new StencilTableReal<double>(*src._localPointVaryingStencils.Get<double>()));
} else {
_localPointVaryingStencils.Set(
new StencilTableReal<float>(*src._localPointVaryingStencils.Get<float>()));
}
}
if (! src._localPointFaceVaryingStencils.empty()) {
_localPointFaceVaryingStencils.resize(src._localPointFaceVaryingStencils.size());
for (int fvc=0; fvc<(int)_localPointFaceVaryingStencils.size(); ++fvc) {
if (src._localPointFaceVaryingStencils[fvc]) {
if (src._faceVaryingPrecisionIsDouble) {
_localPointFaceVaryingStencils[fvc].Set(new StencilTableReal<double>(
*src._localPointFaceVaryingStencils[fvc].Get<double>()));
} else {
_localPointFaceVaryingStencils[fvc].Set(new StencilTableReal<float>(
*src._localPointFaceVaryingStencils[fvc].Get<float>()));
}
}
}
}
}
PatchTable::~PatchTable() {
if (_vertexPrecisionIsDouble) {
delete _localPointStencils.Get<double>();
} else {
delete _localPointStencils.Get<float>();
}
if (_varyingPrecisionIsDouble) {
delete _localPointVaryingStencils.Get<double>();
} else {
delete _localPointVaryingStencils.Get<float>();
}
for (int fvc=0; fvc<(int)_localPointFaceVaryingStencils.size(); ++fvc) {
if (_faceVaryingPrecisionIsDouble) {
delete _localPointFaceVaryingStencils[fvc].Get<double>();
} else {
delete _localPointFaceVaryingStencils[fvc].Get<float>();
}
}
}
//
// PatchArrays
//
// debug helper
void
PatchTable::PatchArray::print() const {
desc.print();
printf(" numPatches=%d vertIndex=%d patchIndex=%d "
"quadOffsetIndex=%d\n", numPatches, vertIndex, patchIndex,
quadOffsetIndex);
}
inline PatchTable::PatchArray &
PatchTable::getPatchArray(Index arrayIndex) {
assert(arrayIndex<(Index)GetNumPatchArrays());
return _patchArrays[arrayIndex];
}
inline PatchTable::PatchArray const &
PatchTable::getPatchArray(Index arrayIndex) const {
assert(arrayIndex<(Index)GetNumPatchArrays());
return _patchArrays[arrayIndex];
}
void
PatchTable::reservePatchArrays(int numPatchArrays) {
_patchArrays.reserve(numPatchArrays);
}
void
PatchTable::allocateVaryingVertices(
PatchDescriptor desc, int numPatches) {
_varyingDesc = desc;
_varyingVerts.resize(numPatches*desc.GetNumControlVertices());
}
inline PatchTable::FVarPatchChannel &
PatchTable::getFVarPatchChannel(int channel) {
assert(channel>=0 && channel<(int)_fvarChannels.size());
return _fvarChannels[channel];
}
inline PatchTable::FVarPatchChannel const &
PatchTable::getFVarPatchChannel(int channel) const {
assert(channel>=0 && channel<(int)_fvarChannels.size());
return _fvarChannels[channel];
}
void
PatchTable::allocateFVarPatchChannels(int numChannels) {
_fvarChannels.resize(numChannels);
}
void
PatchTable::allocateFVarPatchChannelValues(
PatchDescriptor regDesc, PatchDescriptor irregDesc,
int numPatches, int channel) {
FVarPatchChannel & c = getFVarPatchChannel(channel);
c.regDesc = regDesc;
c.irregDesc = irregDesc;
c.stride = std::max(regDesc.GetNumControlVertices(),
irregDesc.GetNumControlVertices());
c.patchValues.resize(numPatches * c.stride);
c.patchParam.resize(numPatches);
}
void
PatchTable::setFVarPatchChannelLinearInterpolation(
Sdc::Options::FVarLinearInterpolation interpolation, int channel) {
FVarPatchChannel & c = getFVarPatchChannel(channel);
c.interpolation = interpolation;
}
//
// PatchTable
//
inline int
getPatchSize(PatchDescriptor desc) {
return desc.GetNumControlVertices();
}
void
PatchTable::pushPatchArray(PatchDescriptor desc, int npatches,
Index * vidx, Index * pidx, Index * qoidx) {
if (npatches>0) {
_patchArrays.push_back(PatchArray(
desc, npatches, *vidx, *pidx, qoidx ? *qoidx : 0));
int nverts = getPatchSize(desc);
*vidx += npatches * nverts;
*pidx += npatches;
if (qoidx) {
*qoidx += (desc.GetType() == PatchDescriptor::GREGORY) ?
npatches*nverts : 0;
}
}
}
int
PatchTable::getPatchIndex(int arrayIndex, int patchIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
assert(patchIndex<pa.numPatches);
return pa.patchIndex + patchIndex;
}
Index *
PatchTable::getSharpnessIndices(int arrayIndex) {
return &_sharpnessIndices[getPatchArray(arrayIndex).patchIndex];
}
float *
PatchTable::getSharpnessValues(int arrayIndex) {
return &_sharpnessValues[getPatchArray(arrayIndex).patchIndex];
}
PatchDescriptor
PatchTable::GetPatchDescriptor(PatchHandle const & handle) const {
return getPatchArray(handle.arrayIndex).desc;
}
PatchDescriptor
PatchTable::GetPatchArrayDescriptor(int arrayIndex) const {
return getPatchArray(arrayIndex).desc;
}
int
PatchTable::GetNumPatchArrays() const {
return (int)_patchArrays.size();
}
int
PatchTable::GetNumPatches(int arrayIndex) const {
return getPatchArray(arrayIndex).numPatches;
}
int
PatchTable::GetNumPatchesTotal() const {
// there is one PatchParam record for each patch in the mesh
return (int)_paramTable.size();
}
int
PatchTable::GetNumControlVertices(int arrayIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
return pa.numPatches * getPatchSize(pa.desc);
}
Index
PatchTable::findPatchArray(PatchDescriptor desc) {
for (int i=0; i<(int)_patchArrays.size(); ++i) {
if (_patchArrays[i].desc==desc)
return i;
}
return Vtr::INDEX_INVALID;
}
IndexArray
PatchTable::getPatchArrayVertices(int arrayIndex) {
PatchArray const & pa = getPatchArray(arrayIndex);
int size = getPatchSize(pa.desc);
assert(pa.vertIndex<(Index)_patchVerts.size());
return IndexArray(&_patchVerts[pa.vertIndex], pa.numPatches * size);
}
ConstIndexArray
PatchTable::GetPatchArrayVertices(int arrayIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
int size = getPatchSize(pa.desc);
assert(pa.vertIndex<(Index)_patchVerts.size());
return ConstIndexArray(&_patchVerts[pa.vertIndex], pa.numPatches * size);
}
ConstIndexArray
PatchTable::GetPatchVertices(PatchHandle const & handle) const {
PatchArray const & pa = getPatchArray(handle.arrayIndex);
Index vert = pa.vertIndex + handle.vertIndex;
return ConstIndexArray(&_patchVerts[vert], getPatchSize(pa.desc));
}
ConstIndexArray
PatchTable::GetPatchVertices(int arrayIndex, int patchIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
int size = getPatchSize(pa.desc);
assert((pa.vertIndex + patchIndex*size)<(Index)_patchVerts.size());
return ConstIndexArray(&_patchVerts[pa.vertIndex + patchIndex*size], size);
}
PatchParam
PatchTable::GetPatchParam(PatchHandle const & handle) const {
assert(handle.patchIndex < (Index)_paramTable.size());
return _paramTable[handle.patchIndex];
}
PatchParam
PatchTable::GetPatchParam(int arrayIndex, int patchIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
assert((pa.patchIndex + patchIndex) < (int)_paramTable.size());
return _paramTable[pa.patchIndex + patchIndex];
}
PatchParamArray
PatchTable::getPatchParams(int arrayIndex) {
PatchArray const & pa = getPatchArray(arrayIndex);
return PatchParamArray(&_paramTable[pa.patchIndex], pa.numPatches);
}
ConstPatchParamArray const
PatchTable::GetPatchParams(int arrayIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
return ConstPatchParamArray(&_paramTable[pa.patchIndex], pa.numPatches);
}
float
PatchTable::GetSingleCreasePatchSharpnessValue(PatchHandle const & handle) const {
assert((handle.patchIndex) < (int)_sharpnessIndices.size());
Index index = _sharpnessIndices[handle.patchIndex];
if (index == Vtr::INDEX_INVALID) {
return 0.0f;
}
assert(index < (Index)_sharpnessValues.size());
return _sharpnessValues[index];
}
float
PatchTable::GetSingleCreasePatchSharpnessValue(int arrayIndex, int patchIndex) const {
PatchArray const & pa = getPatchArray(arrayIndex);
assert((pa.patchIndex + patchIndex) < (int)_sharpnessIndices.size());
Index index = _sharpnessIndices[pa.patchIndex + patchIndex];
if (index == Vtr::INDEX_INVALID) {
return 0.0f;
}
assert(index < (Index)_sharpnessValues.size());
return _sharpnessValues[index];
}
int
PatchTable::GetNumLocalPoints() const {
if (!_localPointStencils) return 0;
return _vertexPrecisionIsDouble
? _localPointStencils.Get<double>()->GetNumStencils()
: _localPointStencils.Get<float>()->GetNumStencils();
}
int
PatchTable::GetNumLocalPointsVarying() const {
if (!_localPointVaryingStencils) return 0;
return _varyingPrecisionIsDouble
? _localPointVaryingStencils.Get<double>()->GetNumStencils()
: _localPointVaryingStencils.Get<float>()->GetNumStencils();
}
int
PatchTable::GetNumLocalPointsFaceVarying(int channel) const {
if (channel>=0 && channel<(int)_localPointFaceVaryingStencils.size()) {
if (!_localPointFaceVaryingStencils[channel]) return 0;
return _faceVaryingPrecisionIsDouble
? _localPointFaceVaryingStencils[channel].Get<double>()->GetNumStencils()
: _localPointFaceVaryingStencils[channel].Get<float>()->GetNumStencils();
}
return 0;
}
PatchTable::ConstQuadOffsetsArray
PatchTable::GetPatchQuadOffsets(PatchHandle const & handle) const {
PatchArray const & pa = getPatchArray(handle.arrayIndex);
return Vtr::ConstArray<unsigned int>(&_quadOffsetsTable[pa.quadOffsetIndex + handle.vertIndex], 4);
}
bool
PatchTable::IsFeatureAdaptive() const {
return !_isUniformLinear;
}
PatchDescriptor
PatchTable::GetVaryingPatchDescriptor() const {
return _varyingDesc;
}
ConstIndexArray
PatchTable::GetPatchVaryingVertices(PatchHandle const & handle) const {
if (_varyingVerts.empty()) {
return ConstIndexArray();
}
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
Index start = handle.patchIndex * numVaryingCVs;
return ConstIndexArray(&_varyingVerts[start], numVaryingCVs);
}
ConstIndexArray
PatchTable::GetPatchVaryingVertices(int array, int patch) const {
if (_varyingVerts.empty()) {
return ConstIndexArray();
}
PatchArray const & pa = getPatchArray(array);
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
Index start = (pa.patchIndex + patch) * numVaryingCVs;
return ConstIndexArray(&_varyingVerts[start], numVaryingCVs);
}
ConstIndexArray
PatchTable::GetPatchArrayVaryingVertices(int array) const {
if (_varyingVerts.empty()) {
return ConstIndexArray();
}
PatchArray const & pa = getPatchArray(array);
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
Index start = pa.patchIndex * numVaryingCVs;
Index count = pa.numPatches * numVaryingCVs;
return ConstIndexArray(&_varyingVerts[start], count);
}
ConstIndexArray
PatchTable::GetVaryingVertices() const {
if (_varyingVerts.empty()) {
return ConstIndexArray();
}
return ConstIndexArray(&_varyingVerts[0], (int)_varyingVerts.size());
}
IndexArray
PatchTable::getPatchArrayVaryingVertices(int arrayIndex) {
PatchArray const & pa = getPatchArray(arrayIndex);
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
Index start = pa.patchIndex * numVaryingCVs;
return IndexArray(&_varyingVerts[start], pa.numPatches * numVaryingCVs);
}
void
PatchTable::populateVaryingVertices() {
// In order to support evaluation of varying data we need to access
// the varying values indexed by the zero ring vertices of the vertex
// patch. This indexing is redundant for triangles and quads and
// could be made redunant for other patch types if we reorganized
// the vertex patch indices so that the zero ring indices always occured
// first. This will also need to be updated when we add support for
// triangle patches.
int numVaryingCVs = _varyingDesc.GetNumControlVertices();
for (int arrayIndex=0; arrayIndex<(int)_patchArrays.size(); ++arrayIndex) {
PatchArray const & pa = getPatchArray(arrayIndex);
PatchDescriptor::Type patchType = pa.desc.GetType();
for (int patch=0; patch<pa.numPatches; ++patch) {
ConstIndexArray vertexCVs = GetPatchVertices(arrayIndex, patch);
int start = (pa.patchIndex + patch) * numVaryingCVs;
if (patchType == PatchDescriptor::REGULAR) {
_varyingVerts[start+0] = vertexCVs[5];
_varyingVerts[start+1] = vertexCVs[6];
_varyingVerts[start+2] = vertexCVs[10];
_varyingVerts[start+3] = vertexCVs[9];
} else if (patchType == PatchDescriptor::GREGORY_BASIS) {
_varyingVerts[start+0] = vertexCVs[0];
_varyingVerts[start+1] = vertexCVs[5];
_varyingVerts[start+2] = vertexCVs[10];
_varyingVerts[start+3] = vertexCVs[15];
} else if (patchType == PatchDescriptor::QUADS) {
_varyingVerts[start+0] = vertexCVs[0];
_varyingVerts[start+1] = vertexCVs[1];
_varyingVerts[start+2] = vertexCVs[2];
_varyingVerts[start+3] = vertexCVs[3];
} else if (patchType == PatchDescriptor::TRIANGLES) {
_varyingVerts[start+0] = vertexCVs[0];
_varyingVerts[start+1] = vertexCVs[1];
_varyingVerts[start+2] = vertexCVs[2];
}
}
}
}
int
PatchTable::GetNumFVarChannels() const {
return (int)_fvarChannels.size();
}
Sdc::Options::FVarLinearInterpolation
PatchTable::GetFVarChannelLinearInterpolation(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.interpolation;
}
PatchDescriptor
PatchTable::GetFVarPatchDescriptorRegular(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.regDesc;
}
PatchDescriptor
PatchTable::GetFVarPatchDescriptorIrregular(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.irregDesc;
}
PatchDescriptor
PatchTable::GetFVarPatchDescriptor(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.irregDesc;
}
ConstIndexArray
PatchTable::GetFVarValues(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return ConstIndexArray(&c.patchValues[0], (int)c.patchValues.size());
}
int
PatchTable::GetFVarValueStride(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.stride;
}
IndexArray
PatchTable::getFVarValues(int channel) {
FVarPatchChannel & c = getFVarPatchChannel(channel);
return IndexArray(&c.patchValues[0], (int)c.patchValues.size());
}
ConstIndexArray
PatchTable::getPatchFVarValues(int patch, int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
int ncvsThisPatch = c.patchParam[patch].IsRegular()
? c.regDesc.GetNumControlVertices()
: c.irregDesc.GetNumControlVertices();
return ConstIndexArray(&c.patchValues[patch * c.stride], ncvsThisPatch);
}
ConstIndexArray
PatchTable::GetPatchFVarValues(PatchHandle const & handle, int channel) const {
return getPatchFVarValues(handle.patchIndex, channel);
}
ConstIndexArray
PatchTable::GetPatchFVarValues(int arrayIndex, int patchIndex, int channel) const {
return getPatchFVarValues(getPatchIndex(arrayIndex, patchIndex), channel);
}
ConstIndexArray
PatchTable::GetPatchArrayFVarValues(int array, int channel) const {
PatchArray const & pa = getPatchArray(array);
FVarPatchChannel const & c = getFVarPatchChannel(channel);
int ncvs = c.stride;
int start = pa.patchIndex * ncvs;
int count = pa.numPatches * ncvs;
return ConstIndexArray(&c.patchValues[start], count);
}
PatchParam
PatchTable::getPatchFVarPatchParam(int patch, int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return c.patchParam[patch];
}
PatchParam
PatchTable::GetPatchFVarPatchParam(PatchHandle const & handle, int channel) const {
return getPatchFVarPatchParam(handle.patchIndex, channel);
}
PatchParam
PatchTable::GetPatchFVarPatchParam(int arrayIndex, int patchIndex, int channel) const {
return getPatchFVarPatchParam(getPatchIndex(arrayIndex, patchIndex), channel);
}
ConstPatchParamArray
PatchTable::GetPatchArrayFVarPatchParams(int array, int channel) const {
PatchArray const & pa = getPatchArray(array);
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return ConstPatchParamArray(&c.patchParam[pa.patchIndex], pa.numPatches);
}
ConstPatchParamArray
PatchTable::GetFVarPatchParams(int channel) const {
FVarPatchChannel const & c = getFVarPatchChannel(channel);
return ConstPatchParamArray(&c.patchParam[0], (int)c.patchParam.size());
}
PatchParamArray
PatchTable::getFVarPatchParams(int channel) {
FVarPatchChannel & c = getFVarPatchChannel(channel);
return PatchParamArray(&c.patchParam[0], (int)c.patchParam.size());
}
void
PatchTable::print() const {
printf("patchTable (0x%p)\n", this);
printf(" numPatches = %d\n", GetNumPatchesTotal());
for (int i=0; i<GetNumPatchArrays(); ++i) {
printf(" patchArray %d:\n", i);
PatchArray const & pa = getPatchArray(i);
pa.print();
}
}
//
// Evaluate basis functions for vertex and derivatives at (s,t):
//
template <typename REAL>
void
PatchTable::EvaluateBasis(
PatchHandle const & handle, REAL s, REAL t,
REAL wP[], REAL wDs[], REAL wDt[],
REAL wDss[], REAL wDst[], REAL wDtt[]) const {
PatchParam const & param = _paramTable[handle.patchIndex];
PatchDescriptor::Type patchType = GetPatchArrayDescriptor(handle.arrayIndex).GetType();
internal::EvaluatePatchBasis(patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
}
//
// Evaluate basis functions for varying and derivatives at (s,t):
//
template <typename REAL>
void
PatchTable::EvaluateBasisVarying(
PatchHandle const & handle, REAL s, REAL t,
REAL wP[], REAL wDs[], REAL wDt[],
REAL wDss[], REAL wDst[], REAL wDtt[]) const {
PatchParam const & param = _paramTable[handle.patchIndex];
PatchDescriptor::Type patchType = GetVaryingPatchDescriptor().GetType();
internal::EvaluatePatchBasis(patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
}
//
// Evaluate basis functions for face-varying and derivatives at (s,t):
//
template <typename REAL>
void
PatchTable::EvaluateBasisFaceVarying(
PatchHandle const & handle, REAL s, REAL t,
REAL wP[], REAL wDs[], REAL wDt[],
REAL wDss[], REAL wDst[], REAL wDtt[],
int channel) const {
PatchParam param = getPatchFVarPatchParam(handle.patchIndex, channel);
PatchDescriptor::Type patchType = param.IsRegular()
? GetFVarPatchDescriptorRegular(channel).GetType()
: GetFVarPatchDescriptorIrregular(channel).GetType();
internal::EvaluatePatchBasis(patchType, param, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
}
//
// Explicit instantiation of EvaluateBasis...() methods for float and double:
//
template void PatchTable::EvaluateBasis<float>(PatchHandle const & handle,
float s, float t, float wP[], float wDs[], float wDt[],
float wDss[], float wDst[], float wDtt[]) const;
template void PatchTable::EvaluateBasisVarying<float>(PatchHandle const & handle,
float s, float t, float wP[], float wDs[], float wDt[],
float wDss[], float wDst[], float wDtt[]) const;
template void PatchTable::EvaluateBasisFaceVarying<float>(PatchHandle const & handle,
float s, float t, float wP[], float wDs[], float wDt[],
float wDss[], float wDst[], float wDtt[], int channel) const;
template void PatchTable::EvaluateBasis<double>(PatchHandle const & handle,
double s, double t, double wP[], double wDs[], double wDt[],
double wDss[], double wDst[], double wDtt[]) const;
template void PatchTable::EvaluateBasisVarying<double>(PatchHandle const & handle,
double s, double t, double wP[], double wDs[], double wDt[],
double wDss[], double wDst[], double wDtt[]) const;
template void PatchTable::EvaluateBasisFaceVarying<double>(PatchHandle const & handle,
double s, double t, double wP[], double wDs[], double wDt[],
double wDss[], double wDst[], double wDtt[], int channel) const;
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,892 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_PATCH_TABLE_H
#define OPENSUBDIV3_FAR_PATCH_TABLE_H
#include "../version.h"
#include "../far/patchDescriptor.h"
#include "../far/patchParam.h"
#include "../far/stencilTable.h"
#include "../sdc/options.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief Container for arrays of parametric patches
///
/// PatchTable contains topology and parametric information about the patches
/// generated by the Refinement process. Patches in the table are sorted into
/// arrays based on their PatchDescriptor Type.
///
/// Note : PatchTable can be accessed either using a PatchHandle or a
/// combination of array and patch indices.
///
/// XXXX manuelk we should add a PatchIterator that can dereference into
/// a PatchHandle for fast linear traversal of the table
///
class PatchTable {
public:
/// \brief Handle that can be used as unique patch identifier within PatchTable
class PatchHandle {
// XXXX manuelk members will eventually be made private
public:
friend class PatchTable;
friend class PatchMap;
Index arrayIndex, // Array index of the patch
patchIndex, // Absolute Index of the patch
vertIndex; // Relative offset to the first CV of the patch in array
};
public:
/// \brief Copy constructor
PatchTable(PatchTable const & src);
/// \brief Destructor
~PatchTable();
/// \brief True if the patches are of feature adaptive types
bool IsFeatureAdaptive() const;
/// \brief Returns the total number of control vertex indices in the table
int GetNumControlVerticesTotal() const {
return (int)_patchVerts.size();
}
/// \brief Returns the total number of patches stored in the table
int GetNumPatchesTotal() const;
/// \brief Returns max vertex valence
int GetMaxValence() const { return _maxValence; }
/// \brief Returns the total number of ptex faces in the mesh
int GetNumPtexFaces() const { return _numPtexFaces; }
//@{
/// @name Individual patches
///
/// \anchor individual_patches
///
/// \brief Accessors for individual patches
///
/// \brief Returns the PatchDescriptor for the patch identified by \p handle
PatchDescriptor GetPatchDescriptor(PatchHandle const & handle) const;
/// \brief Returns the control vertex indices for the patch identified by \p handle
ConstIndexArray GetPatchVertices(PatchHandle const & handle) const;
/// \brief Returns a PatchParam for the patch identified by \p handle
PatchParam GetPatchParam(PatchHandle const & handle) const;
/// \brief Returns the control vertex indices for \p patch in \p array
ConstIndexArray GetPatchVertices(int array, int patch) const;
/// \brief Returns the PatchParam for \p patch in \p array
PatchParam GetPatchParam(int array, int patch) const;
//@}
//@{
/// @name Arrays of patches
///
/// \anchor arrays_of_patches
///
/// \brief Accessors for arrays of patches of the same type
///
/// \brief Returns the number of patch arrays in the table
int GetNumPatchArrays() const;
/// \brief Returns the number of patches in \p array
int GetNumPatches(int array) const;
/// \brief Returns the number of control vertices in \p array
int GetNumControlVertices(int array) const;
/// \brief Returns the PatchDescriptor for the patches in \p array
PatchDescriptor GetPatchArrayDescriptor(int array) const;
/// \brief Returns the control vertex indices for the patches in \p array
ConstIndexArray GetPatchArrayVertices(int array) const;
/// \brief Returns the PatchParams for the patches in \p array
ConstPatchParamArray const GetPatchParams(int array) const;
//@}
//@{
/// @name Change of basis patches
///
/// \anchor change_of_basis_patches
///
/// \brief Accessors for change of basis patches
///
///
/// \brief Returns the number of local vertex points.
int GetNumLocalPoints() const;
/// \brief Returns the stencil table to compute local point vertex values
StencilTable const *GetLocalPointStencilTable() const;
/// \brief Returns the stencil table to compute local point vertex values
template <typename REAL>
StencilTableReal<REAL> const *GetLocalPointStencilTable() const;
/// \brief Tests if the precision of the stencil table to compute local point
/// vertex values matches the given floating point type \<REAL\>.
template <typename REAL> bool LocalPointStencilPrecisionMatchesType() const;
/// \brief Updates local point vertex values.
///
/// @param src Buffer with primvar data for the base and refined
/// vertex values
///
/// @param dst Destination buffer for the computed local point
/// vertex values
///
/// For more flexibility computing local vertex points, retrieval of
/// the local point stencil table and use of its public methods is
/// recommended or often required.
///
template <class T> void
ComputeLocalPointValues(T const *src, T *dst) const;
/// \brief Returns the number of local varying points.
int GetNumLocalPointsVarying() const;
/// \brief Returns the stencil table to compute local point varying values
StencilTable const *GetLocalPointVaryingStencilTable() const;
/// \brief Returns the stencil table to compute local point varying values
template <typename REAL>
StencilTableReal<REAL> const *GetLocalPointVaryingStencilTable() const;
/// \brief Tests if the precision of the stencil table to compute local point
/// varying values matches the given floating point type \<REAL\>.
template <typename REAL> bool LocalPointVaryingStencilPrecisionMatchesType() const;
/// \brief Updates local point varying values.
///
/// @param src Buffer with primvar data for the base and refined
/// varying values
///
/// @param dst Destination buffer for the computed local point
/// varying values
///
/// For more flexibility computing local varying points, retrieval of
/// the local point varying stencil table and use of its public methods
/// is recommended or often required.
///
template <class T> void
ComputeLocalPointValuesVarying(T const *src, T *dst) const;
/// \brief Returns the number of local face-varying points for \p channel
int GetNumLocalPointsFaceVarying(int channel = 0) const;
/// \brief Returns the stencil table to compute local point face-varying values
StencilTable const *GetLocalPointFaceVaryingStencilTable(int channel = 0) const;
/// \brief Returns the stencil table to compute local point face-varying values
template <typename REAL>
StencilTableReal<REAL> const * GetLocalPointFaceVaryingStencilTable(int channel = 0) const;
/// \brief Tests if the precision of the stencil table to compute local point
/// face-varying values matches the given floating point type \<REAL\>.
template <typename REAL> bool LocalPointFaceVaryingStencilPrecisionMatchesType() const;
/// \brief Updates local point face-varying values.
///
/// @param src Buffer with primvar data for the base and refined
/// face-varying values
///
/// @param dst Destination buffer for the computed local point
/// face-varying values
///
/// @param channel face-varying channel
///
/// For more flexibility computing local face-varying points, retrieval
/// of the local point face-varying stencil table and use of its public
/// methods is recommended or often required.
///
template <class T> void
ComputeLocalPointValuesFaceVarying(T const *src, T *dst, int channel = 0) const;
//@}
//@{
/// @name Legacy gregory patch evaluation buffers
/// \brief Accessors for the gregory patch evaluation buffers.
/// These methods will be deprecated.
///
typedef Vtr::ConstArray<unsigned int> ConstQuadOffsetsArray;
/// \brief Returns the 'QuadOffsets' for the Gregory patch identified by \p handle
ConstQuadOffsetsArray GetPatchQuadOffsets(PatchHandle const & handle) const;
typedef std::vector<Index> VertexValenceTable;
/// \brief Returns the 'VertexValences' table (vertex neighborhoods table)
VertexValenceTable const & GetVertexValenceTable() const {
return _vertexValenceTable;
}
//@}
//@{
/// @name Single-crease patches
///
/// \anchor single_crease_patches
///
/// \brief Accessors for single-crease patch edge sharpness
///
/// \brief Returns the crease sharpness for the patch identified by \p handle
/// if it is a single-crease patch, or 0.0f
float GetSingleCreasePatchSharpnessValue(PatchHandle const & handle) const;
/// \brief Returns the crease sharpness for the \p patch in \p array
/// if it is a single-crease patch, or 0.0f
float GetSingleCreasePatchSharpnessValue(int array, int patch) const;
//@}
//@{
/// @name Varying data
///
/// \anchor varying_data
///
/// \brief Accessors for varying data
///
/// \brief Returns the varying patch descriptor
PatchDescriptor GetVaryingPatchDescriptor() const;
/// \brief Returns the varying vertex indices for a given patch
ConstIndexArray GetPatchVaryingVertices(PatchHandle const & handle) const;
/// \brief Returns the varying vertex indices for a given patch
ConstIndexArray GetPatchVaryingVertices(int array, int patch) const;
/// \brief Returns the varying vertex indices for the patches in \p array
ConstIndexArray GetPatchArrayVaryingVertices(int array) const;
/// \brief Returns an array of varying vertex indices for the patches.
ConstIndexArray GetVaryingVertices() const;
//@}
//@{
/// @name Face-varying channels
///
/// \anchor face_varying_channels
///
/// \brief Accessors for face-varying channels
///
/// \brief Returns the number of face-varying channels
int GetNumFVarChannels() const;
/// \brief Returns the regular patch descriptor for \p channel
PatchDescriptor GetFVarPatchDescriptorRegular(int channel = 0) const;
/// \brief Returns the irregular patch descriptor for \p channel
PatchDescriptor GetFVarPatchDescriptorIrregular(int channel = 0) const;
/// \brief Returns the default/irregular patch descriptor for \p channel
PatchDescriptor GetFVarPatchDescriptor(int channel = 0) const;
/// \brief Returns the value indices for a given patch in \p channel
ConstIndexArray GetPatchFVarValues(PatchHandle const & handle, int channel = 0) const;
/// \brief Returns the value indices for a given patch in \p channel
ConstIndexArray GetPatchFVarValues(int array, int patch, int channel = 0) const;
/// \brief Returns the value indices for the patches in \p array in \p channel
ConstIndexArray GetPatchArrayFVarValues(int array, int channel = 0) const;
/// \brief Returns an array of value indices for the patches in \p channel
ConstIndexArray GetFVarValues(int channel = 0) const;
/// \brief Returns the stride between patches in the value index array of \p channel
int GetFVarValueStride(int channel = 0) const;
/// \brief Returns the value indices for a given patch in \p channel
PatchParam GetPatchFVarPatchParam(PatchHandle const & handle, int channel = 0) const;
/// \brief Returns the face-varying params for a given patch \p channel
PatchParam GetPatchFVarPatchParam(int array, int patch, int channel = 0) const;
/// \brief Returns the face-varying for a given patch in \p array in \p channel
ConstPatchParamArray GetPatchArrayFVarPatchParams(int array, int channel = 0) const;
/// \brief Returns an array of face-varying patch param for \p channel
ConstPatchParamArray GetFVarPatchParams(int channel = 0) const;
/// \brief Deprecated @see PatchTable#GetFVarPatchDescriptor
Sdc::Options::FVarLinearInterpolation GetFVarChannelLinearInterpolation(int channel = 0) const;
//@}
//@{
/// @name Direct accessors
///
/// \warning These direct accessors are left for convenience, but they are
/// likely going to be deprecated in future releases
///
typedef std::vector<Index> PatchVertsTable;
/// \brief Get the table of patch control vertices
PatchVertsTable const & GetPatchControlVerticesTable() const { return _patchVerts; }
/// \brief Returns the PatchParamTable (PatchParams order matches patch array sorting)
PatchParamTable const & GetPatchParamTable() const { return _paramTable; }
/// \brief Returns a sharpness index table for each patch (if exists)
std::vector<Index> const &GetSharpnessIndexTable() const { return _sharpnessIndices; }
/// \brief Returns sharpness values table
std::vector<float> const &GetSharpnessValues() const { return _sharpnessValues; }
typedef std::vector<unsigned int> QuadOffsetsTable;
/// \brief Returns the quad-offsets table
QuadOffsetsTable const & GetQuadOffsetsTable() const {
return _quadOffsetsTable;
}
//@}
/// debug helper
void print() const;
public:
//@{
/// @name Evaluation methods
///
/// \brief Evaluate basis functions for position and derivatives at a
/// given (u,v) parametric location of a patch.
///
/// @param handle A patch handle identifying the sub-patch containing the
/// (u,v) location
///
/// @param u Patch coordinate (in base face normalized space)
///
/// @param v Patch coordinate (in base face normalized space)
///
/// @param wP Weights (evaluated basis functions) for the position
///
/// @param wDu Weights (evaluated basis functions) for derivative wrt u
///
/// @param wDv Weights (evaluated basis functions) for derivative wrt v
///
/// @param wDuu Weights (evaluated basis functions) for 2nd derivative wrt u
///
/// @param wDuv Weights (evaluated basis functions) for 2nd derivative wrt u and v
///
/// @param wDvv Weights (evaluated basis functions) for 2nd derivative wrt v
///
template <typename REAL>
void EvaluateBasis(PatchHandle const & handle, REAL u, REAL v,
REAL wP[], REAL wDu[] = 0, REAL wDv[] = 0,
REAL wDuu[] = 0, REAL wDuv[] = 0, REAL wDvv[] = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasis(PatchHandle const & handle, float u, float v,
float wP[], float wDu[] = 0, float wDv[] = 0,
float wDuu[] = 0, float wDuv[] = 0, float wDvv[] = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasis(PatchHandle const & handle, double u, double v,
double wP[], double wDu[] = 0, double wDv[] = 0,
double wDuu[] = 0, double wDuv[] = 0, double wDvv[] = 0) const;
/// \brief Evaluate basis functions for a varying value and
/// derivatives at a given (u,v) parametric location of a patch.
///
/// @param handle A patch handle identifying the sub-patch containing the
/// (u,v) location
///
/// @param u Patch coordinate (in base face normalized space)
///
/// @param v Patch coordinate (in base face normalized space)
///
/// @param wP Weights (evaluated basis functions) for the position
///
/// @param wDu Weights (evaluated basis functions) for derivative wrt u
///
/// @param wDv Weights (evaluated basis functions) for derivative wrt v
///
/// @param wDuu Weights (evaluated basis functions) for 2nd derivative wrt u
///
/// @param wDuv Weights (evaluated basis functions) for 2nd derivative wrt u and v
///
/// @param wDvv Weights (evaluated basis functions) for 2nd derivative wrt v
///
template <typename REAL>
void EvaluateBasisVarying(PatchHandle const & handle, REAL u, REAL v,
REAL wP[], REAL wDu[] = 0, REAL wDv[] = 0,
REAL wDuu[] = 0, REAL wDuv[] = 0, REAL wDvv[] = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasisVarying(PatchHandle const & handle, float u, float v,
float wP[], float wDu[] = 0, float wDv[] = 0,
float wDuu[] = 0, float wDuv[] = 0, float wDvv[] = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasisVarying(PatchHandle const & handle, double u, double v,
double wP[], double wDu[] = 0, double wDv[] = 0,
double wDuu[] = 0, double wDuv[] = 0, double wDvv[] = 0) const;
/// \brief Evaluate basis functions for a face-varying value and
/// derivatives at a given (u,v) parametric location of a patch.
///
/// @param handle A patch handle identifying the sub-patch containing the
/// (u,v) location
///
/// @param u Patch coordinate (in base face normalized space)
///
/// @param v Patch coordinate (in base face normalized space)
///
/// @param wP Weights (evaluated basis functions) for the position
///
/// @param wDu Weights (evaluated basis functions) for derivative wrt u
///
/// @param wDv Weights (evaluated basis functions) for derivative wrt v
///
/// @param wDuu Weights (evaluated basis functions) for 2nd derivative wrt u
///
/// @param wDuv Weights (evaluated basis functions) for 2nd derivative wrt u and v
///
/// @param wDvv Weights (evaluated basis functions) for 2nd derivative wrt v
///
/// @param channel face-varying channel
///
template <typename REAL>
void EvaluateBasisFaceVarying(PatchHandle const & handle, REAL u, REAL v,
REAL wP[], REAL wDu[] = 0, REAL wDv[] = 0,
REAL wDuu[] = 0, REAL wDuv[] = 0, REAL wDvv[] = 0,
int channel = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasisFaceVarying(PatchHandle const & handle, float u, float v,
float wP[], float wDu[] = 0, float wDv[] = 0,
float wDuu[] = 0, float wDuv[] = 0, float wDvv[] = 0,
int channel = 0) const;
/// \brief An overloaded version to assist template parameter resolution
/// when explicitly declaring unused array arguments as 0.
void EvaluateBasisFaceVarying(PatchHandle const & handle, double u, double v,
double wP[], double wDu[] = 0, double wDv[] = 0,
double wDuu[] = 0, double wDuv[] = 0, double wDvv[] = 0,
int channel = 0) const;
//@}
protected:
friend class PatchTableBuilder;
// Factory constructor
PatchTable(int maxvalence);
Index getPatchIndex(int array, int patch) const;
PatchParamArray getPatchParams(int arrayIndex);
Index * getSharpnessIndices(Index arrayIndex);
float * getSharpnessValues(Index arrayIndex);
private:
//
// Patch arrays
//
struct PatchArray {
PatchArray(PatchDescriptor d, int np, Index v, Index p, Index qo) :
desc(d), numPatches(np), vertIndex(v),
patchIndex(p), quadOffsetIndex (qo) { }
void print() const;
PatchDescriptor desc; // type of patches in the array
int numPatches; // number of patches in the array
Index vertIndex, // index to the first control vertex
patchIndex, // absolute index of the first patch in the array
quadOffsetIndex; // index of the first quad offset entry
};
typedef std::vector<PatchArray> PatchArrayVector;
PatchArray & getPatchArray(Index arrayIndex);
PatchArray const & getPatchArray(Index arrayIndex) const;
void reservePatchArrays(int numPatchArrays);
void pushPatchArray(PatchDescriptor desc, int npatches,
Index * vidx, Index * pidx, Index * qoidx=0);
IndexArray getPatchArrayVertices(int arrayIndex);
Index findPatchArray(PatchDescriptor desc);
//
// Varying patch arrays
//
IndexArray getPatchArrayVaryingVertices(int arrayIndex);
void allocateVaryingVertices(
PatchDescriptor desc, int numPatches);
void populateVaryingVertices();
//
// Face-varying patch channels
//
//
// FVarPatchChannel
//
// Stores a record for each patch in the primitive :
//
// - Each patch in the PatchTable has a corresponding patch in each
// face-varying patch channel. Patch vertex indices are sorted in the same
// patch-type order as PatchTable::PTables. Face-varying data for a patch
// can therefore be quickly accessed by using the patch primitive ID as
// index into patchValueOffsets to locate the face-varying control vertex
// indices.
//
// - Face-varying channels can have a different interpolation modes
//
// - Unlike "vertex" patches, there are no transition masks required
// for face-varying patches.
//
// - Face-varying patches still require boundary edge masks.
//
// - currently most patches with sharp boundaries but smooth interiors have
// to be isolated to level 10 : we need a special type of bicubic patch
// similar to single-crease to resolve this condition without requiring
// isolation if possible
//
struct FVarPatchChannel {
Sdc::Options::FVarLinearInterpolation interpolation;
PatchDescriptor regDesc;
PatchDescriptor irregDesc;
int stride;
std::vector<Index> patchValues;
std::vector<PatchParam> patchParam;
};
typedef std::vector<FVarPatchChannel> FVarPatchChannelVector;
FVarPatchChannel & getFVarPatchChannel(int channel);
FVarPatchChannel const & getFVarPatchChannel(int channel) const;
void allocateFVarPatchChannels(int numChannels);
void allocateFVarPatchChannelValues(
PatchDescriptor regDesc, PatchDescriptor irregDesc,
int numPatches, int channel);
// deprecated
void setFVarPatchChannelLinearInterpolation(
Sdc::Options::FVarLinearInterpolation interpolation, int channel);
IndexArray getFVarValues(int channel);
ConstIndexArray getPatchFVarValues(int patch, int channel) const;
PatchParamArray getFVarPatchParams(int channel);
PatchParam getPatchFVarPatchParam(int patch, int channel) const;
private:
//
// Simple private class to hold stencil table pointers of varying precision,
// where the discriminant of the precision is external.
//
// NOTE that this is a simple pointer container and NOT a smart pointer that
// manages the ownership of the object referred to by it.
//
class StencilTablePtr {
private:
typedef StencilTableReal<float> float_type;
typedef StencilTableReal<double> double_type;
union {
float_type * _fPtr;
double_type * _dPtr;
};
public:
StencilTablePtr() { _fPtr = 0; }
StencilTablePtr(float_type * ptr) { _fPtr = ptr; }
StencilTablePtr(double_type * ptr) { _dPtr = ptr; }
operator bool() const { return _fPtr != 0; }
void Set() { _fPtr = 0; }
void Set(float_type * ptr) { _fPtr = ptr; }
void Set(double_type * ptr) { _dPtr = ptr; }
template <typename REAL> StencilTableReal<REAL> * Get() const;
};
private:
//
// Topology
//
int _maxValence, // highest vertex valence found in the mesh
_numPtexFaces; // total number of ptex faces
PatchArrayVector _patchArrays; // Vector of descriptors for arrays of patches
std::vector<Index> _patchVerts; // Indices of the control vertices of the patches
PatchParamTable _paramTable; // PatchParam bitfields (one per patch)
//
// Extraordinary vertex closed-form evaluation / endcap basis conversion
//
// XXXtakahito: these data will probably be replaced with mask coefficient or something
// SchemeWorker populates.
//
QuadOffsetsTable _quadOffsetsTable; // Quad offsets (for Gregory patches)
VertexValenceTable _vertexValenceTable; // Vertex valence table (for Gregory patches)
StencilTablePtr _localPointStencils; // local point conversion stencils
StencilTablePtr _localPointVaryingStencils; // local point varying stencils
//
// Varying data
//
PatchDescriptor _varyingDesc;
std::vector<Index> _varyingVerts;
//
// Face-varying data
//
FVarPatchChannelVector _fvarChannels;
std::vector<StencilTablePtr> _localPointFaceVaryingStencils;
//
// 'single-crease' patch sharpness tables
//
std::vector<Index> _sharpnessIndices; // Indices of single-crease sharpness (one per patch)
std::vector<float> _sharpnessValues; // Sharpness values.
//
// Construction history -- relevant to at least one public query:
//
unsigned int _isUniformLinear : 1;
//
// Precision -- only applies to local-point stencil tables
//
unsigned int _vertexPrecisionIsDouble : 1;
unsigned int _varyingPrecisionIsDouble : 1;
unsigned int _faceVaryingPrecisionIsDouble : 1;
};
//
// Template specializations for float/double -- to be defined before used:
//
template <> inline StencilTableReal<float> *
PatchTable::StencilTablePtr::Get<float>() const { return _fPtr; }
template <> inline StencilTableReal<double> *
PatchTable::StencilTablePtr::Get<double>() const { return _dPtr; }
template <> inline bool
PatchTable::LocalPointStencilPrecisionMatchesType<float>() const {
return !_vertexPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointVaryingStencilPrecisionMatchesType<float>() const {
return !_varyingPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointFaceVaryingStencilPrecisionMatchesType<float>() const {
return !_faceVaryingPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointStencilPrecisionMatchesType<double>() const {
return _vertexPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointVaryingStencilPrecisionMatchesType<double>() const {
return _varyingPrecisionIsDouble;
}
template <> inline bool
PatchTable::LocalPointFaceVaryingStencilPrecisionMatchesType<double>() const {
return _faceVaryingPrecisionIsDouble;
}
//
// StencilTable access -- backward compatible and generic:
//
inline StencilTable const *
PatchTable::GetLocalPointStencilTable() const {
assert(LocalPointStencilPrecisionMatchesType<float>());
return reinterpret_cast<StencilTable const *>(_localPointStencils.Get<float>());
}
inline StencilTable const *
PatchTable::GetLocalPointVaryingStencilTable() const {
assert(LocalPointVaryingStencilPrecisionMatchesType<float>());
return reinterpret_cast<StencilTable const *>(
_localPointVaryingStencils.Get<float>());
}
inline StencilTable const *
PatchTable::GetLocalPointFaceVaryingStencilTable(int channel) const {
assert(LocalPointFaceVaryingStencilPrecisionMatchesType<float>());
if (channel >= 0 && channel < (int)_localPointFaceVaryingStencils.size()) {
return reinterpret_cast<StencilTable const *>(
_localPointFaceVaryingStencils[channel].Get<float>());
}
return NULL;
}
template <typename REAL>
inline StencilTableReal<REAL> const *
PatchTable::GetLocalPointStencilTable() const {
assert(LocalPointStencilPrecisionMatchesType<REAL>());
return _localPointStencils.Get<REAL>();
}
template <typename REAL>
inline StencilTableReal<REAL> const *
PatchTable::GetLocalPointVaryingStencilTable() const {
assert(LocalPointVaryingStencilPrecisionMatchesType<REAL>());
return _localPointVaryingStencils.Get<REAL>();
}
template <typename REAL>
inline StencilTableReal<REAL> const *
PatchTable::GetLocalPointFaceVaryingStencilTable(int channel) const {
assert(LocalPointFaceVaryingStencilPrecisionMatchesType<REAL>());
if (channel >= 0 && channel < (int)_localPointFaceVaryingStencils.size()) {
return _localPointFaceVaryingStencils[channel].Get<REAL>();
}
return NULL;
}
//
// Computation of local point values:
//
template <class T>
inline void
PatchTable::ComputeLocalPointValues(T const *src, T *dst) const {
assert(LocalPointStencilPrecisionMatchesType<float>());
if (_localPointStencils) {
_localPointStencils.Get<float>()->UpdateValues(src, dst);
}
}
template <class T>
inline void
PatchTable::ComputeLocalPointValuesVarying(T const *src, T *dst) const {
assert(LocalPointVaryingStencilPrecisionMatchesType<float>());
if (_localPointVaryingStencils) {
_localPointVaryingStencils.Get<float>()->UpdateValues(src, dst);
}
}
template <class T>
inline void
PatchTable::ComputeLocalPointValuesFaceVarying(T const *src, T *dst, int channel) const {
assert(LocalPointFaceVaryingStencilPrecisionMatchesType<float>());
if (channel >= 0 && channel < (int)_localPointFaceVaryingStencils.size()) {
if (_localPointFaceVaryingStencils[channel]) {
_localPointFaceVaryingStencils[channel].Get<float>()->UpdateValues(src, dst);
}
}
}
//
// Basis evaluation overloads
//
inline void
PatchTable::EvaluateBasis(PatchHandle const & handle, float u, float v,
float wP[], float wDu[], float wDv[],
float wDuu[], float wDuv[], float wDvv[]) const {
EvaluateBasis<float>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
inline void
PatchTable::EvaluateBasis(PatchHandle const & handle, double u, double v,
double wP[], double wDu[], double wDv[],
double wDuu[], double wDuv[], double wDvv[]) const {
EvaluateBasis<double>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
inline void
PatchTable::EvaluateBasisVarying(PatchHandle const & handle, float u, float v,
float wP[], float wDu[], float wDv[],
float wDuu[], float wDuv[], float wDvv[]) const {
EvaluateBasisVarying<float>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
inline void
PatchTable::EvaluateBasisVarying(PatchHandle const & handle, double u, double v,
double wP[], double wDu[], double wDv[],
double wDuu[], double wDuv[], double wDvv[]) const {
EvaluateBasisVarying<double>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv);
}
inline void
PatchTable::EvaluateBasisFaceVarying(PatchHandle const & handle, float u, float v,
float wP[], float wDu[], float wDv[],
float wDuu[], float wDuv[], float wDvv[], int channel) const {
EvaluateBasisFaceVarying<float>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv, channel);
}
inline void
PatchTable::EvaluateBasisFaceVarying(PatchHandle const & handle, double u, double v,
double wP[], double wDu[], double wDv[],
double wDuu[], double wDuv[], double wDvv[], int channel) const {
EvaluateBasisFaceVarying<double>(handle, u, v, wP, wDu, wDv, wDuu, wDuv, wDvv, channel);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_TABLE */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,218 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_PATCH_TABLE_FACTORY_H
#define OPENSUBDIV3_FAR_PATCH_TABLE_FACTORY_H
#include "../version.h"
#include "../far/topologyRefiner.h"
#include "../far/patchTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
/// \brief Factory for constructing a PatchTable from a TopologyRefiner
///
class PatchTableFactory {
public:
/// \brief Public options for the PatchTable factory
///
struct Options {
/// \brief Choice for approximating irregular patches (end-caps)
///
/// This enum specifies how irregular patches (end-caps) are approximated.
/// A basis is chosen, rather than a specific patch type, and has a
/// corresponding patch type for each subdivision scheme, i.e. a quad and
/// triangular patch type exists for each basis. These choices provide a
/// trade-off between surface quality and performance.
///
enum EndCapType {
ENDCAP_NONE = 0, ///< unspecified
ENDCAP_BILINEAR_BASIS, ///< use linear patches (simple quads or tris)
ENDCAP_BSPLINE_BASIS, ///< use BSpline-like patches (same patch type as regular)
ENDCAP_GREGORY_BASIS, ///< use Gregory patches (highest quality, recommended default)
ENDCAP_LEGACY_GREGORY ///< legacy option for 2.x style Gregory patches (Catmark only)
};
Options(unsigned int maxIsolation=10) :
generateAllLevels(false),
includeBaseLevelIndices(true),
includeFVarBaseLevelIndices(false),
triangulateQuads(false),
useSingleCreasePatch(false),
useInfSharpPatch(false),
maxIsolationLevel(maxIsolation & 0xf),
endCapType(ENDCAP_GREGORY_BASIS),
shareEndCapPatchPoints(true),
generateVaryingTables(true),
generateVaryingLocalPoints(true),
generateFVarTables(false),
patchPrecisionDouble(false),
fvarPatchPrecisionDouble(false),
generateFVarLegacyLinearPatches(true),
generateLegacySharpCornerPatches(true),
numFVarChannels(-1),
fvarChannelIndices(0)
{ }
/// \brief Get endcap basis type
EndCapType GetEndCapType() const { return (EndCapType)endCapType; }
/// \brief Set endcap basis type
void SetEndCapType(EndCapType e) { endCapType = e & 0x7; }
/// \brief Set maximum isolation level
void SetMaxIsolationLevel(unsigned int level) { maxIsolationLevel = level & 0xf; }
/// \brief Set precision of vertex patches
template <typename REAL> void SetPatchPrecision();
/// \brief Set precision of face-varying patches
template <typename REAL> void SetFVarPatchPrecision();
/// \brief Determine adaptive refinement options to match assigned patch options
TopologyRefiner::AdaptiveOptions GetRefineAdaptiveOptions() const {
TopologyRefiner::AdaptiveOptions adaptiveOptions(maxIsolationLevel);
adaptiveOptions.useInfSharpPatch = useInfSharpPatch;
adaptiveOptions.useSingleCreasePatch = useSingleCreasePatch;
adaptiveOptions.considerFVarChannels = generateFVarTables &&
!generateFVarLegacyLinearPatches;
return adaptiveOptions;
}
unsigned int generateAllLevels : 1, ///< Generate levels from 'firstLevel' to 'maxLevel' (Uniform mode only)
includeBaseLevelIndices : 1, ///< Include base level in patch point indices (Uniform mode only)
includeFVarBaseLevelIndices : 1, ///< Include base level in face-varying patch point indices (Uniform mode only)
triangulateQuads : 1, ///< Triangulate 'QUADS' primitives (Uniform mode only)
useSingleCreasePatch : 1, ///< Use single crease patch
useInfSharpPatch : 1, ///< Use infinitely-sharp patch
maxIsolationLevel : 4, ///< Cap adaptive feature isolation to the given level (max. 10)
// end-capping
endCapType : 3, ///< EndCapType
shareEndCapPatchPoints : 1, ///< Share endcap patch points among adjacent endcap patches.
///< currently only work with GregoryBasis.
// varying
generateVaryingTables : 1, ///< Generate varying patch tables
generateVaryingLocalPoints : 1, ///< Generate local points with varying patches
// face-varying
generateFVarTables : 1, ///< Generate face-varying patch tables
// precision
patchPrecisionDouble : 1, ///< Generate double-precision stencils for vertex patches
fvarPatchPrecisionDouble : 1, ///< Generate double-precision stencils for face-varying patches
// legacy behaviors (default to true)
generateFVarLegacyLinearPatches : 1, ///< Generate all linear face-varying patches (legacy)
generateLegacySharpCornerPatches : 1; ///< Generate sharp regular patches at smooth corners (legacy)
int numFVarChannels; ///< Number of channel indices and interpolation modes passed
int const * fvarChannelIndices; ///< List containing the indices of the channels selected for the factory
};
/// \brief Instantiates a PatchTable from a client-provided TopologyRefiner.
///
/// A PatchTable can be constructed from a TopologyRefiner that has been
/// either adaptively or uniformly refined. In both cases, the resulting
/// patches reference vertices in the various refined levels by index,
/// and those indices accumulate with the levels in different ways.
///
/// For adaptively refined patches, patches are defined at different levels,
/// including the base level, so the indices of patch vertices include
/// vertices from all levels. A sparse set of patches can be created by
/// restricting the patches generated to those descending from a given set
/// of faces at the base level. This sparse set of base faces is expected
/// to be a subset of the faces that were adaptively refined in the given
/// TopologyRefiner, otherwise results are undefined.
///
/// For uniformly refined patches, all patches are completely defined within
/// the last level. There is often no use for intermediate levels and they
/// can usually be ignored. Indices of patch vertices might therefore be
/// expected to be defined solely within the last level. While this is true
/// for face-varying patches, for historical reasons it is not the case for
/// vertex and varying patches. Indices for vertex and varying patches include
/// the base level in addition to the last level while indices for face-varying
/// patches include only the last level.
///
/// @param refiner TopologyRefiner from which to generate patches
///
/// @param options Options controlling the creation of the table
///
/// @param selectedFaces Only create patches for the given set of base faces.
///
/// @return A new instance of PatchTable
///
static PatchTable * Create(TopologyRefiner const & refiner,
Options options = Options(),
ConstIndexArray selectedFaces = ConstIndexArray());
public:
// PatchFaceTag
//
// This simple struct was previously used within the factory to take inventory of
// various kinds of patches to fully allocate buffers prior to populating them. It
// was not intended to be exposed as part of the public interface.
//
// It is no longer used internally and is being kept here to respect preservation
// of the public interface, but it will be deprecated at the earliest opportunity.
//
/// \brief Obsolete internal struct not intended for public use -- due to
/// be deprecated.
//
struct PatchFaceTag {
public:
unsigned int _hasPatch : 1;
unsigned int _isRegular : 1;
unsigned int _transitionMask : 4;
unsigned int _boundaryMask : 4;
unsigned int _boundaryIndex : 2;
unsigned int _boundaryCount : 3;
unsigned int _hasBoundaryEdge : 3;
unsigned int _isSingleCrease : 1;
void clear();
void assignBoundaryPropertiesFromEdgeMask(int boundaryEdgeMask);
void assignBoundaryPropertiesFromVertexMask(int boundaryVertexMask);
void assignTransitionPropertiesFromEdgeMask(int boundaryVertexMask);
};
typedef std::vector<PatchFaceTag> PatchTagVector;
};
template <> inline void PatchTableFactory::Options::SetPatchPrecision<float>() {
patchPrecisionDouble = false;
}
template <> inline void PatchTableFactory::Options::SetFVarPatchPrecision<float>() {
fvarPatchPrecisionDouble = false;
}
template <> inline void PatchTableFactory::Options::SetPatchPrecision<double>() {
patchPrecisionDouble = true;
}
template <> inline void PatchTableFactory::Options::SetFVarPatchPrecision<double>() {
fvarPatchPrecisionDouble = true;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PATCH_TABLE_FACTORY_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,196 @@
//
// Copyright 2015 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/ptexIndices.h"
#include "../far/error.h"
#include "../vtr/level.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
PtexIndices::PtexIndices(TopologyRefiner const &refiner) {
initializePtexIndices(refiner);
}
PtexIndices::~PtexIndices() {
}
void
PtexIndices::initializePtexIndices(TopologyRefiner const &refiner) {
int regFaceSize = Sdc::SchemeTypeTraits::GetRegularFaceSize(
refiner.GetSchemeType());
Vtr::internal::Level const & coarseLevel = refiner.getLevel(0);
int nfaces = coarseLevel.getNumFaces();
_ptexIndices.resize(nfaces+1);
int ptexID=0;
for (int i = 0; i < nfaces; ++i) {
_ptexIndices[i] = ptexID;
Vtr::ConstIndexArray fverts = coarseLevel.getFaceVertices(i);
ptexID += fverts.size()==regFaceSize ? 1 : fverts.size();
}
// last entry contains the number of ptex texture faces
_ptexIndices[nfaces]=ptexID;
}
int
PtexIndices::GetNumFaces() const {
return _ptexIndices.back();
}
int
PtexIndices::GetFaceId(Index f) const {
assert(f<(int)_ptexIndices.size());
return _ptexIndices[f];
}
namespace {
// Returns the face adjacent to 'face' along edge 'edge'
inline Index
getAdjacentFace(Vtr::internal::Level const & level, Index edge, Index face) {
Far::ConstIndexArray adjFaces = level.getEdgeFaces(edge);
if (adjFaces.size()!=2) {
return -1;
}
return (adjFaces[0]==face) ? adjFaces[1] : adjFaces[0];
}
}
void
PtexIndices::GetAdjacency(
TopologyRefiner const &refiner,
int face, int quadrant,
int adjFaces[4], int adjEdges[4]) const {
int regFaceSize =
Sdc::SchemeTypeTraits::GetRegularFaceSize(refiner.GetSchemeType());
Vtr::internal::Level const & level = refiner.getLevel(0);
ConstIndexArray fedges = level.getFaceEdges(face);
if (fedges.size() == regFaceSize) {
// Regular ptex quad face
for (int i=0; i<regFaceSize; ++i) {
int edge = fedges[i];
Index adjface = getAdjacentFace(level, edge, face);
if (adjface==-1) {
adjFaces[i] = -1; // boundary or non-manifold
adjEdges[i] = 0;
} else {
ConstIndexArray aedges = level.getFaceEdges(adjface);
if (aedges.size()==regFaceSize) {
adjFaces[i] = _ptexIndices[adjface];
adjEdges[i] = aedges.FindIndex(edge);
assert(adjEdges[i]!=-1);
} else {
// neighbor is a sub-face
adjFaces[i] = _ptexIndices[adjface] +
(aedges.FindIndex(edge)+1)%aedges.size();
adjEdges[i] = 3;
}
assert(adjFaces[i]!=-1);
}
}
if (regFaceSize == 3) {
adjFaces[3] = -1;
adjEdges[3] = 0;
}
} else if (regFaceSize == 4) {
// Ptex sub-face 'quadrant' (non-quad)
//
// Ptex adjacency pattern for non-quads:
//
// v2
/* o
// / \
// / \
// /0 3\
// / \
// o_ 1 2 _o
// / -_ _- \
// / 2 -o- 1 \
// /3 | 0\
// / 1|2 \
// / 0 | 3 \
// o----------o----------o
// v0 v1
*/
assert(quadrant>=0 && quadrant<fedges.size());
int nextQuadrant = (quadrant+1) % fedges.size(),
prevQuadrant = (quadrant+fedges.size()-1) % fedges.size();
{ // resolve neighbors within the sub-face (edges 1 & 2)
adjFaces[1] = _ptexIndices[face] + nextQuadrant;
adjEdges[1] = 2;
adjFaces[2] = _ptexIndices[face] + prevQuadrant;
adjEdges[2] = 1;
}
{ // resolve neighbor outside the sub-face (edge 0)
int edge0 = fedges[quadrant];
Index adjface0 = getAdjacentFace(level, edge0, face);
if (adjface0==-1) {
adjFaces[0] = -1; // boundary or non-manifold
adjEdges[0] = 0;
} else {
ConstIndexArray afedges = level.getFaceEdges(adjface0);
if (afedges.size()==4) {
adjFaces[0] = _ptexIndices[adjface0];
adjEdges[0] = afedges.FindIndexIn4Tuple(edge0);
} else {
int subedge = (afedges.FindIndex(edge0)+1)%afedges.size();
adjFaces[0] = _ptexIndices[adjface0] + subedge;
adjEdges[0] = 3;
}
assert(adjFaces[0]!=-1);
}
// resolve neighbor outside the sub-face (edge 3)
int edge3 = fedges[prevQuadrant];
Index adjface3 = getAdjacentFace(level, edge3, face);
if (adjface3==-1) {
adjFaces[3]=-1; // boundary or non-manifold
adjEdges[3]=0;
} else {
ConstIndexArray afedges = level.getFaceEdges(adjface3);
if (afedges.size()==4) {
adjFaces[3] = _ptexIndices[adjface3];
adjEdges[3] = afedges.FindIndexIn4Tuple(edge3);
} else {
int subedge = afedges.FindIndex(edge3);
adjFaces[3] = _ptexIndices[adjface3] + subedge;
adjEdges[3] = 0;
}
assert(adjFaces[3]!=-1);
}
}
} else {
Far::Error(FAR_RUNTIME_ERROR,
"Failure in PtexIndices::GetAdjacency() -- "
"irregular faces only supported for quad schemes.");
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,89 @@
//
// Copyright 2015 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_PTEX_INDICES_H
#define OPENSUBDIV3_FAR_PTEX_INDICES_H
#include "../version.h"
#include "../far/topologyRefiner.h"
#include "../far/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
///
/// \brief Object used to compute and query ptex face indices.
///
/// Given a refiner, constructing a PtexIndices object builds the mapping
/// from coarse faces to ptex ids. Once built, the object can be used to
/// query the mapping.
///
class PtexIndices {
public:
/// \brief Constructor
PtexIndices(TopologyRefiner const &refiner);
/// \brief Destructor
~PtexIndices();
//@{
///
/// Ptex
///
/// \brief Returns the number of ptex faces in the mesh
///
int GetNumFaces() const;
/// \brief Returns the ptex face index given a coarse face 'f' or -1
///
int GetFaceId(Index f) const;
/// \brief Returns ptex face adjacency information for a given coarse face
///
/// @param refiner refiner used to build this PtexIndices object.
///
/// @param face coarse face index
///
/// @param quadrant quadrant index if 'face' is not a quad (the local ptex
/// sub-face index). Must be less than the number of face
/// vertices.
///
/// @param adjFaces ptex face indices of adjacent faces
///
/// @param adjEdges ptex edge indices of adjacent faces
///
void GetAdjacency(
TopologyRefiner const &refiner,
int face, int quadrant,
int adjFaces[4], int adjEdges[4]) const;
//@}
private:
void initializePtexIndices(TopologyRefiner const &refiner);
private:
std::vector<Index> _ptexIndices;
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_PTEX_INDICES_H */

View File

@@ -0,0 +1,182 @@
//
// Copyright 2017 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_SPARSE_MATRIX_H
#define OPENSUBDIV3_FAR_SPARSE_MATRIX_H
#include "../version.h"
#include "../vtr/array.h"
#include <algorithm>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// SparseMatrix
//
// The SparseMatrix class is used by the PatchBuilder to store coefficients
// for a set of patch points derived from some other set of points -- usually
// the refined points in a subdivision level. The compressed sparse row
// format (CSR) is used as it provides us with stencils for points that
// correspond to rows and so can be more directly and efficiently copied.
//
// It has potential for other uses and so may eventually warrant a seperate
// header file of its own. For now, in keeping with the trend of exposing
// classes only where used, it is defined with the PatchBuilder.
//
// We may also want to explore the possibility of being able to assign
// static buffers as members here -- allowing common matrices to be set
// directly rather than repeatedly replicated.
//
template <typename REAL>
class SparseMatrix {
public:
typedef int column_type;
typedef REAL element_type;
public:
// Declaration and access methods:
SparseMatrix() : _numRows(0), _numColumns(0), _numElements(0) { }
int GetNumRows() const { return _numRows; }
int GetNumColumns() const { return _numColumns; }
int GetNumElements() const { return _numElements; }
int GetCapacity() const;
int GetRowSize(int rowIndex) const {
return _rowOffsets[rowIndex + 1] - _rowOffsets[rowIndex];
}
Vtr::ConstArray<column_type> GetRowColumns( int rowIndex) const {
return Vtr::ConstArray<column_type>(&_columns[_rowOffsets[rowIndex]],
GetRowSize(rowIndex));
}
Vtr::ConstArray<element_type> GetRowElements(int rowIndex) const {
return Vtr::ConstArray<element_type>(&_elements[_rowOffsets[rowIndex]],
GetRowSize(rowIndex));
}
Vtr::ConstArray<column_type> GetColumns() const {
return Vtr::ConstArray<column_type>(&_columns[0], GetNumElements());
}
Vtr::ConstArray<element_type> GetElements() const {
return Vtr::ConstArray<element_type>(&_elements[0], GetNumElements());
}
public:
// Modification methods
void Resize(int numRows, int numColumns, int numNonZeroEntriesToReserve);
void Copy(SparseMatrix const & srcMatrix);
void Swap(SparseMatrix & otherMatrix);
void SetRowSize(int rowIndex, int size);
Vtr::Array<column_type> SetRowColumns( int rowIndex) {
return Vtr::Array<column_type>(&_columns[_rowOffsets[rowIndex]],
GetRowSize(rowIndex));
}
Vtr::Array<element_type> SetRowElements(int rowIndex) {
return Vtr::Array<element_type>(&_elements[_rowOffsets[rowIndex]],
GetRowSize(rowIndex));
}
private:
// Simple dimensions:
int _numRows;
int _numColumns;
int _numElements;
std::vector<int> _rowOffsets; // remember one more entry here than rows
// XXXX (barfowl) - Note that the use of std::vector for the columns and
// element arrays was causing performance issues in the incremental
// resizing of consecutive rows, so we've been exploring alternatives...
std::vector<column_type> _columns;
std::vector<element_type> _elements;
};
template <typename REAL>
inline int
SparseMatrix<REAL>::GetCapacity() const {
return (int) _elements.size();
}
template <typename REAL>
inline void
SparseMatrix<REAL>::Resize(int numRows, int numCols, int numElementsToReserve) {
_numRows = numRows;
_numColumns = numCols;
_numElements = 0;
_rowOffsets.resize(0);
_rowOffsets.resize(_numRows + 1, -1);
_rowOffsets[0] = 0;
if (numElementsToReserve > GetCapacity()) {
_columns.resize(numElementsToReserve);
_elements.resize(numElementsToReserve);
}
}
template <typename REAL>
inline void
SparseMatrix<REAL>::SetRowSize(int rowIndex, int rowSize) {
assert(_rowOffsets[rowIndex] == _numElements);
int & newVectorSize = _rowOffsets[rowIndex + 1];
newVectorSize = _rowOffsets[rowIndex] + rowSize;
_numElements = newVectorSize;
if (newVectorSize > GetCapacity()) {
_columns.resize(newVectorSize);
_elements.resize(newVectorSize);
}
}
template <typename REAL>
inline void
SparseMatrix<REAL>::Copy(SparseMatrix const & src) {
_numRows = src._numRows;
_numColumns = src._numColumns;
_rowOffsets = src._rowOffsets;
_numElements = src._numElements;
_columns = src._columns;
_elements = src._elements;
}
template <typename REAL>
inline void
SparseMatrix<REAL>::Swap(SparseMatrix & other) {
std::swap(_numRows, other._numRows);
std::swap(_numColumns, other._numColumns);
std::swap(_numElements, other._numElements);
_rowOffsets.swap(other._rowOffsets);
_columns.swap(other._columns);
_elements.swap(other._elements);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_SPARSE_MATRIX_H */

View File

@@ -0,0 +1,589 @@
//
// Copyright 2015 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/stencilBuilder.h"
#include "../far/topologyRefiner.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
namespace {
#ifdef __INTEL_COMPILER
#pragma warning (push)
#pragma warning disable 1572
#endif
template <typename REAL>
inline bool isWeightZero(REAL w) { return (w == (REAL)0.0); }
#ifdef __INTEL_COMPILER
#pragma warning (pop)
#endif
}
template <typename REAL>
struct Point1stDerivWeight {
REAL p;
REAL du;
REAL dv;
Point1stDerivWeight()
: p(0.0f), du(0.0f), dv(0.0f)
{ }
Point1stDerivWeight(REAL w)
: p(w), du(w), dv(w)
{ }
Point1stDerivWeight(REAL w, REAL wDu, REAL wDv)
: p(w), du(wDu), dv(wDv)
{ }
friend Point1stDerivWeight operator*(Point1stDerivWeight lhs,
Point1stDerivWeight const& rhs) {
lhs.p *= rhs.p;
lhs.du *= rhs.du;
lhs.dv *= rhs.dv;
return lhs;
}
Point1stDerivWeight& operator+=(Point1stDerivWeight const& rhs) {
p += rhs.p;
du += rhs.du;
dv += rhs.dv;
return *this;
}
};
template <typename REAL>
struct Point2ndDerivWeight {
REAL p;
REAL du;
REAL dv;
REAL duu;
REAL duv;
REAL dvv;
Point2ndDerivWeight()
: p(0.0f), du(0.0f), dv(0.0f), duu(0.0f), duv(0.0f), dvv(0.0f)
{ }
Point2ndDerivWeight(REAL w)
: p(w), du(w), dv(w), duu(w), duv(w), dvv(w)
{ }
Point2ndDerivWeight(REAL w, REAL wDu, REAL wDv,
REAL wDuu, REAL wDuv, REAL wDvv)
: p(w), du(wDu), dv(wDv), duu(wDuu), duv(wDuv), dvv(wDvv)
{ }
friend Point2ndDerivWeight operator*(Point2ndDerivWeight lhs,
Point2ndDerivWeight const& rhs) {
lhs.p *= rhs.p;
lhs.du *= rhs.du;
lhs.dv *= rhs.dv;
lhs.duu *= rhs.duu;
lhs.duv *= rhs.duv;
lhs.dvv *= rhs.dvv;
return lhs;
}
Point2ndDerivWeight& operator+=(Point2ndDerivWeight const& rhs) {
p += rhs.p;
du += rhs.du;
dv += rhs.dv;
duu += rhs.duu;
duv += rhs.duv;
dvv += rhs.dvv;
return *this;
}
};
/// Stencil table constructor set.
///
template <typename REAL>
class WeightTable {
public:
WeightTable(int coarseVerts,
bool genCtrlVertStencils,
bool compactWeights)
: _size(0)
, _lastOffset(0)
, _coarseVertCount(coarseVerts)
, _compactWeights(compactWeights)
{
// These numbers were chosen by profiling production assets at uniform
// level 3.
size_t n = std::max(coarseVerts,
std::min(int(5*1024*1024),
coarseVerts*2));
_dests.reserve(n);
_sources.reserve(n);
_weights.reserve(n);
if (!genCtrlVertStencils)
return;
// Generate trivial control vert stencils
_sources.resize(coarseVerts);
_weights.resize(coarseVerts);
_dests.resize(coarseVerts);
_indices.resize(coarseVerts);
_sizes.resize(coarseVerts);
for (int i = 0; i < coarseVerts; i++) {
_indices[i] = i;
_sizes[i] = 1;
_dests[i] = i;
_sources[i] = i;
_weights[i] = 1.0;
}
_size = static_cast<int>(_sources.size());
_lastOffset = _size - 1;
}
template <class W, class WACCUM>
void AddWithWeight(int src, int dest, W weight, WACCUM weights)
{
// Factorized stencils are expressed purely in terms of the control
// mesh verts. Without this flattening, level_i's weights would point
// to level_i-1, which would point to level_i-2, until the final level
// points to the control verts.
//
// So here, we check if the incoming vert (src) is in the control mesh,
// if it is, we can simply merge it without attempting to resolve it
// first.
if (src < _coarseVertCount) {
merge(src, dest, weight, W(1.0), _lastOffset, _size, weights);
return;
}
// src is not in the control mesh, so resolve all contributing coarse
// verts (src itself is made up of many control vert weights).
//
// Find the src stencil and number of contributing CVs.
int len = _sizes[src];
int start = _indices[src];
for (int i = start; i < start+len; i++) {
// Invariant: by processing each level in order and each vertex in
// dependent order, any src stencil vertex reference is guaranteed
// to consist only of coarse verts: therefore resolving src verts
// must yield verts in the coarse mesh.
assert(_sources[i] < _coarseVertCount);
// Merge each of src's contributing verts into this stencil.
merge(_sources[i], dest, weights.Get(i), weight,
_lastOffset, _size, weights);
}
}
class Point1stDerivAccumulator {
WeightTable* _tbl;
public:
Point1stDerivAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(Point1stDerivWeight<REAL> weight) {
_tbl->_weights.push_back(weight.p);
_tbl->_duWeights.push_back(weight.du);
_tbl->_dvWeights.push_back(weight.dv);
}
void Add(size_t i, Point1stDerivWeight<REAL> weight) {
_tbl->_weights[i] += weight.p;
_tbl->_duWeights[i] += weight.du;
_tbl->_dvWeights[i] += weight.dv;
}
Point1stDerivWeight<REAL> Get(size_t index) {
return Point1stDerivWeight<REAL>(_tbl->_weights[index],
_tbl->_duWeights[index],
_tbl->_dvWeights[index]);
}
};
Point1stDerivAccumulator GetPoint1stDerivAccumulator() {
return Point1stDerivAccumulator(this);
};
class Point2ndDerivAccumulator {
WeightTable* _tbl;
public:
Point2ndDerivAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(Point2ndDerivWeight<REAL> weight) {
_tbl->_weights.push_back(weight.p);
_tbl->_duWeights.push_back(weight.du);
_tbl->_dvWeights.push_back(weight.dv);
_tbl->_duuWeights.push_back(weight.duu);
_tbl->_duvWeights.push_back(weight.duv);
_tbl->_dvvWeights.push_back(weight.dvv);
}
void Add(size_t i, Point2ndDerivWeight<REAL> weight) {
_tbl->_weights[i] += weight.p;
_tbl->_duWeights[i] += weight.du;
_tbl->_dvWeights[i] += weight.dv;
_tbl->_duuWeights[i] += weight.duu;
_tbl->_duvWeights[i] += weight.duv;
_tbl->_dvvWeights[i] += weight.dvv;
}
Point2ndDerivWeight<REAL> Get(size_t index) {
return Point2ndDerivWeight<REAL>(_tbl->_weights[index],
_tbl->_duWeights[index],
_tbl->_dvWeights[index],
_tbl->_duuWeights[index],
_tbl->_duvWeights[index],
_tbl->_dvvWeights[index]);
}
};
Point2ndDerivAccumulator GetPoint2ndDerivAccumulator() {
return Point2ndDerivAccumulator(this);
};
class ScalarAccumulator {
WeightTable* _tbl;
public:
ScalarAccumulator(WeightTable* tbl) : _tbl(tbl)
{ }
void PushBack(REAL weight) {
_tbl->_weights.push_back(weight);
}
void Add(size_t i, REAL w) {
_tbl->_weights[i] += w;
}
REAL Get(size_t index) {
return _tbl->_weights[index];
}
};
ScalarAccumulator GetScalarAccumulator() {
return ScalarAccumulator(this);
};
std::vector<int> const&
GetOffsets() const { return _indices; }
std::vector<int> const&
GetSizes() const { return _sizes; }
std::vector<int> const&
GetSources() const { return _sources; }
std::vector<REAL> const&
GetWeights() const { return _weights; }
std::vector<REAL> const&
GetDuWeights() const { return _duWeights; }
std::vector<REAL> const&
GetDvWeights() const { return _dvWeights; }
std::vector<REAL> const&
GetDuuWeights() const { return _duuWeights; }
std::vector<REAL> const&
GetDuvWeights() const { return _duvWeights; }
std::vector<REAL> const&
GetDvvWeights() const { return _dvvWeights; }
void SetCoarseVertCount(int numVerts) {
_coarseVertCount = numVerts;
}
private:
// Merge a vertex weight into the stencil table, if there is an existing
// weight for a given source vertex it will be combined.
//
// PERFORMANCE: caution, this function is super hot.
template <class W, class WACCUM>
void merge(int src, int dst, W weight,
// Delaying weight*factor multiplication hides memory latency of
// accessing weight[i], yielding more stable performance.
W weightFactor,
// Similarly, passing offset & tableSize as params yields higher
// performance than accessing the class members directly.
int lastOffset, int tableSize, WACCUM weights)
{
// The lastOffset is the vertex we're currently processing, by
// leveraging this we need not lookup the dest stencil size or offset.
//
// Additionally, if the client does not want the resulting verts
// compacted, do not attempt to combine weights.
if (_compactWeights && !_dests.empty() && _dests[lastOffset] == dst) {
// tableSize is exactly _sources.size(), but using tableSize is
// significantly faster.
for (int i = lastOffset; i < tableSize; i++) {
// If we find an existing vertex that matches src, we need to
// combine the weights to avoid duplicate entries for src.
if (_sources[i] == src) {
weights.Add(i, weight*weightFactor);
return;
}
}
}
// We haven't seen src yet, insert it as a new vertex weight.
add(src, dst, weight*weightFactor, weights);
}
// Add a new vertex weight to the stencil table.
template <class W, class WACCUM>
void add(int src, int dst, W weight, WACCUM weights)
{
// The _dests array has num(weights) elements mapping each individual
// element back to a specific stencil. The array is constructed in such
// a way that the current stencil being built is always at the end of
// the array, so if the dests array is empty or back() doesn't match
// dst, then we just started building a new stencil.
if (_dests.empty() || dst != _dests.back()) {
// _indices and _sizes always have num(stencils) elements so that
// stencils can be directly looked up by their index in these
// arrays. So here, ensure that they are large enough to hold the
// new stencil about to be built.
if (dst+1 > (int)_indices.size()) {
_indices.resize(dst+1);
_sizes.resize(dst+1);
}
// Initialize the new stencil's meta-data (offset, size).
_indices[dst] = static_cast<int>(_sources.size());
_sizes[dst] = 0;
// Keep track of where the current stencil begins, which lets us
// avoid having to look it up later.
_lastOffset = static_cast<int>(_sources.size());
}
// Cache the number of elements as an optimization, it's faster than
// calling size() on any of the vectors.
_size++;
// Increment the current stencil element size.
_sizes[dst]++;
// Track this element as belonging to the stencil "dst".
_dests.push_back(dst);
// Store the actual stencil data.
_sources.push_back(src);
weights.PushBack(weight);
}
// The following vectors are explicitly stored as non-interleaved elements
// to reduce cache misses.
// Stencil to destination vertex map.
std::vector<int> _dests;
// The actual stencil data.
std::vector<int> _sources;
std::vector<REAL> _weights;
std::vector<REAL> _duWeights;
std::vector<REAL> _dvWeights;
std::vector<REAL> _duuWeights;
std::vector<REAL> _duvWeights;
std::vector<REAL> _dvvWeights;
// Index data used to recover stencil-to-vertex mapping.
std::vector<int> _indices;
std::vector<int> _sizes;
// Acceleration members to avoid pointer chasing and reverse loops.
int _size;
int _lastOffset;
int _coarseVertCount;
bool _compactWeights;
};
template <typename REAL>
StencilBuilder<REAL>::StencilBuilder(int coarseVertCount,
bool genCtrlVertStencils,
bool compactWeights)
: _weightTable(new WeightTable<REAL>(coarseVertCount,
genCtrlVertStencils,
compactWeights))
{
}
template <typename REAL>
StencilBuilder<REAL>::~StencilBuilder()
{
delete _weightTable;
}
template <typename REAL>
size_t
StencilBuilder<REAL>::GetNumVerticesTotal() const
{
return _weightTable->GetWeights().size();
}
template <typename REAL>
int
StencilBuilder<REAL>::GetNumVertsInStencil(size_t stencilIndex) const
{
if (stencilIndex > _weightTable->GetSizes().size() - 1)
return 0;
return (int)_weightTable->GetSizes()[stencilIndex];
}
template <typename REAL>
void
StencilBuilder<REAL>::SetCoarseVertCount(int numVerts)
{
_weightTable->SetCoarseVertCount(numVerts);
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilOffsets() const {
return _weightTable->GetOffsets();
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilSizes() const {
return _weightTable->GetSizes();
}
template <typename REAL>
std::vector<int> const&
StencilBuilder<REAL>::GetStencilSources() const {
return _weightTable->GetSources();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilWeights() const {
return _weightTable->GetWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuWeights() const {
return _weightTable->GetDuWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDvWeights() const {
return _weightTable->GetDvWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuuWeights() const {
return _weightTable->GetDuuWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDuvWeights() const {
return _weightTable->GetDuvWeights();
}
template <typename REAL>
std::vector<REAL> const&
StencilBuilder<REAL>::GetStencilDvvWeights() const {
return _weightTable->GetDvvWeights();
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(Index const & src, REAL weight)
{
// Ignore no-op weights.
if (isWeightZero(weight)) {
return;
}
_owner->_weightTable->AddWithWeight(src._index, _index, weight,
_owner->_weightTable->GetScalarAccumulator());
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src, REAL weight)
{
if (isWeightZero(weight)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
REAL wgt = weight * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetScalarAccumulator());
}
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv)
{
if (isWeightZero(weight) && isWeightZero(du) && isWeightZero(dv)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
Point1stDerivWeight<REAL> wgt = Point1stDerivWeight<REAL>(weight, du, dv) * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetPoint1stDerivAccumulator());
}
}
template <typename REAL>
void
StencilBuilder<REAL>::Index::AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv, REAL duu, REAL duv, REAL dvv)
{
if (isWeightZero(weight) && isWeightZero(du) && isWeightZero(dv) &&
isWeightZero(duu) && isWeightZero(duv) && isWeightZero(dvv)) {
return;
}
int srcSize = *src.GetSizePtr();
Vtr::Index const * srcIndices = src.GetVertexIndices();
REAL const * srcWeights = src.GetWeights();
for (int i = 0; i < srcSize; ++i) {
REAL w = srcWeights[i];
if (isWeightZero(w)) {
continue;
}
Vtr::Index srcIndex = srcIndices[i];
Point2ndDerivWeight<REAL> wgt = Point2ndDerivWeight<REAL>(weight, du, dv, duu, duv, dvv) * w;
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
_owner->_weightTable->GetPoint2ndDerivAccumulator());
}
}
template class StencilBuilder<float>;
template class StencilBuilder<double>;
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,98 @@
//
// Copyright 2015 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_STENCILBUILDER_H
#define OPENSUBDIV3_FAR_STENCILBUILDER_H
#include <vector>
#include "../version.h"
#include "../far/stencilTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace internal {
template <typename REAL> class WeightTable;
template <typename REAL>
class StencilBuilder {
public:
StencilBuilder(int coarseVertCount,
bool genCtrlVertStencils=true,
bool compactWeights=true);
~StencilBuilder();
// TODO: noncopyable.
size_t GetNumVerticesTotal() const;
int GetNumVertsInStencil(size_t stencilIndex) const;
void SetCoarseVertCount(int numVerts);
// Mapping from stencil[i] to its starting offset in the sources[] and weights[] arrays;
std::vector<int> const& GetStencilOffsets() const;
// The number of contributing sources and weights in stencil[i]
std::vector<int> const& GetStencilSizes() const;
// The absolute source vertex offsets.
std::vector<int> const& GetStencilSources() const;
// The individual vertex weights, each weight is paired with one source.
std::vector<REAL> const& GetStencilWeights() const;
std::vector<REAL> const& GetStencilDuWeights() const;
std::vector<REAL> const& GetStencilDvWeights() const;
std::vector<REAL> const& GetStencilDuuWeights() const;
std::vector<REAL> const& GetStencilDuvWeights() const;
std::vector<REAL> const& GetStencilDvvWeights() const;
// Vertex Facade.
class Index {
public:
Index(StencilBuilder* owner, int index)
: _owner(owner)
, _index(index)
{}
// Add with point/vertex weight only.
void AddWithWeight(Index const & src, REAL weight);
void AddWithWeight(StencilReal<REAL> const& src, REAL weight);
// Add with first derivative.
void AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv);
// Add with first and second derivatives.
void AddWithWeight(StencilReal<REAL> const& src,
REAL weight, REAL du, REAL dv, REAL duu, REAL duv, REAL dvv);
Index operator[](int index) const {
return Index(_owner, index+_index);
}
int GetOffset() const { return _index; }
void Clear() {/*nothing to do here*/}
private:
StencilBuilder* _owner;
int _index;
};
private:
WeightTable<REAL>* _weightTable;
};
} // end namespace internal
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv
#endif // FAR_STENCILBUILDER_H

View File

@@ -0,0 +1,217 @@
//
// Copyright 2015 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../version.h"
#include "../far/stencilTable.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
namespace {
template <typename REAL>
void
copyStencilData(int numControlVerts,
bool includeCoarseVerts,
size_t firstOffset,
std::vector<int> const* offsets,
std::vector<int> * _offsets,
std::vector<int> const* sizes,
std::vector<int> * _sizes,
std::vector<int> const* sources,
std::vector<int> * _sources,
std::vector<REAL> const* weights,
std::vector<REAL> * _weights,
std::vector<REAL> const* duWeights=NULL,
std::vector<REAL> * _duWeights=NULL,
std::vector<REAL> const* dvWeights=NULL,
std::vector<REAL> * _dvWeights=NULL,
std::vector<REAL> const* duuWeights=NULL,
std::vector<REAL> * _duuWeights=NULL,
std::vector<REAL> const* duvWeights=NULL,
std::vector<REAL> * _duvWeights=NULL,
std::vector<REAL> const* dvvWeights=NULL,
std::vector<REAL> * _dvvWeights=NULL) {
size_t start = includeCoarseVerts ? 0 : firstOffset;
_offsets->resize(offsets->size());
_sizes->resize(sizes->size());
_sources->resize(sources->size());
_weights->resize(weights->size());
if (_duWeights)
_duWeights->resize(duWeights->size());
if (_dvWeights)
_dvWeights->resize(dvWeights->size());
if (_duuWeights)
_duuWeights->resize(duuWeights->size());
if (_duvWeights)
_duvWeights->resize(duvWeights->size());
if (_dvvWeights)
_dvvWeights->resize(dvvWeights->size());
// The stencils are probably not in order, so we must copy/sort them.
// Note here that loop index 'i' represents stencil_i for vertex_i.
int curOffset = 0;
size_t stencilCount = 0,
weightCount = 0;
for ( size_t i=start; i<offsets->size(); i++ ) {
// Once we've copied out all the control verts, jump to the offset
// where the actual stencils begin.
if (includeCoarseVerts && (int)i == numControlVerts)
i = firstOffset;
// Copy the stencil.
int sz = (*sizes)[i];
int off = (*offsets)[i];
(*_offsets)[stencilCount] = curOffset;
(*_sizes)[stencilCount] = sz;
std::memcpy(&(*_sources)[curOffset],
&(*sources)[off], sz*sizeof(int));
std::memcpy(&(*_weights)[curOffset],
&(*weights)[off], sz*sizeof(REAL));
if (_duWeights && !_duWeights->empty()) {
std::memcpy(&(*_duWeights)[curOffset],
&(*duWeights)[off], sz*sizeof(REAL));
}
if (_dvWeights && !_dvWeights->empty()) {
std::memcpy(&(*_dvWeights)[curOffset],
&(*dvWeights)[off], sz*sizeof(REAL));
}
if (_duuWeights && !_duuWeights->empty()) {
std::memcpy(&(*_duuWeights)[curOffset],
&(*duuWeights)[off], sz*sizeof(REAL));
}
if (_duvWeights && !_duvWeights->empty()) {
std::memcpy(&(*_duvWeights)[curOffset],
&(*duvWeights)[off], sz*sizeof(REAL));
}
if (_dvvWeights && !_dvvWeights->empty()) {
std::memcpy(&(*_dvvWeights)[curOffset],
&(*dvvWeights)[off], sz*sizeof(REAL));
}
curOffset += sz;
stencilCount++;
weightCount += sz;
}
_offsets->resize(stencilCount);
_sizes->resize(stencilCount);
_sources->resize(weightCount);
if (_duWeights && !_duWeights->empty())
_duWeights->resize(weightCount);
if (_dvWeights && !_dvWeights->empty())
_dvWeights->resize(weightCount);
if (_duuWeights && !_duuWeights->empty())
_duuWeights->resize(weightCount);
if (_duvWeights && !_duvWeights->empty())
_duvWeights->resize(weightCount);
if (_dvvWeights && !_dvvWeights->empty())
_dvvWeights->resize(weightCount);
}
};
template <typename REAL>
StencilTableReal<REAL>::StencilTableReal(int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<REAL> const& weights,
bool includeCoarseVerts,
size_t firstOffset)
: _numControlVertices(numControlVerts) {
copyStencilData(numControlVerts,
includeCoarseVerts,
firstOffset,
&offsets, &_offsets,
&sizes, &_sizes,
&sources, &_indices,
&weights, &_weights);
}
template <typename REAL>
void
StencilTableReal<REAL>::Clear() {
_numControlVertices=0;
_sizes.clear();
_offsets.clear();
_indices.clear();
_weights.clear();
}
template <typename REAL>
LimitStencilTableReal<REAL>::LimitStencilTableReal(
int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<REAL> const& weights,
std::vector<REAL> const& duWeights,
std::vector<REAL> const& dvWeights,
std::vector<REAL> const& duuWeights,
std::vector<REAL> const& duvWeights,
std::vector<REAL> const& dvvWeights,
bool includeCoarseVerts,
size_t firstOffset)
: StencilTableReal<REAL>(numControlVerts) {
copyStencilData(numControlVerts,
includeCoarseVerts,
firstOffset,
&offsets, &this->_offsets,
&sizes, &this->_sizes,
&sources, &this->_indices,
&weights, &this->_weights,
&duWeights, &_duWeights,
&dvWeights, &_dvWeights,
&duuWeights, &_duuWeights,
&duvWeights, &_duvWeights,
&dvvWeights, &_dvvWeights);
}
template <typename REAL>
void
LimitStencilTableReal<REAL>::Clear() {
StencilTableReal<REAL>::Clear();
_duWeights.clear();
_dvWeights.clear();
_duuWeights.clear();
_duvWeights.clear();
_dvvWeights.clear();
}
//
// Explicit instantiation for float and double:
//
template class StencilReal<float>;
template class StencilReal<double>;
template class LimitStencilReal<float>;
template class LimitStencilReal<double>;
template class StencilTableReal<float>;
template class StencilTableReal<double>;
template class LimitStencilTableReal<float>;
template class LimitStencilTableReal<double>;
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,776 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_STENCILTABLE_H
#define OPENSUBDIV3_FAR_STENCILTABLE_H
#include "../version.h"
#include "../far/types.h"
#include <cassert>
#include <cstring>
#include <vector>
#include <iostream>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
// Forward declarations for friends:
class PatchTableBuilder;
template <typename REAL> class StencilTableFactoryReal;
template <typename REAL> class LimitStencilTableFactoryReal;
/// \brief Vertex stencil descriptor
///
/// Allows access and manipulation of a single stencil in a StencilTable.
///
template <typename REAL>
class StencilReal {
public:
/// \brief Default constructor
StencilReal() {}
/// \brief Constructor
///
/// @param size Table pointer to the size of the stencil
///
/// @param indices Table pointer to the vertex indices of the stencil
///
/// @param weights Table pointer to the vertex weights of the stencil
///
StencilReal(int * size, Index * indices, REAL * weights)
: _size(size), _indices(indices), _weights(weights) { }
/// \brief Copy constructor
StencilReal(StencilReal const & other) {
_size = other._size;
_indices = other._indices;
_weights = other._weights;
}
/// \brief Returns the size of the stencil
int GetSize() const {
return *_size;
}
/// \brief Returns the size of the stencil as a pointer
int * GetSizePtr() const {
return _size;
}
/// \brief Returns the control vertices' indices
Index const * GetVertexIndices() const {
return _indices;
}
/// \brief Returns the interpolation weights
REAL const * GetWeights() const {
return _weights;
}
/// \brief Advance to the next stencil in the table
void Next() {
int stride = *_size;
++_size;
_indices += stride;
_weights += stride;
}
protected:
friend class StencilTableFactoryReal<REAL>;
friend class LimitStencilTableFactoryReal<REAL>;
int * _size;
Index * _indices;
REAL * _weights;
};
/// \brief Vertex stencil class wrapping the template for compatibility.
///
class Stencil : public StencilReal<float> {
protected:
typedef StencilReal<float> BaseStencil;
public:
Stencil() : BaseStencil() { }
Stencil(BaseStencil const & other) : BaseStencil(other) { }
Stencil(int * size, Index * indices, float * weights)
: BaseStencil(size, indices, weights) { }
};
/// \brief Table of subdivision stencils.
///
/// Stencils are the most direct method of evaluation of locations on the limit
/// of a surface. Every point of a limit surface can be computed by linearly
/// blending a collection of coarse control vertices.
///
/// A stencil assigns a series of control vertex indices with a blending weight
/// that corresponds to a unique parametric location of the limit surface. When
/// the control vertices move in space, the limit location can be very efficiently
/// recomputed simply by applying the blending weights to the series of coarse
/// control vertices.
///
template <typename REAL>
class StencilTableReal {
protected:
StencilTableReal(int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<REAL> const& weights,
bool includeCoarseVerts,
size_t firstOffset);
public:
virtual ~StencilTableReal() {};
/// \brief Returns the number of stencils in the table
int GetNumStencils() const {
return (int)_sizes.size();
}
/// \brief Returns the number of control vertices indexed in the table
int GetNumControlVertices() const {
return _numControlVertices;
}
/// \brief Returns a Stencil at index i in the table
StencilReal<REAL> GetStencil(Index i) const;
/// \brief Returns the number of control vertices of each stencil in the table
std::vector<int> const & GetSizes() const {
return _sizes;
}
/// \brief Returns the offset to a given stencil (factory may leave empty)
std::vector<Index> const & GetOffsets() const {
return _offsets;
}
/// \brief Returns the indices of the control vertices
std::vector<Index> const & GetControlIndices() const {
return _indices;
}
/// \brief Returns the stencil interpolation weights
std::vector<REAL> const & GetWeights() const {
return _weights;
}
/// \brief Returns the stencil at index i in the table
StencilReal<REAL> operator[] (Index index) const;
/// \brief Updates point values based on the control values
///
/// \note The destination buffers are assumed to have allocated at least
/// \c GetNumStencils() elements.
///
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param dstValues Destination buffer for the interpolated primvar data
///
/// @param start Index of first destination value to update
///
/// @param end Index of last destination value to update
///
template <class T, class U>
void UpdateValues(T const &srcValues, U &dstValues, Index start=-1, Index end=-1) const {
this->update(srcValues, dstValues, _weights, start, end);
}
template <class T1, class T2, class U>
void UpdateValues(T1 const &srcBase, int numBase, T2 const &srcRef,
U &dstValues, Index start=-1, Index end=-1) const {
this->update(srcBase, numBase, srcRef, dstValues, _weights, start, end);
}
// Pointer interface for backward compatibility
template <class T, class U>
void UpdateValues(T const *src, U *dst, Index start=-1, Index end=-1) const {
this->update(src, dst, _weights, start, end);
}
template <class T1, class T2, class U>
void UpdateValues(T1 const *srcBase, int numBase, T2 const *srcRef,
U *dst, Index start=-1, Index end=-1) const {
this->update(srcBase, numBase, srcRef, dst, _weights, start, end);
}
/// \brief Clears the stencils from the table
void Clear();
protected:
// Update values by applying cached stencil weights to new control values
template <class T, class U>
void update( T const &srcValues, U &dstValues,
std::vector<REAL> const & valueWeights, Index start, Index end) const;
template <class T1, class T2, class U>
void update( T1 const &srcBase, int numBase, T2 const &srcRef, U &dstValues,
std::vector<REAL> const & valueWeights, Index start, Index end) const;
// Populate the offsets table from the stencil sizes in _sizes (factory helper)
void generateOffsets();
// Resize the table arrays (factory helper)
void resize(int nstencils, int nelems);
// Reserves the table arrays (factory helper)
void reserve(int nstencils, int nelems);
// Reallocates the table arrays to remove excess capacity (factory helper)
void shrinkToFit();
// Performs any final operations on internal tables (factory helper)
void finalize();
protected:
StencilTableReal() : _numControlVertices(0) {}
StencilTableReal(int numControlVerts)
: _numControlVertices(numControlVerts)
{ }
friend class StencilTableFactoryReal<REAL>;
friend class Far::PatchTableBuilder;
int _numControlVertices; // number of control vertices
std::vector<int> _sizes; // number of coefficients for each stencil
std::vector<Index> _offsets, // offset to the start of each stencil
_indices; // indices of contributing coarse vertices
std::vector<REAL> _weights; // stencil weight coefficients
};
/// \brief Stencil table class wrapping the template for compatibility.
///
class StencilTable : public StencilTableReal<float> {
protected:
typedef StencilTableReal<float> BaseTable;
public:
Stencil GetStencil(Index index) const {
return Stencil(BaseTable::GetStencil(index));
}
Stencil operator[] (Index index) const {
return Stencil(BaseTable::GetStencil(index));
}
protected:
StencilTable() : BaseTable() { }
StencilTable(int numControlVerts) : BaseTable(numControlVerts) { }
StencilTable(int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<float> const& weights,
bool includeCoarseVerts,
size_t firstOffset)
: BaseTable(numControlVerts, offsets,
sizes, sources, weights, includeCoarseVerts, firstOffset) { }
};
/// \brief Limit point stencil descriptor
///
template <typename REAL>
class LimitStencilReal : public StencilReal<REAL> {
public:
/// \brief Constructor
///
/// @param size Table pointer to the size of the stencil
///
/// @param indices Table pointer to the vertex indices of the stencil
///
/// @param weights Table pointer to the vertex weights of the stencil
///
/// @param duWeights Table pointer to the 'u' derivative weights
///
/// @param dvWeights Table pointer to the 'v' derivative weights
///
/// @param duuWeights Table pointer to the 'uu' derivative weights
///
/// @param duvWeights Table pointer to the 'uv' derivative weights
///
/// @param dvvWeights Table pointer to the 'vv' derivative weights
///
LimitStencilReal( int* size,
Index * indices,
REAL * weights,
REAL * duWeights=0,
REAL * dvWeights=0,
REAL * duuWeights=0,
REAL * duvWeights=0,
REAL * dvvWeights=0)
: StencilReal<REAL>(size, indices, weights),
_duWeights(duWeights),
_dvWeights(dvWeights),
_duuWeights(duuWeights),
_duvWeights(duvWeights),
_dvvWeights(dvvWeights) {
}
/// \brief Returns the u derivative weights
REAL const * GetDuWeights() const {
return _duWeights;
}
/// \brief Returns the v derivative weights
REAL const * GetDvWeights() const {
return _dvWeights;
}
/// \brief Returns the uu derivative weights
REAL const * GetDuuWeights() const {
return _duuWeights;
}
/// \brief Returns the uv derivative weights
REAL const * GetDuvWeights() const {
return _duvWeights;
}
/// \brief Returns the vv derivative weights
REAL const * GetDvvWeights() const {
return _dvvWeights;
}
/// \brief Advance to the next stencil in the table
void Next() {
int stride = *this->_size;
++this->_size;
this->_indices += stride;
this->_weights += stride;
if (_duWeights) _duWeights += stride;
if (_dvWeights) _dvWeights += stride;
if (_duuWeights) _duuWeights += stride;
if (_duvWeights) _duvWeights += stride;
if (_dvvWeights) _dvvWeights += stride;
}
private:
friend class StencilTableFactoryReal<REAL>;
friend class LimitStencilTableFactoryReal<REAL>;
REAL * _duWeights, // pointer to stencil u derivative limit weights
* _dvWeights, // pointer to stencil v derivative limit weights
* _duuWeights, // pointer to stencil uu derivative limit weights
* _duvWeights, // pointer to stencil uv derivative limit weights
* _dvvWeights; // pointer to stencil vv derivative limit weights
};
/// \brief Limit point stencil class wrapping the template for compatibility.
///
class LimitStencil : public LimitStencilReal<float> {
protected:
typedef LimitStencilReal<float> BaseStencil;
public:
LimitStencil(BaseStencil const & other) : BaseStencil(other) { }
LimitStencil(int* size, Index * indices, float * weights,
float * duWeights=0, float * dvWeights=0,
float * duuWeights=0, float * duvWeights=0, float * dvvWeights=0)
: BaseStencil(size, indices, weights,
duWeights, dvWeights, duuWeights, duvWeights, dvvWeights) { }
};
/// \brief Table of limit subdivision stencils.
///
template <typename REAL>
class LimitStencilTableReal : public StencilTableReal<REAL> {
protected:
LimitStencilTableReal(
int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<REAL> const& weights,
std::vector<REAL> const& duWeights,
std::vector<REAL> const& dvWeights,
std::vector<REAL> const& duuWeights,
std::vector<REAL> const& duvWeights,
std::vector<REAL> const& dvvWeights,
bool includeCoarseVerts,
size_t firstOffset);
public:
/// \brief Returns a LimitStencil at index i in the table
LimitStencilReal<REAL> GetLimitStencil(Index i) const;
/// \brief Returns the limit stencil at index i in the table
LimitStencilReal<REAL> operator[] (Index index) const;
/// \brief Returns the 'u' derivative stencil interpolation weights
std::vector<REAL> const & GetDuWeights() const {
return _duWeights;
}
/// \brief Returns the 'v' derivative stencil interpolation weights
std::vector<REAL> const & GetDvWeights() const {
return _dvWeights;
}
/// \brief Returns the 'uu' derivative stencil interpolation weights
std::vector<REAL> const & GetDuuWeights() const {
return _duuWeights;
}
/// \brief Returns the 'uv' derivative stencil interpolation weights
std::vector<REAL> const & GetDuvWeights() const {
return _duvWeights;
}
/// \brief Returns the 'vv' derivative stencil interpolation weights
std::vector<REAL> const & GetDvvWeights() const {
return _dvvWeights;
}
/// \brief Updates derivative values based on the control values
///
/// \note The destination buffers ('uderivs' & 'vderivs') are assumed to
/// have allocated at least \c GetNumStencils() elements.
///
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param uderivs Destination buffer for the interpolated 'u'
/// derivative primvar data
///
/// @param vderivs Destination buffer for the interpolated 'v'
/// derivative primvar data
///
/// @param start Index of first destination derivative to update
///
/// @param end Index of last destination derivative to update
///
template <class T, class U>
void UpdateDerivs(T const & srcValues, U & uderivs, U & vderivs,
int start=-1, int end=-1) const {
this->update(srcValues, uderivs, _duWeights, start, end);
this->update(srcValues, vderivs, _dvWeights, start, end);
}
template <class T1, class T2, class U>
void UpdateDerivs(T1 const & srcBase, int numBase, T2 const & srcRef,
U & uderivs, U & vderivs, int start=-1, int end=-1) const {
this->update(srcBase, numBase, srcRef, uderivs, _duWeights, start, end);
this->update(srcBase, numBase, srcRef, vderivs, _dvWeights, start, end);
}
// Pointer interface for backward compatibility
template <class T, class U>
void UpdateDerivs(T const *src, U *uderivs, U *vderivs,
int start=-1, int end=-1) const {
this->update(src, uderivs, _duWeights, start, end);
this->update(src, vderivs, _dvWeights, start, end);
}
template <class T1, class T2, class U>
void UpdateDerivs(T1 const *srcBase, int numBase, T2 const *srcRef,
U *uderivs, U *vderivs, int start=-1, int end=-1) const {
this->update(srcBase, numBase, srcRef, uderivs, _duWeights, start, end);
this->update(srcBase, numBase, srcRef, vderivs, _dvWeights, start, end);
}
/// \brief Updates 2nd derivative values based on the control values
///
/// \note The destination buffers ('uuderivs', 'uvderivs', & 'vderivs') are
/// assumed to have allocated at least \c GetNumStencils() elements.
///
/// @param srcValues Buffer with primvar data for the control vertices
///
/// @param uuderivs Destination buffer for the interpolated 'uu'
/// derivative primvar data
///
/// @param uvderivs Destination buffer for the interpolated 'uv'
/// derivative primvar data
///
/// @param vvderivs Destination buffer for the interpolated 'vv'
/// derivative primvar data
///
/// @param start Index of first destination derivative to update
///
/// @param end Index of last destination derivative to update
///
template <class T, class U>
void Update2ndDerivs(T const & srcValues,
U & uuderivs, U & uvderivs, U & vvderivs,
int start=-1, int end=-1) const {
this->update(srcValues, uuderivs, _duuWeights, start, end);
this->update(srcValues, uvderivs, _duvWeights, start, end);
this->update(srcValues, vvderivs, _dvvWeights, start, end);
}
template <class T1, class T2, class U>
void Update2ndDerivs(T1 const & srcBase, int numBase, T2 const & srcRef,
U & uuderivs, U & uvderivs, U & vvderivs, int start=-1, int end=-1) const {
this->update(srcBase, numBase, srcRef, uuderivs, _duuWeights, start, end);
this->update(srcBase, numBase, srcRef, uvderivs, _duvWeights, start, end);
this->update(srcBase, numBase, srcRef, vvderivs, _dvvWeights, start, end);
}
// Pointer interface for backward compatibility
template <class T, class U>
void Update2ndDerivs(T const *src, T *uuderivs, U *uvderivs, U *vvderivs,
int start=-1, int end=-1) const {
this->update(src, uuderivs, _duuWeights, start, end);
this->update(src, uvderivs, _duvWeights, start, end);
this->update(src, vvderivs, _dvvWeights, start, end);
}
template <class T1, class T2, class U>
void Update2ndDerivs(T1 const *srcBase, int numBase, T2 const *srcRef,
U *uuderivs, U *uvderivs, U *vvderivs, int start=-1, int end=-1) const {
this->update(srcBase, numBase, srcRef, uuderivs, _duuWeights, start, end);
this->update(srcBase, numBase, srcRef, uvderivs, _duvWeights, start, end);
this->update(srcBase, numBase, srcRef, vvderivs, _dvvWeights, start, end);
}
/// \brief Clears the stencils from the table
void Clear();
private:
friend class LimitStencilTableFactoryReal<REAL>;
// Resize the table arrays (factory helper)
void resize(int nstencils, int nelems);
private:
std::vector<REAL> _duWeights, // u derivative limit stencil weights
_dvWeights, // v derivative limit stencil weights
_duuWeights, // uu derivative limit stencil weights
_duvWeights, // uv derivative limit stencil weights
_dvvWeights; // vv derivative limit stencil weights
};
/// \brief Limit stencil table class wrapping the template for compatibility.
///
class LimitStencilTable : public LimitStencilTableReal<float> {
protected:
typedef LimitStencilTableReal<float> BaseTable;
public:
LimitStencil GetLimitStencil(Index index) const {
return LimitStencil(BaseTable::GetLimitStencil(index));
}
LimitStencil operator[] (Index index) const {
return LimitStencil(BaseTable::GetLimitStencil(index));
}
protected:
LimitStencilTable(int numControlVerts,
std::vector<int> const& offsets,
std::vector<int> const& sizes,
std::vector<int> const& sources,
std::vector<float> const& weights,
std::vector<float> const& duWeights,
std::vector<float> const& dvWeights,
std::vector<float> const& duuWeights,
std::vector<float> const& duvWeights,
std::vector<float> const& dvvWeights,
bool includeCoarseVerts,
size_t firstOffset)
: BaseTable(numControlVerts,
offsets, sizes, sources, weights,
duWeights, dvWeights, duuWeights, duvWeights, dvvWeights,
includeCoarseVerts, firstOffset) { }
};
// Update values by applying cached stencil weights to new control values
template <typename REAL>
template <class T1, class T2, class U> void
StencilTableReal<REAL>::update(T1 const &srcBase, int numBase,
T2 const &srcRef, U &dstValues,
std::vector<REAL> const &valueWeights, Index start, Index end) const {
int const * sizes = &_sizes.at(0);
Index const * indices = &_indices.at(0);
REAL const * weights = &valueWeights.at(0);
if (start > 0) {
assert(start < (Index)_offsets.size());
sizes += start;
indices += _offsets[start];
weights += _offsets[start];
} else {
start = 0;
}
int nstencils = ((end < start) ? GetNumStencils() : end) - start;
for (int i = 0; i < nstencils; ++i, ++sizes) {
dstValues[start + i].Clear();
for (int j = 0; j < *sizes; ++j, ++indices, ++weights) {
if (*indices < numBase) {
dstValues[start + i].AddWithWeight(srcBase[*indices], *weights);
} else {
dstValues[start + i].AddWithWeight(srcRef[*indices - numBase], *weights);
}
}
}
}
template <typename REAL>
template <class T, class U> void
StencilTableReal<REAL>::update(T const &srcValues, U &dstValues,
std::vector<REAL> const &valueWeights, Index start, Index end) const {
int const * sizes = &_sizes.at(0);
Index const * indices = &_indices.at(0);
REAL const * weights = &valueWeights.at(0);
if (start > 0) {
assert(start < (Index)_offsets.size());
sizes += start;
indices += _offsets[start];
weights += _offsets[start];
} else {
start = 0;
}
int nstencils = ((end < start) ? GetNumStencils() : end) - start;
for (int i = 0; i < nstencils; ++i, ++sizes) {
dstValues[start + i].Clear();
for (int j = 0; j < *sizes; ++j, ++indices, ++weights) {
dstValues[start + i].AddWithWeight(srcValues[*indices], *weights);
}
}
}
template <typename REAL>
inline void
StencilTableReal<REAL>::generateOffsets() {
Index offset=0;
int noffsets = (int)_sizes.size();
_offsets.resize(noffsets);
for (int i=0; i<(int)_sizes.size(); ++i ) {
_offsets[i]=offset;
offset+=_sizes[i];
}
}
template <typename REAL>
inline void
StencilTableReal<REAL>::resize(int nstencils, int nelems) {
_sizes.resize(nstencils);
_indices.resize(nelems);
_weights.resize(nelems);
}
template <typename REAL>
inline void
StencilTableReal<REAL>::reserve(int nstencils, int nelems) {
_sizes.reserve(nstencils);
_indices.reserve(nelems);
_weights.reserve(nelems);
}
template <typename REAL>
inline void
StencilTableReal<REAL>::shrinkToFit() {
std::vector<int>(_sizes).swap(_sizes);
std::vector<Index>(_indices).swap(_indices);
std::vector<REAL>(_weights).swap(_weights);
}
template <typename REAL>
inline void
StencilTableReal<REAL>::finalize() {
shrinkToFit();
generateOffsets();
}
// Returns a Stencil at index i in the table
template <typename REAL>
inline StencilReal<REAL>
StencilTableReal<REAL>::GetStencil(Index i) const {
assert((! _offsets.empty()) && i<(int)_offsets.size());
Index ofs = _offsets[i];
return StencilReal<REAL>(const_cast<int*>(&_sizes[i]),
const_cast<Index*>(&_indices[ofs]),
const_cast<REAL*>(&_weights[ofs]));
}
template <typename REAL>
inline StencilReal<REAL>
StencilTableReal<REAL>::operator[] (Index index) const {
return GetStencil(index);
}
template <typename REAL>
inline void
LimitStencilTableReal<REAL>::resize(int nstencils, int nelems) {
StencilTableReal<REAL>::resize(nstencils, nelems);
_duWeights.resize(nelems);
_dvWeights.resize(nelems);
}
// Returns a LimitStencil at index i in the table
template <typename REAL>
inline LimitStencilReal<REAL>
LimitStencilTableReal<REAL>::GetLimitStencil(Index i) const {
assert((! this->GetOffsets().empty()) && i<(int)this->GetOffsets().size());
Index ofs = this->GetOffsets()[i];
if (!_duWeights.empty() && !_dvWeights.empty() &&
!_duuWeights.empty() && !_duvWeights.empty() && !_dvvWeights.empty()) {
return LimitStencilReal<REAL>(
const_cast<int *>(&this->GetSizes()[i]),
const_cast<Index *>(&this->GetControlIndices()[ofs]),
const_cast<REAL *>(&this->GetWeights()[ofs]),
const_cast<REAL *>(&GetDuWeights()[ofs]),
const_cast<REAL *>(&GetDvWeights()[ofs]),
const_cast<REAL *>(&GetDuuWeights()[ofs]),
const_cast<REAL *>(&GetDuvWeights()[ofs]),
const_cast<REAL *>(&GetDvvWeights()[ofs]) );
} else if (!_duWeights.empty() && !_dvWeights.empty()) {
return LimitStencilReal<REAL>(
const_cast<int *>(&this->GetSizes()[i]),
const_cast<Index *>(&this->GetControlIndices()[ofs]),
const_cast<REAL *>(&this->GetWeights()[ofs]),
const_cast<REAL *>(&GetDuWeights()[ofs]),
const_cast<REAL *>(&GetDvWeights()[ofs]) );
} else {
return LimitStencilReal<REAL>(
const_cast<int *>(&this->GetSizes()[i]),
const_cast<Index *>(&this->GetControlIndices()[ofs]),
const_cast<REAL *>(&this->GetWeights()[ofs]) );
}
}
template <typename REAL>
inline LimitStencilReal<REAL>
LimitStencilTableReal<REAL>::operator[] (Index index) const {
return GetLimitStencil(index);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_FAR_STENCILTABLE_H

View File

@@ -0,0 +1,660 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/stencilTableFactory.h"
#include "../far/stencilBuilder.h"
#include "../far/patchTable.h"
#include "../far/patchTableFactory.h"
#include "../far/patchMap.h"
#include "../far/topologyRefiner.h"
#include "../far/primvarRefiner.h"
#include <cassert>
#include <algorithm>
#include <iostream>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
using internal::StencilBuilder;
namespace {
#ifdef __INTEL_COMPILER
#pragma warning (push)
#pragma warning disable 1572
#endif
template <typename REAL>
inline bool isWeightZero(REAL w) { return (w == (REAL) 0.0); }
#ifdef __INTEL_COMPILER
#pragma warning (pop)
#endif
}
//------------------------------------------------------------------------------
template <typename REAL>
void
StencilTableFactoryReal<REAL>::generateControlVertStencils(
int numControlVerts,
StencilReal<REAL> & dst) {
// Control vertices contribute a single index with a weight of 1.0
for (int i=0; i<numControlVerts; ++i) {
*dst._size = 1;
*dst._indices = i;
*dst._weights = (REAL) 1.0;
dst.Next();
}
}
//
// StencilTable factory
//
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::Create(TopologyRefiner const & refiner,
Options options) {
bool interpolateVertex = options.interpolationMode==INTERPOLATE_VERTEX;
bool interpolateVarying = options.interpolationMode==INTERPOLATE_VARYING;
bool interpolateFaceVarying = options.interpolationMode==INTERPOLATE_FACE_VARYING;
int numControlVertices = !interpolateFaceVarying
? refiner.GetLevel(0).GetNumVertices()
: refiner.GetLevel(0).GetNumFVarValues(options.fvarChannel);
int maxlevel = std::min(int(options.maxLevel), refiner.GetMaxLevel());
if (maxlevel==0 && (! options.generateControlVerts)) {
StencilTableReal<REAL> * result = new StencilTableReal<REAL>;
result->_numControlVertices = numControlVertices;
return result;
}
StencilBuilder<REAL> builder(numControlVertices,
/*genControlVerts*/ true,
/*compactWeights*/ true);
//
// Interpolate stencils for each refinement level
//
PrimvarRefinerReal<REAL> primvarRefiner(refiner);
typename StencilBuilder<REAL>::Index srcIndex(&builder, 0);
typename StencilBuilder<REAL>::Index dstIndex(&builder, numControlVertices);
for (int level=1; level<=maxlevel; ++level) {
if (interpolateVertex) {
primvarRefiner.Interpolate(level, srcIndex, dstIndex);
} else if (interpolateVarying) {
primvarRefiner.InterpolateVarying(level, srcIndex, dstIndex);
} else {
primvarRefiner.InterpolateFaceVarying(level, srcIndex, dstIndex, options.fvarChannel);
}
if (options.factorizeIntermediateLevels) {
srcIndex = dstIndex;
}
int dstVertex = !interpolateFaceVarying
? refiner.GetLevel(level).GetNumVertices()
: refiner.GetLevel(level).GetNumFVarValues(options.fvarChannel);
dstIndex = dstIndex[dstVertex];
if (! options.factorizeIntermediateLevels) {
// All previous verts are considered as coarse verts, as a
// result, we don't update the srcIndex and update the coarse
// vertex count.
builder.SetCoarseVertCount(dstIndex.GetOffset());
}
}
size_t firstOffset = numControlVertices;
if (! options.generateIntermediateLevels)
firstOffset = srcIndex.GetOffset();
// Copy stencils from the StencilBuilder into the StencilTable.
// Always initialize numControlVertices (useful for torus case)
StencilTableReal<REAL> * result =
new StencilTableReal<REAL>(numControlVertices,
builder.GetStencilOffsets(),
builder.GetStencilSizes(),
builder.GetStencilSources(),
builder.GetStencilWeights(),
options.generateControlVerts,
firstOffset);
return result;
}
//------------------------------------------------------------------------------
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::Create(int numTables,
StencilTableReal<REAL> const ** tables) {
// XXXtakahito:
// This function returns NULL for empty inputs or erroneous condition.
// It's convenient for skipping varying stencils etc, however,
// other Create() API returns an empty stencil instead of NULL.
// They need to be consistent.
if ( (numTables<=0) || (! tables)) {
return NULL;
}
int ncvs = -1,
nstencils = 0,
nelems = 0;
for (int i=0; i<numTables; ++i) {
StencilTableReal<REAL> const * st = tables[i];
// allow the tables could have a null entry.
if (!st) continue;
if (ncvs >= 0 && st->GetNumControlVertices() != ncvs) {
return NULL;
}
ncvs = st->GetNumControlVertices();
nstencils += st->GetNumStencils();
nelems += (int)st->GetControlIndices().size();
}
if (ncvs == -1) {
return NULL;
}
StencilTableReal<REAL> * result = new StencilTableReal<REAL>;
result->resize(nstencils, nelems);
int * sizes = &result->_sizes[0];
Index * indices = &result->_indices[0];
REAL * weights = &result->_weights[0];
for (int i=0; i<numTables; ++i) {
StencilTableReal<REAL> const * st = tables[i];
if (!st) continue;
int st_nstencils = st->GetNumStencils(),
st_nelems = (int)st->_indices.size();
memcpy(sizes, &st->_sizes[0], st_nstencils*sizeof(int));
memcpy(indices, &st->_indices[0], st_nelems*sizeof(Index));
memcpy(weights, &st->_weights[0], st_nelems*sizeof(REAL));
sizes += st_nstencils;
indices += st_nelems;
weights += st_nelems;
}
result->_numControlVertices = ncvs;
// have to re-generate offsets from scratch
result->generateOffsets();
return result;
}
//------------------------------------------------------------------------------
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::AppendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const * baseStencilTable,
StencilTableReal<REAL> const * localPointStencilTable,
bool factorize) {
return appendLocalPointStencilTable(
refiner,
baseStencilTable,
localPointStencilTable,
/*channel*/-1,
factorize);
}
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::AppendLocalPointStencilTableFaceVarying(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const * baseStencilTable,
StencilTableReal<REAL> const * localPointStencilTable,
int channel,
bool factorize) {
return appendLocalPointStencilTable(
refiner,
baseStencilTable,
localPointStencilTable,
channel,
factorize);
}
template <typename REAL>
StencilTableReal<REAL> const *
StencilTableFactoryReal<REAL>::appendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const * baseStencilTable,
StencilTableReal<REAL> const * localPointStencilTable,
int channel,
bool factorize) {
// require the local point stencils exist and be non-empty
if ((localPointStencilTable == NULL) ||
(localPointStencilTable->GetNumStencils() == 0)) {
return NULL;
}
int nControlVerts = channel < 0
? refiner.GetLevel(0).GetNumVertices()
: refiner.GetLevel(0).GetNumFVarValues(channel);
// if no base stencils or empty, return copy of local point stencils
if ((baseStencilTable == NULL) ||
(baseStencilTable->GetNumStencils() == 0)) {
StencilTableReal<REAL> * result =
new StencilTableReal<REAL>(*localPointStencilTable);
result->_numControlVertices = nControlVerts;
return result;
}
// baseStencilTable can be built with or without singular stencils
// (single weight of 1.0f) as place-holders for coarse mesh vertices.
int controlVertsIndexOffset = 0;
int nBaseStencils = baseStencilTable->GetNumStencils();
int nBaseStencilsElements = (int)baseStencilTable->_indices.size();
{
int nverts = channel < 0
? refiner.GetNumVerticesTotal()
: refiner.GetNumFVarValuesTotal(channel);
if (nBaseStencils == nverts) {
// the table contains stencils for the control vertices
//
// <----------------- nverts ------------------>
//
// +---------------+----------------------------+-----------------+
// | control verts | refined verts : (max lv) | local points |
// +---------------+----------------------------+-----------------+
// | base stencil table | LP stencils |
// +--------------------------------------------+-----------------+
// ^ /
// \_________________________/
//
//
controlVertsIndexOffset = 0;
} else if (nBaseStencils == (nverts - nControlVerts)) {
// the table does not contain stencils for the control vertices
//
// <----------------- nverts ------------------>
// <------ nBaseStencils ------->
// +---------------+----------------------------+-----------------+
// | control verts | refined verts : (max lv) | local points |
// +---------------+----------------------------+-----------------+
// | base stencil table | LP stencils |
// +----------------------------+-----------------+
// ^ /
// \_________________/
// <-------------->
// controlVertsIndexOffset
//
controlVertsIndexOffset = nControlVerts;
} else {
// these are not the stencils you are looking for.
assert(0);
return NULL;
}
}
// copy all local point stencils to proto stencils, and factorize if needed.
int nLocalPointStencils = localPointStencilTable->GetNumStencils();
int nLocalPointStencilsElements = 0;
StencilBuilder<REAL> builder(nControlVerts,
/*genControlVerts*/ false,
/*compactWeights*/ factorize);
typename StencilBuilder<REAL>::Index origin(&builder, 0);
typename StencilBuilder<REAL>::Index dst = origin;
typename StencilBuilder<REAL>::Index srcIdx = origin;
for (int i = 0 ; i < nLocalPointStencils; ++i) {
StencilReal<REAL> src = localPointStencilTable->GetStencil(i);
dst = origin[i];
for (int j = 0; j < src.GetSize(); ++j) {
Index index = src.GetVertexIndices()[j];
REAL weight = src.GetWeights()[j];
if (isWeightZero<REAL>(weight)) continue;
if (factorize) {
dst.AddWithWeight(
// subtracting controlVertsIndex if the baseStencil doesn't
// include control vertices (see above diagram)
// since currently local point stencils are created with
// absolute indices including control (level=0) vertices.
baseStencilTable->GetStencil(index - controlVertsIndexOffset),
weight);
} else {
srcIdx = origin[index + controlVertsIndexOffset];
dst.AddWithWeight(srcIdx, weight);
}
}
nLocalPointStencilsElements += builder.GetNumVertsInStencil(i);
}
// create new stencil table
StencilTableReal<REAL> * result = new StencilTableReal<REAL>;
result->_numControlVertices = nControlVerts;
result->resize(nBaseStencils + nLocalPointStencils,
nBaseStencilsElements + nLocalPointStencilsElements);
int* sizes = &result->_sizes[0];
Index * indices = &result->_indices[0];
REAL * weights = &result->_weights[0];
// put base stencils first
memcpy(sizes, &baseStencilTable->_sizes[0],
nBaseStencils*sizeof(int));
memcpy(indices, &baseStencilTable->_indices[0],
nBaseStencilsElements*sizeof(Index));
memcpy(weights, &baseStencilTable->_weights[0],
nBaseStencilsElements*sizeof(REAL));
sizes += nBaseStencils;
indices += nBaseStencilsElements;
weights += nBaseStencilsElements;
// endcap stencils second
for (int i = 0 ; i < nLocalPointStencils; ++i) {
int size = builder.GetNumVertsInStencil(i);
int idx = builder.GetStencilOffsets()[i];
for (int j = 0; j < size; ++j) {
*indices++ = builder.GetStencilSources()[idx+j];
*weights++ = builder.GetStencilWeights()[idx+j];
}
*sizes++ = size;
}
// have to re-generate offsets from scratch
result->generateOffsets();
return result;
}
//------------------------------------------------------------------------------
template <typename REAL>
LimitStencilTableReal<REAL> const *
LimitStencilTableFactoryReal<REAL>::Create(TopologyRefiner const & refiner,
LocationArrayVec const & locationArrays,
StencilTableReal<REAL> const * cvStencilsIn,
PatchTable const * patchTableIn,
Options options) {
// Compute the total number of stencils to generate
int numStencils=0, numLimitStencils=0;
for (int i=0; i<(int)locationArrays.size(); ++i) {
assert(locationArrays[i].numLocations>=0);
numStencils += locationArrays[i].numLocations;
}
if (numStencils<=0) {
return 0;
}
bool uniform = refiner.IsUniform();
int maxlevel = refiner.GetMaxLevel();
bool interpolateVertex = (options.interpolationMode == INTERPOLATE_VERTEX);
bool interpolateVarying = (options.interpolationMode == INTERPOLATE_VARYING);
bool interpolateFaceVarying = (options.interpolationMode == INTERPOLATE_FACE_VARYING);
int fvarChannel = options.fvarChannel;
//
// Quick sanity checks for given PatchTable and/or StencilTables:
//
int nRefinedStencils = 0;
if (uniform) {
// Uniform stencils must include at least the last level points:
nRefinedStencils = interpolateFaceVarying
? refiner.GetLevel(maxlevel).GetNumFVarValues(fvarChannel)
: refiner.GetLevel(maxlevel).GetNumVertices();
} else {
// Adaptive stencils must include at least all refined points:
nRefinedStencils = interpolateFaceVarying
? refiner.GetNumFVarValuesTotal(fvarChannel)
: refiner.GetNumVerticesTotal();
}
if (cvStencilsIn && (cvStencilsIn->GetNumStencils() < nRefinedStencils)) {
// Too few stencils in given StencilTable
return 0;
}
if (patchTableIn && (patchTableIn->IsFeatureAdaptive() == uniform)) {
// Adaptive/uniform mismatch with given PatchTable and refiner
return 0;
}
// If an appropriate StencilTable was given, use it, otherwise, create a new one
StencilTableReal<REAL> const * cvstencils = cvStencilsIn;
if (! cvstencils) {
//
// Generate stencils for the control vertices - this is necessary to
// properly factorize patches with control vertices at level 0 (natural
// regular patches, such as in a torus)
// note: the control vertices of the mesh are added as single-index
// stencils of weight 1.0f
//
typename StencilTableFactoryReal<REAL>::Options stencilTableOptions;
stencilTableOptions.generateIntermediateLevels = uniform ? false :true;
stencilTableOptions.generateControlVerts = true;
stencilTableOptions.generateOffsets = true;
stencilTableOptions.interpolationMode = options.interpolationMode;
stencilTableOptions.fvarChannel = options.fvarChannel;
cvstencils = StencilTableFactoryReal<REAL>::Create(refiner, stencilTableOptions);
}
// If an appropriate PatchTable was given, use it, otherwise, create a new one
PatchTable const * patchtable = patchTableIn;
if (! patchtable) {
//
// Ideally we could create a sparse PatchTable here for the given
// Locations, but that requires inverting the ptex/base-face relation.
// so the caller must deal with that and provide such a PatchTable
//
PatchTableFactory::Options patchTableOptions;
patchTableOptions.SetPatchPrecision<REAL>();
patchTableOptions.includeBaseLevelIndices = true;
patchTableOptions.generateVaryingTables = interpolateVarying;
patchTableOptions.generateFVarTables = interpolateFaceVarying;
if (interpolateFaceVarying) {
patchTableOptions.includeFVarBaseLevelIndices = true;
patchTableOptions.numFVarChannels = 1;
patchTableOptions.fvarChannelIndices = &fvarChannel;
patchTableOptions.generateFVarLegacyLinearPatches = uniform ||
!refiner.GetAdaptiveOptions().considerFVarChannels;
}
patchTableOptions.SetEndCapType(
Far::PatchTableFactory::Options::ENDCAP_GREGORY_BASIS);
patchTableOptions.useInfSharpPatch = !uniform &&
refiner.GetAdaptiveOptions().useInfSharpPatch;
patchtable = PatchTableFactory::Create(refiner, patchTableOptions);
}
// Append local point stencils and further verfiy size of given StencilTable:
StencilTableReal<REAL> const * localstencils = 0;
if (interpolateVertex) {
localstencils = patchtable->GetLocalPointStencilTable<REAL>();
} else if (interpolateFaceVarying) {
localstencils = patchtable->GetLocalPointFaceVaryingStencilTable<REAL>(fvarChannel);
} else {
localstencils = patchtable->GetLocalPointVaryingStencilTable<REAL>();
}
if (localstencils && (cvstencils->GetNumStencils() == nRefinedStencils)) {
StencilTableReal<REAL> const *refinedstencils = cvstencils;
if (interpolateFaceVarying) {
cvstencils = StencilTableFactoryReal<REAL>::AppendLocalPointStencilTableFaceVarying(
refiner, refinedstencils, localstencils, fvarChannel);
} else {
cvstencils = StencilTableFactoryReal<REAL>::AppendLocalPointStencilTable(
refiner, refinedstencils, localstencils);
}
if (!cvStencilsIn) delete refinedstencils;
}
assert(patchtable && cvstencils);
// Create a patch-map to locate sub-patches faster
PatchMap patchmap( *patchtable );
//
// Generate limit stencils for locations
//
int nControlVertices = interpolateFaceVarying
? refiner.GetLevel(0).GetNumFVarValues(fvarChannel)
: refiner.GetLevel(0).GetNumVertices();
StencilBuilder<REAL> builder(nControlVertices,
/*genControlVerts*/ false,
/*compactWeights*/ true);
typename StencilBuilder<REAL>::Index origin(&builder, 0);
typename StencilBuilder<REAL>::Index dst = origin;
//
// Generally use the patches corresponding to the interpolation mode, but Uniform
// PatchTables do not have varying patches -- use the equivalent linear vertex
// patches in this case:
//
bool useVertexPatches = interpolateVertex || (interpolateVarying && uniform);
bool useFVarPatches = interpolateFaceVarying;
REAL wP[20], wDs[20], wDt[20], wDss[20], wDst[20], wDtt[20];
for (size_t i=0; i<locationArrays.size(); ++i) {
LocationArray const & array = locationArrays[i];
assert(array.ptexIdx>=0);
for (int j=0; j<array.numLocations; ++j) { // for each face we're working on
REAL s = array.s[j],
t = array.t[j]; // for each target (s,t) point on that face
PatchMap::Handle const * handle =
patchmap.FindPatch(array.ptexIdx, s, t);
if (handle) {
ConstIndexArray cvs;
if (useVertexPatches) {
cvs = patchtable->GetPatchVertices(*handle);
} else if (useFVarPatches) {
cvs = patchtable->GetPatchFVarValues(*handle, fvarChannel);
} else {
cvs = patchtable->GetPatchVaryingVertices(*handle);
}
StencilTableReal<REAL> const & src = *cvstencils;
dst = origin[numLimitStencils];
if (options.generate2ndDerivatives) {
if (useVertexPatches) {
patchtable->EvaluateBasis<REAL>(
*handle, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
} else if (useFVarPatches) {
patchtable->EvaluateBasisFaceVarying<REAL>(
*handle, s, t, wP, wDs, wDt, wDss, wDst, wDtt, fvarChannel);
} else {
patchtable->EvaluateBasisVarying<REAL>(
*handle, s, t, wP, wDs, wDt, wDss, wDst, wDtt);
}
dst.Clear();
for (int k = 0; k < cvs.size(); ++k) {
dst.AddWithWeight(src[cvs[k]], wP[k], wDs[k], wDt[k], wDss[k], wDst[k], wDtt[k]);
}
} else if (options.generate1stDerivatives) {
if (useVertexPatches) {
patchtable->EvaluateBasis<REAL>(
*handle, s, t, wP, wDs, wDt);
} else if (useFVarPatches) {
patchtable->EvaluateBasisFaceVarying<REAL>(
*handle, s, t, wP, wDs, wDt, 0, 0, 0, fvarChannel);
} else {
patchtable->EvaluateBasisVarying<REAL>(
*handle, s, t, wP, wDs, wDt);
}
dst.Clear();
for (int k = 0; k < cvs.size(); ++k) {
dst.AddWithWeight(src[cvs[k]], wP[k], wDs[k], wDt[k]);
}
} else {
if (useVertexPatches) {
patchtable->EvaluateBasis<REAL>(
*handle, s, t, wP);
} else if (useFVarPatches) {
patchtable->EvaluateBasisFaceVarying<REAL>(
*handle, s, t, wP, 0, 0, 0, 0, 0, fvarChannel);
} else {
patchtable->EvaluateBasisVarying<REAL>(
*handle, s, t, wP);
}
dst.Clear();
for (int k = 0; k < cvs.size(); ++k) {
dst.AddWithWeight(src[cvs[k]], wP[k]);
}
}
++numLimitStencils;
}
}
}
if (! cvStencilsIn) {
delete cvstencils;
}
if (! patchTableIn) {
delete patchtable;
}
//
// Copy the proto-stencils into the limit stencil table
//
LimitStencilTableReal<REAL> * result = new LimitStencilTableReal<REAL>(
nControlVertices,
builder.GetStencilOffsets(),
builder.GetStencilSizes(),
builder.GetStencilSources(),
builder.GetStencilWeights(),
builder.GetStencilDuWeights(),
builder.GetStencilDvWeights(),
builder.GetStencilDuuWeights(),
builder.GetStencilDuvWeights(),
builder.GetStencilDvvWeights(),
/*ctrlVerts*/false,
/*fristOffset*/0);
return result;
}
//
// Explicit instantiation for float and double:
//
template class StencilTableFactoryReal<float>;
template class StencilTableFactoryReal<double>;
template class LimitStencilTableFactoryReal<float>;
template class LimitStencilTableFactoryReal<double>;
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,367 @@
//
// Copyright 2013 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_STENCILTABLE_FACTORY_H
#define OPENSUBDIV3_FAR_STENCILTABLE_FACTORY_H
#include "../version.h"
#include "../far/patchTable.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
class TopologyRefiner;
template <typename REAL> class StencilReal;
template <typename REAL> class StencilTableReal;
template <typename REAL> class LimitStencilReal;
template <typename REAL> class LimitStencilTableReal;
/// \brief A specialized factory for StencilTable
///
template <typename REAL>
class StencilTableFactoryReal {
public:
enum Mode {
INTERPOLATE_VERTEX=0, ///< vertex primvar stencils
INTERPOLATE_VARYING, ///< varying primvar stencils
INTERPOLATE_FACE_VARYING ///< face-varying primvar stencils
};
struct Options {
Options() : interpolationMode(INTERPOLATE_VERTEX),
generateOffsets(false),
generateControlVerts(false),
generateIntermediateLevels(true),
factorizeIntermediateLevels(true),
maxLevel(10),
fvarChannel(0) { }
unsigned int interpolationMode : 2, ///< interpolation mode
generateOffsets : 1, ///< populate optional "_offsets" field
generateControlVerts : 1, ///< generate stencils for control-vertices
generateIntermediateLevels : 1, ///< vertices at all levels or highest only
factorizeIntermediateLevels : 1, ///< accumulate stencil weights from control
/// vertices or from the stencils of the
/// previous level
maxLevel : 4; ///< generate stencils up to 'maxLevel'
unsigned int fvarChannel; ///< face-varying channel to use
/// when generating face-varying stencils
};
/// \brief Instantiates StencilTable from TopologyRefiner that have been
/// refined uniformly or adaptively.
///
/// \note The factory only creates stencils for vertices that have already
/// been refined in the TopologyRefiner. Use RefineUniform() or
/// RefineAdaptive() before constructing the stencils.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param options Options controlling the creation of the table
///
static StencilTableReal<REAL> const * Create(
TopologyRefiner const & refiner, Options options = Options());
/// \brief Instantiates StencilTable by concatenating an array of existing
/// stencil tables.
///
/// \note This factory checks that the stencil tables point to the same set
/// of supporting control vertices - no re-indexing is done.
/// GetNumControlVertices() *must* return the same value for all input
/// tables.
///
/// @param numTables Number of input StencilTables
///
/// @param tables Array of input StencilTables
///
static StencilTableReal<REAL> const * Create(
int numTables, StencilTableReal<REAL> const ** tables);
/// \brief Utility function for stencil splicing for local point stencils.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param baseStencilTable Input StencilTable for refined vertices
///
/// @param localPointStencilTable
/// StencilTable for the change of basis patch points.
///
/// @param factorize If factorize is set to true, endcap stencils will be
/// factorized with supporting vertices from baseStencil
/// table so that the endcap points can be computed
/// directly from control vertices.
///
static StencilTableReal<REAL> const * AppendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const *baseStencilTable,
StencilTableReal<REAL> const *localPointStencilTable,
bool factorize = true);
/// \brief Utility function for stencil splicing for local point varying stencils.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param baseStencilTable Input StencilTable for refined vertices
///
/// @param localPointStencilTable
/// StencilTable for the change of basis patch points.
///
/// @param factorize If factorize is set to true, endcap stencils will be
/// factorized with supporting vertices from baseStencil
/// table so that the endcap points can be computed
/// directly from control vertices.
///
static StencilTableReal<REAL> const * AppendLocalPointStencilTableVarying(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const *baseStencilTable,
StencilTableReal<REAL> const *localPointStencilTable,
bool factorize = true) {
return AppendLocalPointStencilTable(
refiner, baseStencilTable, localPointStencilTable, factorize);
}
/// \brief Utility function for stencil splicing for local point
/// face-varying stencils.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param baseStencilTable Input StencilTable for refined vertices
///
/// @param localPointStencilTable
/// StencilTable for the change of basis patch points.
///
/// @param channel face-varying channel
///
/// @param factorize If factorize is set to true, endcap stencils will be
/// factorized with supporting vertices from baseStencil
/// table so that the endcap points can be computed
/// directly from control vertices.
///
static StencilTableReal<REAL> const * AppendLocalPointStencilTableFaceVarying(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const *baseStencilTable,
StencilTableReal<REAL> const *localPointStencilTable,
int channel = 0,
bool factorize = true);
private:
// Generate stencils for the coarse control-vertices (single weight = 1.0f)
static void generateControlVertStencils(
int numControlVerts,
StencilReal<REAL> & dst);
// Internal method to splice local point stencils
static StencilTableReal<REAL> const * appendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTableReal<REAL> const * baseStencilTable,
StencilTableReal<REAL> const * localPointStencilTable,
int channel,
bool factorize);
};
/// \brief A specialized factory for LimitStencilTable
///
/// The LimitStencilTableFactory creates a table of limit stencils. Limit
/// stencils can interpolate any arbitrary location on the limit surface.
/// The stencils will be bilinear if the surface is refined uniformly, and
/// bicubic if feature adaptive isolation is used instead.
///
/// Surface locations are expressed as a combination of ptex face index and
/// normalized (s,t) patch coordinates. The factory exposes the LocationArray
/// struct as a container for these location descriptors.
///
template <typename REAL>
class LimitStencilTableFactoryReal {
public:
enum Mode {
INTERPOLATE_VERTEX=0, ///< vertex primvar stencils
INTERPOLATE_VARYING, ///< varying primvar stencils
INTERPOLATE_FACE_VARYING ///< face-varying primvar stencils
};
struct Options {
Options() : interpolationMode(INTERPOLATE_VERTEX),
generate1stDerivatives(true),
generate2ndDerivatives(false),
fvarChannel(0) { }
unsigned int interpolationMode : 2, ///< interpolation mode
generate1stDerivatives : 1, ///< Generate weights for 1st derivatives
generate2ndDerivatives : 1; ///< Generate weights for 2nd derivatives
unsigned int fvarChannel; ///< face-varying channel to use
};
/// \brief Descriptor for limit surface locations
struct LocationArray {
LocationArray() : ptexIdx(-1), numLocations(0), s(0), t(0) { }
int ptexIdx, ///< ptex face index
numLocations; ///< number of (u,v) coordinates in the array
REAL const * s, ///< array of u coordinates
* t; ///< array of v coordinates
};
typedef std::vector<LocationArray> LocationArrayVec;
/// \brief Instantiates LimitStencilTable from a TopologyRefiner that has
/// been refined either uniformly or adaptively.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param locationArrays An array of surface location descriptors
/// (see LocationArray)
///
/// @param cvStencils A StencilTable generated from the TopologyRefiner
/// (Optional: prevents redundant instantiation of the
/// table if available. The given table must at least
/// contain stencils for all control points and all
/// refined points -- any stencils for local points of
/// a PatchTable must match the PatchTable provided or
/// internally generated)
///
/// @param patchTable A PatchTable generated from the TopologyRefiner
/// (Optional: prevents redundant instantiation of the
/// table if available. The given table must match
/// the optional StencilTable if also provided)
///
/// @param options Options controlling the creation of the table
///
static LimitStencilTableReal<REAL> const * Create(
TopologyRefiner const & refiner,
LocationArrayVec const & locationArrays,
StencilTableReal<REAL> const * cvStencils = 0,
PatchTable const * patchTable = 0,
Options options = Options());
};
//
// Public wrapper classes for the templates
//
class Stencil;
class StencilTable;
/// \brief Stencil table factory class wrapping the template for compatibility.
///
class StencilTableFactory : public StencilTableFactoryReal<float> {
private:
typedef StencilTableFactoryReal<float> BaseFactory;
typedef StencilTableReal<float> BaseTable;
public:
static StencilTable const * Create(
TopologyRefiner const & refiner, Options options = Options()) {
return reinterpret_cast<StencilTable const *>(
BaseFactory::Create(refiner, options));
}
static StencilTable const * Create(
int numTables, StencilTable const ** tables) {
return reinterpret_cast<StencilTable const *>(
BaseFactory::Create(numTables,
reinterpret_cast<BaseTable const **>(tables)));
}
static StencilTable const * AppendLocalPointStencilTable(
TopologyRefiner const &refiner,
StencilTable const *baseStencilTable,
StencilTable const *localPointStencilTable,
bool factorize = true) {
return reinterpret_cast<StencilTable const *>(
BaseFactory::AppendLocalPointStencilTable(refiner,
static_cast<BaseTable const *>(baseStencilTable),
static_cast<BaseTable const *>(localPointStencilTable),
factorize));
}
static StencilTable const * AppendLocalPointStencilTableVarying(
TopologyRefiner const &refiner,
StencilTable const *baseStencilTable,
StencilTable const *localPointStencilTable,
bool factorize = true) {
return reinterpret_cast<StencilTable const *>(
BaseFactory::AppendLocalPointStencilTableVarying(refiner,
static_cast<BaseTable const *>(baseStencilTable),
static_cast<BaseTable const *>(localPointStencilTable),
factorize));
}
static StencilTable const * AppendLocalPointStencilTableFaceVarying(
TopologyRefiner const &refiner,
StencilTable const *baseStencilTable,
StencilTable const *localPointStencilTable,
int channel = 0,
bool factorize = true) {
return reinterpret_cast<StencilTable const *>(
BaseFactory::AppendLocalPointStencilTableFaceVarying(refiner,
static_cast<BaseTable const *>(baseStencilTable),
static_cast<BaseTable const *>(localPointStencilTable),
channel, factorize));
}
};
class LimitStencil;
class LimitStencilTable;
/// \brief Stencil table factory class wrapping the template for compatibility.
///
class LimitStencilTableFactory : public LimitStencilTableFactoryReal<float> {
private:
typedef LimitStencilTableFactoryReal<float> BaseFactory;
typedef StencilTableReal<float> BaseTable;
public:
static LimitStencilTable const * Create(
TopologyRefiner const & refiner,
LocationArrayVec const & locationArrays,
StencilTable const * cvStencils = 0,
PatchTable const * patchTable = 0,
Options options = Options()) {
return reinterpret_cast<LimitStencilTable const *>(
BaseFactory::Create(
refiner,
locationArrays,
static_cast<BaseTable const *>(cvStencils),
patchTable,
options));
}
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif // OPENSUBDIV3_FAR_STENCILTABLE_FACTORY_H

View File

@@ -0,0 +1,168 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/topologyDescriptor.h"
#include "../far/topologyRefinerFactory.h"
#include "../far/topologyRefiner.h"
// Unfortunately necessary for error codes that should be more accessible...
#include "../vtr/level.h"
#include <cstdio>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Definitions for TopologyDescriptor:
//
TopologyDescriptor::TopologyDescriptor() {
memset(this, 0, sizeof(TopologyDescriptor));
}
//
// Definitions/specializations for its RefinerFactory<TopologyDescriptor>:
//
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::resizeComponentTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc) {
setNumBaseVertices(refiner, desc.numVertices);
setNumBaseFaces(refiner, desc.numFaces);
for (int face=0; face<desc.numFaces; ++face) {
setNumBaseFaceVertices(refiner, face, desc.numVertsPerFace[face]);
}
return true;
}
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc) {
for (int face=0, idx=0; face<desc.numFaces; ++face) {
IndexArray dstFaceVerts = getBaseFaceVertices(refiner, face);
if (desc.isLeftHanded) {
dstFaceVerts[0] = desc.vertIndicesPerFace[idx++];
for (int vert=dstFaceVerts.size()-1; vert > 0; --vert) {
dstFaceVerts[vert] = desc.vertIndicesPerFace[idx++];
}
} else {
for (int vert=0; vert<dstFaceVerts.size(); ++vert) {
dstFaceVerts[vert] = desc.vertIndicesPerFace[idx++];
}
}
}
return true;
}
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTags(
TopologyRefiner & refiner, TopologyDescriptor const & desc) {
if ((desc.numCreases>0) && desc.creaseVertexIndexPairs && desc.creaseWeights) {
int const * vertIndexPairs = desc.creaseVertexIndexPairs;
for (int edge=0; edge<desc.numCreases; ++edge, vertIndexPairs+=2) {
Index idx = findBaseEdge(refiner, vertIndexPairs[0], vertIndexPairs[1]);
if (idx!=INDEX_INVALID) {
setBaseEdgeSharpness(refiner, idx, desc.creaseWeights[edge]);
} else {
char msg[1024];
snprintf(msg, 1024, "Edge %d specified to be sharp does not exist (%d, %d)",
edge, vertIndexPairs[0], vertIndexPairs[1]);
reportInvalidTopology(Vtr::internal::Level::TOPOLOGY_INVALID_CREASE_EDGE, msg, desc);
}
}
}
if ((desc.numCorners>0) && desc.cornerVertexIndices && desc.cornerWeights) {
for (int vert=0; vert<desc.numCorners; ++vert) {
int idx = desc.cornerVertexIndices[vert];
if (idx >= 0 && idx < getNumBaseVertices(refiner)) {
setBaseVertexSharpness(refiner, idx, desc.cornerWeights[vert]);
} else {
char msg[1024];
snprintf(msg, 1024, "Vertex %d specified to be sharp does not exist", idx);
reportInvalidTopology(Vtr::internal::Level::TOPOLOGY_INVALID_CREASE_VERT, msg, desc);
}
}
}
if (desc.numHoles>0) {
for (int i=0; i<desc.numHoles; ++i) {
setBaseFaceHole(refiner, desc.holeIndices[i], true);
}
}
return true;
}
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignFaceVaryingTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc) {
if (desc.numFVarChannels>0) {
for (int channel=0; channel<desc.numFVarChannels; ++channel) {
int numFVarValues = desc.fvarChannels[channel].numValues;
int const* srcFVarValues = desc.fvarChannels[channel].valueIndices;
createBaseFVarChannel(refiner, numFVarValues);
for (int face = 0, srcNext = 0; face < desc.numFaces; ++face) {
IndexArray dstFaceFVarValues = getBaseFaceFVarValues(refiner, face, channel);
if (desc.isLeftHanded) {
dstFaceFVarValues[0] = srcFVarValues[srcNext++];
for (int vert = dstFaceFVarValues.size() - 1; vert > 0; --vert) {
dstFaceFVarValues[vert] = srcFVarValues[srcNext++];
}
} else {
for (int vert = 0; vert < dstFaceFVarValues.size(); ++vert) {
dstFaceFVarValues[vert] = srcFVarValues[srcNext++];
}
}
}
}
}
return true;
}
template <>
void
TopologyRefinerFactory<TopologyDescriptor>::reportInvalidTopology(
TopologyError /* errCode */, char const * msg, TopologyDescriptor const& /* mesh */) {
Warning(msg);
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,110 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_TOPOLOGY_DESCRIPTOR_H
#define OPENSUBDIV3_FAR_TOPOLOGY_DESCRIPTOR_H
#include "../version.h"
#include "../far/topologyRefiner.h"
#include "../far/topologyRefinerFactory.h"
#include "../far/error.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
///
/// \brief A simple reference to raw topology data for use with TopologyRefinerFactory
///
/// TopologyDescriptor is a simple struct containing references to raw topology data used
/// to construct a TopologyRefiner. It is not a requirement but a convenience for use
/// with TopologyRefinerFactory when mesh topology is not available in an existing mesh
/// data structure. It should be functionally complete and simple to use, but for more
/// demanding situations, writing a custom Factory is usually warranted.
///
struct TopologyDescriptor {
int numVertices,
numFaces;
int const * numVertsPerFace;
Index const * vertIndicesPerFace;
int numCreases;
Index const * creaseVertexIndexPairs;
float const * creaseWeights;
int numCorners;
Index const * cornerVertexIndices;
float const * cornerWeights;
int numHoles;
Index const * holeIndices;
bool isLeftHanded;
// Face-varying data channel -- value indices correspond to vertex indices,
// i.e. one for every vertex of every face:
//
struct FVarChannel {
int numValues;
Index const * valueIndices;
FVarChannel() : numValues(0), valueIndices(0) { }
};
int numFVarChannels;
FVarChannel const * fvarChannels;
TopologyDescriptor();
};
//
// Forward declarations of required TopologyRefinerFactory<TopologyDescriptor>
// specializations (defined internally):
//
// @cond EXCLUDE_DOXYGEN
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::resizeComponentTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc);
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc);
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignComponentTags(
TopologyRefiner & refiner, TopologyDescriptor const & desc);
template <>
bool
TopologyRefinerFactory<TopologyDescriptor>::assignFaceVaryingTopology(
TopologyRefiner & refiner, TopologyDescriptor const & desc);
template <>
void
TopologyRefinerFactory<TopologyDescriptor>::reportInvalidTopology(
TopologyError errCode, char const * msg, TopologyDescriptor const & desc);
// @endcond
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TOPOLOGY_DESCRIPTOR_H */

View File

@@ -0,0 +1,293 @@
//
// Copyright 2015 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_TOPOLOGY_LEVEL_H
#define OPENSUBDIV3_FAR_TOPOLOGY_LEVEL_H
#include "../version.h"
#include "../vtr/level.h"
#include "../vtr/refinement.h"
#include "../far/types.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
///
/// \brief An interface for accessing data in a specific level of a refined topology hierarchy.
///
/// TopologyLevel provides an interface to data in a specific level of a topology hierarchy.
/// Instances of TopologyLevel are created and owned by a TopologyRefiner,
/// which will return const-references to them. Such references are only valid during the
/// lifetime of the TopologyRefiner that created and returned them, and only for a given refinement,
/// i.e. if the TopologyRefiner is re-refined, any references to TopoologyLevels are invalidated.
///
class TopologyLevel {
public:
//@{
/// @name Methods to inspect the overall inventory of components:
///
/// All three main component types are indexed locally within each level. For
/// some topological relationships -- notably face-vertices, which is often
/// the only relationship of interest -- the total number of entries is also
/// made available.
///
/// \brief Return the number of vertices in this level
int GetNumVertices() const { return _level->getNumVertices(); }
/// \brief Return the number of faces in this level
int GetNumFaces() const { return _level->getNumFaces(); }
/// \brief Return the number of edges in this level
int GetNumEdges() const { return _level->getNumEdges(); }
/// \brief Return the total number of face-vertices, i.e. the sum of all vertices for all faces
int GetNumFaceVertices() const { return _level->getNumFaceVerticesTotal(); }
//@}
//@{
/// @name Methods to inspect topological relationships for individual components:
///
/// With three main component types (vertices, faces and edges), for each of the
/// three components the TopologyLevel stores the incident/adjacent components of
/// the other two types. So there are six relationships available for immediate
/// inspection. All are accessed by methods that return an array of fixed size
/// containing the indices of the incident components.
///
/// For some of the relations, i.e. those for which the incident components are
/// of higher order or 'contain' the component itself (e.g. a vertex has incident
/// faces that contain it), an additional 'local index' is available that identifies
/// the component within each of its neighbors. For example, if vertex V is the k'th
/// vertex in some face F, then when F occurs in the set of incident vertices of V,
/// the local index corresponding to F will be k. The ordering of local indices
/// matches the ordering of the incident component to which it corresponds.
//
/// \brief Access the vertices incident a given face
ConstIndexArray GetFaceVertices(Index f) const { return _level->getFaceVertices(f); }
/// \brief Access the edges incident a given face
ConstIndexArray GetFaceEdges(Index f) const { return _level->getFaceEdges(f); }
/// \brief Access the vertices incident a given edge
ConstIndexArray GetEdgeVertices(Index e) const { return _level->getEdgeVertices(e); }
/// \brief Access the faces incident a given edge
ConstIndexArray GetEdgeFaces(Index e) const { return _level->getEdgeFaces(e); }
/// \brief Access the faces incident a given vertex
ConstIndexArray GetVertexFaces(Index v) const { return _level->getVertexFaces(v); }
/// \brief Access the edges incident a given vertex
ConstIndexArray GetVertexEdges(Index v) const { return _level->getVertexEdges(v); }
/// \brief Access the local indices of a vertex with respect to its incident faces
ConstLocalIndexArray GetVertexFaceLocalIndices(Index v) const { return _level->getVertexFaceLocalIndices(v); }
/// \brief Access the local indices of a vertex with respect to its incident edges
ConstLocalIndexArray GetVertexEdgeLocalIndices(Index v) const { return _level->getVertexEdgeLocalIndices(v); }
/// \brief Access the local indices of an edge with respect to its incident faces
ConstLocalIndexArray GetEdgeFaceLocalIndices(Index e) const { return _level->getEdgeFaceLocalIndices(e); }
/// \brief Identify the edge matching the given vertex pair
Index FindEdge(Index v0, Index v1) const { return _level->findEdge(v0, v1); }
//@}
//@{
/// @name Methods to inspect other topological properties of individual components:
///
/// \brief Return if the edge is non-manifold
bool IsEdgeNonManifold(Index e) const { return _level->isEdgeNonManifold(e); }
/// \brief Return if the vertex is non-manifold
bool IsVertexNonManifold(Index v) const { return _level->isVertexNonManifold(v); }
/// \brief Return if the edge is a boundary (only one incident face)
bool IsEdgeBoundary(Index e) const { return _level->getEdgeTag(e)._boundary; }
/// \brief Return if the vertex is on a boundary (at least one incident boundary edge)
bool IsVertexBoundary(Index v) const { return _level->getVertexTag(v)._boundary; }
/// \brief Return if the vertex is a corner (only one incident face)
bool IsVertexCorner(Index v) const { return (_level->getNumVertexFaces(v) == 1); }
/// \brief Return if the valence of the vertex is regular (must be manifold)
///
/// Note that this test only determines if the valence of the vertex is regular
/// with respect to the assigned subdivision scheme -- not if the neighborhood
/// around the vertex is regular. The latter depends on a number of factors
/// including the incident faces of the vertex (they must all be regular) and
/// the presence of sharpness at the vertex itself or its incident edges.
///
/// The regularity of the valence is a necessary but not a sufficient condition
/// in determining the regularity of the neighborhood. For example, while the
/// valence of an interior vertex may be regular, its neighborhood is not if the
/// vertex was made infinitely sharp. Conversely, a corner vertex is considered
/// regular by its valence but its neighborhood is not if the vertex was not made
/// infinitely sharp.
///
/// Whether the valence of the vertex is regular is also a property that remains
/// the same for the vertex in all subdivision levels. In contrast, the regularity
/// of the region around the vertex may change as the presence of irregular faces
/// or semi-sharp features is reduced by subdivision.
///
bool IsVertexValenceRegular(Index v) const { return !_level->getVertexTag(v)._xordinary || IsVertexCorner(v); }
//@}
//@{
/// @name Methods to inspect feature tags for individual components:
///
/// While only a subset of components may have been tagged with features such
/// as sharpness, all such features have a default value and so all components
/// can be inspected.
/// \brief Return the sharpness assigned a given edge
float GetEdgeSharpness(Index e) const { return _level->getEdgeSharpness(e); }
/// \brief Return the sharpness assigned a given vertex
float GetVertexSharpness(Index v) const { return _level->getVertexSharpness(v); }
/// \brief Return if the edge is infinitely-sharp
bool IsEdgeInfSharp(Index e) const { return _level->getEdgeTag(e)._infSharp; }
/// \brief Return if the vertex is infinitely-sharp
bool IsVertexInfSharp(Index v) const { return _level->getVertexTag(v)._infSharp; }
/// \brief Return if the edge is semi-sharp
bool IsEdgeSemiSharp(Index e) const { return _level->getEdgeTag(e)._semiSharp; }
/// \brief Return if the vertex is semi-sharp
bool IsVertexSemiSharp(Index v) const { return _level->getVertexTag(v)._semiSharp; }
/// \brief Return if a given face has been tagged as a hole
bool IsFaceHole(Index f) const { return _level->isFaceHole(f); }
/// \brief Return the subdivision rule assigned a given vertex specific to this level
Sdc::Crease::Rule GetVertexRule(Index v) const { return _level->getVertexRule(v); }
//@}
//@{
/// @name Methods to inspect face-varying data:
///
/// Face-varying data is organized into topologically independent channels,
/// each with an integer identifier. Access to face-varying data generally
/// requires the specification of a channel, though with a single channel
/// being a common situation the first/only channel will be assumed if
/// unspecified.
///
/// A face-varying channel is composed of a set of values that may be shared
/// by faces meeting at a common vertex. Just as there are sets of vertices
/// that are associated with faces by index (ranging from 0 to
/// num-vertices - 1), face-varying values are also referenced by index
/// (ranging from 0 to num-values -1).
///
/// The face-varying values associated with a face are accessed similarly to
/// the way in which vertices associated with the face are accessed -- an
/// array of fixed size containing the indices for each corner is provided
/// for inspection, iteration, etc.
///
/// When the face-varying topology around a vertex "matches", it has the
/// same limit properties and so results in the same limit surface when
/// collections of adjacent vertices match. Like other references to
/// "topology", this includes consideration of sharpness. So it may be
/// that face-varying values are assigned around a vertex on a boundary in
/// a way that appears to match, but the face-varying interpolation option
/// requires sharpening of that vertex in face-varying space -- the
/// difference in the topology of the resulting limit surfaces leading to
/// the query returning false for the match. The edge case is simpler in
/// that it only considers continuity across the edge, not the entire
/// neighborhood around each end vertex.
/// \brief Return the number of face-varying channels (should be same for all levels)
int GetNumFVarChannels() const { return _level->getNumFVarChannels(); }
/// \brief Return the total number of face-varying values in a particular channel
/// (the upper bound of a face-varying value index)
int GetNumFVarValues(int channel = 0) const { return _level->getNumFVarValues(channel); }
/// \brief Access the face-varying values associated with a particular face
ConstIndexArray GetFaceFVarValues(Index f, int channel = 0) const {
return _level->getFaceFVarValues(f, channel);
}
/// \brief Return if face-varying topology around a vertex matches
bool DoesVertexFVarTopologyMatch(Index v, int channel = 0) const {
return _level->doesVertexFVarTopologyMatch(v, channel);
}
/// \brief Return if face-varying topology across the edge only matches
bool DoesEdgeFVarTopologyMatch(Index e, int channel = 0) const {
return _level->doesEdgeFVarTopologyMatch(e, channel);
}
/// \brief Return if face-varying topology around a face matches
bool DoesFaceFVarTopologyMatch(Index f, int channel = 0) const {
return _level->doesFaceFVarTopologyMatch(f, channel);
}
//@}
//@{
/// @name Methods to identify parent or child components in adjoining levels of refinement:
/// \brief Access the child faces (in the next level) of a given face
ConstIndexArray GetFaceChildFaces(Index f) const { return _refToChild->getFaceChildFaces(f); }
/// \brief Access the child edges (in the next level) of a given face
ConstIndexArray GetFaceChildEdges(Index f) const { return _refToChild->getFaceChildEdges(f); }
/// \brief Access the child edges (in the next level) of a given edge
ConstIndexArray GetEdgeChildEdges(Index e) const { return _refToChild->getEdgeChildEdges(e); }
/// \brief Return the child vertex (in the next level) of a given face
Index GetFaceChildVertex( Index f) const { return _refToChild->getFaceChildVertex(f); }
/// \brief Return the child vertex (in the next level) of a given edge
Index GetEdgeChildVertex( Index e) const { return _refToChild->getEdgeChildVertex(e); }
/// \brief Return the child vertex (in the next level) of a given vertex
Index GetVertexChildVertex(Index v) const { return _refToChild->getVertexChildVertex(v); }
/// \brief Return the parent face (in the previous level) of a given face
Index GetFaceParentFace(Index f) const { return _refToParent->getChildFaceParentFace(f); }
//@}
//@{
/// @name Debugging aides:
bool ValidateTopology() const { return _level->validateTopology(); }
void PrintTopology(bool children = true) const { _level->print((children && _refToChild) ? _refToChild : 0); }
//@}
private:
friend class TopologyRefiner;
Vtr::internal::Level const * _level;
Vtr::internal::Refinement const * _refToParent;
Vtr::internal::Refinement const * _refToChild;
public:
// Not intended for public use, but required by std::vector, etc...
TopologyLevel() { }
~TopologyLevel() { }
};
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TOPOLOGY_LEVEL_H */

View File

@@ -0,0 +1,815 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/topologyRefiner.h"
#include "../far/error.h"
#include "../vtr/fvarLevel.h"
#include "../vtr/sparseSelector.h"
#include "../vtr/quadRefinement.h"
#include "../vtr/triRefinement.h"
#include <cassert>
#include <cstdio>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Relatively trivial construction/destruction -- the base level (level[0]) needs
// to be explicitly initialized after construction and refinement then applied
//
TopologyRefiner::TopologyRefiner(Sdc::SchemeType schemeType, Sdc::Options schemeOptions) :
_subdivType(schemeType),
_subdivOptions(schemeOptions),
_isUniform(true),
_hasHoles(false),
_hasIrregFaces(false),
_regFaceSize(Sdc::SchemeTypeTraits::GetRegularFaceSize(schemeType)),
_maxLevel(0),
_uniformOptions(0),
_adaptiveOptions(0),
_totalVertices(0),
_totalEdges(0),
_totalFaces(0),
_totalFaceVertices(0),
_maxValence(0),
_baseLevelOwned(true) {
// Need to revisit allocation scheme here -- want to use smart-ptrs for these
// but will probably have to settle for explicit new/delete...
_levels.reserve(10);
_levels.push_back(new Vtr::internal::Level);
_farLevels.reserve(10);
assembleFarLevels();
}
//
// The copy constructor is protected and used by the factory to create a new instance
// from only the base level of the given instance -- it does not create a full copy.
// So members reflecting any refinement are default-initialized while those dependent
// on the base level are copied or explicitly initialized after its assignment.
//
TopologyRefiner::TopologyRefiner(TopologyRefiner const & source) :
_subdivType(source._subdivType),
_subdivOptions(source._subdivOptions),
_isUniform(true),
_hasHoles(source._hasHoles),
_hasIrregFaces(source._hasIrregFaces),
_regFaceSize(source._regFaceSize),
_maxLevel(0),
_uniformOptions(0),
_adaptiveOptions(0),
_baseLevelOwned(false) {
_levels.reserve(10);
_levels.push_back(source._levels[0]);
initializeInventory();
_farLevels.reserve(10);
assembleFarLevels();
}
TopologyRefiner::~TopologyRefiner() {
for (int i=0; i<(int)_levels.size(); ++i) {
if ((i > 0) || _baseLevelOwned) delete _levels[i];
}
for (int i=0; i<(int)_refinements.size(); ++i) {
delete _refinements[i];
}
}
void
TopologyRefiner::Unrefine() {
if (_levels.size()) {
for (int i=1; i<(int)_levels.size(); ++i) {
delete _levels[i];
}
_levels.resize(1);
initializeInventory();
}
for (int i=0; i<(int)_refinements.size(); ++i) {
delete _refinements[i];
}
_refinements.clear();
_maxLevel = 0;
assembleFarLevels();
}
//
// Initializing and updating the component inventory:
//
void
TopologyRefiner::initializeInventory() {
if (_levels.size()) {
assert(_levels.size() == 1);
Vtr::internal::Level const & baseLevel = *_levels[0];
_totalVertices = baseLevel.getNumVertices();
_totalEdges = baseLevel.getNumEdges();
_totalFaces = baseLevel.getNumFaces();
_totalFaceVertices = baseLevel.getNumFaceVerticesTotal();
_maxValence = baseLevel.getMaxValence();
} else {
_totalVertices = 0;
_totalEdges = 0;
_totalFaces = 0;
_totalFaceVertices = 0;
_maxValence = 0;
}
}
void
TopologyRefiner::updateInventory(Vtr::internal::Level const & newLevel) {
_totalVertices += newLevel.getNumVertices();
_totalEdges += newLevel.getNumEdges();
_totalFaces += newLevel.getNumFaces();
_totalFaceVertices += newLevel.getNumFaceVerticesTotal();
_maxValence = std::max(_maxValence, newLevel.getMaxValence());
}
void
TopologyRefiner::appendLevel(Vtr::internal::Level & newLevel) {
_levels.push_back(&newLevel);
updateInventory(newLevel);
}
void
TopologyRefiner::appendRefinement(Vtr::internal::Refinement & newRefinement) {
_refinements.push_back(&newRefinement);
}
void
TopologyRefiner::assembleFarLevels() {
_farLevels.resize(_levels.size());
_farLevels[0]._refToParent = 0;
_farLevels[0]._level = _levels[0];
_farLevels[0]._refToChild = 0;
int nRefinements = (int)_refinements.size();
if (nRefinements) {
_farLevels[0]._refToChild = _refinements[0];
for (int i = 1; i < nRefinements; ++i) {
_farLevels[i]._refToParent = _refinements[i - 1];
_farLevels[i]._level = _levels[i];
_farLevels[i]._refToChild = _refinements[i];;
}
_farLevels[nRefinements]._refToParent = _refinements[nRefinements - 1];
_farLevels[nRefinements]._level = _levels[nRefinements];
_farLevels[nRefinements]._refToChild = 0;
}
}
//
// Accessors to the topology information:
//
int
TopologyRefiner::GetNumFVarValuesTotal(int channel) const {
int sum = 0;
for (int i = 0; i < (int)_levels.size(); ++i) {
sum += _levels[i]->getNumFVarValues(channel);
}
return sum;
}
//
// Main refinement method -- allocating and initializing levels and refinements:
//
void
TopologyRefiner::RefineUniform(UniformOptions options) {
if (_levels[0]->getNumVertices() == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefiner::RefineUniform() -- base level is uninitialized.");
return;
}
if (_refinements.size()) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefiner::RefineUniform() -- previous refinements already applied.");
return;
}
//
// Allocate the stack of levels and the refinements between them:
//
_uniformOptions = options;
_isUniform = true;
_maxLevel = options.refinementLevel;
Sdc::Split splitType = Sdc::SchemeTypeTraits::GetTopologicalSplitType(_subdivType);
//
// Initialize refinement options for Vtr -- adjusting full-topology for the last level:
//
Vtr::internal::Refinement::Options refineOptions;
refineOptions._sparse = false;
refineOptions._faceVertsFirst = options.orderVerticesFromFacesFirst;
for (int i = 1; i <= (int)options.refinementLevel; ++i) {
refineOptions._minimalTopology =
options.fullTopologyInLastLevel ? false : (i == (int)options.refinementLevel);
Vtr::internal::Level& parentLevel = getLevel(i-1);
Vtr::internal::Level& childLevel = *(new Vtr::internal::Level);
Vtr::internal::Refinement* refinement = 0;
if (splitType == Sdc::SPLIT_TO_QUADS) {
refinement = new Vtr::internal::QuadRefinement(parentLevel, childLevel, _subdivOptions);
} else {
refinement = new Vtr::internal::TriRefinement(parentLevel, childLevel, _subdivOptions);
}
refinement->refine(refineOptions);
appendLevel(childLevel);
appendRefinement(*refinement);
}
assembleFarLevels();
}
//
// Internal utility class and function supporting feature adaptive selection of faces...
//
namespace internal {
//
// FeatureMask is a simple set of bits identifying features to be selected during a level of
// adaptive refinement. Adaptive refinement options passed the Refiner are interpreted as a
// specific set of features defined here. Given options to reduce faces generated at deeper
// levels, a method to "reduce" the set of features is also provided here.
//
// This class was specifically not nested in TopologyRefiner to allow simple non-class methods
// to make use of it in the core selection methods. Those selection methods were similarly
// made non-class methods to ensure they conform to the feature set defined by the FeatureMask
// and not some internal class state.
//
class FeatureMask {
public:
typedef TopologyRefiner::AdaptiveOptions Options;
typedef unsigned int int_type;
void Clear() { *((int_type*)this) = 0; }
bool IsEmpty() const { return *((int_type*)this) == 0; }
FeatureMask() { Clear(); }
FeatureMask(Options const & options, int regFaceSize) {
Clear();
InitializeFeatures(options, regFaceSize);
}
// These are the two primary methods intended for use -- intialization via a set of Options
// and reduction of the subsequent feature set (which presumes prior initialization with the
// same set as give)
//
void InitializeFeatures(Options const & options, int regFaceSize);
void ReduceFeatures( Options const & options);
public:
int_type selectXOrdinaryInterior : 1;
int_type selectXOrdinaryBoundary : 1;
int_type selectSemiSharpSingle : 1;
int_type selectSemiSharpNonSingle : 1;
int_type selectInfSharpRegularCrease : 1;
int_type selectInfSharpRegularCorner : 1;
int_type selectInfSharpIrregularDart : 1;
int_type selectInfSharpIrregularCrease : 1;
int_type selectInfSharpIrregularCorner : 1;
int_type selectUnisolatedInteriorEdge : 1;
int_type selectNonManifold : 1;
int_type selectFVarFeatures : 1;
};
void
FeatureMask::InitializeFeatures(Options const & options, int regFaceSize) {
//
// Support for the "single-crease patch" case is limited to the subdivision scheme
// (currently only Catmull-Clark). It has historically been applied to both semi-
// sharp and inf-sharp creases -- the semi-sharp application is still relevant,
// but the inf-sharp has been superceded.
//
// The inf-sharp single-crease case now corresponds to an inf-sharp regular crease
// in the interior -- and since such regular creases on the boundary are never
// considered for selection (just as interior smoot regular faces are not), this
// feature is only relevant for the interior case. So aside from it being used
// when regular inf-sharp features are all selected, it can also be used for the
// single-crease case.
//
bool useSingleCreasePatch = options.useSingleCreasePatch && (regFaceSize == 4);
// Extra-ordinary features (independent of the inf-sharp options):
selectXOrdinaryInterior = true;
selectXOrdinaryBoundary = true;
// Semi-sharp features -- the regular single crease case and all others:
selectSemiSharpSingle = !useSingleCreasePatch;
selectSemiSharpNonSingle = true;
// Inf-sharp features -- boundary extra-ordinary vertices are irreg creases:
selectInfSharpRegularCrease = !(options.useInfSharpPatch || useSingleCreasePatch);
selectInfSharpRegularCorner = !options.useInfSharpPatch;
selectInfSharpIrregularDart = true;
selectInfSharpIrregularCrease = true;
selectInfSharpIrregularCorner = true;
selectUnisolatedInteriorEdge = useSingleCreasePatch && !options.useInfSharpPatch;
selectNonManifold = true;
selectFVarFeatures = options.considerFVarChannels;
}
void
FeatureMask::ReduceFeatures(Options const & options) {
// Disable typical xordinary vertices:
selectXOrdinaryInterior = false;
selectXOrdinaryBoundary = false;
// If minimizing inf-sharp patches, disable all but sharp/corner irregularities
if (options.useInfSharpPatch) {
selectInfSharpRegularCrease = false;
selectInfSharpRegularCorner = false;
selectInfSharpIrregularDart = false;
selectInfSharpIrregularCrease = false;
}
}
} // end namespace internal
void
TopologyRefiner::RefineAdaptive(AdaptiveOptions options,
ConstIndexArray baseFacesToRefine) {
if (_levels[0]->getNumVertices() == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefiner::RefineAdaptive() -- base level is uninitialized.");
return;
}
if (_refinements.size()) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefiner::RefineAdaptive() -- previous refinements already applied.");
return;
}
//
// Initialize member and local variables from the adaptive options:
//
_isUniform = false;
_adaptiveOptions = options;
//
// Initialize the feature-selection options based on given options -- with two sets
// of levels isolating different sets of features, initialize the two feature sets
// up front and use the appropriate one for each level:
//
int nonLinearScheme = Sdc::SchemeTypeTraits::GetLocalNeighborhoodSize(_subdivType);
int shallowLevel = std::min<int>(options.secondaryLevel, options.isolationLevel);
int deeperLevel = options.isolationLevel;
int potentialMaxLevel = nonLinearScheme ? deeperLevel : _hasIrregFaces;
internal::FeatureMask moreFeaturesMask(options, _regFaceSize);
internal::FeatureMask lessFeaturesMask = moreFeaturesMask;
if (shallowLevel < potentialMaxLevel) {
lessFeaturesMask.ReduceFeatures(options);
}
//
// If face-varying channels are considered, make sure non-linear channels are present
// and turn off consideration if none present:
//
if (moreFeaturesMask.selectFVarFeatures && nonLinearScheme) {
bool nonLinearChannelsPresent = false;
for (int channel = 0; channel < _levels[0]->getNumFVarChannels(); ++channel) {
nonLinearChannelsPresent |= !_levels[0]->getFVarLevel(channel).isLinear();
}
if (!nonLinearChannelsPresent) {
moreFeaturesMask.selectFVarFeatures = false;
lessFeaturesMask.selectFVarFeatures = false;
}
}
//
// Initialize refinement options for Vtr -- full topology is always generated in
// the last level as expected usage is for patch retrieval:
//
Vtr::internal::Refinement::Options refineOptions;
refineOptions._sparse = true;
refineOptions._minimalTopology = false;
refineOptions._faceVertsFirst = options.orderVerticesFromFacesFirst;
Sdc::Split splitType = Sdc::SchemeTypeTraits::GetTopologicalSplitType(_subdivType);
for (int i = 1; i <= potentialMaxLevel; ++i) {
Vtr::internal::Level& parentLevel = getLevel(i-1);
Vtr::internal::Level& childLevel = *(new Vtr::internal::Level);
Vtr::internal::Refinement* refinement = 0;
if (splitType == Sdc::SPLIT_TO_QUADS) {
refinement = new Vtr::internal::QuadRefinement(parentLevel, childLevel, _subdivOptions);
} else {
refinement = new Vtr::internal::TriRefinement(parentLevel, childLevel, _subdivOptions);
}
//
// Initialize a Selector to mark a sparse set of components for refinement -- choose
// the feature selection mask appropriate to the level:
//
Vtr::internal::SparseSelector selector(*refinement);
internal::FeatureMask const & levelFeatures = (i <= shallowLevel) ? moreFeaturesMask
: lessFeaturesMask;
if (i > 1) {
selectFeatureAdaptiveComponents(selector, levelFeatures, ConstIndexArray());
} else if (nonLinearScheme) {
selectFeatureAdaptiveComponents(selector, levelFeatures, baseFacesToRefine);
} else {
selectLinearIrregularFaces(selector, baseFacesToRefine);
}
if (selector.isSelectionEmpty()) {
delete refinement;
delete &childLevel;
break;
} else {
refinement->refine(refineOptions);
appendLevel(childLevel);
appendRefinement(*refinement);
}
}
_maxLevel = (unsigned int) _refinements.size();
assembleFarLevels();
}
//
// Local utility functions for selecting features in faces for adaptive refinement:
//
namespace {
//
// First are a couple of low-level utility methods to perform the same analysis
// at a corner or the entire face for specific detection of inf-sharp or boundary
// features. These are shared between the analysis of the main face and those in
// face-varying channels (which only differ from the main face in the presence of
// face-varying boundaries).
//
// The first can be applied equally to an individual corner or to the entire face
// (using its composite tag). The second applies to the entire face, making use
// of the first, and is the main entry point for dealng with inf-sharp features.
//
// Note we can use the composite tag here even though it arises from all corners
// of the face and so does not represent a specific corner. When at least one
// smooth interior vertex exists, it limits the combinations that can exist on the
// remaining corners (though quads and tris cannot be treated equally here).
//
// If any inf-sharp features are to be selected, identify them first as irregular
// or not, then qualify them more specifically. (Remember that a regular vertex
// may have its neighboring faces partitioned into irregular regions in the
// presence of inf-sharp edges. Similarly an irregular vertex may have its
// neighborhood partitioned into regular regions.)
//
inline bool
doesInfSharpVTagHaveFeatures(Vtr::internal::Level::VTag compVTag,
internal::FeatureMask const & featureMask) {
// Note that even though the given VTag may represent an individual corner, we
// use more general bitwise tests here (particularly the Rule) so that we can
// pass in a composite tag for the entire face and have the same tests applied:
//
if (compVTag._infIrregular) {
if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
return featureMask.selectInfSharpIrregularCorner;
} else if (compVTag._rule & Sdc::Crease::RULE_CREASE) {
return compVTag._boundary ? featureMask.selectXOrdinaryBoundary :
featureMask.selectInfSharpIrregularCrease;
} else if (compVTag._rule & Sdc::Crease::RULE_DART) {
return featureMask.selectInfSharpIrregularDart;
}
} else if (compVTag._boundary) {
// Remember that regular boundary features should never be selected, except
// for a boundary crease sharpened (and so a Corner) by an interior edge:
if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
return compVTag._corner ? false : featureMask.selectInfSharpRegularCorner;
} else {
return false;
}
} else {
if (compVTag._rule & Sdc::Crease::RULE_CORNER) {
return featureMask.selectInfSharpRegularCorner;
} else {
return featureMask.selectInfSharpRegularCrease;
}
}
return false;
}
inline bool
doesInfSharpFaceHaveFeatures(Vtr::internal::Level::VTag compVTag,
Vtr::internal::Level::VTag vTags[], int numVerts,
internal::FeatureMask const & featureMask) {
//
// For quads, if at least one smooth corner of a regular face, features
// are isolated enough to make use of the composite tag alone (unless
// boundary isolation is enabled, in which case trivially return).
//
// For tris, the presence of boundaries creates more ambiguity, so we
// need to exclude that case and inspect corner features individually.
//
bool isolateQuadBoundaries = false;
bool atLeastOneSmoothCorner = (compVTag._rule & Sdc::Crease::RULE_SMOOTH);
if (numVerts == 4) {
if (atLeastOneSmoothCorner) {
return doesInfSharpVTagHaveFeatures(compVTag, featureMask);
} else if (isolateQuadBoundaries) {
return true;
} else if (featureMask.selectUnisolatedInteriorEdge) {
// Needed for single-crease approximation to inf-sharp interior edge:
for (int i = 0; i < 4; ++i) {
if (vTags[i]._infSharpEdges && !vTags[i]._boundary) {
return true;
}
}
}
} else {
if (atLeastOneSmoothCorner && !compVTag._boundary) {
return doesInfSharpVTagHaveFeatures(compVTag, featureMask);
}
}
for (int i = 0; i < numVerts; ++i) {
if (!(vTags[i]._rule & Sdc::Crease::RULE_SMOOTH)) {
if (doesInfSharpVTagHaveFeatures(vTags[i], featureMask)) {
return true;
}
}
}
return false;
}
//
// This is the core method/function for analyzing a face and deciding whether or not
// to included it during feature-adaptive refinement.
//
// Topological analysis of the face exploits tags that are applied to corner vertices
// and carried through the refinement hierarchy. The tags were designed with this
// in mind and also to be combined via bitwise-OR to make collective decisions about
// the neighborhood of the entire face.
//
// After a few trivial acceptances/rejections, feature detection is divided up into
// semi-sharp and inf-sharp cases -- note that both may be present, but semi-sharp
// features have an implicit precedence until they decay and so are handled first.
// They are also fairly trivial to deal with (most often requiring selection) while
// the presence of boundaries and additional options complicates the inf-sharp case.
// Since the inf-sharp logic needs to be applied in face-varying cases, it exists in
// a separate method.
//
// This was originally written specific to the quad-centric Catmark scheme and was
// since generalized to support Loop given the enhanced tagging of components based
// on the scheme. Any enhancements here should be aware of the intended generality.
// Ultimately it may not be worth trying to keep this general and we will be better
// off specializing it for each scheme. The fact that this method is intimately tied
// to patch generation also begs for it to become part of a class that encompasses
// both the feature adaptive tagging and the identification of the intended patches
// that result from it.
//
bool
doesFaceHaveFeatures(Vtr::internal::Level const& level, Index face,
internal::FeatureMask const & featureMask, int regFaceSize) {
using Vtr::internal::Level;
ConstIndexArray fVerts = level.getFaceVertices(face);
// Irregular faces (base level) are unconditionally included:
if (fVerts.size() != regFaceSize) {
return true;
}
// Gather and combine the VTags:
Level::VTag vTags[4];
level.getFaceVTags(face, vTags);
Level::VTag compFaceVTag = Level::VTag::BitwiseOr(vTags, fVerts.size());
// Faces incident irregular faces (base level) are unconditionally included:
if (compFaceVTag._incidIrregFace) {
return true;
}
// Incomplete faces (incomplete neighborhood) are unconditionally excluded:
if (compFaceVTag._incomplete) {
return false;
}
// Select non-manifold features if specified, otherwise treat as inf-sharp:
if (compFaceVTag._nonManifold && featureMask.selectNonManifold) {
return true;
}
// Select (smooth) xord vertices if specified, boundaries handled with inf-sharp:
if (compFaceVTag._xordinary && featureMask.selectXOrdinaryInterior) {
if (compFaceVTag._rule == Sdc::Crease::RULE_SMOOTH) {
return true;
} else if (level.getDepth() < 2) {
for (int i = 0; i < fVerts.size(); ++i) {
if (vTags[i]._xordinary && (vTags[i]._rule == Sdc::Crease::RULE_SMOOTH)) {
return true;
}
}
}
}
// If all smooth corners, no remaining features to select (x-ordinary dealt with):
if (compFaceVTag._rule == Sdc::Crease::RULE_SMOOTH) {
return false;
}
// Semi-sharp features -- select all immediately or test the single-crease case:
if (compFaceVTag._semiSharp || compFaceVTag._semiSharpEdges) {
if (featureMask.selectSemiSharpSingle && featureMask.selectSemiSharpNonSingle) {
return true;
} else if (level.isSingleCreasePatch(face)) {
return featureMask.selectSemiSharpSingle;
} else {
return featureMask.selectSemiSharpNonSingle;
}
}
// Inf-sharp features (including boundaries) -- delegate to shared method:
if (compFaceVTag._infSharp || compFaceVTag._infSharpEdges) {
return doesInfSharpFaceHaveFeatures(compFaceVTag, vTags, fVerts.size(), featureMask);
}
return false;
}
//
// Analyzing the face-varying topology for selection is considerably simpler that
// for the face and its vertices -- in part due to the fact that these faces lie on
// face-varying boundaries, and also due to assumptions about prior inspection:
//
// - it is assumed the face topologgy does not match, so the face must lie on
// a FVar boundary, i.e. inf-sharp
//
// - it is assumed the face vertices were already inspected, so cases such as
// semi-sharp or smooth interior x-ordinary features have already triggered
// selection
//
// That leaves the inspection of inf-sharp features, for the tags from the face
// varying channel -- code that is shared with the main face.
//
bool
doesFaceHaveDistinctFaceVaryingFeatures(Vtr::internal::Level const& level, Index face,
internal::FeatureMask const & featureMask, int fvarChannel) {
using Vtr::internal::Level;
ConstIndexArray fVerts = level.getFaceVertices(face);
assert(!level.doesFaceFVarTopologyMatch(face, fvarChannel));
// We can't use the composite VTag for the face here as it only includes the FVar
// values specific to this face. We need to account for all FVar values around
// each corner of the face -- including those in potentially completely disjoint
// sets -- to ensure that adjacent faces remain compatibly refined (i.e. differ
// by only one level), so we use the composite tags for the corner vertices:
//
Level::VTag vTags[4];
for (int i = 0; i < fVerts.size(); ++i) {
vTags[i] = level.getVertexCompositeFVarVTag(fVerts[i], fvarChannel);
}
Level::VTag compVTag = Level::VTag::BitwiseOr(vTags, fVerts.size());
// Incomplete faces (incomplete neighborhood) are unconditionally excluded:
if (compVTag._incomplete) {
return false;
}
// Select non-manifold features if specified, otherwise treat as inf-sharp:
if (compVTag._nonManifold && featureMask.selectNonManifold) {
return true;
}
// Any remaining locally extra-ordinary face-varying boundaries warrant selection:
if (compVTag._xordinary && featureMask.selectXOrdinaryInterior) {
return true;
}
// Given faces with differing FVar topology are on boundaries, defer to inf-sharp:
return doesInfSharpFaceHaveFeatures(compVTag, vTags, fVerts.size(), featureMask);
}
} // end namespace
//
// Method for selecting components for sparse refinement based on the feature-adaptive needs
// of patch generation.
//
// It assumes we have a freshly initialized SparseSelector (i.e. nothing already selected)
// and will select all relevant topological features for inclusion in the subsequent sparse
// refinement.
//
void
TopologyRefiner::selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector& selector,
internal::FeatureMask const & featureMask,
ConstIndexArray facesToRefine) {
//
// Inspect each face and the properties tagged at all of its corners:
//
Vtr::internal::Level const& level = selector.getRefinement().parent();
int numFacesToRefine = facesToRefine.size() ? facesToRefine.size() : level.getNumFaces();
int numFVarChannels = featureMask.selectFVarFeatures ? level.getNumFVarChannels() : 0;
for (int fIndex = 0; fIndex < numFacesToRefine; ++fIndex) {
Vtr::Index face = facesToRefine.size() ? facesToRefine[fIndex] : (Index) fIndex;
if (HasHoles() && level.isFaceHole(face)) continue;
//
// Test if the face has any of the specified features present. If not, and FVar
// channels are to be considered, look for features in the FVar channels:
//
bool selectFace = doesFaceHaveFeatures(level, face, featureMask, _regFaceSize);
if (!selectFace && featureMask.selectFVarFeatures) {
for (int channel = 0; !selectFace && (channel < numFVarChannels); ++channel) {
// Only test the face for this channel if the topology does not match:
if (!level.doesFaceFVarTopologyMatch(face, channel)) {
selectFace = doesFaceHaveDistinctFaceVaryingFeatures(
level, face, featureMask, channel);
}
}
}
if (selectFace) {
selector.selectFace(face);
}
}
}
void
TopologyRefiner::selectLinearIrregularFaces(Vtr::internal::SparseSelector& selector,
ConstIndexArray facesToRefine) {
//
// Inspect each face and select only irregular faces:
//
Vtr::internal::Level const& level = selector.getRefinement().parent();
int numFacesToRefine = facesToRefine.size() ? facesToRefine.size() : level.getNumFaces();
for (int fIndex = 0; fIndex < numFacesToRefine; ++fIndex) {
Vtr::Index face = facesToRefine.size() ? facesToRefine[fIndex] : (Index) fIndex;
if (HasHoles() && level.isFaceHole(face)) continue;
if (level.getFaceVertices(face).size() != _regFaceSize) {
selector.selectFace(face);
}
}
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,292 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_TOPOLOGY_REFINER_H
#define OPENSUBDIV3_FAR_TOPOLOGY_REFINER_H
#include "../version.h"
#include "../sdc/types.h"
#include "../sdc/options.h"
#include "../far/types.h"
#include "../far/topologyLevel.h"
#include <vector>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Vtr { namespace internal { class SparseSelector; } }
namespace Far { namespace internal { class FeatureMask; } }
namespace Far {
template <typename REAL> class PrimvarRefinerReal;
template <class MESH> class TopologyRefinerFactory;
///
/// \brief Stores topology data for a specified set of refinement options.
///
class TopologyRefiner {
public:
/// \brief Constructor
TopologyRefiner(Sdc::SchemeType type, Sdc::Options options = Sdc::Options());
/// \brief Destructor
~TopologyRefiner();
/// \brief Returns the subdivision scheme
Sdc::SchemeType GetSchemeType() const { return _subdivType; }
/// \brief Returns the subdivision options
Sdc::Options GetSchemeOptions() const { return _subdivOptions; }
/// \brief Returns true if uniform refinement has been applied
bool IsUniform() const { return _isUniform; }
/// \brief Returns the number of refinement levels
int GetNumLevels() const { return (int)_farLevels.size(); }
/// \brief Returns the highest level of refinement
int GetMaxLevel() const { return _maxLevel; }
/// \brief Returns the maximum vertex valence in all levels
int GetMaxValence() const { return _maxValence; }
/// \brief Returns true if faces have been tagged as holes
bool HasHoles() const { return _hasHoles; }
/// \brief Returns the total number of vertices in all levels
int GetNumVerticesTotal() const { return _totalVertices; }
/// \brief Returns the total number of edges in all levels
int GetNumEdgesTotal() const { return _totalEdges; }
/// \brief Returns the total number of edges in all levels
int GetNumFacesTotal() const { return _totalFaces; }
/// \brief Returns the total number of face vertices in all levels
int GetNumFaceVerticesTotal() const { return _totalFaceVertices; }
/// \brief Returns a handle to access data specific to a particular level
TopologyLevel const & GetLevel(int level) const { return _farLevels[level]; }
//@{
/// @name High-level refinement and related methods
///
//
// Uniform refinement
//
/// \brief Uniform refinement options
///
/// Options for uniform refinement, including the number of levels, vertex
/// ordering and generation of topology information.
///
/// Note the impact of the option to generate fullTopologyInLastLevel. Given
/// subsequent levels of uniform refinement typically reguire 4x the data
/// of the previous level, only the minimum amount of data is generated in the
/// last level by default, i.e. a vertex and face-vertex list. If requiring
/// topology traversal of the last level, e.g. inspecting edges or incident
/// faces of vertices, the option to generate full topology in the last
/// level should be enabled.
///
struct UniformOptions {
UniformOptions(int level) :
refinementLevel(level & 0xf),
orderVerticesFromFacesFirst(false),
fullTopologyInLastLevel(false) { }
/// \brief Set uniform refinement level
void SetRefinementLevel(int level) { refinementLevel = level & 0xf; }
unsigned int refinementLevel:4, ///< Number of refinement iterations
orderVerticesFromFacesFirst:1, ///< Order child vertices from faces first
///< instead of child vertices of vertices
fullTopologyInLastLevel:1; ///< Skip topological relationships in the last
///< level of refinement that are not needed for
///< interpolation (keep false if using limit).
};
/// \brief Refine the topology uniformly
///
/// This method applies uniform refinement to the level specified in the
/// given UniformOptions.
///
/// Note the impact of the UniformOption to generate fullTopologyInLastLevel
/// and be sure it is assigned to satisfy the needs of the resulting refinement.
///
/// @param options Options controlling uniform refinement
///
void RefineUniform(UniformOptions options);
/// \brief Returns the options specified on refinement
UniformOptions GetUniformOptions() const { return _uniformOptions; }
//
// Adaptive refinement
//
/// \brief Adaptive refinement options
struct AdaptiveOptions {
AdaptiveOptions(int level) :
isolationLevel(level & 0xf),
secondaryLevel(0xf),
useSingleCreasePatch(false),
useInfSharpPatch(false),
considerFVarChannels(false),
orderVerticesFromFacesFirst(false) { }
/// \brief Set isolation level
void SetIsolationLevel(int level) { isolationLevel = level & 0xf; }
/// \brief Set secondary isolation level
void SetSecondaryLevel(int level) { secondaryLevel = level & 0xf; }
unsigned int isolationLevel:4; ///< Number of iterations applied to isolate
///< extraordinary vertices and creases
unsigned int secondaryLevel:4; ///< Shallower level to stop isolation of
///< smooth irregular features
unsigned int useSingleCreasePatch:1; ///< Use 'single-crease' patch and stop
///< isolation where applicable
unsigned int useInfSharpPatch:1; ///< Use infinitely sharp patches and stop
///< isolation where applicable
unsigned int considerFVarChannels:1; ///< Inspect face-varying channels and
///< isolate when irregular features present
unsigned int orderVerticesFromFacesFirst:1; ///< Order child vertices from faces first
///< instead of child vertices of vertices
};
/// \brief Feature Adaptive topology refinement
///
/// @param options Options controlling adaptive refinement
///
/// @param selectedFaces Limit adaptive refinement to the specified faces
///
void RefineAdaptive(AdaptiveOptions options,
ConstIndexArray selectedFaces = ConstIndexArray());
/// \brief Returns the options specified on refinement
AdaptiveOptions GetAdaptiveOptions() const { return _adaptiveOptions; }
/// \brief Unrefine the topology, keeping only the base level.
void Unrefine();
//@{
/// @name Number and properties of face-varying channels:
///
/// \brief Returns the number of face-varying channels in the tables
int GetNumFVarChannels() const;
/// \brief Returns the face-varying interpolation rule set for a given channel
Sdc::Options::FVarLinearInterpolation GetFVarLinearInterpolation(int channel = 0) const;
/// \brief Returns the total number of face-varying values in all levels
int GetNumFVarValuesTotal(int channel = 0) const;
//@}
protected:
//
// Lower level protected methods intended strictly for internal use:
//
template <class MESH>
friend class TopologyRefinerFactory;
friend class TopologyRefinerFactoryBase;
friend class PatchTableBuilder;
friend class PatchBuilder;
friend class PtexIndices;
template <typename REAL>
friend class PrimvarRefinerReal;
// Copy constructor exposed via the factory class:
TopologyRefiner(TopologyRefiner const & source);
public:
// Levels and Refinements available internally (avoids need for more friends)
Vtr::internal::Level & getLevel(int l) { return *_levels[l]; }
Vtr::internal::Level const & getLevel(int l) const { return *_levels[l]; }
Vtr::internal::Refinement & getRefinement(int l) { return *_refinements[l]; }
Vtr::internal::Refinement const & getRefinement(int l) const { return *_refinements[l]; }
private:
// Not default constructible or copyable:
TopologyRefiner() : _uniformOptions(0), _adaptiveOptions(0) { }
TopologyRefiner & operator=(TopologyRefiner const &) { return *this; }
void selectFeatureAdaptiveComponents(Vtr::internal::SparseSelector& selector,
internal::FeatureMask const & mask,
ConstIndexArray selectedFaces);
void selectLinearIrregularFaces(Vtr::internal::SparseSelector& selector,
ConstIndexArray selectedFaces);
void initializeInventory();
void updateInventory(Vtr::internal::Level const & newLevel);
void appendLevel(Vtr::internal::Level & newLevel);
void appendRefinement(Vtr::internal::Refinement & newRefinement);
void assembleFarLevels();
private:
Sdc::SchemeType _subdivType;
Sdc::Options _subdivOptions;
unsigned int _isUniform : 1;
unsigned int _hasHoles : 1;
unsigned int _hasIrregFaces : 1;
unsigned int _regFaceSize : 3;
unsigned int _maxLevel : 4;
// Options assigned on refinement:
UniformOptions _uniformOptions;
AdaptiveOptions _adaptiveOptions;
// Cumulative properties of all levels:
int _totalVertices;
int _totalEdges;
int _totalFaces;
int _totalFaceVertices;
int _maxValence;
// Note the base level may be shared with another instance
bool _baseLevelOwned;
std::vector<Vtr::internal::Level *> _levels;
std::vector<Vtr::internal::Refinement *> _refinements;
std::vector<TopologyLevel> _farLevels;
};
inline int
TopologyRefiner::GetNumFVarChannels() const {
return _levels[0]->getNumFVarChannels();
}
inline Sdc::Options::FVarLinearInterpolation
TopologyRefiner::GetFVarLinearInterpolation(int channel) const {
return _levels[0]->getFVarOptions(channel).GetFVarLinearInterpolation();
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TOPOLOGY_REFINER_H */

View File

@@ -0,0 +1,434 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#include "../far/topologyRefinerFactory.h"
#include "../far/topologyRefiner.h"
#include "../sdc/types.h"
#include "../vtr/level.h"
#include <cstdio>
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Methods for the Factory base class -- general enough to warrant including
// in the base class rather than the subclass template (and so replicated for
// each usage)
//
//
bool
TopologyRefinerFactoryBase::prepareComponentTopologySizing(
TopologyRefiner& refiner) {
Vtr::internal::Level& baseLevel = refiner.getLevel(0);
//
// At minimum we require face-vertices (the total count of which can be
// determined from the offsets accumulated during sizing pass) and we
// need to resize members related to them to be populated during
// assignment:
//
int vCount = baseLevel.getNumVertices();
int fCount = baseLevel.getNumFaces();
if (vCount == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"mesh contains no vertices.");
return false;
}
if (fCount == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"meshes without faces not yet supported.");
return false;
}
// Make sure no face was defined that would lead to a valence overflow --
// the max valence has been initialized with the maximum number of
// face-vertices:
if (baseLevel.getMaxValence() > Vtr::VALENCE_LIMIT) {
char msg[1024];
snprintf(msg, 1024,
"Failure in TopologyRefinerFactory<>::Create() -- "
"face with %d vertices > %d max.",
baseLevel.getMaxValence(), Vtr::VALENCE_LIMIT);
Error(FAR_RUNTIME_ERROR, msg);
return false;
}
int fVertCount = baseLevel.getNumFaceVertices(fCount - 1) +
baseLevel.getOffsetOfFaceVertices(fCount - 1);
if (fVertCount == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"mesh contains no face-vertices.");
return false;
}
if ((refiner.GetSchemeType() == Sdc::SCHEME_LOOP) &&
(fVertCount != (3 * fCount))) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"non-triangular faces not supported by Loop scheme.");
return false;
}
baseLevel.resizeFaceVertices(fVertCount);
//
// If edges were sized, all other topological relations must be sized
// with it, in which case we allocate those members to be populated.
// Otherwise, sizing of the other topology members is deferred until
// the face-vertices are assigned and the resulting relationships
// determined:
//
int eCount = baseLevel.getNumEdges();
if (eCount > 0) {
baseLevel.resizeFaceEdges(baseLevel.getNumFaceVerticesTotal());
baseLevel.resizeEdgeVertices();
baseLevel.resizeEdgeFaces( baseLevel.getNumEdgeFaces(eCount-1) +
baseLevel.getOffsetOfEdgeFaces(eCount-1));
baseLevel.resizeVertexFaces(baseLevel.getNumVertexFaces(vCount-1) +
baseLevel.getOffsetOfVertexFaces(vCount-1));
baseLevel.resizeVertexEdges(baseLevel.getNumVertexEdges(vCount-1) +
baseLevel.getOffsetOfVertexEdges(vCount-1));
assert(baseLevel.getNumFaceEdgesTotal() > 0);
assert(baseLevel.getNumEdgeVerticesTotal() > 0);
assert(baseLevel.getNumEdgeFacesTotal() > 0);
assert(baseLevel.getNumVertexFacesTotal() > 0);
assert(baseLevel.getNumVertexEdgesTotal() > 0);
}
return true;
}
bool
TopologyRefinerFactoryBase::prepareComponentTopologyAssignment(
TopologyRefiner& refiner, bool fullValidation,
TopologyCallback callback, void const * callbackData) {
Vtr::internal::Level& baseLevel = refiner.getLevel(0);
bool completeMissingTopology = (baseLevel.getNumEdges() == 0);
if (completeMissingTopology) {
if (! baseLevel.completeTopologyFromFaceVertices()) {
char msg[1024];
snprintf(msg, 1024,
"Failure in TopologyRefinerFactory<>::Create() -- "
"vertex with valence %d > %d max.",
baseLevel.getMaxValence(), Vtr::VALENCE_LIMIT);
Error(FAR_RUNTIME_ERROR, msg);
return false;
}
} else {
if (baseLevel.getMaxValence() == 0) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"maximum valence not assigned.");
return false;
}
}
if (fullValidation) {
if (! baseLevel.validateTopology(callback, callbackData)) {
if (completeMissingTopology) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"invalid topology detected from partial specification.");
} else {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::Create() -- "
"invalid topology detected as fully specified.");
}
return false;
}
}
// Now that we have a valid base level, initialize the Refiner's
// component inventory:
refiner.initializeInventory();
return true;
}
bool
TopologyRefinerFactoryBase::prepareComponentTagsAndSharpness(
TopologyRefiner& refiner) {
//
// This method combines the initialization of internal component tags
// with the sharpening of edges and vertices according to the given
// boundary interpolation rule in the Options.
// Since both involve traversing the edge and vertex lists and noting
// the presence of boundaries -- best to do both at once...
//
Vtr::internal::Level& baseLevel = refiner.getLevel(0);
Sdc::Options options = refiner.GetSchemeOptions();
Sdc::Crease creasing(options);
bool makeBoundaryFacesHoles =
(options.GetVtxBoundaryInterpolation() ==
Sdc::Options::VTX_BOUNDARY_NONE) &&
(Sdc::SchemeTypeTraits::GetLocalNeighborhoodSize(
refiner.GetSchemeType()) > 0);
bool sharpenCornerVerts =
(options.GetVtxBoundaryInterpolation() ==
Sdc::Options::VTX_BOUNDARY_EDGE_AND_CORNER);
bool sharpenNonManFeatures = true;
//
// Before initializing edge and vertex tags, tag any qualifying boundary
// faces as holes before the sharpness of incident vertices and edges is
// affected by boundary interpolation rules.
//
// Faces will be excluded (tagged as holes) if they contain a vertex on a
// boundary that did not have all of its incident boundary edges sharpened
// (not just the boundary edges within the face), so inspect the vertices
// and tag their incident faces when necessary:
//
if (makeBoundaryFacesHoles) {
for (Vtr::Index vIndex = 0; vIndex < baseLevel.getNumVertices();
++vIndex) {
Vtr::ConstIndexArray vEdges = baseLevel.getVertexEdges(vIndex);
Vtr::ConstIndexArray vFaces = baseLevel.getVertexFaces(vIndex);
// Ignore manifold interior vertices:
if ((vEdges.size() == vFaces.size()) &&
!baseLevel.getVertexTag(vIndex)._nonManifold) {
continue;
}
bool excludeFaces = false;
for (int i = 0; !excludeFaces && (i < vEdges.size()); ++i) {
excludeFaces = (baseLevel.getNumEdgeFaces(vEdges[i]) == 1) &&
!Sdc::Crease::IsInfinite(
baseLevel.getEdgeSharpness(vEdges[i]));
}
if (excludeFaces) {
for (int i = 0; i < vFaces.size(); ++i) {
baseLevel.getFaceTag(vFaces[i])._hole = true;
}
// Need to tag Refiner (the Level does not keep track of this)
refiner._hasHoles = true;
}
}
}
//
// Process the Edge tags first, as Vertex tags (notably the Rule) are
// dependent on properties of their incident edges.
//
for (Vtr::Index eIndex = 0; eIndex < baseLevel.getNumEdges(); ++eIndex) {
Vtr::internal::Level::ETag& eTag = baseLevel.getEdgeTag(eIndex);
float& eSharpness = baseLevel.getEdgeSharpness(eIndex);
eTag._boundary = (baseLevel.getNumEdgeFaces(eIndex) < 2);
if (eTag._boundary || (eTag._nonManifold && sharpenNonManFeatures)) {
eSharpness = Sdc::Crease::SHARPNESS_INFINITE;
}
eTag._infSharp = Sdc::Crease::IsInfinite(eSharpness);
eTag._semiSharp = Sdc::Crease::IsSharp(eSharpness) && !eTag._infSharp;
}
//
// Process the Vertex tags now -- for some tags (semi-sharp and its rule)
// we need to inspect all incident edges:
//
int schemeRegularInteriorValence =
Sdc::SchemeTypeTraits::GetRegularVertexValence(refiner.GetSchemeType());
int schemeRegularBoundaryValence = schemeRegularInteriorValence / 2;
for (Vtr::Index vIndex = 0; vIndex < baseLevel.getNumVertices(); ++vIndex) {
Vtr::internal::Level::VTag& vTag = baseLevel.getVertexTag(vIndex);
float& vSharpness = baseLevel.getVertexSharpness(vIndex);
Vtr::ConstIndexArray vEdges = baseLevel.getVertexEdges(vIndex);
Vtr::ConstIndexArray vFaces = baseLevel.getVertexFaces(vIndex);
//
// Take inventory of properties of incident edges that affect this
// vertex:
//
int boundaryEdgeCount = 0;
int infSharpEdgeCount = 0;
int semiSharpEdgeCount = 0;
int nonManifoldEdgeCount = 0;
for (int i = 0; i < vEdges.size(); ++i) {
Vtr::internal::Level::ETag const& eTag =
baseLevel.getEdgeTag(vEdges[i]);
boundaryEdgeCount += eTag._boundary;
infSharpEdgeCount += eTag._infSharp;
semiSharpEdgeCount += eTag._semiSharp;
nonManifoldEdgeCount += eTag._nonManifold;
}
int sharpEdgeCount = infSharpEdgeCount + semiSharpEdgeCount;
//
// Sharpen the vertex before using it in conjunction with incident edge
// properties to determine the semi-sharp tag and rule:
//
bool isTopologicalCorner = (vFaces.size() == 1) && (vEdges.size() == 2);
bool isSharpenedCorner = isTopologicalCorner && sharpenCornerVerts;
if (isSharpenedCorner) {
vSharpness = Sdc::Crease::SHARPNESS_INFINITE;
} else if (vTag._nonManifold && sharpenNonManFeatures &&
!Sdc::Crease::IsInfinite(vSharpness)) {
//
// We avoid sharpening non-manifold vertices when they occur on
// interior non-manifold creases, i.e. a pair of opposing non-
// manifold edges with more than two incident faces. In these
// cases there are more incident faces than edges (1 more for
// each additional "fin") and no boundaries. Closer inspection
// of manifold subsets around the vertex is required to truly
// determine the crease case, so avoid it using pre-conditions
// that are available here:
//
bool isNonManCrease = (nonManifoldEdgeCount == 2) &&
(boundaryEdgeCount == 0) &&
(vFaces.size() > vEdges.size()) &&
baseLevel.testVertexNonManifoldCrease(vIndex);
if (!isNonManCrease) {
vSharpness = Sdc::Crease::SHARPNESS_INFINITE;
}
}
vTag._infSharp = Sdc::Crease::IsInfinite(vSharpness);
vTag._semiSharp = Sdc::Crease::IsSemiSharp(vSharpness);
vTag._semiSharpEdges = (semiSharpEdgeCount > 0);
vTag._rule = (Vtr::internal::Level::VTag::VTagSize)
creasing.DetermineVertexVertexRule(vSharpness, sharpEdgeCount);
//
// Assign topological tags -- note that the "xordinary" tag is not
// assigned if non-manifold:
//
vTag._boundary = (boundaryEdgeCount > 0);
vTag._corner = isTopologicalCorner && vTag._infSharp;
if (vTag._nonManifold) {
vTag._xordinary = false;
} else if (vTag._corner) {
vTag._xordinary = false;
} else if (vTag._boundary) {
vTag._xordinary = (vFaces.size() != schemeRegularBoundaryValence);
} else {
vTag._xordinary = (vFaces.size() != schemeRegularInteriorValence);
}
vTag._incomplete = 0;
//
// Assign tags specific to inf-sharp features to identify regular
// topologies partitioned by inf-sharp creases -- must be no semi-
// harp features here (and manifold for now):
//
vTag._infSharpEdges = (infSharpEdgeCount > 0);
vTag._infSharpCrease = false;
vTag._infIrregular = vTag._infSharp || vTag._infSharpEdges;
if (vTag._infSharpEdges) {
// Ignore semi-sharp vertex sharpness when computing the
// inf-sharp Rule:
Sdc::Crease::Rule infRule = creasing.DetermineVertexVertexRule(
(vTag._infSharp ? vSharpness : 0.0f), infSharpEdgeCount);
if (infRule == Sdc::Crease::RULE_CREASE) {
vTag._infSharpCrease = true;
// A "regular" inf-crease can only occur along a manifold
// regular boundary or by bisecting a manifold interior
// region (it is also possible along non-manifold vertices
// in some cases, but that requires much more effort to
// detect -- perhaps later...)
//
if (!vTag._xordinary && !vTag._nonManifold) {
if (vTag._boundary) {
vTag._infIrregular = false;
} else {
assert((schemeRegularInteriorValence == 4) ||
(schemeRegularInteriorValence == 6));
if (schemeRegularInteriorValence == 4) {
vTag._infIrregular =
(baseLevel.getEdgeTag(vEdges[0])._infSharp !=
baseLevel.getEdgeTag(vEdges[2])._infSharp);
} else if (schemeRegularInteriorValence == 6) {
vTag._infIrregular =
(baseLevel.getEdgeTag(vEdges[0])._infSharp !=
baseLevel.getEdgeTag(vEdges[3])._infSharp) ||
(baseLevel.getEdgeTag(vEdges[1])._infSharp !=
baseLevel.getEdgeTag(vEdges[4])._infSharp);
}
}
}
} else if (infRule == Sdc::Crease::RULE_CORNER) {
// A regular set of inf-corners occurs when all edges are
// sharp and not a smooth corner:
//
if ((infSharpEdgeCount == vEdges.size() &&
((vEdges.size() > 2) || vTag._infSharp))) {
vTag._infIrregular = false;
}
}
}
//
// If any irregular faces are present, mark whether or not a vertex
// is incident any irregular face:
//
if (refiner._hasIrregFaces) {
int regSize = refiner._regFaceSize;
for (int i = 0; i < vFaces.size(); ++i) {
if (baseLevel.getFaceVertices(vFaces[i]).size() != regSize) {
vTag._incidIrregFace = true;
break;
}
}
}
}
return true;
}
bool
TopologyRefinerFactoryBase::prepareFaceVaryingChannels(
TopologyRefiner& refiner) {
Vtr::internal::Level& baseLevel = refiner.getLevel(0);
int regVertexValence =
Sdc::SchemeTypeTraits::GetRegularVertexValence(refiner.GetSchemeType());
int regBoundaryValence = regVertexValence / 2;
for (int channel=0; channel<refiner.GetNumFVarChannels(); ++channel) {
if (baseLevel.getNumFVarValues(channel) == 0) {
char msg[1024];
snprintf(msg, 1024,
"Failure in TopologyRefinerFactory<>::Create() -- "
"face-varying channel %d has no values.", channel);
Error(FAR_RUNTIME_ERROR, msg);
return false;
}
baseLevel.completeFVarChannelTopology(channel, regBoundaryValence);
}
return true;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
} // end namespace OpenSubdiv

View File

@@ -0,0 +1,706 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H
#define OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H
#include "../version.h"
#include "../far/topologyRefiner.h"
#include "../far/error.h"
#include <cassert>
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
///\brief Private base class of Factories for constructing TopologyRefiners
///
/// TopologyRefinerFactoryBase is the base class for subclasses that are intended to
/// construct TopologyRefiners directly from meshes in their native representations.
/// The subclasses are parameterized by the mesh type \<class MESH\> and are expected
/// to inherit the details related to assembly and validation provided here that are
/// independent of the subclass' mesh type.
//
class TopologyRefinerFactoryBase {
protected:
//
// Protected methods invoked by the subclass template to verify and process each
// stage of construction implemented by the subclass:
//
typedef Vtr::internal::Level::ValidationCallback TopologyCallback;
static bool prepareComponentTopologySizing(TopologyRefiner& refiner);
static bool prepareComponentTopologyAssignment(TopologyRefiner& refiner, bool fullValidation,
TopologyCallback callback, void const * callbackData);
static bool prepareComponentTagsAndSharpness(TopologyRefiner& refiner);
static bool prepareFaceVaryingChannels(TopologyRefiner& refiner);
};
///\brief Factory for constructing TopologyRefiners from specific mesh classes.
///
/// TopologyRefinerFactory<MESH> is the factory class template to convert an instance of
/// TopologyRefiner from an arbitrary mesh class. While a class template, the implementation
/// is not (cannot) be complete, so specialization of a few methods is required (it is a
/// stateless factory, so no instance and only static methods).
///
/// This template provides both the interface and high level assembly for the construction
/// of the TopologyRefiner instance. The high level construction executes a specific set
/// of operations to convert the client's MESH into TopologyRefiner. This set of operations
/// combines methods independent of MESH from the base class with those specialized here for
/// class MESH.
///
template <class MESH>
class TopologyRefinerFactory : public TopologyRefinerFactoryBase {
public:
/// \brief Options related to the construction of each TopologyRefiner.
///
struct Options {
Options(Sdc::SchemeType sdcType = Sdc::SCHEME_CATMARK, Sdc::Options sdcOptions = Sdc::Options()) :
schemeType(sdcType),
schemeOptions(sdcOptions),
validateFullTopology(false) { }
Sdc::SchemeType schemeType; ///< The subdivision scheme type identifier
Sdc::Options schemeOptions; ///< The full set of options for the scheme,
///< e.g. boundary interpolation rules...
unsigned int validateFullTopology : 1; ///< Apply more extensive validation of
///< the constructed topology -- intended
///< for debugging.
};
/// \brief Instantiates a TopologyRefiner from client-provided topological
/// representation.
///
/// If only the face-vertices topological relationships are specified
/// with this factory, edge relationships have to be inferred, which
/// requires additional processing. If the client topological rep can
/// provide this information, it is highly recommended to do so.
///
/// @param mesh Client's topological representation (or a converter)
//
/// @param options Options controlling the creation of the TopologyRefiner
///
/// @return A new instance of TopologyRefiner or 0 for failure
///
static TopologyRefiner* Create(MESH const& mesh, Options options = Options());
/// \brief Instantiates a TopologyRefiner from the base level of an
/// existing instance.
///
/// This allows lightweight copies of the same topology to be refined
/// differently for each new instance. As with other classes that refer
/// to an existing TopologyRefiner, it must generally exist for the entire
/// lifetime of the new instance. In this case, the base level of the
/// original instance must be preserved.
///
/// @param baseLevel An existing TopologyRefiner to share base level.
///
/// @return A new instance of TopologyRefiner or 0 for failure
///
static TopologyRefiner* Create(TopologyRefiner const & baseLevel);
protected:
typedef Vtr::internal::Level::TopologyError TopologyError;
//@{
/// @name Methods to be provided to complete assembly of the TopologyRefiner
///
///
/// These methods are to be specialized to implement all details specific to
/// class MESH required to convert MESH data to TopologyRefiner. Note that
/// some of these *must* be specialized in order to complete construction while
/// some are optional.
///
/// There are two minimal construction requirements (to specify the size and
/// content of all topology relations) and three optional (to specify feature
/// tags, face-varying data, and runtime validation and error reporting).
///
/// See comments in the generic stubs, the factory for Far::TopologyDescriptor
/// or the tutorials for more details on writing these.
///
/// \brief Specify the number of vertices, faces, face-vertices, etc.
static bool resizeComponentTopology(TopologyRefiner& newRefiner, MESH const& mesh);
/// \brief Specify the relationships between vertices, faces, etc. ie the
/// face-vertices, vertex-faces, edge-vertices, etc.
static bool assignComponentTopology(TopologyRefiner& newRefiner, MESH const& mesh);
/// \brief (Optional) Specify edge or vertex sharpness or face holes
static bool assignComponentTags(TopologyRefiner& newRefiner, MESH const& mesh);
/// \brief (Optional) Specify face-varying data per face
static bool assignFaceVaryingTopology(TopologyRefiner& newRefiner, MESH const& mesh);
/// \brief (Optional) Control run-time topology validation and error reporting
static void reportInvalidTopology(TopologyError errCode, char const * msg, MESH const& mesh);
//@}
protected:
//@{
/// @name Base level assembly methods to be used within resizeComponentTopology()
///
/// \brief These methods specify sizes of various quantities, e.g. the number of
/// vertices, faces, face-vertices, etc. The number of the primary components
/// (vertices, faces and edges) should be specified prior to anything else that
/// references them (e.g. we need to know the number of faces before specifying
/// the vertices for that face.
///
/// If a full boundary representation with all neighborhood information is not
/// available, e.g. faces and vertices are available but not edges, only the
/// face-vertices should be specified. The remaining topological relationships
/// will be constructed later in the assembly (though at greater cost than if
/// specified directly).
///
/// The sizes for topological relationships between individual components should be
/// specified in order, i.e. the number of face-vertices for each successive face.
///
/// \brief Specify the number of vertices to be accommodated
static void setNumBaseVertices(TopologyRefiner & newRefiner, int count);
/// \brief Specify the number of faces to be accommodated
static void setNumBaseFaces(TopologyRefiner & newRefiner, int count);
/// \brief Specify the number of edges to be accommodated
static void setNumBaseEdges(TopologyRefiner & newRefiner, int count);
/// \brief Specify the number of vertices incident each face
static void setNumBaseFaceVertices(TopologyRefiner & newRefiner, Index f, int count);
/// \brief Specify the number of faces incident each edge
static void setNumBaseEdgeFaces(TopologyRefiner & newRefiner, Index e, int count);
/// \brief Specify the number of faces incident each vertex
static void setNumBaseVertexFaces(TopologyRefiner & newRefiner, Index v, int count);
/// \brief Specify the number of edges incident each vertex
static void setNumBaseVertexEdges(TopologyRefiner & newRefiner, Index v, int count);
static int getNumBaseVertices(TopologyRefiner const & newRefiner);
static int getNumBaseFaces(TopologyRefiner const & newRefiner);
static int getNumBaseEdges(TopologyRefiner const & newRefiner);
//@}
//@{
/// @name Base level assembly methods to be used within assignComponentTopology()
///
/// \brief These methods populate relationships between components -- in much the
/// same manner as they are inspected once the TopologyRefiner is completed.
///
/// An array of fixed size is returned from these methods and its entries are to be
/// populated with the appropriate indices for its neighbors. At minimum, the
/// vertices for each face must be specified. As noted previously, the remaining
/// relationships will be constructed as needed.
///
/// The ordering of entries in these arrays is important -- they are expected to
/// be ordered counter-clockwise for a right-hand orientation.
///
/// Non-manifold components must be explicitly tagged as such and they do not
/// require the ordering expected of manifold components. Special consideration
/// must also be given to certain non-manifold situations, e.g. the same edge
/// cannot appear twice in a face, and a degenerate edge (same vertex at both
/// ends) can only have one incident face. Such considerations are typically
/// achievable by creating multiple instances of an edge. So while there will
/// always be a one-to-one correspondence between vertices and faces, the same
/// is not guaranteed of edges in certain non-manifold circumstances.
///
/// \brief Assign the vertices incident each face
static IndexArray getBaseFaceVertices(TopologyRefiner & newRefiner, Index f);
/// \brief Assign the edges incident each face
static IndexArray getBaseFaceEdges(TopologyRefiner & newRefiner, Index f);
/// \brief Assign the vertices incident each edge
static IndexArray getBaseEdgeVertices(TopologyRefiner & newRefiner, Index e);
/// \brief Assign the faces incident each edge
static IndexArray getBaseEdgeFaces(TopologyRefiner & newRefiner, Index e);
/// \brief Assign the faces incident each vertex
static IndexArray getBaseVertexFaces(TopologyRefiner & newRefiner, Index v);
/// \brief Assign the edges incident each vertex
static IndexArray getBaseVertexEdges(TopologyRefiner & newRefiner, Index v);
/// \brief Assign the local indices of a vertex within each of its incident faces
static LocalIndexArray getBaseVertexFaceLocalIndices(TopologyRefiner & newRefiner, Index v);
/// \brief Assign the local indices of a vertex within each of its incident edges
static LocalIndexArray getBaseVertexEdgeLocalIndices(TopologyRefiner & newRefiner, Index v);
/// \brief Assign the local indices of an edge within each of its incident faces
static LocalIndexArray getBaseEdgeFaceLocalIndices(TopologyRefiner & newRefiner, Index e);
/// \brief Determine all local indices by inspection (only for pure manifold meshes)
static void populateBaseLocalIndices(TopologyRefiner & newRefiner);
/// \brief Tag an edge as non-manifold
static void setBaseEdgeNonManifold(TopologyRefiner & newRefiner, Index e, bool b);
/// \brief Tag a vertex as non-manifold
static void setBaseVertexNonManifold(TopologyRefiner & newRefiner, Index v, bool b);
//@}
//@{
/// @name Base level assembly methods to be used within assignComponentTags()
///
/// These methods are used to assign edge or vertex sharpness, for tagging faces
/// as holes, etc. Unlike topological assignment, only those components that
/// possess a feature of interest need be explicitly assigned.
///
/// Since topological construction is largely complete by this point, a method is
/// available to identify an edge for sharpness assignment given a pair of vertices.
///
/// \brief Identify an edge to be assigned a sharpness value given a vertex pair
static Index findBaseEdge(TopologyRefiner const & newRefiner, Index v0, Index v1);
/// \brief Assign a sharpness value to a given edge
static void setBaseEdgeSharpness(TopologyRefiner & newRefiner, Index e, float sharpness);
/// \brief Assign a sharpness value to a given vertex
static void setBaseVertexSharpness(TopologyRefiner & newRefiner, Index v, float sharpness);
/// \brief Tag a face as a hole
static void setBaseFaceHole(TopologyRefiner & newRefiner, Index f, bool isHole);
//@}
//@{
/// @name Base level assembly methods to be used within assignFaceVaryingTopology()
///
/// Face-varying data is assigned to faces in much the same way as face-vertex
/// topology is assigned -- indices for face-varying values are assigned to the
/// corners of each face just as indices for vertices were assigned.
///
/// Independent sets of face-varying data are stored in channels. The identifier
/// of each channel (an integer) is expected whenever referring to face-varying
/// data in any form.
///
/// \brief Create a new face-varying channel with the given number of values
static int createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues);
/// \brief Create a new face-varying channel with the given number of values and independent interpolation options
static int createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues, Sdc::Options const& fvarOptions);
/// \brief Assign the face-varying values for the corners of each face
static IndexArray getBaseFaceFVarValues(TopologyRefiner & newRefiner, Index face, int channel = 0);
//@}
protected:
//
// Not to be specialized:
//
static bool populateBaseLevel(TopologyRefiner& refiner, MESH const& mesh, Options options);
private:
//
// An oversight in the interfaces of the error reporting function between the factory
// class and the Vtr::Level requires this adapter function to avoid warnings.
//
// The static class method requires a reference as the MESH argument, but the interface
// for Vtr::Level requires a pointer (void*). So this adapter with a MESH* argument is
// used to effectively cast the function pointer required by Vtr::Level error reporting:
//
static void reportInvalidTopologyAdapter(TopologyError errCode, char const * msg, MESH const * mesh) {
reportInvalidTopology(errCode, msg, *mesh);
}
};
//
// Generic implementations:
//
template <class MESH>
TopologyRefiner*
TopologyRefinerFactory<MESH>::Create(MESH const& mesh, Options options) {
TopologyRefiner * refiner = new TopologyRefiner(options.schemeType, options.schemeOptions);
if (! populateBaseLevel(*refiner, mesh, options)) {
delete refiner;
return 0;
}
// Eventually want to move the Refiner's inventory initialization here. Currently it
// is handled after topology assignment, but if the inventory is to include additional
// features (e.g. holes, etc.) it is better off deferred to here.
return refiner;
}
template <class MESH>
TopologyRefiner*
TopologyRefinerFactory<MESH>::Create(TopologyRefiner const & source) {
return new TopologyRefiner(source);
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::populateBaseLevel(TopologyRefiner& refiner, MESH const& mesh, Options options) {
//
// Construction of a specialized topology refiner involves four steps, each of which
// involves a method specialized for MESH followed by one that takes an action in
// response to it or in preparation for the next step.
//
// Both the specialized methods and those that follow them may find fault in the
// construction and trigger failure at any time:
//
//
// Sizing of the topology -- this is a required specialization for MESH. This defines
// an inventory of all components and their relations that is used to allocate buffers
// to be efficiently populated in the subsequent topology assignment step.
//
if (! resizeComponentTopology(refiner, mesh)) return false;
if (! prepareComponentTopologySizing(refiner)) return false;
//
// Assignment of the topology -- this is a required specialization for MESH. If edges
// are specified, all other topological relations are expected to be defined for them.
// Otherwise edges and remaining topology will be completed from the face-vertices:
//
bool validate = options.validateFullTopology;
TopologyCallback callback = reinterpret_cast<TopologyCallback>(reportInvalidTopologyAdapter);
void const * userData = &mesh;
if (! assignComponentTopology(refiner, mesh)) return false;
if (! prepareComponentTopologyAssignment(refiner, validate, callback, userData)) return false;
//
// User assigned and internal tagging of components -- an optional specialization for
// MESH. Allows the specification of sharpness values, holes, etc.
//
if (! assignComponentTags(refiner, mesh)) return false;
if (! prepareComponentTagsAndSharpness(refiner)) return false;
//
// Defining channels of face-varying primvar data -- an optional specialization for MESH.
//
if (! assignFaceVaryingTopology(refiner, mesh)) return false;
if (! prepareFaceVaryingChannels(refiner)) return false;
return true;
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseFaces(TopologyRefiner & newRefiner, int count) {
newRefiner._levels[0]->resizeFaces(count);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseEdges(TopologyRefiner & newRefiner, int count) {
newRefiner._levels[0]->resizeEdges(count);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseVertices(TopologyRefiner & newRefiner, int count) {
newRefiner._levels[0]->resizeVertices(count);
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::getNumBaseFaces(TopologyRefiner const & newRefiner) {
return newRefiner._levels[0]->getNumFaces();
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::getNumBaseEdges(TopologyRefiner const & newRefiner) {
return newRefiner._levels[0]->getNumEdges();
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::getNumBaseVertices(TopologyRefiner const & newRefiner) {
return newRefiner._levels[0]->getNumVertices();
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseFaceVertices(TopologyRefiner & newRefiner, Index f, int count) {
newRefiner._levels[0]->resizeFaceVertices(f, count);
newRefiner._hasIrregFaces = newRefiner._hasIrregFaces || (count != newRefiner._regFaceSize);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseEdgeFaces(TopologyRefiner & newRefiner, Index e, int count) {
newRefiner._levels[0]->resizeEdgeFaces(e, count);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseVertexFaces(TopologyRefiner & newRefiner, Index v, int count) {
newRefiner._levels[0]->resizeVertexFaces(v, count);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setNumBaseVertexEdges(TopologyRefiner & newRefiner, Index v, int count) {
newRefiner._levels[0]->resizeVertexEdges(v, count);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseFaceVertices(TopologyRefiner & newRefiner, Index f) {
return newRefiner._levels[0]->getFaceVertices(f);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseFaceEdges(TopologyRefiner & newRefiner, Index f) {
return newRefiner._levels[0]->getFaceEdges(f);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseEdgeVertices(TopologyRefiner & newRefiner, Index e) {
return newRefiner._levels[0]->getEdgeVertices(e);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseEdgeFaces(TopologyRefiner & newRefiner, Index e) {
return newRefiner._levels[0]->getEdgeFaces(e);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseVertexFaces(TopologyRefiner & newRefiner, Index v) {
return newRefiner._levels[0]->getVertexFaces(v);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseVertexEdges(TopologyRefiner & newRefiner, Index v) {
return newRefiner._levels[0]->getVertexEdges(v);
}
template <class MESH>
inline LocalIndexArray
TopologyRefinerFactory<MESH>::getBaseEdgeFaceLocalIndices(TopologyRefiner & newRefiner, Index e) {
return newRefiner._levels[0]->getEdgeFaceLocalIndices(e);
}
template <class MESH>
inline LocalIndexArray
TopologyRefinerFactory<MESH>::getBaseVertexFaceLocalIndices(TopologyRefiner & newRefiner, Index v) {
return newRefiner._levels[0]->getVertexFaceLocalIndices(v);
}
template <class MESH>
inline LocalIndexArray
TopologyRefinerFactory<MESH>::getBaseVertexEdgeLocalIndices(TopologyRefiner & newRefiner, Index v) {
return newRefiner._levels[0]->getVertexEdgeLocalIndices(v);
}
template <class MESH>
inline Index
TopologyRefinerFactory<MESH>::findBaseEdge(TopologyRefiner const & newRefiner, Index v0, Index v1) {
return newRefiner._levels[0]->findEdge(v0, v1);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::populateBaseLocalIndices(TopologyRefiner & newRefiner) {
newRefiner._levels[0]->populateLocalIndices();
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseEdgeNonManifold(TopologyRefiner & newRefiner, Index e, bool b) {
newRefiner._levels[0]->setEdgeNonManifold(e, b);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseVertexNonManifold(TopologyRefiner & newRefiner, Index v, bool b) {
newRefiner._levels[0]->setVertexNonManifold(v, b);
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseEdgeSharpness(TopologyRefiner & newRefiner, Index e, float s) {
newRefiner._levels[0]->getEdgeSharpness(e) = s;
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseVertexSharpness(TopologyRefiner & newRefiner, Index v, float s) {
newRefiner._levels[0]->getVertexSharpness(v) = s;
}
template <class MESH>
inline void
TopologyRefinerFactory<MESH>::setBaseFaceHole(TopologyRefiner & newRefiner, Index f, bool b) {
newRefiner._levels[0]->setFaceHole(f, b);
newRefiner._hasHoles = newRefiner._hasHoles || b;
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues) {
return newRefiner._levels[0]->createFVarChannel(numValues, newRefiner._subdivOptions);
}
template <class MESH>
inline int
TopologyRefinerFactory<MESH>::createBaseFVarChannel(TopologyRefiner & newRefiner, int numValues, Sdc::Options const& fvarOptions) {
Sdc::Options newOptions = newRefiner._subdivOptions;
newOptions.SetFVarLinearInterpolation(fvarOptions.GetFVarLinearInterpolation());
return newRefiner._levels[0]->createFVarChannel(numValues, newOptions);
}
template <class MESH>
inline IndexArray
TopologyRefinerFactory<MESH>::getBaseFaceFVarValues(TopologyRefiner & newRefiner, Index face, int channel) {
return newRefiner._levels[0]->getFaceFVarValues(face, channel);
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::resizeComponentTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::resizeComponentTopology() -- no specialization provided.");
//
// Sizing the topology tables:
// This method is for determining the sizes of the various topology tables (and other
// data) associated with the mesh. Once completed, appropriate memory will be allocated
// and an additional method invoked to populate it accordingly.
//
// The following methods should be called -- first those to specify the number of faces,
// edges and vertices in the mesh:
//
// void setBaseFaceCount( TopologyRefiner& newRefiner, int count)
// void setBaseEdgeCount( TopologyRefiner& newRefiner, int count)
// void setBaseVertexCount(TopologyRefiner& newRefiner, int count)
//
// and then for each face, edge and vertex, the number of its incident components:
//
// void setBaseFaceVertexCount(TopologyRefiner& newRefiner, Index face, int count)
// void setBaseEdgeFaceCount( TopologyRefiner& newRefiner, Index edge, int count)
// void setBaseVertexFaceCount(TopologyRefiner& newRefiner, Index vertex, int count)
// void setBaseVertexEdgeCount(TopologyRefiner& newRefiner, Index vertex, int count)
//
// The count/size for a component type must be set before indices associated with that
// component type can be used.
//
// Note that it is only necessary to size 4 of the 6 supported topological relations --
// the number of edge-vertices is fixed at two per edge, and the number of face-edges is
// the same as the number of face-vertices.
//
// So a single pass through your mesh to gather up all of this sizing information will
// allow the Tables to be allocated appropriately once and avoid any dynamic resizing as
// it grows.
//
return false;
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::assignComponentTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) {
Error(FAR_RUNTIME_ERROR,
"Failure in TopologyRefinerFactory<>::assignComponentTopology() -- no specialization provided.");
//
// Assigning the topology tables:
// Once the topology tables have been allocated, the six required topological
// relations can be directly populated using the following methods:
//
// IndexArray setBaseFaceVertices(TopologyRefiner& newRefiner, Index face)
// IndexArray setBaseFaceEdges(TopologyRefiner& newRefiner, Index face)
//
// IndexArray setBaseEdgeVertices(TopologyRefiner& newRefiner, Index edge)
// IndexArray setBaseEdgeFaces(TopologyRefiner& newRefiner, Index edge)
//
// IndexArray setBaseVertexEdges(TopologyRefiner& newRefiner, Index vertex)
// IndexArray setBaseVertexFaces(TopologyRefiner& newRefiner, Index vertex)
//
// For the last two relations -- the faces and edges incident a vertex -- there are
// also "local indices" that must be specified (considering doing this internally),
// where the "local index" of each incident face or edge is the index of the vertex
// within that face or edge, and so ranging from 0-3 for incident quads and 0-1 for
// incident edges. These are assigned through similarly retrieved arrays:
//
// LocalIndexArray setBaseVertexFaceLocalIndices(TopologyRefiner& newRefiner, Index vertex)
// LocalIndexArray setBaseVertexEdgeLocalIndices(TopologyRefiner& newRefiner, Index vertex)
// LocalIndexArray setBaseEdgeFaceLocalIndices( TopologyRefiner& newRefiner, Index edge)
//
// or, if the mesh is manifold, explicit assignment of these can be deferred and
// all can be determined by calling:
//
// void populateBaseLocalIndices(TopologyRefiner& newRefiner)
//
// All components are assumed to be locally manifold and ordering of components in
// the above relations is expected to be counter-clockwise.
//
// For non-manifold components, no ordering/orientation of incident components is
// assumed or required, but be sure to explicitly tag such components (vertices and
// edges) as non-manifold:
//
// void setBaseEdgeNonManifold(TopologyRefiner& newRefiner, Index edge, bool b);
//
// void setBaseVertexNonManifold(TopologyRefiner& newRefiner, Index vertex, bool b);
//
// Also consider using TopologyLevel::ValidateTopology() when debugging to ensure
// that topology has been completely and correctly specified.
//
return false;
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::assignFaceVaryingTopology(TopologyRefiner& /* refiner */, MESH const& /* mesh */) {
//
// Optional assigning face-varying topology tables:
//
// Create independent face-varying primitive variable channels:
// int createBaseFVarChannel(TopologyRefiner& newRefiner, int numValues)
//
// For each channel, populate the face-vertex values:
// IndexArray setBaseFaceFVarValues(TopologyRefiner& newRefiner, Index face, int channel = 0)
//
return true;
}
template <class MESH>
bool
TopologyRefinerFactory<MESH>::assignComponentTags(TopologyRefiner& /* refiner */, MESH const& /* mesh */) {
//
// Optional tagging:
// This is where any additional feature tags -- sharpness, holes, etc. -- can be
// specified using:
//
// void setBaseEdgeSharpness(TopologyRefiner& newRefiner, Index edge, float sharpness)
// void setBaseVertexSharpness(TopologyRefiner& newRefiner, Index vertex, float sharpness)
//
// void setBaseFaceHole(TopologyRefiner& newRefiner, Index face, bool hole)
//
return true;
}
template <class MESH>
void
TopologyRefinerFactory<MESH>::reportInvalidTopology(
TopologyError /* errCode */, char const * /* msg */, MESH const& /* mesh */) {
//
// Optional topology validation error reporting:
// This method is called whenever the factory encounters topology validation
// errors. By default, nothing is reported
//
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TOPOLOGY_REFINER_FACTORY_H */

View File

@@ -0,0 +1,44 @@
//
// Copyright 2014 DreamWorks Animation LLC.
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://opensubdiv.org/license.
//
#ifndef OPENSUBDIV3_FAR_TYPES_H
#define OPENSUBDIV3_FAR_TYPES_H
#include "../version.h"
#include "../vtr/types.h"
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
namespace Far {
//
// Typedefs for indices that are inherited from the Vtr level -- eventually
// these primitive Vtr types may be declared at a lower, more public level.
//
typedef Vtr::Index Index;
typedef Vtr::LocalIndex LocalIndex;
typedef Vtr::IndexArray IndexArray;
typedef Vtr::LocalIndexArray LocalIndexArray;
typedef Vtr::ConstIndexArray ConstIndexArray;
typedef Vtr::ConstLocalIndexArray ConstLocalIndexArray;
inline bool IndexIsValid(Index index) { return Vtr::IndexIsValid(index); }
static const Index INDEX_INVALID = Vtr::INDEX_INVALID;
static const int VALENCE_LIMIT = Vtr::VALENCE_LIMIT;
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION
using namespace OPENSUBDIV_VERSION;
} // end namespace OpenSubdiv
#endif /* OPENSUBDIV3_FAR_TYPES_H */