mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-13 20:40:09 +00:00
35d8519895
Re-organized the implementation of the patch table factory in advance of adding support for bicubic face-varying patches. - Renamed the AdaptiveContext internal class to BuilderContext since this can be used (eventually) to build patch tables for uniformly refined topology as well as adaptively refined topology. - Added utility methods to gather patch vertices from the levels (and eventually fvarLevels) of a refiner. - Replaced the FVarChannelCursor with a simpler array of integer face-varying channel indices. - Extracted the topology inspection needed to identify patches into a computePatchTag() method. - Simplified the two-pass traversal of the topology into a single pass traversal of the refined topology followed by an iteration over the patches that have been identified. - Replaced internal templated PatchTypes class with simpler and more direct accounting and patch array building facilities. - Changed the API of the end cap patch factories to no longer depend on the patch table factory's PatchFaceTag. This has been superseded by the specification of appropriate VSpans at the corners of the end patch.
246 lines
9.9 KiB
C++
246 lines
9.9 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/gregoryBasis.h"
|
|
#include "../far/endCapGregoryBasisPatchFactory.h"
|
|
#include "../far/error.h"
|
|
#include "../far/stencilTableFactory.h"
|
|
#include "../far/topologyRefiner.h"
|
|
|
|
#include <cassert>
|
|
#include <cmath>
|
|
#include <cstring>
|
|
|
|
namespace OpenSubdiv {
|
|
namespace OPENSUBDIV_VERSION {
|
|
|
|
namespace Far {
|
|
|
|
//
|
|
// EndCapGregoryBasisPatchFactory for Vertex StencilTable
|
|
//
|
|
EndCapGregoryBasisPatchFactory::EndCapGregoryBasisPatchFactory(
|
|
TopologyRefiner const & refiner,
|
|
StencilTable * vertexStencils,
|
|
StencilTable * varyingStencils,
|
|
bool shareBoundaryVertices) :
|
|
_vertexStencils(vertexStencils), _varyingStencils(varyingStencils),
|
|
_refiner(&refiner), _shareBoundaryVertices(shareBoundaryVertices),
|
|
_numGregoryBasisVertices(0), _numGregoryBasisPatches(0) {
|
|
|
|
// Sanity check: the mesh must be adaptively refined
|
|
assert(! refiner.IsUniform());
|
|
|
|
// Reserve the patch point stencils. Ideally topology refiner
|
|
// would have an API to return how many endcap patches will be required.
|
|
// Instead we conservatively estimate by the number of patches at the
|
|
// finest level.
|
|
int numMaxLevelFaces = refiner.GetLevel(refiner.GetMaxLevel()).GetNumFaces();
|
|
|
|
int numPatchPointsExpected = numMaxLevelFaces * 20;
|
|
// limits to 100M (=800M bytes) entries for the reserved size.
|
|
int numStencilsExpected = std::min(numPatchPointsExpected * 16,
|
|
100*1024*1024);
|
|
_vertexStencils->reserve(numPatchPointsExpected, numStencilsExpected);
|
|
// varying stencils use only 1 index with weight=1.0
|
|
_varyingStencils->reserve(numPatchPointsExpected, numPatchPointsExpected);
|
|
}
|
|
|
|
//
|
|
// Stateless EndCapGregoryBasisPatchFactory
|
|
//
|
|
GregoryBasis const *
|
|
EndCapGregoryBasisPatchFactory::Create(TopologyRefiner const & refiner,
|
|
Index faceIndex, int fvarChannel) {
|
|
|
|
// Gregory patches are end-caps: they only exist on max-level
|
|
Vtr::internal::Level const & level = refiner.getLevel(refiner.GetMaxLevel());
|
|
|
|
// Is this method used/supported? If so, needs corner spans (and vert offset?)...
|
|
GregoryBasis::ProtoBasis basis(level, faceIndex, 0, 0, fvarChannel);
|
|
GregoryBasis * result = new GregoryBasis;
|
|
basis.Copy(result);
|
|
|
|
// note: this function doesn't create varying stencils.
|
|
return result;
|
|
}
|
|
|
|
bool
|
|
EndCapGregoryBasisPatchFactory::addPatchBasis(Vtr::internal::Level const & level, Index faceIndex,
|
|
Vtr::internal::Level::VSpan const cornerSpans[],
|
|
bool verticesMask[4][5],
|
|
int levelVertOffset) {
|
|
|
|
// Gather the CVs that influence the Gregory patch and their relative
|
|
// weights in a basis
|
|
GregoryBasis::ProtoBasis basis(level, faceIndex, cornerSpans, levelVertOffset, -1);
|
|
|
|
for (int i = 0; i < 4; ++i) {
|
|
if (verticesMask[i][0]) {
|
|
GregoryBasis::AppendToStencilTable(basis.P[i], _vertexStencils);
|
|
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
|
}
|
|
if (verticesMask[i][1]) {
|
|
GregoryBasis::AppendToStencilTable(basis.Ep[i], _vertexStencils);
|
|
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
|
}
|
|
if (verticesMask[i][2]) {
|
|
GregoryBasis::AppendToStencilTable(basis.Em[i], _vertexStencils);
|
|
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
|
}
|
|
if (verticesMask[i][3]) {
|
|
GregoryBasis::AppendToStencilTable(basis.Fp[i], _vertexStencils);
|
|
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
|
}
|
|
if (verticesMask[i][4]) {
|
|
GregoryBasis::AppendToStencilTable(basis.Fm[i], _vertexStencils);
|
|
GregoryBasis::AppendToStencilTable(basis.varyingIndex[i], _varyingStencils);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Populates the topology table used by Gregory-basis patches
|
|
//
|
|
// Note : 'faceIndex' values are expected to be sorted in ascending order !!!
|
|
// Note 2: this code attempts to identify basis vertices shared along
|
|
// gregory patch edges
|
|
ConstIndexArray
|
|
EndCapGregoryBasisPatchFactory::GetPatchPoints(
|
|
Vtr::internal::Level const * level, Index faceIndex,
|
|
Vtr::internal::Level::VSpan const cornerSpans[],
|
|
int levelVertOffset) {
|
|
|
|
// allocate indices (awkward)
|
|
// assert(Vtr::INDEX_INVALID==0xFFFFFFFF);
|
|
for (int i = 0; i < 20; ++i) {
|
|
_patchPoints.push_back(Vtr::INDEX_INVALID);
|
|
}
|
|
Index * dest = &_patchPoints[_numGregoryBasisPatches * 20];
|
|
|
|
int gregoryVertexOffset = _refiner->GetNumVerticesTotal();
|
|
|
|
if (_shareBoundaryVertices) {
|
|
int levelIndex = level->getDepth();
|
|
|
|
// Simple struct with encoding of <level,face> index as an unsigned int and a
|
|
// comparison method for use with std::bsearch
|
|
struct LevelAndFaceIndex {
|
|
static inline unsigned int create(unsigned int levelIndex, Index faceIndex) {
|
|
return (levelIndex << 28) | (unsigned int) faceIndex;
|
|
}
|
|
static int compare(void const * a, void const * b) {
|
|
return *(unsigned int const*)a - *(unsigned int const*)b;
|
|
}
|
|
};
|
|
|
|
ConstIndexArray fedges = level->getFaceEdges(faceIndex);
|
|
assert(fedges.size()==4);
|
|
|
|
for (int i=0; i<4; ++i) {
|
|
Index edge = fedges[i];
|
|
Index adjFaceIndex = 0;
|
|
|
|
{ // Gather adjacent faces
|
|
ConstIndexArray adjfaces = level->getEdgeFaces(edge);
|
|
for (int j=0; j<adjfaces.size(); ++j) {
|
|
if (adjfaces[j]==faceIndex) {
|
|
// XXXX manuelk if 'edge' is non-manifold, arbitrarily pick the
|
|
// next face in the list of adjacent faces
|
|
adjFaceIndex = (adjfaces[(j+1)%adjfaces.size()]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// We are looking for adjacent faces that:
|
|
// - exist (no boundary)
|
|
// - have already been processed (known CV indices)
|
|
// - are also Gregory basis patches
|
|
if ((adjFaceIndex != Vtr::INDEX_INVALID) && (adjFaceIndex < faceIndex)) {
|
|
|
|
ConstIndexArray aedges = level->getFaceEdges(adjFaceIndex);
|
|
int aedge = aedges.FindIndexIn4Tuple(edge);
|
|
assert(aedge!=Vtr::INDEX_INVALID);
|
|
|
|
// Find index of basis in the list of basis already generated
|
|
unsigned int adjLevelAndFaceIndex = LevelAndFaceIndex::create(levelIndex, adjFaceIndex);
|
|
unsigned int * ptr = (unsigned int *)std::bsearch(&adjLevelAndFaceIndex,
|
|
&_levelAndFaceIndices[0],
|
|
_levelAndFaceIndices.size(),
|
|
sizeof(unsigned int),
|
|
LevelAndFaceIndex::compare);
|
|
if (ptr == 0) {
|
|
break;
|
|
}
|
|
|
|
int adjPatchIndex = (int)(ptr - &_levelAndFaceIndices[0]);
|
|
assert(adjPatchIndex>=0 && adjPatchIndex<(int)_levelAndFaceIndices.size());
|
|
|
|
// Copy the indices of CVs from the face on the other side of the shared edge
|
|
static int const gregoryEdgeVerts[4][4] = { { 0, 1, 7, 5},
|
|
{ 5, 6, 12, 10},
|
|
{10, 11, 17, 15},
|
|
{15, 16, 2, 0} };
|
|
Index * src = &_patchPoints[adjPatchIndex*20];
|
|
for (int j=0; j<4; ++j) {
|
|
// invert direction
|
|
// note that src indices have already been offsetted.
|
|
dest[gregoryEdgeVerts[i][3-j]] = src[gregoryEdgeVerts[aedge][j]];
|
|
}
|
|
}
|
|
}
|
|
_levelAndFaceIndices.push_back(LevelAndFaceIndex::create(levelIndex, faceIndex));
|
|
}
|
|
|
|
bool newVerticesMask[4][5];
|
|
for (int i = 0; i < 4; ++i) {
|
|
for (int j = 0; j < 5; ++j) {
|
|
if (dest[i*5+j]==Vtr::INDEX_INVALID) {
|
|
// assign new vertex
|
|
dest[i*5+j] =
|
|
_numGregoryBasisVertices + gregoryVertexOffset;
|
|
++_numGregoryBasisVertices;
|
|
newVerticesMask[i][j] = true;
|
|
} else {
|
|
// share vertex
|
|
newVerticesMask[i][j] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// add basis
|
|
addPatchBasis(*level, faceIndex, cornerSpans, newVerticesMask, levelVertOffset);
|
|
|
|
++_numGregoryBasisPatches;
|
|
|
|
// return cvs;
|
|
return ConstIndexArray(dest, 20);
|
|
}
|
|
|
|
} // end namespace Far
|
|
|
|
} // end namespace OPENSUBDIV_VERSION
|
|
} // end namespace OpenSubdiv
|