WIP check-point for Gregory basis factorization

- adding support for StencilTables creation from a Gregory basis
- fix a bug in the prot-stencil allocator (slow memory pool was not being cleared properly)
This commit is contained in:
manuelk 2014-11-04 11:14:21 -08:00
parent c2fa2616d2
commit c237ab00fc
6 changed files with 398 additions and 209 deletions

View File

@ -156,7 +156,7 @@ GLMesh::initializeVertexComponentBuffer(float const * vertData, int nverts) {
//------------------------------------------------------------------------------
void
GLMesh::Initialize(Options options,
GLMesh::Initialize(Options /* options */,
int nverts, int nfaces, int * vertsperface, int * faceverts,
float const * vertexData) {

View File

@ -115,6 +115,7 @@ int g_displayPatchColor = 1,
g_VtrDrawPtexIDs = false,
g_VtrDrawEdgeSharpness = false,
g_numPatches = 0,
g_maxValence = 0,
g_currentPatch = 0,
g_Adaptive = true;
@ -546,68 +547,68 @@ static GLMesh gregoryWire;
static void
createGregoryBasis(OpenSubdiv::Far::TopologyRefiner const & refiner,
std::vector<Vertex> const & vertexBuffer) {
OpenSubdiv::Far::StencilTables const & stencils, int maxvalence,
std::vector<Vertex> const & vertexBuffer) {
int level = refiner.GetMaxLevel(),
nfaces = refiner.GetNumFaces(level);
int vertOffset = 0;
for (int i=0; i<level; ++i) {
vertOffset += refiner.GetNumVertices(i);
std::vector<OpenSubdiv::Far::Index> gpatches(nfaces);
for (int face=0; face<nfaces; ++face) {
if (not refiner.FaceIsRegular(level, face)) {
gpatches.push_back(face);
}
}
std::vector<int> vertsperedge, edgeindices;
std::vector<Vertex> edgeverts;
int npatches = (int)gpatches.size();
for (int face=0; face<nfaces; ++face) {
bool regular = refiner.FaceIsRegular(level, face);
if (not regular) {
OpenSubdiv::Far::GregoryBasis const * gbasis =
OpenSubdiv::Far::GregoryBasisFactory::Create(refiner, face);
OpenSubdiv::Far::GregoryBasisFactory factory(
refiner, stencils, npatches, maxvalence);
for (int i=0; i<npatches; ++i) {
factory.AddPatchBasis(gpatches[i]);
}
OpenSubdiv::Far::StencilTables const * gstencils =
factory.CreateStencilTables();
{ // initialize wireframe
int nedges = npatches * 20;
static int basisedges[40] = { 0, 1, 0, 2, 1, 3, 2, 4,
5, 6, 5, 7, 6, 8, 7, 9,
10, 11, 10, 12, 11, 13, 12, 14,
15, 16, 15, 17, 16, 18, 17, 19,
1, 7, 6, 12, 11, 17, 16, 2 };
std::vector<int> vertsperedge(nedges), edgeindices(nedges*2);
std::vector<Vertex> edgeverts(npatches*20);
gstencils->UpdateValues(&vertexBuffer[0], &edgeverts[0]);
int nedges = (int)vertsperedge.size();
vertsperedge.resize(nedges+20);
edgeindices.resize(nedges*2+40);
for (int patch=0; patch<npatches; ++patch) {
int * vpe = &vertsperedge[nedges],
* ev = &edgeindices[nedges*2];
for (int i=0; i<20; ++i) {
vpe[i] = 2;
ev[i*2] = basisedges[i*2] + nedges;
ev[i*2+1] = basisedges[i*2+1] + nedges;
}
}
static int basisedges[40] = { 0, 1, 0, 2, 1, 3, 2, 4,
5, 6, 5, 7, 6, 8, 7, 9,
10, 11, 10, 12, 11, 13, 12, 14,
15, 16, 15, 17, 16, 18, 17, 19,
1, 7, 6, 12, 11, 17, 16, 2 };
std::vector<Vertex> gverts(20);
gbasis->Evaluate(&vertexBuffer[vertOffset], &gverts[0]);
int offset = patch * 20,
* vpe = &vertsperedge[offset],
* indices = &edgeindices[patch * 40];
for (int i=0; i<20; ++i) {
vpe[i] = 2;
indices[i*2] = basisedges[i*2] + offset;
indices[i*2+1] =basisedges[i*2+1] + offset;
}
static char buf[16];
for (int i=0; i<4; ++i) {
int vid = i * 5;
snprintf(buf, 16, " P%d", i);
g_font->Print3D(gverts[vid].GetPos(), buf, 3);
snprintf(buf, 16, " Ep%d", i);
g_font->Print3D(gverts[vid+1].GetPos(), buf, 3);
snprintf(buf, 16, " Em%d", i);
g_font->Print3D(gverts[vid+2].GetPos(), buf, 3);
snprintf(buf, 16, " Fp%d", i);
g_font->Print3D(gverts[vid+3].GetPos(), buf, 3);
snprintf(buf, 16, " Fm%d", i);
g_font->Print3D(gverts[vid+4].GetPos(), buf, 3);
}
delete gbasis;
edgeverts.insert(edgeverts.end(), gverts.begin(), gverts.end());
Vertex const * verts = &edgeverts[offset];
static char buf[16];
for (int i=0; i<4; ++i) {
int vid = i * 5;
snprintf(buf, 16, " P%d", i);
g_font->Print3D(verts[vid].GetPos(), buf, 3);
snprintf(buf, 16, " Ep%d", i);
g_font->Print3D(verts[vid+1].GetPos(), buf, 3);
snprintf(buf, 16, " Em%d", i);
g_font->Print3D(verts[vid+2].GetPos(), buf, 3);
snprintf(buf, 16, " Fp%d", i);
g_font->Print3D(verts[vid+3].GetPos(), buf, 3);
snprintf(buf, 16, " Fm%d", i);
g_font->Print3D(verts[vid+4].GetPos(), buf, 3);
}
}
@ -697,6 +698,7 @@ createVtrMesh(Shape * shape, int maxlevel) {
patchTables = OpenSubdiv::Far::PatchTablesFactory::Create(*refiner);
g_numPatches = patchTables->GetNumPatchesTotal();
g_maxValence = patchTables->GetMaxValence();
} else {
refiner->RefineUniform(maxlevel, /*fullTopology*/true);
}
@ -726,12 +728,13 @@ createVtrMesh(Shape * shape, int maxlevel) {
//printf(" %f ms (total)\n", float(s.GetTotalElapsed())*1000.0f);
}
#else
OpenSubdiv::Far::StencilTables const * stencilTables = 0;
{
OpenSubdiv::Far::StencilTablesFactory::Options options;
options.generateOffsets=true;
options.generateIntermediateLevels=true;
OpenSubdiv::Far::StencilTables const * stencilTables =
stencilTables =
OpenSubdiv::Far::StencilTablesFactory::Create(*refiner, options);
stencilTables->UpdateValues(verts, verts + ncoarseverts);
@ -755,7 +758,7 @@ createVtrMesh(Shape * shape, int maxlevel) {
}
if (g_Adaptive and g_drawGregoryBasis) {
createGregoryBasis(*refiner, vertexBuffer);
createGregoryBasis(*refiner, *stencilTables, g_maxValence, vertexBuffer);
}
createEdgeNumbers(*refiner, vertexBuffer, g_VtrDrawEdgeIDs!=0, g_VtrDrawEdgeSharpness!=0);
@ -780,6 +783,7 @@ createVtrMesh(Shape * shape, int maxlevel) {
delete refiner;
delete patchTables;
delete stencilTables;
}
//------------------------------------------------------------------------------

View File

@ -32,6 +32,26 @@
namespace OpenSubdiv {
namespace OPENSUBDIV_VERSION {
// Builds a table of local indices pairs for each vertex of the patch.
//
// o
// N0 |
// | ....
// | .... : Gregory patch
// o ------ o ------ o ....
// N1 V | .... M3
// | .......
// | .......
// o .......
// N2
//
// [...] [N2 - N3] [...]
//
// Each value pair is composed of 2 index values in range [0-4[ pointing
// to the 2 neighbor vertices of the vertex 'V' belonging to the Gregory patch.
// Neighbor ordering is valence CCW and must match the winding of the 1-ring
// vertices.
//
static void
getQuadOffsets(Vtr::Level const& level, Vtr::Index fIndex, Vtr::Index offsets[]) {
@ -61,10 +81,12 @@ getQuadOffsets(Vtr::Level const& level, Vtr::Index fIndex, Vtr::Index offsets[])
}
}
#define GetNumMaxElems( maxvalence ) \
16 + maxvalence - 3
// limit valence of 30 because we use a pre-computed closed-form 'ef' table
static const int MAX_VALENCE=30,
MAX_ELEMS = 16 + MAX_VALENCE - 3;
MAX_ELEMS = GetNumMaxElems(MAX_VALENCE);
namespace Far {
@ -72,7 +94,7 @@ namespace Far {
// Basis point
//
// Implements arithmetic operators to manipulate the influence of the
// control vertices supporting the patch basis
// 1-ring control vertices supporting the patch basis
//
class Point {
@ -93,11 +115,11 @@ public:
int GetSize() const {
return _size;
}
Index const * GetIndices() const {
return _indices;
}
float const * GetWeights() const {
return _weights;
}
@ -152,6 +174,12 @@ public:
Point p(*this); return p-=other;
}
void OffsetIndices(Index offset) {
for (int i=0; i<_size; ++i) {
_indices[i] += offset;
}
}
void Copy(int ** size, Index ** indices, float ** weights) const;
private:
@ -196,12 +224,17 @@ Point::Copy(int ** size, Index ** indices, float ** weights) const {
//
// ProtoBasis
//
// Given a Vtr::Level and a face index, gathers all the influences of the 1-ring
// that supports the 20 CVs of a Gregory patch basis.
//
struct ProtoBasis {
ProtoBasis(Vtr::Level const & level, Index faceIndex);
int GetNumElements() const;
void OffsetIndices(Index offset);
void Copy(int * sizes, Index * indices, float * weights) const;
// Control Vertices based on :
@ -233,7 +266,6 @@ struct ProtoBasis {
int
ProtoBasis::GetNumElements() const {
int nelems=0;
for (int vid=0; vid<4; ++vid) {
nelems += P[vid].GetSize();
@ -244,10 +276,18 @@ ProtoBasis::GetNumElements() const {
}
return nelems;
}
void
ProtoBasis::OffsetIndices(Index offset) {
for (int vid=0; vid<4; ++vid) {
P[vid].OffsetIndices(offset);
Ep[vid].OffsetIndices(offset);
Em[vid].OffsetIndices(offset);
Fp[vid].OffsetIndices(offset);
Fm[vid].OffsetIndices(offset);
}
}
void
ProtoBasis::Copy(int * sizes, Index * indices, float * weights) const {
for (int vid=0; vid<4; ++vid) {
P[vid].Copy(&sizes, &indices, &weights);
Ep[vid].Copy(&sizes, &indices, &weights);
@ -256,7 +296,6 @@ ProtoBasis::Copy(int * sizes, Index * indices, float * weights) const {
Fm[vid].Copy(&sizes, &indices, &weights);
}
}
inline float csf(Index n, Index j) {
if (j%2 == 0) {
return cosf((2.0f * float(M_PI) * float(float(j-0)/2.0f))/(float(n)+3.0f));
@ -264,7 +303,6 @@ inline float csf(Index n, Index j) {
return sinf((2.0f * float(M_PI) * float(float(j-1)/2.0f))/(float(n)+3.0f));
}
}
ProtoBasis::ProtoBasis(Vtr::Level const & level, Index faceIndex) {
static float ef[MAX_VALENCE-3] = {
@ -502,18 +540,21 @@ ProtoBasis::ProtoBasis(Vtr::Level const & level, Index faceIndex) {
}
}
int GregoryBasisFactory::GetMaxValence() {
return MAX_VALENCE;
}
//
// GregoryBasisFactory
// Stateless GregoryBasisFactory
//
GregoryBasis const *
GregoryBasisFactory::Create(TopologyRefiner const & refiner, Index faceIndex) {
// Gregory patches only exist on the hight
// Gregory patches are end-caps: they only exist on max-level
Vtr::Level const & level = refiner.getLevel(refiner.GetMaxLevel());
if (level.getMaxValence()>MAX_VALENCE) {
if (level.getMaxValence()>GetMaxValence()) {
// The proto-basis closed-form table limits valence to 'MAX_VALENCE'
return 0;
}
@ -536,48 +577,97 @@ GregoryBasisFactory::Create(TopologyRefiner const & refiner, Index faceIndex) {
return result;
}
GregoryBasisFactory::GregoryBasisFactory(StencilTables const & stencils,
int numpatches, int maxvalence) :
_currentStencil(0), _stencils(stencils), _alloc(16 + maxvalence - 3) {
//
// GregoryBasisFactory for StencilTables
//
GregoryBasisFactory::GregoryBasisFactory(TopologyRefiner const & refiner,
StencilTables const & stencils, int numpatches, int maxvalence) :
_currentStencil(0), _refiner(refiner),
_stencils(stencils), _alloc(GetNumMaxElems(maxvalence)) {
_alloc.Resize(numpatches * 20);
}
inline void
factorizeBasisVertex(StencilTables const & stencils, Point const & p, ProtoStencil dst) {
// Use the Allocator to factorize the Gregory patch influence CVs with the
// supporting CVs from the stencil tables.
dst.Clear();
for (int j=0; j<p.GetSize(); ++j) {
dst.AddWithWeight(stencils,
p.GetIndices()[j], p.GetWeights()[j]);
}
}
bool
GregoryBasisFactory::AddBasis(TopologyRefiner const & refiner, Index faceIndex) {
GregoryBasisFactory::AddPatchBasis(Index faceIndex) {
// Gregory patches only exist on the hight
Vtr::Level const & level = refiner.getLevel(refiner.GetMaxLevel());
Vtr::Level const & level = _refiner.getLevel(_refiner.GetMaxLevel());
assert( (not refiner.IsUniform()) and
// Sanity checks: the mesh must be adaptively refined and the stencil tables
// must have the correct size to factorize all the CVs at
// intermediate subdivision levels back to the coarse mesh.
assert( (not _refiner.IsUniform()) and
(_stencils.GetNumStencils() ==
refiner.GetNumVerticesTotal()-refiner.GetNumVertices(0)) );
_refiner.GetNumVerticesTotal()-_refiner.GetNumVertices(0)) );
if (level.getMaxValence()>MAX_VALENCE) {
if (level.getMaxValence()>GetMaxValence()) {
// The proto-basis closed-form table limits valence to 'MAX_VALENCE'
return false;
}
int voffset = 0;
for (int i=0; i<refiner.GetMaxLevel(); ++i) {
voffset += refiner.GetNumVertices(i);
}
// Gather the CVs that influence the Gregory patch and their relative
// weights in a basis
ProtoBasis basis(level, faceIndex);
for (int i=0; i<4; ++i) {
int idx = _currentStencil + i*5;
// The basis vertex indices are currently local to maxlevel: need to offset
// to match layout of adaptive StencilTables
int voffset = 0;
for (int i=1; i<_refiner.GetMaxLevel(); ++i) {
voffset += _refiner.GetNumVertices(i);
}
basis.OffsetIndices(voffset);
ProtoStencil stencilP = _alloc[idx];
stencilP.Clear();
for (int j=0; j<basis.P[i].GetSize(); ++j) {
}
// Factorize the basis CVs with the stencil tables: the basis is now
// expressed as a linear combination of vertices from the coarse control
// mesh with no data dependencies
for (int i=0; i<4; ++i) {
int offset = _currentStencil + i * 5;
factorizeBasisVertex(_stencils, basis.P[i], _alloc[offset]);
factorizeBasisVertex(_stencils, basis.Ep[i], _alloc[offset+1]);
factorizeBasisVertex(_stencils, basis.Em[i], _alloc[offset+2]);
factorizeBasisVertex(_stencils, basis.Fp[i], _alloc[offset+3]);
factorizeBasisVertex(_stencils, basis.Fm[i], _alloc[offset+4]);
}
_currentStencil += 20;
return true;
}
StencilTables const *
GregoryBasisFactory::CreateStencilTables( ) {
// Finalize the stencil tables from the temporary pool allocator
StencilTables * result = new StencilTables;
int nstencils = (int)_alloc.GetNumStencils(),
nelems = _alloc.GetNumVerticesTotal();
result->_numControlVertices = _refiner.GetNumVertices(0);
result->resize(nstencils, nelems);
Stencil dst(&result->_sizes.at(0),
&result->_indices.at(0), &result->_weights.at(0));
for (int i=0; i<_alloc.GetNumStencils(); ++i) {
*dst._size = _alloc.CopyStencil(i, dst._indices, dst._weights);
dst.Next();
}
return true;
result->generateOffsets();
return result;
}
} // end namespace Far
} // end namespace OPENSUBDIV_VERSION

View File

@ -40,8 +40,18 @@ class GregoryBasis {
public:
template <class T>
void Evaluate(T const * controlValues, T values[20]) 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 controlValues Buffer with primvar data for the control vertices
///
/// @param values Destination buffer for the interpolated primvar
/// data
///
template <class T, class U>
void Evaluate(T const & controlValues, U values[20]) const {
Index const * indices = &_indices.at(0);
float const * weights = &_weights.at(0);
@ -71,19 +81,50 @@ class GregoryBasisFactory {
public:
//
// Single patch GregoryBasis basis factory
//
/// \brief Instantiates a GregoryBasis from a TopologyRefiner that has been
/// refined adaptively for a given face.
///
/// @param refiner The TopologyRefiner containing the topology
///
/// @param faceIndex The index of the face (level is assumed to be MaxLevel)
///
static GregoryBasis const * Create(TopologyRefiner const & refiner, Index faceIndex);
/// \brief Returns the maximum valence of a vertex in the mesh that the
/// Gregory patches can handle
static int GetMaxValence();
public:
//
// Multi-patch Gregory stencils factory
//
// This factory accumulates Gregory patch basis into StencilTables
//
// Note: the TopologyRefiner and StencilTables references are held for the
// lifespan of the factory - neither can be deleted or modified while
// this factory is active.
//
GregoryBasisFactory(TopologyRefiner const & refiner,
StencilTables const & stencils, int numpatches, int maxvalence);
// Creates a basis for the face and adds it to the stencil pool allocator
bool AddPatchBasis(Index faceIndex);
// After all the patches have been collected, create the final table
StencilTables const * CreateStencilTables();
private:
GregoryBasisFactory(StencilTables const & stencils, int numpatches, int maxvalence);
bool AddBasis(TopologyRefiner const & refiner, Index faceIndex);
friend class Point;
friend struct ProtoBasis;
int _currentStencil;
TopologyRefiner const & _refiner; // XXXX these should be smart pointers !
StencilTables const & _stencils;
StencilAllocator _alloc;
};

View File

@ -40,17 +40,19 @@ namespace Far {
// Proto-stencil Pool Allocator classes
//
// Strategy: allocate up-front a data pool for supporting PROTOSTENCILS of a size
// slightly above average. For the (rare) BIG_PROTOSTENCILS that require more support
// vertices, switch to (slow) heap allocation.
// (maxsize) slightly above average. For the (rare) BIG_PROTOSTENCILS that
// require more support vertices, switch to (slow) heap allocation.
//
template <typename PROTOSTENCIL, class BIG_PROTOSTENCIL>
class Allocator {
public:
// Constructor
Allocator(int maxSize, bool interpolateVarying=false) :
_maxsize(maxSize), _interpolateVarying(interpolateVarying) { }
// Returns the number of stencils in the allocator
int GetNumStencils() const {
return (int)_sizes.size();
}
@ -64,6 +66,8 @@ public:
return nverts;
}
// Returns true if the pool allocator executes AddVaryingWithWeight
// factorization
bool GetInterpolateVarying() const {
return _interpolateVarying;
}
@ -81,10 +85,10 @@ public:
// Adds the contribution of a supporting vertex that was not yet
// in the stencil
void PushBackVertex(Index stencil, Index vert, float weight) {
void PushBackVertex(Index protoStencil, Index vert, float weight) {
assert(weight!=0.0f);
unsigned char & size = _sizes[stencil];
Index idx = stencil*_maxsize;
unsigned char & size = _sizes[protoStencil];
Index idx = protoStencil*_maxsize;
if (size < (_maxsize-1)) {
idx += size;
_indices[idx] = vert;
@ -93,11 +97,11 @@ public:
BIG_PROTOSTENCIL * dst = 0;
if (size==(_maxsize-1)) {
dst = new BIG_PROTOSTENCIL(size, &_indices[idx], &_weights[idx]);
assert(_bigStencils.find(stencil)==_bigStencils.end());
_bigStencils[stencil] = dst;
assert(_bigStencils.find(protoStencil)==_bigStencils.end());
_bigStencils[protoStencil] = dst;
} else {
assert(_bigStencils.find(stencil)!=_bigStencils.end());
dst = _bigStencils[stencil];
assert(_bigStencils.find(protoStencil)!=_bigStencils.end());
dst = _bigStencils[protoStencil];
}
dst->_indices.push_back(vert);
dst->_weights.push_back(weight);
@ -105,14 +109,11 @@ public:
++size;
}
unsigned char GetSize(Index stencil) const {
assert(stencil<(int)_sizes.size());
return _sizes[stencil];
}
Index FindVertex(Index stencil, Index vert) {
int size = _sizes[stencil];
Index const * indices = GetIndices(stencil);
// Returns the local index in 'stencil' of a given vertex index, or
// INDEX_INVALID if the stencil does not contain this vertex
int FindVertex(Index protoStencil, Index vert) {
int size = _sizes[protoStencil];
Index const * indices = GetIndices(protoStencil);
for (int i=0; i<size; ++i) {
if (indices[i]==vert) {
return i;
@ -121,82 +122,102 @@ public:
return Vtr::INDEX_INVALID;
}
bool IsBigStencil(Index stencil) const {
assert(stencil<(int)_sizes.size());
return _sizes[stencil]>=_maxsize;
// Returns true of the stencil does not fit in the pool allocator and
// has been moved to the 'big' (slow) allocation pool
bool IsBigStencil(Index protoStencil) const {
assert(protoStencil<(int)_sizes.size());
return _sizes[protoStencil]>=_maxsize;
}
Index * GetIndices(Index stencil) {
if (not IsBigStencil(stencil)) {
return &_indices[stencil*_maxsize];
// Returns the size of a given proto-stencil
unsigned char GetSize(Index protoStencil) const {
assert(protoStencil<(int)_sizes.size());
return _sizes[protoStencil];
}
// Resolve memory pool and return a pointer to the indices of a given
// proto-stencil
Index * GetIndices(Index protoStencil) {
if (not IsBigStencil(protoStencil)) {
return &_indices[protoStencil*_maxsize];
} else {
assert(_bigStencils.find(stencil)!=_bigStencils.end());
return &_bigStencils[stencil]->_indices[0];
assert(_bigStencils.find(protoStencil)!=_bigStencils.end());
return &_bigStencils[protoStencil]->_indices[0];
}
}
float * GetWeights(Index stencil) {
if (not IsBigStencil(stencil)) {
return &_weights[stencil*_maxsize];
// Resolve memory pool and return a pointer to the weights of a given
// proto-stencil
float * GetWeights(Index protoStencil) {
if (not IsBigStencil(protoStencil)) {
return &_weights[protoStencil*_maxsize];
} else {
assert(_bigStencils.find(stencil)!=_bigStencils.end());
return &_bigStencils[stencil]->_weights[0];
assert(_bigStencils.find(protoStencil)!=_bigStencils.end());
return &_bigStencils[protoStencil]->_weights[0];
}
}
PROTOSTENCIL operator[] (Index i) {
// Returns the proto-stencil at a given index
PROTOSTENCIL operator[] (Index protoStencil) {
// If the allocator is empty, AddWithWeight() expects a coarse control
// vertex instead of a stencil and we only need to pass the index
return PROTOSTENCIL(i, this->GetNumStencils()>0 ? this : 0);
return PROTOSTENCIL(protoStencil, this->GetNumStencils()>0 ? this : 0);
}
PROTOSTENCIL operator[] (Index i) const {
// Returns the proto-stencil at a given index
PROTOSTENCIL operator[] (Index protoStencil) const {
// If the allocator is empty, AddWithWeight() expects a coarse control
// vertex instead of a stencil and we only need to pass the index
return PROTOSTENCIL(i, this->GetNumStencils()>0 ?
return PROTOSTENCIL(protoStencil, this->GetNumStencils()>0 ?
const_cast<Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL> *>(this) : 0);
}
void ClearStencil(Index stencil) {
memset(GetWeights(stencil), 0, _sizes[stencil]*sizeof(float));
}
unsigned char CopyStencil(Index i, Index * indices, float * weights) {
unsigned char size = GetSize(i);
memcpy(indices, this->GetIndices(i), size*sizeof(Index));
memcpy(weights, this->GetWeights(i), size*sizeof(float));
// Copy the proto-stencil out of the pool
unsigned char CopyStencil(Index protoStencil,
Index * indices, float * weights) {
unsigned char size = GetSize(protoStencil);
memcpy(indices, this->GetIndices(protoStencil), size*sizeof(Index));
memcpy(weights, this->GetWeights(protoStencil), size*sizeof(float));
return size;
}
protected:
// delete 'slow' memory pool
void clearBigStencils() {
typename BigStencilMap::iterator it;
for (it=_bigStencils.begin(); it!=_bigStencils.end(); ++it) {
delete it->second;
}
_bigStencils.clear();
}
protected:
int _maxsize;
int _maxsize; // max size of stencil that fits in the 'fast' pool
bool _interpolateVarying;
bool _interpolateVarying; // true for varying interpolation
std::vector<unsigned char> _sizes;
std::vector<unsigned char> _sizes; // 'fast' memory pool
std::vector<int> _indices;
std::vector<float> _weights;
typedef std::map<int, BIG_PROTOSTENCIL *> BigStencilMap;
BigStencilMap _bigStencils;
BigStencilMap _bigStencils; // 'slow' memory pool
};
//
// Specialization of the Allocator for stencils with tangents that require
// additional derivative weights.
//
template <typename PROTOSTENCIL, class BIG_PROTOSTENCIL>
class LimitAllocator : public Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL> {
public:
LimitAllocator(int maxSize) : Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>(maxSize) { }
// Constructor
LimitAllocator(int maxSize) :
Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>(maxSize) { }
void Resize(int size) {
Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>::Resize(size);
@ -205,11 +226,11 @@ public:
_tan2Weights.resize(nelems);
}
void PushBackVertex(Index stencil,
void PushBackVertex(Index protoStencil,
Index vert, float weight, float tan1Weight, float tan2Weight) {
assert(weight!=0.0f);
unsigned char & size = this->_sizes[stencil];
Index idx = stencil*this->_maxsize;
unsigned char & size = this->_sizes[protoStencil];
Index idx = protoStencil*this->_maxsize;
if (size < (this->_maxsize-1)) {
idx += size;
this->_indices[idx] = vert;
@ -219,13 +240,14 @@ public:
} else {
BIG_PROTOSTENCIL * dst = 0;
if (size==(this->_maxsize-1)) {
dst = new BIG_PROTOSTENCIL(size, &this->_indices[idx],
&this->_weights[idx], &this->_tan1Weights[idx], &this->_tan2Weights[idx]);
assert(this->_bigStencils.find(stencil)==this->_bigStencils.end());
this->_bigStencils[stencil] = dst;
dst = new BIG_PROTOSTENCIL(size,
&this->_indices[idx], &this->_weights[idx],
&this->_tan1Weights[idx], &this->_tan2Weights[idx]);
assert(this->_bigStencils.find(protoStencil)==this->_bigStencils.end());
this->_bigStencils[protoStencil] = dst;
} else {
assert(this->_bigStencils.find(stencil)!=this->_bigStencils.end());
dst = this->_bigStencils[stencil];
assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end());
dst = this->_bigStencils[protoStencil];
}
dst->_indices.push_back(vert);
dst->_weights.push_back(weight);
@ -235,41 +257,42 @@ public:
++size;
}
float * GetTan1Weights(Index stencil) {
if (not this->IsBigStencil(stencil)) {
return &_tan1Weights[stencil*this->_maxsize];
float * GetTan1Weights(Index protoStencil) {
if (not this->IsBigStencil(protoStencil)) {
return &_tan1Weights[protoStencil*this->_maxsize];
} else {
assert(this->_bigStencils.find(stencil)!=this->_bigStencils.end());
return &this->_bigStencils[stencil]->_tan1Weights[0];
assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end());
return &this->_bigStencils[protoStencil]->_tan1Weights[0];
}
}
float * GetTan2Weights(Index stencil) {
if (not this->IsBigStencil(stencil)) {
return &_tan2Weights[stencil*this->_maxsize];
float * GetTan2Weights(Index protoStencil) {
if (not this->IsBigStencil(protoStencil)) {
return &_tan2Weights[protoStencil*this->_maxsize];
} else {
assert(this->_bigStencils.find(stencil)!=this->_bigStencils.end());
return &this->_bigStencils[stencil]->_tan2Weights[0];
assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end());
return &this->_bigStencils[protoStencil]->_tan2Weights[0];
}
}
PROTOSTENCIL operator[] (Index i) {
PROTOSTENCIL operator[] (Index protoStencil) {
assert(this->GetNumStencils()>0);
return PROTOSTENCIL(i, this);
return PROTOSTENCIL(protoStencil, this);
}
void ClearStencil(Index stencil) {
Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>::ClearStencil(stencil);
memset(GetTan1Weights(stencil), 0, this->_sizes[stencil]*sizeof(float));
memset(GetTan2Weights(stencil), 0, this->_sizes[stencil]*sizeof(float));
void ClearStencil(Index protoStencil) {
Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>::ClearStencil(protoStencil);
memset(GetTan1Weights(protoStencil), 0, this->_sizes[protoStencil]*sizeof(float));
memset(GetTan2Weights(protoStencil), 0, this->_sizes[protoStencil]*sizeof(float));
}
unsigned char CopyLimitStencil(Index i, Index * indices,
float * weights, float * tan1Weights, float * tan2Weights) {
unsigned char CopyLimitStencil(Index protoStencil,
Index * indices, float * weights, float * tan1Weights, float * tan2Weights) {
unsigned char size =
Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>::CopyStencil(i, indices, weights);
memcpy(tan1Weights, this->GetTan1Weights(i), size*sizeof(Index));
memcpy(tan2Weights, this->GetTan2Weights(i), size*sizeof(float));
Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>::CopyStencil(
protoStencil, indices, weights);
memcpy(tan1Weights, this->GetTan1Weights(protoStencil), size*sizeof(Index));
memcpy(tan2Weights, this->GetTan2Weights(protoStencil), size*sizeof(float));
return size;
}
@ -281,8 +304,8 @@ private:
//
// 'Big' Proto stencil classes
//
// When proto-stencils exceed _maxsize, fall back to dynamically
// allocated "BigStencils"
// When proto-stencils exceed _maxsize, fall back to dynamically allocated
// "BigStencils" (with 'Limit' specialization to handle tangents)
//
struct BigStencil {
@ -299,9 +322,11 @@ struct BigStencil {
};
struct BigLimitStencil : public BigStencil {
BigLimitStencil(unsigned char size, Index const * indices,
float const * weights, float const * tan1Weights, float const * tan2Weights) :
BigStencil(size, indices, weights) {
BigLimitStencil(unsigned char size,
Index const * indices, float const * weights,
float const * tan1Weights, float const * tan2Weights) :
BigStencil(size, indices, weights) {
_tan1Weights.reserve(size+5); _tan1Weights.resize(size);
memcpy(&_tan1Weights.at(0), tan1Weights, size*sizeof(float));
_tan2Weights.reserve(size+5); _tan2Weights.resize(size);
@ -327,9 +352,11 @@ public:
_id(id), _alloc(alloc) { }
void Clear() {
_alloc->ClearStencil(_id);
// Clear() can only ever be called on an empty stencil: nothing to do
assert(_alloc->GetSize(_id)==0);
}
// Factorize from a proto-stencil allocator
void AddWithWeight(ProtoStencil const & src, float weight) {
if(weight==0.0f) {
@ -342,24 +369,7 @@ public:
Index const * srcIndices = src._alloc->GetIndices(src._id);
float const * srcWeights = src._alloc->GetWeights(src._id);
for (unsigned char i=0; i<srcSize; ++i) {
assert(srcWeights[i]!=0.0f);
float w = weight * srcWeights[i];
if (w==0.0f) {
continue;
}
Index vertIndex = srcIndices[i],
n = _alloc->FindVertex(_id, vertIndex);
if (Vtr::IndexIsValid(n)) {
_alloc->GetWeights(_id)[n] += w;
assert(_alloc->GetWeights(_id)[n]!=0.0f);
} else {
_alloc->PushBackVertex(_id, vertIndex, w);
}
}
addWithWeight(weight, srcSize, srcIndices, srcWeights);
} else {
// Coarse vertex contribution
Index n = _alloc->FindVertex(_id, src._id);
@ -372,16 +382,56 @@ public:
}
}
// Factorize from a finished stencil table
void AddWithWeight(StencilTables const & table, Index idx, float weight) {
assert(idx<table.GetNumStencils());
if(weight==0.0f) {
return;
}
unsigned char srcSize = table.GetSizes()[idx];
Index offset = table.GetOffsets()[idx];
Index const * srcIndices = &table.GetControlIndices()[offset];
float const * srcWeights = &table.GetWeights()[offset];
addWithWeight(weight, srcSize, srcIndices, srcWeights);
}
void AddVaryingWithWeight(ProtoStencil const & src, float weight) {
if (_alloc->GetInterpolateVarying()) {
AddWithWeight(src, weight);
}
}
private:
protected:
friend class ProtoLimitStencil;
void addWithWeight(float weight, unsigned char srcSize,
Index const * srcIndices, float const * srcWeights) {
for (unsigned char i=0; i<srcSize; ++i) {
assert(srcWeights[i]!=0.0f);
float w = weight * srcWeights[i];
if (w==0.0f) {
continue;
}
Index vertIndex = srcIndices[i],
n = _alloc->FindVertex(_id, vertIndex);
if (Vtr::IndexIsValid(n)) {
_alloc->GetWeights(_id)[n] += w;
assert(_alloc->GetWeights(_id)[n]!=0.0f);
} else {
_alloc->PushBackVertex(_id, vertIndex, w);
}
}
}
Index _id;
Allocator<ProtoStencil, BigStencil> * _alloc;
};
@ -395,12 +445,14 @@ typedef Allocator<ProtoStencil, BigStencil> StencilAllocator;
class ProtoLimitStencil {
public:
ProtoLimitStencil(Index id,
LimitAllocator<ProtoLimitStencil, BigLimitStencil> * alloc) :
_id(id), _alloc(alloc) { }
void Clear() {
_alloc->ClearStencil(_id);
// Clear() can only ever be called on an empty stencil: nothing to do
assert(_alloc->GetSize(_id)==0);
}
void AddWithWeight(Stencil const & src,

View File

@ -100,6 +100,7 @@ public:
}
protected:
friend class GregoryBasisFactory;
friend class StencilTablesFactory;
friend class LimitStencilTablesFactory;
@ -135,7 +136,7 @@ public:
}
/// \brief Returns a Stencil at index i in the tables
Stencil GetStencil(int i) const;
Stencil GetStencil(Index i) const;
/// \brief Returns the number of control vertices of each stencil in the table
std::vector<unsigned char> const & GetSizes() const {
@ -158,12 +159,12 @@ public:
}
/// \brief Returns the stencil at index i in the tables
Stencil operator[] (int index) const;
Stencil operator[] (Index index) const;
/// \brief Updates point values based on the control values
///
/// \note The destination buffers ('uderivs' & 'vderivs') are assumed to
/// have allocated at least \c GetNumStencils() elements.
/// \note The destination buffers are assumed to have allocated at least
/// \c GetNumStencils() elements.
///
/// @param controlValues Buffer with primvar data for the control vertices
///
@ -175,7 +176,7 @@ public:
/// @param end Index of last value to update
///
template <class T>
void UpdateValues(T const *controlValues, T *values, int start=-1, int end=-1) const {
void UpdateValues(T const *controlValues, T *values, Index start=-1, Index end=-1) const {
update(controlValues, values, _weights, start, end);
}
@ -193,7 +194,7 @@ protected:
// Update values by appling cached stencil weights to new control values
template <class T> void update( T const *controlValues, T *values,
std::vector<float> const & valueWeights, int start, int end) const;
std::vector<float> const & valueWeights, Index start, Index end) const;
// Populate the offsets table from the stencil sizes in _sizes (factory helper)
void generateOffsets();
@ -204,6 +205,7 @@ protected:
protected:
friend class StencilTablesFactory;
friend class GregoryBasisFactory;
int _numControlVertices; // number of control vertices
@ -335,7 +337,7 @@ private:
// Update values by appling cached stencil weights to new control values
template <class T> void
StencilTables::update(T const *controlValues, T *values,
std::vector<float> const &valueWeights, int start, int end) const {
std::vector<float> const &valueWeights, Index start, Index end) const {
unsigned char const * sizes = &_sizes.at(0);
Index const * indices = &_indices.at(0);
@ -387,7 +389,7 @@ StencilTables::resize(int nstencils, int nelems) {
// Returns a Stencil at index i in the table
inline Stencil
StencilTables::GetStencil(int i) const {
StencilTables::GetStencil(Index i) const {
assert((not _offsets.empty()) and i<(int)_offsets.size());
@ -399,7 +401,7 @@ StencilTables::GetStencil(int i) const {
}
inline Stencil
StencilTables::operator[] (int index) const {
StencilTables::operator[] (Index index) const {
return GetStencil(index);
}