mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-09 22:00:06 +00:00
2051 lines
76 KiB
C++
2051 lines
76 KiB
C++
//
|
|
// Copyright 2013 Pixar
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "Apache License")
|
|
// with the following modification; you may not use this file except in
|
|
// compliance with the Apache License and the following modification to it:
|
|
// Section 6. Trademarks. is deleted and replaced with:
|
|
//
|
|
// 6. Trademarks. This License does not grant permission to use the trade
|
|
// names, trademarks, service marks, or product names of the Licensor
|
|
// and its affiliates, except as required to comply with Section 4(c) of
|
|
// the License and to reproduce the content of the NOTICE file.
|
|
//
|
|
// You may obtain a copy of the Apache License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the Apache License with the above modification is
|
|
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the Apache License for the specific
|
|
// language governing permissions and limitations under the Apache License.
|
|
//
|
|
#include "../far/patchTableFactory.h"
|
|
#include "../far/patchBuilder.h"
|
|
#include "../far/error.h"
|
|
#include "../far/ptexIndices.h"
|
|
#include "../far/topologyRefiner.h"
|
|
#include "../vtr/level.h"
|
|
#include "../vtr/fvarLevel.h"
|
|
#include "../vtr/refinement.h"
|
|
#include "../vtr/stackBuffer.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
|
|
#include <cstdio>
|
|
|
|
namespace OpenSubdiv {
|
|
namespace OPENSUBDIV_VERSION {
|
|
namespace Far {
|
|
|
|
using Vtr::Array;
|
|
using Vtr::ConstArray;
|
|
using Vtr::IndexVector;
|
|
using Vtr::internal::Level;
|
|
using Vtr::internal::StackBuffer;
|
|
|
|
namespace {
|
|
//
|
|
// Helpers for compiler warnings and floating point equality tests
|
|
//
|
|
#ifdef __INTEL_COMPILER
|
|
#pragma warning (push)
|
|
#pragma warning disable 1572
|
|
#endif
|
|
inline bool isSharpnessEqual(float s1, float s2) { return (s1 == s2); }
|
|
#ifdef __INTEL_COMPILER
|
|
#pragma warning (pop)
|
|
#endif
|
|
|
|
inline int
|
|
assignSharpnessIndex(float sharpness, std::vector<float> & sharpnessValues) {
|
|
|
|
// linear search
|
|
for (int i=0; i<(int)sharpnessValues.size(); ++i) {
|
|
if (isSharpnessEqual(sharpnessValues[i], sharpness)) {
|
|
return i;
|
|
}
|
|
}
|
|
sharpnessValues.push_back(sharpness);
|
|
return (int)sharpnessValues.size()-1;
|
|
}
|
|
|
|
inline bool
|
|
isBoundaryFace(Level const & level, Index face) {
|
|
|
|
return (level.getFaceCompositeVTag(face)._boundary != 0);
|
|
}
|
|
|
|
inline void
|
|
offsetIndices(Index indices[], int size, int offset) {
|
|
|
|
for (int i = 0; i < size; ++i) {
|
|
indices[i] += offset;
|
|
}
|
|
}
|
|
} // namespace anon
|
|
|
|
|
|
//
|
|
// The main PatchTableBuilder class with context
|
|
//
|
|
// Helper class aggregating transient contextual data structures during the
|
|
// creation of a patch table. This helps keeping the factory class stateless.
|
|
//
|
|
class PatchTableBuilder {
|
|
public:
|
|
//
|
|
// Public interface intended for use by the PatchTableFactory -- all
|
|
// else is solely for internal use:
|
|
//
|
|
typedef PatchTableFactory::Options Options;
|
|
|
|
PatchTableBuilder(TopologyRefiner const & refiner, Options options,
|
|
ConstIndexArray selectedFaces);
|
|
~PatchTableBuilder();
|
|
|
|
bool UniformPolygonsSpecified() const { return _buildUniformLinear; }
|
|
|
|
void BuildUniformPolygons();
|
|
void BuildPatches();
|
|
|
|
PatchTable * GetPatchTable() const { return _table; };
|
|
|
|
private:
|
|
typedef PatchTable::StencilTablePtr StencilTablePtr;
|
|
|
|
// Simple struct to store <face,level> pair for a patch:
|
|
struct PatchTuple {
|
|
PatchTuple(Index face, int level) : faceIndex(face), levelIndex(level) { }
|
|
|
|
Index faceIndex;
|
|
int levelIndex;
|
|
};
|
|
typedef std::vector<PatchTuple> PatchTupleVector;
|
|
|
|
// Struct comprising a collection of topological properties for a patch that
|
|
// may be shared (between vertex and face-varying patches):
|
|
struct PatchInfo {
|
|
PatchInfo() : isRegular(false), isRegSingleCrease(false),
|
|
regBoundaryMask(0), regSharpness(0.0f),
|
|
paramBoundaryMask(0) { }
|
|
|
|
bool isRegular;
|
|
bool isRegSingleCrease;
|
|
int regBoundaryMask;
|
|
float regSharpness;
|
|
Level::VSpan irregCornerSpans[4];
|
|
int paramBoundaryMask;
|
|
|
|
SparseMatrix<float> fMatrix;
|
|
SparseMatrix<double> dMatrix;
|
|
};
|
|
|
|
private:
|
|
//
|
|
// Internal LocalPointHelper class
|
|
//
|
|
// A LocalPointHelper manages the number, sharing of and StencilTable for
|
|
// the local points of a patch or one of its face-varying channels. An
|
|
// instance of the helper does not know anything about the properties of
|
|
// the Builder classes that use it. It can combine local points for any
|
|
// patch types all in one, so the regular and irregular patch types can
|
|
// both be local and differ, e.g. Bezier for regular and Gregory for
|
|
// irregular, and points can be effectively shared, appropriate stencils
|
|
// created, etc.
|
|
//
|
|
// While not dependent on a particular patch type, some methods do require
|
|
// a patch type argument in order to know what can be done with its local
|
|
// points, e.g. which points can be shared with adjacent patches.
|
|
//
|
|
class LocalPointHelper {
|
|
public:
|
|
struct Options {
|
|
Options() : shareLocalPoints(false),
|
|
reuseSourcePoints(false),
|
|
createStencilTable(true),
|
|
createVaryingTable(false),
|
|
doubleStencilTable(false) { }
|
|
|
|
unsigned int shareLocalPoints : 1;
|
|
unsigned int reuseSourcePoints : 1;
|
|
unsigned int createStencilTable : 1;
|
|
unsigned int createVaryingTable : 1;
|
|
unsigned int doubleStencilTable : 1;
|
|
};
|
|
|
|
public:
|
|
LocalPointHelper(TopologyRefiner const & refiner,
|
|
Options const & options,
|
|
int fvarChannel,
|
|
int numLocalPointsExpected);
|
|
~LocalPointHelper();
|
|
|
|
public:
|
|
int GetNumLocalPoints() const { return _numLocalPoints; }
|
|
|
|
template <typename REAL>
|
|
int AppendLocalPatchPoints(int levelIndex, Index faceIndex,
|
|
SparseMatrix<REAL> const & conversionMatrix,
|
|
PatchDescriptor::Type patchType,
|
|
Index const sourcePoints[],
|
|
int sourcePointOffset,
|
|
Index patchPoints[]);
|
|
|
|
StencilTablePtr AcquireStencilTable() {
|
|
return _options.doubleStencilTable
|
|
? acquireStencilTable<double>(_stencilTable)
|
|
: acquireStencilTable<float>(_stencilTable);
|
|
}
|
|
|
|
private:
|
|
// Internal methods:
|
|
template <typename REAL>
|
|
void initializeStencilTable(int numLocalPointsExpected);
|
|
|
|
template <typename REAL>
|
|
void appendLocalPointStencil(SparseMatrix<REAL> const & conversionMatrix,
|
|
int stencilRow,
|
|
Index const sourcePoints[],
|
|
int sourcePointOffset);
|
|
|
|
template <typename REAL>
|
|
void appendLocalPointStencils(SparseMatrix<REAL> const & conversionMatrix,
|
|
Index const sourcePoints[],
|
|
int sourcePointOffset);
|
|
|
|
// Methods for local point Varying stencils
|
|
// XXXX -- hope to get rid of these...
|
|
template <typename REAL>
|
|
void appendLocalPointVaryingStencil(int const * varyingIndices,
|
|
int patchPointIndex,
|
|
Index const sourcePoints[],
|
|
int sourcePointOffset);
|
|
|
|
template <typename REAL>
|
|
StencilTablePtr acquireStencilTable(StencilTablePtr& stencilTableMember);
|
|
|
|
Index findSharedCornerPoint(int levelIndex, Index valueIndex,
|
|
Index newIndex);
|
|
Index findSharedEdgePoint(int levelIndex, Index edgeIndex, int edgeEnd,
|
|
Index newIndex);
|
|
|
|
private:
|
|
// Member variables:
|
|
TopologyRefiner const& _refiner;
|
|
Options _options;
|
|
|
|
int _fvarChannel;
|
|
int _numLocalPoints;
|
|
int _localPointOffset;
|
|
|
|
std::vector<IndexVector> _sharedCornerPoints;
|
|
std::vector<IndexVector> _sharedEdgePoints;
|
|
|
|
StencilTablePtr _stencilTable;
|
|
|
|
// This was hopefully transitional but will persist -- the should be
|
|
// no need for Varying local points or stencils associated with them.
|
|
public:
|
|
StencilTablePtr AcquireStencilTableVarying() {
|
|
return _options.doubleStencilTable
|
|
? acquireStencilTable<double>(_stencilTableVarying)
|
|
: acquireStencilTable<float>(_stencilTableVarying);
|
|
}
|
|
|
|
StencilTablePtr _stencilTableVarying;
|
|
};
|
|
|
|
private:
|
|
//
|
|
// Internal LegacyGregoryHelper class
|
|
//
|
|
// This local class helps to populate the arrays in the PatchTable
|
|
// associated with legacy-Gregory patches, i.e. the quad-offset and
|
|
// vertex-valence tables. These patches are always associated with
|
|
// faces at the last level of refinement, so only the face index in
|
|
// that level is required to identify them.
|
|
//
|
|
class LegacyGregoryHelper {
|
|
public:
|
|
LegacyGregoryHelper(TopologyRefiner const & ref) : _refiner(ref) { }
|
|
~LegacyGregoryHelper() { }
|
|
|
|
public:
|
|
int GetNumBoundaryPatches() const { return (int)_boundaryFaceIndices.size(); }
|
|
int GetNumInteriorPatches() const { return (int)_interiorFaceIndices.size(); }
|
|
|
|
void AddPatchFace(int level, Index face);
|
|
void FinalizeQuadOffsets( PatchTable::QuadOffsetsTable & qTable);
|
|
void FinalizeVertexValence(PatchTable::VertexValenceTable & vTable,
|
|
int lastLevelVertOffset);
|
|
private:
|
|
TopologyRefiner const& _refiner;
|
|
std::vector<Index> _interiorFaceIndices;
|
|
std::vector<Index> _boundaryFaceIndices;
|
|
};
|
|
|
|
private:
|
|
// Builder methods for internal use:
|
|
|
|
// Simple queries:
|
|
int getRefinerFVarChannel(int fvcInTable) const {
|
|
return (fvcInTable >= 0) ? _fvarChannelIndices[fvcInTable] : -1;
|
|
}
|
|
|
|
bool isFVarChannelLinear(int fvcInTable) const {
|
|
if (_options.generateFVarLegacyLinearPatches) return true;
|
|
return (_refiner.GetFVarLinearInterpolation(
|
|
getRefinerFVarChannel(fvcInTable)) == Sdc::Options::FVAR_LINEAR_ALL);
|
|
}
|
|
|
|
bool doesFVarTopologyMatch(PatchTuple const & patch, int fvcInTable) {
|
|
return _patchBuilder->DoesFaceVaryingPatchMatch(
|
|
patch.levelIndex, patch.faceIndex,
|
|
getRefinerFVarChannel(fvcInTable));
|
|
}
|
|
|
|
// Methods for identifying and assigning patch-related data:
|
|
void identifyPatchTopology(PatchTuple const & patch, PatchInfo & patchInfo,
|
|
int fvcInTable = -1);
|
|
|
|
int assignPatchPointsAndStencils(PatchTuple const & patch,
|
|
PatchInfo const & patchInfo,
|
|
Index * patchPoints,
|
|
LocalPointHelper & localHelper,
|
|
int fvcInTable = -1);
|
|
|
|
int assignFacePoints(PatchTuple const & patch,
|
|
Index * patchPoints,
|
|
int fvcInTable = -1) const;
|
|
|
|
// High level methods for assembling the table:
|
|
void identifyPatches();
|
|
void appendPatch(int levelIndex, Index faceIndex);
|
|
void findDescendantPatches(int levelIndex, Index faceIndex, int targetLevel);
|
|
void populatePatches();
|
|
|
|
void allocateVertexTables();
|
|
void allocateFVarChannels();
|
|
|
|
int estimateLocalPointCount(LocalPointHelper::Options const & options,
|
|
int fvcInTable = -1) const;
|
|
|
|
private:
|
|
// Refiner and Options passed on construction:
|
|
TopologyRefiner const & _refiner;
|
|
Options const _options;
|
|
ConstIndexArray _selectedFaces;
|
|
|
|
// Flags indicating the need for processing based on provided options
|
|
unsigned int _requiresLocalPoints : 1;
|
|
unsigned int _requiresRegularLocalPoints : 1;
|
|
unsigned int _requiresIrregularLocalPoints : 1;
|
|
unsigned int _requiresSharpnessArray : 1;
|
|
unsigned int _requiresFVarPatches : 1;
|
|
unsigned int _requiresVaryingPatches : 1;
|
|
unsigned int _requiresVaryingLocalPoints : 1;
|
|
|
|
unsigned int _buildUniformLinear : 1;
|
|
|
|
// The PatchTable being constructed and classes to help its construction:
|
|
PatchTable * _table;
|
|
|
|
PatchBuilder * _patchBuilder;
|
|
PtexIndices const _ptexIndices;
|
|
|
|
// Vector of tuples for each patch identified during topology traversal
|
|
// and the numbers of irregular and irregular patches identifed:
|
|
PatchTupleVector _patches;
|
|
|
|
int _numRegularPatches;
|
|
int _numIrregularPatches;
|
|
|
|
// Vectors for remapping indices of vertices and fvar values as well
|
|
// as the fvar channels (when a subset is chosen)
|
|
std::vector<int> _levelVertOffsets;
|
|
std::vector< std::vector<int> > _levelFVarValueOffsets;
|
|
std::vector<int> _fvarChannelIndices;
|
|
|
|
// State and helpers for legacy features
|
|
bool _requiresLegacyGregoryTables;
|
|
LegacyGregoryHelper * _legacyGregoryHelper;
|
|
};
|
|
|
|
// Constructor
|
|
PatchTableBuilder::PatchTableBuilder(
|
|
TopologyRefiner const & refiner, Options opts, ConstIndexArray faces) :
|
|
_refiner(refiner), _options(opts), _selectedFaces(faces),
|
|
_table(0), _patchBuilder(0), _ptexIndices(refiner),
|
|
_numRegularPatches(0), _numIrregularPatches(0),
|
|
_legacyGregoryHelper(0) {
|
|
|
|
if (_options.generateFVarTables) {
|
|
// If client-code does not select specific channels, default to all
|
|
// the channels in the refiner.
|
|
if (_options.numFVarChannels==-1) {
|
|
_fvarChannelIndices.resize(_refiner.GetNumFVarChannels());
|
|
for (int fvc=0;fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
_fvarChannelIndices[fvc] = fvc; // std::iota
|
|
}
|
|
} else {
|
|
_fvarChannelIndices.assign(
|
|
_options.fvarChannelIndices,
|
|
_options.fvarChannelIndices + _options.numFVarChannels);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Will need to translate PatchTableFactory options to the newer set of
|
|
// PatchBuilder options in the near future. And the state variables are
|
|
// a potentially complex combination of options and so are handled after
|
|
// the PatchBuilder construction (to potentially help) rather than in
|
|
// the initializer list.
|
|
//
|
|
PatchBuilder::Options patchOptions;
|
|
|
|
patchOptions.regBasisType = PatchBuilder::BASIS_REGULAR;
|
|
switch (_options.GetEndCapType()) {
|
|
case Options::ENDCAP_BILINEAR_BASIS:
|
|
patchOptions.irregBasisType = PatchBuilder::BASIS_LINEAR;
|
|
break;
|
|
case Options::ENDCAP_BSPLINE_BASIS:
|
|
patchOptions.irregBasisType = PatchBuilder::BASIS_REGULAR;
|
|
break;
|
|
case Options::ENDCAP_GREGORY_BASIS:
|
|
patchOptions.irregBasisType = PatchBuilder::BASIS_GREGORY;
|
|
break;
|
|
default:
|
|
// The PatchBuilder will infer if left un-specified
|
|
patchOptions.irregBasisType = PatchBuilder::BASIS_UNSPECIFIED;
|
|
break;
|
|
}
|
|
patchOptions.fillMissingBoundaryPoints = true;
|
|
patchOptions.approxInfSharpWithSmooth = !_options.useInfSharpPatch;
|
|
patchOptions.approxSmoothCornerWithSharp =
|
|
_options.generateLegacySharpCornerPatches;
|
|
|
|
_patchBuilder = PatchBuilder::Create(_refiner, patchOptions);
|
|
|
|
//
|
|
// Initialize member variables that capture specified options:
|
|
//
|
|
_requiresRegularLocalPoints =
|
|
(patchOptions.regBasisType != PatchBuilder::BASIS_REGULAR);
|
|
_requiresIrregularLocalPoints =
|
|
(_options.GetEndCapType() != Options::ENDCAP_LEGACY_GREGORY);
|
|
_requiresLocalPoints =
|
|
_requiresIrregularLocalPoints || _requiresRegularLocalPoints;
|
|
|
|
_requiresSharpnessArray = _options.useSingleCreasePatch;
|
|
_requiresFVarPatches = ! _fvarChannelIndices.empty();
|
|
|
|
_requiresVaryingPatches = _options.generateVaryingTables;
|
|
_requiresVaryingLocalPoints = _options.generateVaryingTables &&
|
|
_options.generateVaryingLocalPoints;
|
|
|
|
// Option to be made public in future:
|
|
bool options_generateNonLinearUniformPatches = false;
|
|
|
|
_buildUniformLinear = _refiner.IsUniform() && !options_generateNonLinearUniformPatches;
|
|
|
|
//
|
|
// Create and initialize the new PatchTable instance to be assembled:
|
|
//
|
|
_table = new PatchTable(_refiner.GetMaxValence());
|
|
|
|
_table->_numPtexFaces = _ptexIndices.GetNumFaces();
|
|
|
|
_table->_vertexPrecisionIsDouble = _options.patchPrecisionDouble;
|
|
_table->_varyingPrecisionIsDouble = _options.patchPrecisionDouble;
|
|
_table->_faceVaryingPrecisionIsDouble = _options.fvarPatchPrecisionDouble;
|
|
|
|
_table->_varyingDesc = PatchDescriptor(_patchBuilder->GetLinearPatchType());
|
|
|
|
// State and helper to support LegacyGregory arrays in the PatchTable:
|
|
_requiresLegacyGregoryTables = !_refiner.IsUniform() &&
|
|
(_options.GetEndCapType() == Options::ENDCAP_LEGACY_GREGORY);
|
|
|
|
if (_requiresLegacyGregoryTables) {
|
|
_legacyGregoryHelper = new LegacyGregoryHelper(_refiner);
|
|
}
|
|
|
|
}
|
|
|
|
PatchTableBuilder::~PatchTableBuilder() {
|
|
|
|
delete _patchBuilder;
|
|
delete _legacyGregoryHelper;
|
|
}
|
|
|
|
|
|
void
|
|
PatchTableBuilder::identifyPatchTopology(PatchTuple const & patch,
|
|
PatchInfo & patchInfo, int fvarInTable) {
|
|
|
|
int patchLevel = patch.levelIndex;
|
|
Index patchFace = patch.faceIndex;
|
|
|
|
int fvarInRefiner = getRefinerFVarChannel(fvarInTable);
|
|
|
|
patchInfo.isRegular = _patchBuilder->IsPatchRegular(
|
|
patchLevel, patchFace, fvarInTable);
|
|
|
|
bool useDoubleMatrix = (fvarInRefiner < 0)
|
|
? _options.patchPrecisionDouble
|
|
: _options.fvarPatchPrecisionDouble;
|
|
|
|
if (patchInfo.isRegular) {
|
|
patchInfo.regBoundaryMask = _patchBuilder->GetRegularPatchBoundaryMask(
|
|
patchLevel, patchFace, fvarInRefiner);
|
|
|
|
patchInfo.isRegSingleCrease = false;
|
|
patchInfo.regSharpness = 0.0f;
|
|
patchInfo.paramBoundaryMask = patchInfo.regBoundaryMask;
|
|
|
|
// If converting to another basis, get the change-of-basis matrix:
|
|
if (_requiresRegularLocalPoints) {
|
|
// _patchBuilder->GetRegularConversionMatrix(...);
|
|
}
|
|
|
|
//
|
|
// Test regular interior patches for a single-crease patch when it
|
|
// was specified.
|
|
//
|
|
// Note that the PatchTable clamps the sharpness of single-crease
|
|
// patches to that of the maximimu refinement level, so any single-
|
|
// crease patches at the last level will be reduced to regular
|
|
// patches (maintaining continuity with other semi-sharp patches
|
|
// also reduced to regular).
|
|
//
|
|
if (_requiresSharpnessArray &&
|
|
(patchInfo.regBoundaryMask == 0) && (fvarInRefiner < 0)) {
|
|
if (patchLevel < (int) _options.maxIsolationLevel) {
|
|
PatchBuilder::SingleCreaseInfo creaseInfo;
|
|
|
|
if (_patchBuilder->IsRegularSingleCreasePatch(
|
|
patchLevel, patchFace, creaseInfo)) {
|
|
creaseInfo.creaseSharpness =
|
|
std::min(creaseInfo.creaseSharpness,
|
|
(float)(_options.maxIsolationLevel - patchLevel));
|
|
|
|
patchInfo.isRegSingleCrease = true;
|
|
patchInfo.regSharpness = creaseInfo.creaseSharpness;
|
|
patchInfo.paramBoundaryMask = (1 << creaseInfo.creaseEdgeInFace);
|
|
}
|
|
}
|
|
}
|
|
} else if (_requiresIrregularLocalPoints) {
|
|
_patchBuilder->GetIrregularPatchCornerSpans(
|
|
patchLevel, patchFace, patchInfo.irregCornerSpans, fvarInRefiner);
|
|
|
|
if (useDoubleMatrix) {
|
|
_patchBuilder->GetIrregularPatchConversionMatrix(
|
|
patchLevel, patchFace, patchInfo.irregCornerSpans, patchInfo.dMatrix);
|
|
} else {
|
|
_patchBuilder->GetIrregularPatchConversionMatrix(
|
|
patchLevel, patchFace, patchInfo.irregCornerSpans, patchInfo.fMatrix);
|
|
}
|
|
|
|
patchInfo.paramBoundaryMask = 0;
|
|
}
|
|
}
|
|
|
|
int
|
|
PatchTableBuilder::assignPatchPointsAndStencils(PatchTuple const & patch,
|
|
PatchInfo const & patchInfo, Index * patchPoints,
|
|
LocalPointHelper & localHelper, int fvarInTable) {
|
|
|
|
//
|
|
// This is where the interesting/complicated new work will take place
|
|
// when a change-of-basis is determined necessary and previously assigned
|
|
// to the PatchInfo
|
|
//
|
|
// No change-of-basis means no local points or stencils associated with
|
|
// them, which should be trivial but should also only be true in the
|
|
// regular case. (It is also the case that no local points will be
|
|
// generated for irregular patches when the LegacyGregory option is
|
|
// used -- so that possibility is still accounted for here.)
|
|
//
|
|
// Regarding the return result, it should just be the size of the patch
|
|
// associated with the regular/irregular patch type chosen. This could
|
|
// be retrieved from the PatchBuilder or PatchDescriptors -- alternatively
|
|
// a return value could be removed and the client left to increment by
|
|
// such a fixed step (which is already the case for FVar channels)
|
|
//
|
|
// The more interesting size here is the number of local points/stencils
|
|
// added.
|
|
//
|
|
int fvarInRefiner = getRefinerFVarChannel(fvarInTable);
|
|
|
|
int sourcePointOffset = (fvarInTable < 0)
|
|
? _levelVertOffsets[patch.levelIndex]
|
|
: _levelFVarValueOffsets[fvarInTable][patch.levelIndex];
|
|
|
|
bool useDoubleMatrix = (fvarInTable < 0)
|
|
? _options.patchPrecisionDouble
|
|
: _options.fvarPatchPrecisionDouble;
|
|
|
|
int numPatchPoints = 0;
|
|
if (patchInfo.isRegular) {
|
|
if (!_requiresRegularLocalPoints) {
|
|
numPatchPoints = _patchBuilder->GetRegularPatchPoints(
|
|
patch.levelIndex, patch.faceIndex,
|
|
patchInfo.regBoundaryMask, patchPoints, fvarInRefiner);
|
|
|
|
// PatchBuilder set to fill missing boundary points so offset all
|
|
offsetIndices(patchPoints, numPatchPoints, sourcePointOffset);
|
|
} else {
|
|
//
|
|
// Future support for regular patches converted to another basis.
|
|
// Note the "source points" are not returned in the same
|
|
// orientation and there may be fewer than expected number in the
|
|
// case of boundaries:
|
|
/*
|
|
StackBuffer<Index,64,true> sourcePoints(
|
|
patchInfo.matrix.GetNumColumns());
|
|
|
|
_patchBuilder->GetRegularPatchSourcePoints(
|
|
patch.levelIndex, patch.faceIndex,
|
|
patchInfo.regBoundaryMask, sourcePoints, fvarInRefiner);
|
|
|
|
localHelper.AppendLocalPatchPoints(
|
|
patch.levelIndex, patch.faceIndex,
|
|
patchInfo.matrix, _patchBuilder->GetRegularPatchType(),
|
|
sourcePoints, sourcePointOffset, patchPoints);
|
|
|
|
numPatchPoints = patchInfo.matrix.GetNumRows();
|
|
*/
|
|
}
|
|
} else if (_requiresIrregularLocalPoints) {
|
|
int numSourcePoints = 0;
|
|
if (useDoubleMatrix) {
|
|
numSourcePoints = patchInfo.dMatrix.GetNumColumns();
|
|
numPatchPoints = patchInfo.dMatrix.GetNumRows();
|
|
} else {
|
|
numSourcePoints = patchInfo.fMatrix.GetNumColumns();
|
|
numPatchPoints = patchInfo.fMatrix.GetNumRows();
|
|
}
|
|
|
|
StackBuffer<Index,64,true> sourcePoints(numSourcePoints);
|
|
|
|
_patchBuilder->GetIrregularPatchSourcePoints(
|
|
patch.levelIndex, patch.faceIndex,
|
|
patchInfo.irregCornerSpans, sourcePoints, fvarInRefiner);
|
|
|
|
if (useDoubleMatrix) {
|
|
localHelper.AppendLocalPatchPoints(
|
|
patch.levelIndex, patch.faceIndex,
|
|
patchInfo.dMatrix, _patchBuilder->GetIrregularPatchType(),
|
|
sourcePoints, sourcePointOffset, patchPoints);
|
|
} else {
|
|
localHelper.AppendLocalPatchPoints(
|
|
patch.levelIndex, patch.faceIndex,
|
|
patchInfo.fMatrix, _patchBuilder->GetIrregularPatchType(),
|
|
sourcePoints, sourcePointOffset, patchPoints);
|
|
}
|
|
}
|
|
return numPatchPoints;
|
|
}
|
|
|
|
int
|
|
PatchTableBuilder::assignFacePoints(PatchTuple const & patch,
|
|
Index * patchPoints,
|
|
int fvarInTable) const {
|
|
|
|
Level const & level = _refiner.getLevel(patch.levelIndex);
|
|
|
|
int facePointOffset = (fvarInTable < 0)
|
|
? _levelVertOffsets[patch.levelIndex]
|
|
: _levelFVarValueOffsets[fvarInTable][patch.levelIndex];
|
|
|
|
int fvarInRefiner = getRefinerFVarChannel(fvarInTable);
|
|
|
|
ConstIndexArray facePoints = (fvarInRefiner < 0)
|
|
? level.getFaceVertices(patch.faceIndex)
|
|
: level.getFaceFVarValues(patch.faceIndex, fvarInRefiner);
|
|
|
|
for (int i = 0; i < facePoints.size(); ++i) {
|
|
patchPoints[i] = facePoints[i] + facePointOffset;
|
|
}
|
|
return facePoints.size();
|
|
}
|
|
|
|
//
|
|
// Reserves tables based on contents of the PatchArrayVector in the PatchTable:
|
|
//
|
|
void
|
|
PatchTableBuilder::allocateVertexTables() {
|
|
|
|
int ncvs = 0, npatches = 0;
|
|
for (int i=0; i<_table->GetNumPatchArrays(); ++i) {
|
|
npatches += _table->GetNumPatches(i);
|
|
ncvs += _table->GetNumControlVertices(i);
|
|
}
|
|
|
|
if (ncvs==0 || npatches==0)
|
|
return;
|
|
|
|
_table->_patchVerts.resize( ncvs );
|
|
|
|
_table->_paramTable.resize( npatches );
|
|
|
|
if (_requiresVaryingPatches && !_buildUniformLinear) {
|
|
_table->allocateVaryingVertices(
|
|
PatchDescriptor(_patchBuilder->GetLinearPatchType()), npatches);
|
|
}
|
|
|
|
if (_requiresSharpnessArray) {
|
|
_table->_sharpnessIndices.resize( npatches, Vtr::INDEX_INVALID );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate face-varying tables
|
|
//
|
|
void
|
|
PatchTableBuilder::allocateFVarChannels() {
|
|
|
|
int npatches = _table->GetNumPatchesTotal();
|
|
|
|
_table->allocateFVarPatchChannels((int)_fvarChannelIndices.size());
|
|
|
|
// Initialize each channel
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
int refinerChannel = _fvarChannelIndices[fvc];
|
|
|
|
Sdc::Options::FVarLinearInterpolation interpolation =
|
|
_refiner.GetFVarLinearInterpolation(refinerChannel);
|
|
|
|
_table->setFVarPatchChannelLinearInterpolation(interpolation, fvc);
|
|
|
|
PatchDescriptor::Type regPatchType = _patchBuilder->GetLinearPatchType();
|
|
PatchDescriptor::Type irregPatchType = regPatchType;
|
|
if (_buildUniformLinear) {
|
|
if (_options.triangulateQuads) {
|
|
regPatchType = PatchDescriptor::TRIANGLES;
|
|
irregPatchType = regPatchType;
|
|
}
|
|
} else {
|
|
if (!isFVarChannelLinear(fvc)) {
|
|
regPatchType = _patchBuilder->GetRegularPatchType();
|
|
irregPatchType = _patchBuilder->GetIrregularPatchType();
|
|
}
|
|
}
|
|
_table->allocateFVarPatchChannelValues(
|
|
PatchDescriptor(regPatchType), PatchDescriptor(irregPatchType),
|
|
npatches, fvc);
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchTableBuilder::BuildUniformPolygons() {
|
|
|
|
// Default behavior is to include base level vertices in the patch vertices
|
|
// for vertex and varying patches, but not face-varying. Consider exposing
|
|
// these as public options in future so that clients can create consistent
|
|
// behavior:
|
|
|
|
bool includeBaseLevelIndices = _options.includeBaseLevelIndices;
|
|
bool includeBaseLevelFVarIndices = _options.includeFVarBaseLevelIndices;
|
|
|
|
// ensure that triangulateQuads is only set for quadrilateral schemes
|
|
bool triangulateQuads =
|
|
_options.triangulateQuads && (_patchBuilder->GetRegularFaceSize() == 4);
|
|
|
|
// level=0 may contain n-gons, which are not supported in PatchTable.
|
|
// even if generateAllLevels = true, we start from level 1.
|
|
|
|
int maxlevel = _refiner.GetMaxLevel(),
|
|
firstlevel = _options.generateAllLevels ? 1 : maxlevel,
|
|
nlevels = maxlevel-firstlevel+1;
|
|
|
|
PatchDescriptor::Type ptype = triangulateQuads
|
|
? PatchDescriptor::TRIANGLES
|
|
: _patchBuilder->GetLinearPatchType();
|
|
|
|
//
|
|
// Allocate and initialize the table's members.
|
|
//
|
|
_table->_isUniformLinear = true;
|
|
|
|
_table->reservePatchArrays(nlevels);
|
|
|
|
PatchDescriptor desc(ptype);
|
|
|
|
// generate patch arrays
|
|
for (int level=firstlevel, poffset=0, voffset=0; level<=maxlevel; ++level) {
|
|
|
|
TopologyLevel const & refLevel = _refiner.GetLevel(level);
|
|
|
|
int npatches = refLevel.GetNumFaces();
|
|
if (_refiner.HasHoles()) {
|
|
for (int i = npatches - 1; i >= 0; --i) {
|
|
npatches -= refLevel.IsFaceHole(i);
|
|
}
|
|
}
|
|
assert(npatches>=0);
|
|
|
|
if (triangulateQuads)
|
|
npatches *= 2;
|
|
|
|
_table->pushPatchArray(desc, npatches, &voffset, &poffset, 0);
|
|
}
|
|
|
|
// Allocate various tables
|
|
allocateVertexTables();
|
|
|
|
if (_requiresFVarPatches) {
|
|
allocateFVarChannels();
|
|
}
|
|
|
|
//
|
|
// Now populate the patches:
|
|
//
|
|
|
|
Index * iptr = &_table->_patchVerts[0];
|
|
PatchParam * pptr = &_table->_paramTable[0];
|
|
Index ** fptr = 0;
|
|
PatchParam ** fpptr = 0;
|
|
|
|
Index levelVertOffset = includeBaseLevelIndices
|
|
? _refiner.GetLevel(0).GetNumVertices() : 0;
|
|
|
|
Index * levelFVarVertOffsets = 0;
|
|
if (_requiresFVarPatches) {
|
|
|
|
levelFVarVertOffsets =
|
|
(Index *)alloca(_fvarChannelIndices.size()*sizeof(Index));
|
|
memset(levelFVarVertOffsets, 0, _fvarChannelIndices.size()*sizeof(Index));
|
|
|
|
fptr = (Index **)alloca(_fvarChannelIndices.size()*sizeof(Index *));
|
|
fpptr = (PatchParam **)alloca(_fvarChannelIndices.size()*sizeof(PatchParam *));
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
fptr[fvc] = _table->getFVarValues(fvc).begin();
|
|
fpptr[fvc] = _table->getFVarPatchParams(fvc).begin();
|
|
|
|
if (includeBaseLevelFVarIndices) {
|
|
int refinerChannel = _fvarChannelIndices[fvc];
|
|
levelFVarVertOffsets[fvc] =
|
|
_refiner.GetLevel(0).GetNumFVarValues(refinerChannel);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int level=1; level<=maxlevel; ++level) {
|
|
|
|
TopologyLevel const & refLevel = _refiner.GetLevel(level);
|
|
|
|
int nfaces = refLevel.GetNumFaces();
|
|
if (level>=firstlevel) {
|
|
for (int face=0; face<nfaces; ++face) {
|
|
|
|
if (_refiner.HasHoles() && refLevel.IsFaceHole(face)) {
|
|
continue;
|
|
}
|
|
|
|
ConstIndexArray fverts = refLevel.GetFaceVertices(face);
|
|
for (int vert=0; vert<fverts.size(); ++vert) {
|
|
*iptr++ = levelVertOffset + fverts[vert];
|
|
}
|
|
|
|
PatchParam pparam = _patchBuilder->ComputePatchParam(
|
|
level, face, _ptexIndices);
|
|
*pptr++ = pparam;
|
|
|
|
if (_requiresFVarPatches) {
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
int refinerChannel = _fvarChannelIndices[fvc];
|
|
|
|
ConstIndexArray fvalues =
|
|
refLevel.GetFaceFVarValues(face, refinerChannel);
|
|
for (int vert=0; vert<fvalues.size(); ++vert) {
|
|
assert((levelFVarVertOffsets[fvc] + fvalues[vert])
|
|
< (int)_table->getFVarValues(fvc).size());
|
|
fptr[fvc][vert] =
|
|
levelFVarVertOffsets[fvc] + fvalues[vert];
|
|
}
|
|
fptr[fvc]+=fvalues.size();
|
|
*fpptr[fvc]++ = pparam;
|
|
}
|
|
}
|
|
|
|
if (triangulateQuads) {
|
|
// Triangulate the quadrilateral:
|
|
// {v0,v1,v2,v3} -> {v0,v1,v2},{v3,v0,v2}.
|
|
*iptr = *(iptr - 4); // copy v0 index
|
|
++iptr;
|
|
*iptr = *(iptr - 3); // copy v2 index
|
|
++iptr;
|
|
|
|
*pptr++ = pparam;
|
|
|
|
if (_requiresFVarPatches) {
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
*fptr[fvc] = *(fptr[fvc]-4); // copy fv0 index
|
|
++fptr[fvc];
|
|
*fptr[fvc] = *(fptr[fvc]-3); // copy fv2 index
|
|
++fptr[fvc];
|
|
|
|
*fpptr[fvc]++ = pparam;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_options.generateAllLevels) {
|
|
levelVertOffset += _refiner.GetLevel(level).GetNumVertices();
|
|
|
|
if (_requiresFVarPatches) {
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
int refinerChannel = _fvarChannelIndices[fvc];
|
|
levelFVarVertOffsets[fvc] +=
|
|
_refiner.GetLevel(level).GetNumFVarValues(refinerChannel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchTableBuilder::BuildPatches() {
|
|
|
|
identifyPatches();
|
|
populatePatches();
|
|
}
|
|
|
|
//
|
|
// Identify all patches required for faces at all levels -- appending the
|
|
// <level,face> pairs to identify each patch for later construction, while
|
|
// accumulating the number of regular vs irregular patches to size tables.
|
|
//
|
|
inline void
|
|
PatchTableBuilder::appendPatch(int levelIndex, Index faceIndex) {
|
|
|
|
_patches.push_back(PatchTuple(faceIndex, levelIndex));
|
|
|
|
// Count the patches here to simplify subsequent allocation.
|
|
if (_patchBuilder->IsPatchRegular(levelIndex, faceIndex)) {
|
|
++_numRegularPatches;
|
|
} else {
|
|
++_numIrregularPatches;
|
|
|
|
// LegacyGregory needs to distinguish boundary vs interior
|
|
if (_requiresLegacyGregoryTables) {
|
|
_legacyGregoryHelper->AddPatchFace(levelIndex, faceIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void
|
|
PatchTableBuilder::findDescendantPatches(int levelIndex, Index faceIndex, int targetLevel) {
|
|
|
|
//
|
|
// If we have reached the target level or a leaf, append the patch (if
|
|
// the face qualifies), otherwise recursively search the children:
|
|
//
|
|
if ((levelIndex == targetLevel) || _patchBuilder->IsFaceALeaf(levelIndex, faceIndex)) {
|
|
if (_patchBuilder->IsFaceAPatch(levelIndex, faceIndex)) {
|
|
appendPatch(levelIndex, faceIndex);
|
|
}
|
|
} else {
|
|
TopologyLevel const & level = _refiner.GetLevel(levelIndex);
|
|
ConstIndexArray childFaces = level.GetFaceChildFaces(faceIndex);
|
|
for (int i = 0; i < childFaces.size(); ++i) {
|
|
if (Vtr::IndexIsValid(childFaces[i])) {
|
|
findDescendantPatches(levelIndex + 1, childFaces[i], targetLevel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchTableBuilder::identifyPatches() {
|
|
|
|
//
|
|
// First initialize the offsets for all levels
|
|
//
|
|
_levelVertOffsets.push_back(0);
|
|
_levelFVarValueOffsets.resize(_fvarChannelIndices.size());
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
_levelFVarValueOffsets[fvc].push_back(0);
|
|
}
|
|
|
|
for (int levelIndex=0; levelIndex<_refiner.GetNumLevels(); ++levelIndex) {
|
|
Level const & level = _refiner.getLevel(levelIndex);
|
|
|
|
_levelVertOffsets.push_back(
|
|
_levelVertOffsets.back() + level.getNumVertices());
|
|
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
int refinerChannel = _fvarChannelIndices[fvc];
|
|
_levelFVarValueOffsets[fvc].push_back(
|
|
_levelFVarValueOffsets[fvc].back()
|
|
+ level.getNumFVarValues(refinerChannel));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a set of selected base faces is present, identify the patches
|
|
// depth first. Otherwise search breadth first through the levels:
|
|
//
|
|
int uniformLevel = _refiner.IsUniform() ? _options.maxIsolationLevel : -1;
|
|
|
|
_patches.reserve(_refiner.GetNumFacesTotal());
|
|
|
|
if (_selectedFaces.size()) {
|
|
for (int i = 0; i < (int)_selectedFaces.size(); ++i) {
|
|
findDescendantPatches(0, _selectedFaces[i], uniformLevel);
|
|
}
|
|
} else if (uniformLevel >= 0) {
|
|
int numFaces = _refiner.getLevel(uniformLevel).getNumFaces();
|
|
|
|
for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
|
|
|
|
if (_patchBuilder->IsFaceAPatch(uniformLevel, faceIndex)) {
|
|
appendPatch(uniformLevel, faceIndex);
|
|
}
|
|
}
|
|
} else {
|
|
for (int levelIndex=0; levelIndex<_refiner.GetNumLevels(); ++levelIndex) {
|
|
int numFaces = _refiner.getLevel(levelIndex).getNumFaces();
|
|
|
|
for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) {
|
|
|
|
if (_patchBuilder->IsFaceAPatch(levelIndex, faceIndex) &&
|
|
_patchBuilder->IsFaceALeaf(levelIndex, faceIndex)) {
|
|
appendPatch(levelIndex, faceIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Populate patches that were previously identified.
|
|
//
|
|
void
|
|
PatchTableBuilder::populatePatches() {
|
|
|
|
// State needed to populate an array in the patch table.
|
|
// Pointers in this structure are initialized after the patch array
|
|
// data buffers have been allocated and are then incremented as we
|
|
// populate data into the patch table. Currently, we'll have at
|
|
// most 3 patch arrays: Regular, Irregular, and IrregularBoundary.
|
|
struct PatchArrayBuilder {
|
|
PatchArrayBuilder()
|
|
: patchType(PatchDescriptor::NON_PATCH), numPatches(0)
|
|
, iptr(NULL), pptr(NULL), sptr(NULL), vptr(NULL) { }
|
|
|
|
PatchDescriptor::Type patchType;
|
|
int numPatches;
|
|
|
|
Index *iptr;
|
|
PatchParam *pptr;
|
|
Index *sptr;
|
|
Index *vptr;
|
|
|
|
StackBuffer<Index*,1> fptr; // fvar indices
|
|
StackBuffer<PatchParam*,1> fpptr; // fvar patch-params
|
|
|
|
private:
|
|
// Non-copyable
|
|
PatchArrayBuilder(PatchArrayBuilder const &) {}
|
|
PatchArrayBuilder & operator=(PatchArrayBuilder const &) {return *this;}
|
|
|
|
} arrayBuilders[3];
|
|
|
|
// Regular patches patches will be packed into the first patch array
|
|
// Irregular patches will be packed into arrays according to optional
|
|
// specification -- sharing the array with regular patches or packed
|
|
// into an array of their own.
|
|
int ARRAY_REGULAR = 0;
|
|
int ARRAY_IRREGULAR = 1;
|
|
int ARRAY_BOUNDARY = 2; // only used by LegacyGregory
|
|
|
|
arrayBuilders[ARRAY_REGULAR].patchType = _patchBuilder->GetRegularPatchType();
|
|
arrayBuilders[ARRAY_REGULAR].numPatches = _numRegularPatches;
|
|
|
|
int numPatchArrays = (_numRegularPatches > 0);
|
|
if (_numIrregularPatches > 0) {
|
|
if (!_requiresLegacyGregoryTables) {
|
|
//
|
|
// Pack irregular patches into same array as regular or separately:
|
|
//
|
|
if (_patchBuilder->GetRegularPatchType() ==
|
|
_patchBuilder->GetIrregularPatchType()) {
|
|
ARRAY_IRREGULAR = ARRAY_REGULAR;
|
|
numPatchArrays = 1; // needed in case no regular patches
|
|
} else {
|
|
ARRAY_IRREGULAR = numPatchArrays;
|
|
numPatchArrays ++;
|
|
}
|
|
arrayBuilders[ARRAY_IRREGULAR].patchType =
|
|
_patchBuilder->GetIrregularPatchType();
|
|
arrayBuilders[ARRAY_IRREGULAR].numPatches += _numIrregularPatches;
|
|
} else {
|
|
//
|
|
// Arrays for Legacy-Gregory tables -- irregular patches are split
|
|
// into two arrays for interior and boundary patches
|
|
//
|
|
ARRAY_IRREGULAR = numPatchArrays;
|
|
arrayBuilders[ARRAY_IRREGULAR].patchType =
|
|
PatchDescriptor::GREGORY;
|
|
arrayBuilders[ARRAY_IRREGULAR].numPatches =
|
|
_legacyGregoryHelper->GetNumInteriorPatches();
|
|
numPatchArrays += (arrayBuilders[ARRAY_IRREGULAR].numPatches > 0);
|
|
|
|
ARRAY_BOUNDARY = numPatchArrays;
|
|
arrayBuilders[ARRAY_BOUNDARY].patchType =
|
|
PatchDescriptor::GREGORY_BOUNDARY;
|
|
arrayBuilders[ARRAY_BOUNDARY].numPatches =
|
|
_legacyGregoryHelper->GetNumBoundaryPatches();
|
|
numPatchArrays += (arrayBuilders[ARRAY_BOUNDARY].numPatches > 0);
|
|
}
|
|
}
|
|
|
|
// Create patch arrays
|
|
_table->reservePatchArrays(numPatchArrays);
|
|
|
|
int voffset=0, poffset=0, qoffset=0;
|
|
for (int arrayIndex=0; arrayIndex<numPatchArrays; ++arrayIndex) {
|
|
PatchArrayBuilder & arrayBuilder = arrayBuilders[arrayIndex];
|
|
_table->pushPatchArray(PatchDescriptor(arrayBuilder.patchType),
|
|
arrayBuilder.numPatches, &voffset, &poffset, &qoffset );
|
|
}
|
|
|
|
// Allocate patch array data buffers
|
|
allocateVertexTables();
|
|
|
|
if (_requiresFVarPatches) {
|
|
allocateFVarChannels();
|
|
}
|
|
|
|
// Initialize pointers used while populating patch array data buffers
|
|
for (int arrayIndex=0; arrayIndex<numPatchArrays; ++arrayIndex) {
|
|
PatchArrayBuilder & arrayBuilder = arrayBuilders[arrayIndex];
|
|
|
|
arrayBuilder.iptr = _table->getPatchArrayVertices(arrayIndex).begin();
|
|
arrayBuilder.pptr = _table->getPatchParams(arrayIndex).begin();
|
|
if (_requiresSharpnessArray) {
|
|
arrayBuilder.sptr = _table->getSharpnessIndices(arrayIndex);
|
|
}
|
|
if (_requiresVaryingPatches) {
|
|
arrayBuilder.vptr = _table->getPatchArrayVaryingVertices(arrayIndex).begin();
|
|
}
|
|
|
|
if (_requiresFVarPatches) {
|
|
arrayBuilder.fptr.SetSize((int)_fvarChannelIndices.size());
|
|
arrayBuilder.fpptr.SetSize((int)_fvarChannelIndices.size());
|
|
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
|
|
Index pidx = _table->getPatchIndex(arrayIndex, 0);
|
|
int ofs = pidx * _table->GetFVarValueStride(fvc);
|
|
|
|
arrayBuilder.fptr[fvc] = &_table->getFVarValues(fvc)[ofs];
|
|
arrayBuilder.fpptr[fvc] = &_table->getFVarPatchParams(fvc)[pidx];
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initializing StencilTable and other helpers. Note Varying local points
|
|
// are managed by the LocalPointHelper for Vertex patches (as Varying local
|
|
// points are tightly coupled to their local points)
|
|
//
|
|
LocalPointHelper * vertexLocalPointHelper = 0;
|
|
|
|
StackBuffer<LocalPointHelper*,4> fvarLocalPointHelpers;
|
|
|
|
if (_requiresLocalPoints) {
|
|
LocalPointHelper::Options opts;
|
|
opts.createStencilTable = true;
|
|
opts.createVaryingTable = _requiresVaryingLocalPoints;
|
|
opts.doubleStencilTable = _options.patchPrecisionDouble;
|
|
opts.shareLocalPoints = _options.shareEndCapPatchPoints;
|
|
opts.reuseSourcePoints = (_patchBuilder->GetIrregularPatchType() ==
|
|
_patchBuilder->GetNativePatchType() );
|
|
|
|
vertexLocalPointHelper = new LocalPointHelper(
|
|
_refiner, opts, -1, estimateLocalPointCount(opts, -1));
|
|
|
|
if (_requiresFVarPatches) {
|
|
opts.createStencilTable = true;
|
|
opts.createVaryingTable = false;
|
|
opts.doubleStencilTable = _options.fvarPatchPrecisionDouble;
|
|
|
|
fvarLocalPointHelpers.SetSize((int)_fvarChannelIndices.size());
|
|
|
|
for (int fvc = 0; fvc < (int)_fvarChannelIndices.size(); ++fvc) {
|
|
fvarLocalPointHelpers[fvc] = new LocalPointHelper(
|
|
_refiner, opts, getRefinerFVarChannel(fvc),
|
|
estimateLocalPointCount(opts, fvc));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Populate patch data buffers
|
|
//
|
|
// Intentionally declare local vairables to contain patch topology info
|
|
// outside the loop to avoid repeated memory de-allocation/re-allocation
|
|
// associated with a change-of-basis.
|
|
PatchInfo patchInfo;
|
|
PatchInfo fvarPatchInfo;
|
|
|
|
bool fvarPrecisionMatches = (_options.patchPrecisionDouble ==
|
|
_options.fvarPatchPrecisionDouble);
|
|
|
|
for (int patchIndex = 0; patchIndex < (int)_patches.size(); ++patchIndex) {
|
|
|
|
PatchTuple const & patch = _patches[patchIndex];
|
|
|
|
//
|
|
// Identify and assign points, stencils, sharpness, etc. for this patch:
|
|
//
|
|
identifyPatchTopology(patch, patchInfo);
|
|
|
|
PatchArrayBuilder * arrayBuilder = &arrayBuilders[ARRAY_REGULAR];
|
|
if (!patchInfo.isRegular) {
|
|
arrayBuilder = &arrayBuilders[ARRAY_IRREGULAR];
|
|
}
|
|
|
|
if (_requiresLegacyGregoryTables && !patchInfo.isRegular) {
|
|
if (isBoundaryFace(
|
|
_refiner.getLevel(patch.levelIndex), patch.faceIndex)) {
|
|
arrayBuilder = &arrayBuilders[ARRAY_BOUNDARY];
|
|
}
|
|
arrayBuilder->iptr += assignFacePoints(patch, arrayBuilder->iptr);
|
|
} else {
|
|
arrayBuilder->iptr += assignPatchPointsAndStencils(patch,
|
|
patchInfo, arrayBuilder->iptr, *vertexLocalPointHelper);
|
|
}
|
|
if (_requiresSharpnessArray) {
|
|
*arrayBuilder->sptr++ = assignSharpnessIndex(patchInfo.regSharpness,
|
|
_table->_sharpnessValues);
|
|
}
|
|
|
|
//
|
|
// Identify the PatchParam -- which may vary slightly for face-varying
|
|
// channels but will still be used to initialize them when so to avoid
|
|
// recomputing from scratch
|
|
//
|
|
PatchParam patchParam =
|
|
_patchBuilder->ComputePatchParam(patch.levelIndex, patch.faceIndex,
|
|
_ptexIndices, patchInfo.isRegular, patchInfo.paramBoundaryMask,
|
|
true/* condition to compute transition mask */);
|
|
*arrayBuilder->pptr++ = patchParam;
|
|
|
|
//
|
|
// Assignment of face-varying patches is split between the comon/trivial
|
|
// case of linear patches and the more complex non-linear cases:
|
|
//
|
|
assert((_fvarChannelIndices.size() > 0) == _requiresFVarPatches);
|
|
for (int fvc = 0; fvc < (int)_fvarChannelIndices.size(); ++fvc) {
|
|
|
|
if (isFVarChannelLinear(fvc)) {
|
|
assignFacePoints(patch, arrayBuilder->fptr[fvc], fvc);
|
|
|
|
*arrayBuilder->fpptr[fvc] = patchParam;
|
|
} else {
|
|
//
|
|
// For non-linear patches, reuse patch information when the
|
|
// topology of the face in face-varying space matches the
|
|
// original patch:
|
|
//
|
|
bool fvcTopologyMatches = fvarPrecisionMatches &&
|
|
doesFVarTopologyMatch(patch, fvc);
|
|
|
|
PatchInfo & fvcPatchInfo = fvcTopologyMatches
|
|
? patchInfo : fvarPatchInfo;
|
|
|
|
if (!fvcTopologyMatches) {
|
|
identifyPatchTopology(patch, fvcPatchInfo, fvc);
|
|
}
|
|
assignPatchPointsAndStencils(patch, fvcPatchInfo,
|
|
arrayBuilder->fptr[fvc], *fvarLocalPointHelpers[fvc], fvc);
|
|
|
|
// Initialize and assign the face-varying PatchParam:
|
|
PatchParam & fvcPatchParam = *arrayBuilder->fpptr[fvc];
|
|
fvcPatchParam.Set(
|
|
patchParam.GetFaceId(),
|
|
patchParam.GetU(), patchParam.GetV(),
|
|
patchParam.GetDepth(),
|
|
patchParam.NonQuadRoot(),
|
|
(unsigned short) fvcPatchInfo.paramBoundaryMask,
|
|
patchParam.GetTransition(),
|
|
fvcPatchInfo.isRegular);
|
|
}
|
|
arrayBuilder->fpptr[fvc] ++;
|
|
arrayBuilder->fptr[fvc] += _table->GetFVarValueStride(fvc);
|
|
}
|
|
|
|
if (_requiresVaryingPatches) {
|
|
arrayBuilder->vptr += assignFacePoints(patch, arrayBuilder->vptr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finalizing and destroying StencilTable and other helpers:
|
|
//
|
|
if (_requiresLocalPoints) {
|
|
_table->_localPointStencils =
|
|
vertexLocalPointHelper->AcquireStencilTable();
|
|
if (_requiresVaryingLocalPoints) {
|
|
_table->_localPointVaryingStencils =
|
|
vertexLocalPointHelper->AcquireStencilTableVarying();
|
|
}
|
|
delete vertexLocalPointHelper;
|
|
|
|
if (_requiresFVarPatches) {
|
|
_table->_localPointFaceVaryingStencils.resize(_fvarChannelIndices.size());
|
|
|
|
for (int fvc=0; fvc<(int)_fvarChannelIndices.size(); ++fvc) {
|
|
_table->_localPointFaceVaryingStencils[fvc] =
|
|
fvarLocalPointHelpers[fvc]->AcquireStencilTable();
|
|
delete fvarLocalPointHelpers[fvc];
|
|
}
|
|
}
|
|
}
|
|
if (_requiresLegacyGregoryTables) {
|
|
_legacyGregoryHelper->FinalizeQuadOffsets(_table->_quadOffsetsTable);
|
|
_legacyGregoryHelper->FinalizeVertexValence(_table->_vertexValenceTable,
|
|
_levelVertOffsets[_refiner.GetMaxLevel()]);
|
|
}
|
|
}
|
|
|
|
int
|
|
PatchTableBuilder::estimateLocalPointCount(
|
|
LocalPointHelper::Options const & options,
|
|
int fvarChannel) const {
|
|
|
|
if ((fvarChannel >= 0) && isFVarChannelLinear(fvarChannel)) return 0;
|
|
|
|
//
|
|
// Estimate the local points required for the Vertex topology in all cases
|
|
// as it may be used in the estimates for face-varying channels:
|
|
//
|
|
int estLocalPoints = 0;
|
|
|
|
if (_requiresRegularLocalPoints) {
|
|
//
|
|
// Either all regular patches are being converted to a non-native
|
|
// basis, or boundary points are being extrapolated in the regular
|
|
// basis. The latter case typically involves a small fraction of
|
|
// patches and points, so we don't estimate any local points and
|
|
// leave it up to incremental allocation later to account for them.
|
|
//
|
|
int numPointsPerPatch = PatchDescriptor(
|
|
_patchBuilder->GetRegularPatchType()).GetNumControlVertices();
|
|
|
|
if (_patchBuilder->GetRegularPatchType() !=
|
|
_patchBuilder->GetNativePatchType()) {
|
|
estLocalPoints += _numRegularPatches * numPointsPerPatch;
|
|
}
|
|
}
|
|
if (_requiresIrregularLocalPoints) {
|
|
bool oldEstimate = false;
|
|
int numIrregularPatches = oldEstimate ?
|
|
_refiner.GetLevel(_refiner.GetMaxLevel()).GetNumFaces() :
|
|
_numIrregularPatches;
|
|
|
|
//
|
|
// If converting to a basis other than regular, its difficult to
|
|
// predict the degree of point-sharing that may occur, and in general,
|
|
// the maximal benefit is small so we don't attempt to compensate for
|
|
// it here. If converting to the same basis as regular, roughly half
|
|
// of the points of the patch are involved in approximating the
|
|
// irregularity (and cannot be shared) so don't include the source
|
|
// points that will be used in such patches.
|
|
//
|
|
int numPointsPerPatch = PatchDescriptor(
|
|
_patchBuilder->GetIrregularPatchType()).GetNumControlVertices();
|
|
|
|
if (options.reuseSourcePoints && (_patchBuilder->GetIrregularPatchType() ==
|
|
_patchBuilder->GetNativePatchType())) {
|
|
numPointsPerPatch /= 2;
|
|
}
|
|
estLocalPoints += numIrregularPatches * numPointsPerPatch;
|
|
}
|
|
|
|
//
|
|
// Its difficult to estimate the differences in number of irregularities
|
|
// between the vertex topology and a face-varying channel without more
|
|
// detailed inspection.
|
|
//
|
|
// A much higher number of fvar-values than vertices is an indication that
|
|
// the number of differences increases, and that generally lowers the
|
|
// number of irregular patches due to more regular patches on face-varying
|
|
// boundaries, but not always. The use of some interpolation types, e.g.
|
|
// LINEAR_BOUNDARIES, combined with inf-sharp patches can increase the
|
|
// number of irregularities significantly.
|
|
//
|
|
if ((fvarChannel >= 0) && (_refiner.GetNumLevels() > 1)) {
|
|
//
|
|
// We're seeing face-varying stencil table sizes about 1/4 the size of
|
|
// the vertex stencil table for what seem like typical cases...
|
|
//
|
|
// Identify the ratio of fvar-values to vertices that typically leads
|
|
// to these reductions and reduce the number of expected local points
|
|
// proportionally. Use the number of fvar-values at level 1: level 0
|
|
// can be misleading as there can be many FEWER than the number of
|
|
// vertices, but subsequent levels will have at least one unique
|
|
// fvar-value per vertex, and later levels will have a much higher
|
|
// percentage shared as a result of refinement.
|
|
//
|
|
int fvc = getRefinerFVarChannel(fvarChannel);
|
|
if (_refiner.GetLevel(1).GetNumFVarValues(fvc) >
|
|
_refiner.GetLevel(1).GetNumVertices()) {
|
|
// Scale this based on ratio of fvar-values to vertices...
|
|
float fvarReduction = 0.5;
|
|
|
|
estLocalPoints = (int) ((float)estLocalPoints * fvarReduction);
|
|
}
|
|
}
|
|
return estLocalPoints;
|
|
}
|
|
|
|
|
|
//
|
|
// Member function definitions for the LocalPointHelper class:
|
|
//
|
|
PatchTableBuilder::LocalPointHelper::LocalPointHelper(
|
|
TopologyRefiner const & refiner, Options const & options,
|
|
int fvarChannel, int numLocalPointsExpected) :
|
|
_refiner(refiner), _options(options), _fvarChannel(fvarChannel),
|
|
_numLocalPoints(0), _stencilTable(), _stencilTableVarying() {
|
|
|
|
_localPointOffset = (_fvarChannel < 0)
|
|
? _refiner.GetNumVerticesTotal()
|
|
: _refiner.GetNumFVarValuesTotal(_fvarChannel);
|
|
|
|
if (_options.createStencilTable) {
|
|
if (_options.doubleStencilTable) {
|
|
initializeStencilTable<double>(numLocalPointsExpected);
|
|
} else {
|
|
initializeStencilTable<float>(numLocalPointsExpected);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename REAL>
|
|
void
|
|
PatchTableBuilder::LocalPointHelper::initializeStencilTable(int numLocalPointsExpected) {
|
|
|
|
//
|
|
// Reserving space for the local-point stencils has been a source of
|
|
// problems in the past so we rely on the PatchTableBuilder to provide
|
|
// a reasonable estimate to the LocalPointHelper on construction. For
|
|
// large meshes a limit on the size initially reserve is capped.
|
|
//
|
|
// The average number of entries per stencil has been historically set
|
|
// at 16, which seemed high and was reduced on further investigation.
|
|
//
|
|
StencilTableReal<REAL> * stencilTable = new StencilTableReal<REAL>(0);
|
|
StencilTableReal<REAL> * varyingTable = _options.createVaryingTable
|
|
? new StencilTableReal<REAL>(0) : 0;
|
|
|
|
// Historic note: limits to 100M (=800M bytes) entries for reserved size
|
|
size_t const MaxEntriesToReserve = 100 * 1024 * 1024;
|
|
size_t const AvgEntriesPerStencil = 9; // originally 16
|
|
|
|
size_t numStencilsExpected = numLocalPointsExpected;
|
|
size_t numStencilEntriesExpected = numStencilsExpected * AvgEntriesPerStencil;
|
|
|
|
size_t numEntriesToReserve = std::min(numStencilEntriesExpected,
|
|
MaxEntriesToReserve);
|
|
if (numEntriesToReserve) {
|
|
stencilTable->reserve(
|
|
(int)numStencilsExpected, (int)numEntriesToReserve);
|
|
|
|
if (varyingTable) {
|
|
// Varying stencils have only one entry per point
|
|
varyingTable->reserve(
|
|
(int)numStencilsExpected, (int)numStencilsExpected);
|
|
}
|
|
}
|
|
|
|
_stencilTable.Set(stencilTable);
|
|
_stencilTableVarying.Set(varyingTable);
|
|
}
|
|
|
|
template <typename REAL>
|
|
PatchTableBuilder::StencilTablePtr
|
|
PatchTableBuilder::LocalPointHelper::acquireStencilTable(
|
|
StencilTablePtr& stencilTableMember) {
|
|
|
|
StencilTableReal<REAL> * stencilTable = stencilTableMember.Get<REAL>();
|
|
|
|
if (stencilTable) {
|
|
if (stencilTable->GetNumStencils() > 0) {
|
|
stencilTable->finalize();
|
|
} else {
|
|
delete stencilTable;
|
|
stencilTable = 0;
|
|
}
|
|
}
|
|
|
|
stencilTableMember.Set();
|
|
return StencilTablePtr(stencilTable);
|
|
}
|
|
|
|
PatchTableBuilder::LocalPointHelper::~LocalPointHelper() {
|
|
|
|
if (_options.doubleStencilTable) {
|
|
delete _stencilTable.Get<double>();
|
|
delete _stencilTableVarying.Get<double>();
|
|
} else {
|
|
delete _stencilTable.Get<float>();
|
|
delete _stencilTableVarying.Get<float>();
|
|
}
|
|
}
|
|
|
|
Index
|
|
PatchTableBuilder::LocalPointHelper::findSharedCornerPoint(int levelIndex,
|
|
Index vertIndex, Index localPointIndex) {
|
|
|
|
if (_sharedCornerPoints.empty()) {
|
|
_sharedCornerPoints.resize(_refiner.GetNumLevels());
|
|
}
|
|
|
|
IndexVector & vertexPoints = _sharedCornerPoints[levelIndex];
|
|
if (vertexPoints.empty()) {
|
|
Level const & level = _refiner.getLevel(levelIndex);
|
|
|
|
if (_fvarChannel < 0) {
|
|
vertexPoints.resize(level.getNumVertices(), INDEX_INVALID);
|
|
} else {
|
|
vertexPoints.resize(level.getNumFVarValues(_fvarChannel), INDEX_INVALID);
|
|
}
|
|
}
|
|
|
|
Index & assignedIndex = vertexPoints[vertIndex];
|
|
if (!IndexIsValid(assignedIndex)) {
|
|
assignedIndex = localPointIndex;
|
|
}
|
|
return assignedIndex;
|
|
}
|
|
|
|
Index
|
|
PatchTableBuilder::LocalPointHelper::findSharedEdgePoint(int levelIndex,
|
|
Index edgeIndex, int edgeEnd, Index localPointIndex) {
|
|
|
|
if (_sharedEdgePoints.empty()) {
|
|
_sharedEdgePoints.resize(_refiner.GetNumLevels());
|
|
}
|
|
|
|
IndexVector & edgePoints = _sharedEdgePoints[levelIndex];
|
|
if (edgePoints.empty()) {
|
|
edgePoints.resize(
|
|
2 * _refiner.GetLevel(levelIndex).GetNumEdges(), INDEX_INVALID);
|
|
}
|
|
|
|
Index & assignedIndex = edgePoints[2 * edgeIndex + edgeEnd];
|
|
if (!IndexIsValid(assignedIndex)) {
|
|
assignedIndex = localPointIndex;
|
|
} else {
|
|
}
|
|
return assignedIndex;
|
|
}
|
|
|
|
template <typename REAL>
|
|
void
|
|
PatchTableBuilder::LocalPointHelper::appendLocalPointStencil(
|
|
SparseMatrix<REAL> const & conversionMatrix,
|
|
int stencilRow,
|
|
Index const sourcePoints[],
|
|
int sourcePointOffset) {
|
|
|
|
int stencilSize = conversionMatrix.GetRowSize(stencilRow);
|
|
ConstArray<int> matrixColumns = conversionMatrix.GetRowColumns(stencilRow);
|
|
ConstArray<REAL> matrixWeights = conversionMatrix.GetRowElements(stencilRow);
|
|
|
|
StencilTableReal<REAL>* stencilTable = _stencilTable.Get<REAL>();
|
|
|
|
stencilTable->_sizes.push_back(stencilSize);
|
|
for (int i = 0; i < stencilSize; ++i) {
|
|
stencilTable->_weights.push_back(matrixWeights[i]);
|
|
stencilTable->_indices.push_back(
|
|
sourcePoints[matrixColumns[i]] + sourcePointOffset);
|
|
}
|
|
}
|
|
|
|
template <typename REAL>
|
|
void
|
|
PatchTableBuilder::LocalPointHelper::appendLocalPointStencils(
|
|
SparseMatrix<REAL> const & conversionMatrix,
|
|
Index const sourcePoints[],
|
|
int sourcePointOffset) {
|
|
|
|
//
|
|
// Resize the StencilTable members to accomodate all rows and elements from
|
|
// the given set of points represented by the matrix
|
|
//
|
|
StencilTableReal<REAL>* stencilTable = _stencilTable.Get<REAL>();
|
|
|
|
int numNewStencils = conversionMatrix.GetNumRows();
|
|
int numNewElements = conversionMatrix.GetNumElements();
|
|
|
|
size_t numOldStencils = stencilTable->_sizes.size();
|
|
size_t numOldElements = stencilTable->_indices.size();
|
|
|
|
// Assign the sizes for the new stencils:
|
|
stencilTable->_sizes.resize(numOldStencils + numNewStencils);
|
|
|
|
int * newSizes = &stencilTable->_sizes[numOldStencils];
|
|
for (int i = 0; i < numNewStencils; ++i) {
|
|
newSizes[i] = conversionMatrix.GetRowSize(i);
|
|
}
|
|
|
|
// Assign remapped indices for the stencils:
|
|
stencilTable->_indices.resize(numOldElements + numNewElements);
|
|
|
|
int const * mtxIndices = &conversionMatrix.GetColumns()[0];
|
|
int * newIndices = &stencilTable->_indices[numOldElements];
|
|
|
|
for (int i = 0; i < numNewElements; ++i) {
|
|
newIndices[i] = sourcePoints[mtxIndices[i]] + sourcePointOffset;
|
|
}
|
|
|
|
// Copy the stencil weights direct from the matrix elements:
|
|
stencilTable->_weights.resize(numOldElements + numNewElements);
|
|
|
|
REAL const * mtxWeights = &conversionMatrix.GetElements()[0];
|
|
REAL * newWeights = &stencilTable->_weights[numOldElements];
|
|
|
|
std::memcpy(newWeights, mtxWeights, numNewElements * sizeof(REAL));
|
|
}
|
|
|
|
|
|
//
|
|
// Its unfortunate that varying stencils for local points were ever created
|
|
// and external dependency on them forces a certain coordination here. Each
|
|
// patch type is expected to have a varying value computed for each patch
|
|
// point and shaders retrieve the varying value associated with particular
|
|
// points. So we need to store that mapping from control point to varying
|
|
// point (or corner of the patch) somewhere. We are trying to avoid adding
|
|
// more to the PatchDescriptor interface, so we'll keep it here for now in
|
|
// the hope we may be able to eliminate the need for it.
|
|
//
|
|
namespace {
|
|
inline int const *
|
|
GetVaryingIndicesPerType(PatchDescriptor::Type type) {
|
|
|
|
// Note that we can use the linear and gregory vectors here for
|
|
// both quads and tris
|
|
static int const linearIndices[4] = { 0, 1, 2, 3 };
|
|
static int const bsplineIndices[] =
|
|
{ 0, 0, 1, 1, 0, 0, 1, 1, 3, 3, 2, 2, 3, 3, 2, 2 };
|
|
static int const boxsplineIndices[] =
|
|
{ 0, 0, 1, 0, 0, 1, 1, 2, 2, 1, 2, 2 };
|
|
static int const gregoryIndices[] =
|
|
{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3 };
|
|
static int const gregoryTriIndices[] =
|
|
{ 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 0, 1, 2 };
|
|
|
|
if (type == PatchDescriptor::GREGORY_BASIS) {
|
|
return gregoryIndices;
|
|
} else if (type == PatchDescriptor::GREGORY_TRIANGLE) {
|
|
return gregoryTriIndices;
|
|
} else if (type == PatchDescriptor::REGULAR) {
|
|
return bsplineIndices;
|
|
} else if (type == PatchDescriptor::LOOP) {
|
|
return boxsplineIndices;
|
|
} else if (type == PatchDescriptor::QUADS) {
|
|
return linearIndices;
|
|
} else if (type == PatchDescriptor::TRIANGLES) {
|
|
return linearIndices;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template <typename REAL>
|
|
void
|
|
PatchTableBuilder::LocalPointHelper::appendLocalPointVaryingStencil(
|
|
int const * varyingIndices, int patchPointIndex,
|
|
Index const sourcePoints[], int sourcePointOffset) {
|
|
|
|
Index varyingPoint =
|
|
sourcePoints[varyingIndices[patchPointIndex]] + sourcePointOffset;
|
|
|
|
StencilTableReal<REAL>* t = _stencilTableVarying.Get<REAL>();
|
|
|
|
t->_sizes.push_back(1);
|
|
t->_indices.push_back(varyingPoint);
|
|
t->_weights.push_back((REAL) 1.0);
|
|
}
|
|
|
|
namespace {
|
|
typedef short ShareBits;
|
|
|
|
static ShareBits const SHARE_CORNER = 0x10;
|
|
static ShareBits const SHARE_EDGE_BEGIN = 0x20;
|
|
static ShareBits const SHARE_EDGE_END = 0x40;
|
|
static ShareBits const SHARE_EDGE = SHARE_EDGE_BEGIN | SHARE_EDGE_END;
|
|
static ShareBits const SHARE_INDEX_MASK = 0x0f;
|
|
|
|
inline ShareBits const *
|
|
GetShareBitsPerType(PatchDescriptor::Type type) {
|
|
|
|
static ShareBits const linearQuadBits[5] = { 0x10, 0x11, 0x12, 0x13,
|
|
SHARE_CORNER };
|
|
static ShareBits const gregoryQuadBits[21] = { 0x10, 0x20, 0x43, 0, 0,
|
|
0x11, 0x21, 0x40, 0, 0,
|
|
0x12, 0x22, 0x41, 0, 0,
|
|
0x13, 0x23, 0x42, 0, 0,
|
|
SHARE_CORNER | SHARE_EDGE };
|
|
|
|
if (type == PatchDescriptor::QUADS) {
|
|
return linearQuadBits;
|
|
} else if (type == PatchDescriptor::GREGORY_BASIS) {
|
|
return gregoryQuadBits;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
template <typename REAL>
|
|
int
|
|
PatchTableBuilder::LocalPointHelper::AppendLocalPatchPoints(
|
|
int levelIndex, Index faceIndex,
|
|
SparseMatrix<REAL> const & matrix,
|
|
PatchDescriptor::Type patchType,
|
|
Index const sourcePoints[],
|
|
int sourcePointOffset,
|
|
Index patchPoints[]) {
|
|
|
|
//
|
|
// If sharing local points, verify the type of this patch supports
|
|
// sharing and disable it if not:
|
|
//
|
|
int numPatchPoints = matrix.GetNumRows();
|
|
|
|
int firstNewLocalPoint = _localPointOffset + _numLocalPoints;
|
|
int nextNewLocalPoint = firstNewLocalPoint;
|
|
|
|
ShareBits const* shareBitsPerPoint = _options.shareLocalPoints ?
|
|
GetShareBitsPerType(patchType) : 0;
|
|
|
|
bool shareLocalPointsForThisPatch = (shareBitsPerPoint != 0);
|
|
|
|
int const * varyingIndices = 0;
|
|
if (_stencilTableVarying) {
|
|
varyingIndices = GetVaryingIndicesPerType(patchType);
|
|
}
|
|
|
|
bool applyVertexStencils = (_stencilTable.Get<REAL>() != 0);
|
|
bool applyVaryingStencils = (varyingIndices != 0);
|
|
|
|
//
|
|
// When point-sharing is not enabled, all patch points are generally
|
|
// new local points -- the exception to this occurs when "re-using"
|
|
// source points, i.e. the resulting patch can be a mixture of source
|
|
// and local points (typically when the irregular patch type is the
|
|
// same as the regular patch type native to the scheme).
|
|
//
|
|
if (!shareLocalPointsForThisPatch) {
|
|
if (!_options.reuseSourcePoints) {
|
|
if (applyVertexStencils) {
|
|
appendLocalPointStencils(
|
|
matrix, sourcePoints, sourcePointOffset);
|
|
if (applyVaryingStencils) {
|
|
for (int i = 0; i < numPatchPoints; ++i) {
|
|
appendLocalPointVaryingStencil<REAL>(
|
|
varyingIndices, i, sourcePoints, sourcePointOffset);
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < numPatchPoints; ++i) {
|
|
patchPoints[i] = nextNewLocalPoint ++;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < numPatchPoints; ++i) {
|
|
if (_options.reuseSourcePoints && (matrix.GetRowSize(i) == 1)) {
|
|
patchPoints[i] = sourcePoints[matrix.GetRowColumns(i)[0]]
|
|
+ sourcePointOffset;
|
|
continue;
|
|
}
|
|
if (applyVertexStencils) {
|
|
appendLocalPointStencil(
|
|
matrix, i, sourcePoints, sourcePointOffset);
|
|
if (applyVaryingStencils) {
|
|
appendLocalPointVaryingStencil<REAL>(
|
|
varyingIndices, i, sourcePoints, sourcePointOffset);
|
|
}
|
|
}
|
|
patchPoints[i] = nextNewLocalPoint ++;
|
|
}
|
|
}
|
|
} else {
|
|
// Gather topology info according to the sharing for this patch type
|
|
//
|
|
Level const & level = _refiner.getLevel(levelIndex);
|
|
|
|
ConstIndexArray fCorners;
|
|
ConstIndexArray fVerts;
|
|
ConstIndexArray fEdges;
|
|
bool fEdgeReversed[4];
|
|
bool fEdgeBoundary[4];
|
|
|
|
ShareBits const shareBitsForType = shareBitsPerPoint[numPatchPoints];
|
|
if (shareBitsForType) {
|
|
if (shareBitsForType & SHARE_CORNER) {
|
|
fCorners = (_fvarChannel < 0)
|
|
? level.getFaceVertices(faceIndex)
|
|
: level.getFaceFVarValues(faceIndex, _fvarChannel);
|
|
}
|
|
if (shareBitsForType & SHARE_EDGE) {
|
|
fEdges = level.getFaceEdges(faceIndex);
|
|
fVerts = (_fvarChannel < 0) ? fCorners
|
|
: level.getFaceVertices(faceIndex);
|
|
|
|
Level::ETag fEdgeTags[4];
|
|
level.getFaceETags(faceIndex, fEdgeTags, _fvarChannel);
|
|
|
|
for (int i = 0; i < fEdges.size(); ++i) {
|
|
fEdgeReversed[i] = level.getEdgeVertices(fEdges[i])[0] != fVerts[i];
|
|
fEdgeBoundary[i] = fEdgeTags[i]._boundary;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Inspect the sharing bits for each point -- if set, see if a local
|
|
// point for the corresponding vertex/fvar-value or edge was
|
|
// previously used:
|
|
//
|
|
for (int i = 0; i < numPatchPoints; ++i) {
|
|
if (_options.reuseSourcePoints && (matrix.GetRowSize(i) == 1)) {
|
|
patchPoints[i] = sourcePoints[matrix.GetRowColumns(i)[0]]
|
|
+ sourcePointOffset;
|
|
continue;
|
|
}
|
|
|
|
int patchPoint = nextNewLocalPoint;
|
|
|
|
if (shareBitsPerPoint[i]) {
|
|
int index = shareBitsPerPoint[i] & SHARE_INDEX_MASK;
|
|
|
|
if (shareBitsPerPoint[i] & SHARE_CORNER) {
|
|
patchPoint = findSharedCornerPoint(
|
|
levelIndex, fCorners[index], nextNewLocalPoint);
|
|
} else if (!fEdgeBoundary[index]) {
|
|
int edgeEnd = (((shareBitsPerPoint[i] & SHARE_EDGE_END) > 0) !=
|
|
fEdgeReversed[index]);
|
|
patchPoint = findSharedEdgePoint(
|
|
levelIndex, fEdges[index], edgeEnd, nextNewLocalPoint);
|
|
}
|
|
}
|
|
if (patchPoint == nextNewLocalPoint) {
|
|
if (applyVertexStencils) {
|
|
appendLocalPointStencil(
|
|
matrix, i, sourcePoints, sourcePointOffset);
|
|
if (applyVaryingStencils) {
|
|
appendLocalPointVaryingStencil<REAL>(
|
|
varyingIndices, i, sourcePoints, sourcePointOffset);
|
|
}
|
|
}
|
|
nextNewLocalPoint ++;
|
|
}
|
|
patchPoints[i] = patchPoint;
|
|
}
|
|
}
|
|
|
|
int numNewLocalPoints = nextNewLocalPoint - firstNewLocalPoint;
|
|
_numLocalPoints += numNewLocalPoints;
|
|
return numNewLocalPoints;
|
|
}
|
|
|
|
|
|
//
|
|
// Member function definitions for the LegacyGregoryHelper class:
|
|
//
|
|
void
|
|
PatchTableBuilder::LegacyGregoryHelper::AddPatchFace(int level, Index face) {
|
|
|
|
if (_refiner.getLevel(level).getFaceCompositeVTag(face)._boundary) {
|
|
_boundaryFaceIndices.push_back(face);
|
|
} else {
|
|
_interiorFaceIndices.push_back(face);
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchTableBuilder::LegacyGregoryHelper::FinalizeQuadOffsets(
|
|
PatchTable::QuadOffsetsTable & qTable) {
|
|
|
|
size_t numInteriorPatches = _interiorFaceIndices.size();
|
|
size_t numBoundaryPatches = _boundaryFaceIndices.size();
|
|
size_t numTotalPatches = numInteriorPatches + numBoundaryPatches;
|
|
|
|
struct QuadOffset {
|
|
static int Assign(Level const& level, Index faceIndex,
|
|
unsigned int offsets[]) {
|
|
ConstIndexArray fVerts = level.getFaceVertices(faceIndex);
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
ConstIndexArray vFaces = level.getVertexFaces(fVerts[i]);
|
|
int faceInVFaces = vFaces.FindIndex(faceIndex);
|
|
|
|
// we have to use number of incident edges to modulo the local
|
|
// index as there could be 2 consecutive edges in the face
|
|
// belonging to the patch
|
|
int vOffset0 = faceInVFaces;
|
|
int vOffset1 = (faceInVFaces + 1) %
|
|
level.getVertexEdges(fVerts[i]).size();
|
|
offsets[i] = vOffset0 | (vOffset1 << 8);
|
|
}
|
|
return 4;
|
|
}
|
|
};
|
|
|
|
if (numTotalPatches > 0) {
|
|
qTable.resize(numTotalPatches*4);
|
|
|
|
// all patches assumed to be at the last level
|
|
Level const &maxLevel = _refiner.getLevel(_refiner.GetMaxLevel());
|
|
|
|
PatchTable::QuadOffsetsTable::value_type *p = &(qTable[0]);
|
|
for (size_t i = 0; i < numInteriorPatches; ++i) {
|
|
p += QuadOffset::Assign(maxLevel, _interiorFaceIndices[i], p);
|
|
}
|
|
for (size_t i = 0; i < numBoundaryPatches; ++i) {
|
|
p += QuadOffset::Assign(maxLevel, _boundaryFaceIndices[i], p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
PatchTableBuilder::LegacyGregoryHelper::FinalizeVertexValence(
|
|
PatchTable::VertexValenceTable & vTable, int lastLevelOffset) {
|
|
|
|
// Populate the "vertex valence" table for Gregory patches -- this table
|
|
// contains the one-ring of vertices around each vertex. Currently it is
|
|
// extremely wasteful for the following reasons:
|
|
// - it allocates 2*maxvalence+1 for ALL vertices
|
|
// - it initializes the one-ring for ALL vertices
|
|
// We use the full size expected (not sure what else relies on that) but
|
|
// we avoid initializing the vast majority of vertices that are not
|
|
// associated with gregory patches -- only populating those in the last
|
|
// level (an older version attempted to avoid vertices not involved with
|
|
// Gregory patches)
|
|
//
|
|
int vWidth = 2*_refiner.GetMaxValence() + 1;
|
|
|
|
vTable.resize((long)_refiner.GetNumVerticesTotal() * vWidth);
|
|
|
|
Level const & lastLevel = _refiner.getLevel(_refiner.GetMaxLevel());
|
|
|
|
int * vTableEntry = &vTable[lastLevelOffset * vWidth];
|
|
|
|
for (int vIndex = 0; vIndex < lastLevel.getNumVertices(); ++vIndex) {
|
|
// Gather the one-ring around the vertex and set its resulting size
|
|
// (note negative size used to distinguish between boundary/interior):
|
|
//
|
|
int *ringDest = vTableEntry + 1;
|
|
int ringSize =
|
|
lastLevel.gatherQuadRegularRingAroundVertex(vIndex, ringDest);
|
|
|
|
for (int j = 0; j < ringSize; ++j) {
|
|
ringDest[j] += lastLevelOffset;
|
|
}
|
|
if (ringSize & 1) {
|
|
// boundary: duplicate end vertex index and store negative valence
|
|
ringSize++;
|
|
vTableEntry[ringSize]=vTableEntry[ringSize-1];
|
|
vTableEntry[0] = -ringSize/2;
|
|
} else {
|
|
vTableEntry[0] = ringSize/2;
|
|
}
|
|
vTableEntry += vWidth;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// The sole public PatchTableFactory method to create a PatchTable -- deferring
|
|
// to the PatchTableBuilder implementation
|
|
//
|
|
PatchTable *
|
|
PatchTableFactory::Create(TopologyRefiner const & refiner,
|
|
Options options,
|
|
ConstIndexArray selectedFaces) {
|
|
|
|
PatchTableBuilder builder(refiner, options, selectedFaces);
|
|
|
|
if (builder.UniformPolygonsSpecified()) {
|
|
builder.BuildUniformPolygons();
|
|
} else {
|
|
builder.BuildPatches();
|
|
}
|
|
return builder.GetPatchTable();
|
|
}
|
|
|
|
|
|
//
|
|
// Implementation of PatchTableFactory::PatchFaceTag -- unintentionally
|
|
// exposed to the public interface, no longer internally used and so
|
|
// planned for deprecation
|
|
//
|
|
void
|
|
PatchTableFactory::PatchFaceTag::clear() {
|
|
std::memset(this, 0, sizeof(*this));
|
|
}
|
|
|
|
void
|
|
PatchTableFactory::PatchFaceTag::assignTransitionPropertiesFromEdgeMask(
|
|
int tMask) {
|
|
|
|
_transitionMask = tMask;
|
|
}
|
|
|
|
void
|
|
PatchTableFactory::PatchFaceTag::assignBoundaryPropertiesFromEdgeMask(
|
|
int eMask) {
|
|
|
|
static int const edgeMaskToCount[16] =
|
|
{ 0, 1, 1, 2, 1, -1, 2, -1, 1, 2, -1, -1, 2, -1, -1, -1 };
|
|
static int const edgeMaskToIndex[16] =
|
|
{ -1, 0, 1, 1, 2, -1, 2, -1, 3, 0, -1, -1, 3, -1, -1,-1 };
|
|
|
|
assert(edgeMaskToCount[eMask] != -1);
|
|
assert(edgeMaskToIndex[eMask] != -1);
|
|
|
|
_boundaryMask = eMask;
|
|
_hasBoundaryEdge = (eMask > 0);
|
|
|
|
_boundaryCount = edgeMaskToCount[eMask];
|
|
_boundaryIndex = edgeMaskToIndex[eMask];
|
|
}
|
|
|
|
void
|
|
PatchTableFactory::PatchFaceTag::assignBoundaryPropertiesFromVertexMask(
|
|
int vMask) {
|
|
|
|
// This is only intended to support the case of a single boundary vertex
|
|
// with no boundary edges, which can only occur with an irregular vertex
|
|
|
|
static int const singleBitVertexMaskToCount[16] =
|
|
{ 0, 1, 1, -1, 1, -1 , -1, -1, 1, -1 , -1, -1, -1, -1 , -1, -1 };
|
|
static int const singleBitVertexMaskToIndex[16] =
|
|
{ 0, 0, 1, -1, 2, -1 , -1, -1, 3, -1 , -1, -1, -1, -1 , -1, -1 };
|
|
|
|
assert(_hasBoundaryEdge == false);
|
|
assert(singleBitVertexMaskToCount[vMask] != -1);
|
|
assert(singleBitVertexMaskToIndex[vMask] != -1);
|
|
|
|
_boundaryMask = vMask;
|
|
|
|
_boundaryCount = singleBitVertexMaskToCount[vMask];
|
|
_boundaryIndex = singleBitVertexMaskToIndex[vMask];
|
|
}
|
|
|
|
} // end namespace Far
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
} // end namespace OpenSubdiv
|