mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-11-30 23:30:07 +00:00
Merge pull request #519 from jcowles/dev
Faster, simpler stencil table construction
This commit is contained in:
commit
5ca67a9383
@ -37,12 +37,13 @@ set(SOURCE_FILES
|
||||
patchTableFactory.cpp
|
||||
ptexIndices.cpp
|
||||
stencilTableFactory.cpp
|
||||
stencilBuilder.cpp
|
||||
topologyRefiner.cpp
|
||||
topologyRefinerFactory.cpp
|
||||
)
|
||||
|
||||
set(PRIVATE_HEADER_FILES
|
||||
protoStencil.h
|
||||
stencilBuilder.h
|
||||
)
|
||||
|
||||
set(PUBLIC_HEADER_FILES
|
||||
|
@ -27,7 +27,6 @@
|
||||
|
||||
#include "../far/patchTableFactory.h"
|
||||
#include "../far/gregoryBasis.h"
|
||||
#include "../far/protoStencil.h"
|
||||
#include "../vtr/level.h"
|
||||
|
||||
namespace OpenSubdiv {
|
||||
|
@ -25,8 +25,9 @@
|
||||
#ifndef OPENSUBDIV3_FAR_GREGORY_BASIS_H
|
||||
#define OPENSUBDIV3_FAR_GREGORY_BASIS_H
|
||||
|
||||
#include "../far/protoStencil.h"
|
||||
#include "../vtr/level.h"
|
||||
#include "../far/types.h"
|
||||
#include "../far/stencilTable.h"
|
||||
#include <cstring>
|
||||
|
||||
namespace OpenSubdiv {
|
||||
@ -59,7 +60,7 @@ public:
|
||||
template <class T, class U>
|
||||
void Evaluate(T const & controlValues, U values[20]) const {
|
||||
|
||||
Index const * indices = &_indices.at(0);
|
||||
Vtr::Index const * indices = &_indices.at(0);
|
||||
float const * weights = &_weights.at(0);
|
||||
|
||||
for (int i=0; i<20; ++i) {
|
||||
@ -85,7 +86,7 @@ public:
|
||||
_weights.reserve(RESERVED_ENTRY_SIZE);
|
||||
}
|
||||
|
||||
Point(Index idx, float weight = 1.0f) {
|
||||
Point(Vtr::Index idx, float weight = 1.0f) {
|
||||
_indices.reserve(RESERVED_ENTRY_SIZE);
|
||||
_weights.reserve(RESERVED_ENTRY_SIZE);
|
||||
_size = 1;
|
||||
@ -101,7 +102,7 @@ public:
|
||||
return _size;
|
||||
}
|
||||
|
||||
Index const * GetIndices() const {
|
||||
Vtr::Index const * GetIndices() const {
|
||||
return &_indices[0];
|
||||
}
|
||||
|
||||
@ -118,7 +119,7 @@ public:
|
||||
|
||||
Point & operator += (Point const & other) {
|
||||
for (int i=0; i<other._size; ++i) {
|
||||
Index idx = findIndex(other._indices[i]);
|
||||
Vtr::Index idx = findIndex(other._indices[i]);
|
||||
_weights[idx] += other._weights[i];
|
||||
}
|
||||
return *this;
|
||||
@ -126,7 +127,7 @@ public:
|
||||
|
||||
Point & operator -= (Point const & other) {
|
||||
for (int i=0; i<other._size; ++i) {
|
||||
Index idx = findIndex(other._indices[i]);
|
||||
Vtr::Index idx = findIndex(other._indices[i]);
|
||||
_weights[idx] -= other._weights[i];
|
||||
}
|
||||
return *this;
|
||||
@ -159,14 +160,14 @@ public:
|
||||
Point p(*this); return p-=other;
|
||||
}
|
||||
|
||||
void OffsetIndices(Index offset) {
|
||||
void OffsetIndices(Vtr::Index offset) {
|
||||
for (int i=0; i<_size; ++i) {
|
||||
_indices[i] += offset;
|
||||
}
|
||||
}
|
||||
|
||||
void Copy(int ** size, Index ** indices, float ** weights) const {
|
||||
memcpy(*indices, &_indices[0], _size*sizeof(Index));
|
||||
void Copy(int ** size, Vtr::Index ** indices, float ** weights) const {
|
||||
memcpy(*indices, &_indices[0], _size*sizeof(Vtr::Index));
|
||||
memcpy(*weights, &_weights[0], _size*sizeof(float));
|
||||
**size = _size;
|
||||
*indices += _size;
|
||||
@ -176,7 +177,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
int findIndex(Index idx) {
|
||||
int findIndex(Vtr::Index idx) {
|
||||
for (int i=0; i<_size; ++i) {
|
||||
if (_indices[i]==idx) {
|
||||
return i;
|
||||
@ -189,7 +190,7 @@ public:
|
||||
}
|
||||
|
||||
int _size;
|
||||
std::vector<Index> _indices;
|
||||
std::vector<Vtr::Index> _indices;
|
||||
std::vector<float> _weights;
|
||||
};
|
||||
|
||||
@ -201,11 +202,11 @@ public:
|
||||
//
|
||||
struct ProtoBasis {
|
||||
|
||||
ProtoBasis(Vtr::Level const & level, Index faceIndex, int fvarChannel=-1);
|
||||
ProtoBasis(Vtr::Level const & level, Vtr::Index faceIndex, int fvarChannel=-1);
|
||||
|
||||
int GetNumElements() const;
|
||||
|
||||
void Copy(int * sizes, Index * indices, float * weights) const;
|
||||
void Copy(int * sizes, Vtr::Index * indices, float * weights) const;
|
||||
void Copy(GregoryBasis* dest) const;
|
||||
|
||||
// Control Vertices based on :
|
||||
@ -247,7 +248,7 @@ private:
|
||||
|
||||
int _sizes[20];
|
||||
|
||||
std::vector<Index> _indices;
|
||||
std::vector<Vtr::Index> _indices;
|
||||
std::vector<float> _weights;
|
||||
};
|
||||
|
||||
|
@ -1,504 +0,0 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifndef OPENSUBDIV3_FAR_PROTOSTENCIL_H
|
||||
#define OPENSUBDIV3_FAR_PROTOSTENCIL_H
|
||||
|
||||
#include "../far/stencilTable.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenSubdiv {
|
||||
namespace OPENSUBDIV_VERSION {
|
||||
|
||||
namespace Far {
|
||||
|
||||
//
|
||||
// Proto-stencil Pool Allocator classes
|
||||
//
|
||||
// Strategy: allocate up-front a data pool for supporting PROTOSTENCILS of a size
|
||||
// (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) { }
|
||||
|
||||
~Allocator() {
|
||||
clearBigStencils();
|
||||
}
|
||||
|
||||
// Returns the number of stencils in the allocator
|
||||
int GetNumStencils() const {
|
||||
return (int)_sizes.size();
|
||||
}
|
||||
|
||||
// Returns the total number of control vertices used by the all the stencils
|
||||
int GetNumVerticesTotal() const {
|
||||
int nverts=0;
|
||||
for (int i=0; i<GetNumStencils(); ++i) {
|
||||
nverts += _sizes[i];
|
||||
}
|
||||
return nverts;
|
||||
}
|
||||
|
||||
// Returns true if the pool allocator executes AddVaryingWithWeight
|
||||
// factorization
|
||||
bool GetInterpolateVarying() const {
|
||||
return _interpolateVarying;
|
||||
}
|
||||
|
||||
// Allocates storage for 'size' stencils with a fixed '_maxsize' supporting
|
||||
// basis of control-vertices
|
||||
void Resize(int numStencils) {
|
||||
clearBigStencils();
|
||||
int nelems = numStencils * _maxsize;
|
||||
_sizes.clear();
|
||||
_sizes.resize(numStencils);
|
||||
_indices.resize(nelems);
|
||||
_weights.resize(nelems);
|
||||
}
|
||||
|
||||
// Adds the contribution of a supporting vertex that was not yet
|
||||
// in the stencil
|
||||
void PushBackVertex(Index protoStencil, Index vert, float weight) {
|
||||
assert(weight!=0.0f);
|
||||
int & size = _sizes[protoStencil];
|
||||
Index idx = protoStencil*_maxsize;
|
||||
if (size < (_maxsize-1)) {
|
||||
idx += size;
|
||||
_indices[idx] = vert;
|
||||
_weights[idx] = weight;
|
||||
} else {
|
||||
BIG_PROTOSTENCIL * dst = 0;
|
||||
if (size==(_maxsize-1)) {
|
||||
dst = new BIG_PROTOSTENCIL(size, &_indices[idx], &_weights[idx]);
|
||||
assert(_bigStencils.find(protoStencil)==_bigStencils.end());
|
||||
_bigStencils[protoStencil] = dst;
|
||||
} else {
|
||||
assert(_bigStencils.find(protoStencil)!=_bigStencils.end());
|
||||
dst = _bigStencils[protoStencil];
|
||||
}
|
||||
dst->_indices.push_back(vert);
|
||||
dst->_weights.push_back(weight);
|
||||
}
|
||||
++size;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
return Vtr::INDEX_INVALID;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Returns the size of a given proto-stencil
|
||||
int 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(protoStencil)!=_bigStencils.end());
|
||||
return &_bigStencils[protoStencil]->_indices[0];
|
||||
}
|
||||
}
|
||||
|
||||
// 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(protoStencil)!=_bigStencils.end());
|
||||
return &_bigStencils[protoStencil]->_weights[0];
|
||||
}
|
||||
}
|
||||
|
||||
// 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(protoStencil, this->GetNumStencils()>0 ? this : 0);
|
||||
}
|
||||
|
||||
// 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(protoStencil, this->GetNumStencils()>0 ?
|
||||
const_cast<Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL> *>(this) : 0);
|
||||
}
|
||||
|
||||
// Copy the proto-stencil out of the pool
|
||||
int CopyStencil(Index protoStencil,
|
||||
Index * indices, float * weights) {
|
||||
int 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; // max size of stencil that fits in the 'fast' pool
|
||||
|
||||
bool _interpolateVarying; // true for varying interpolation
|
||||
|
||||
std::vector<int> _sizes; // 'fast' memory pool
|
||||
std::vector<int> _indices;
|
||||
std::vector<float> _weights;
|
||||
|
||||
typedef std::map<int, BIG_PROTOSTENCIL *> BigStencilMap;
|
||||
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:
|
||||
|
||||
// Constructor
|
||||
LimitAllocator(int maxSize) :
|
||||
Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>(maxSize) { }
|
||||
|
||||
void Resize(int size) {
|
||||
Allocator<PROTOSTENCIL, BIG_PROTOSTENCIL>::Resize(size);
|
||||
int nelems = (int)this->_weights.size();
|
||||
_tan1Weights.resize(nelems);
|
||||
_tan2Weights.resize(nelems);
|
||||
}
|
||||
|
||||
void PushBackVertex(Index protoStencil,
|
||||
Index vert, float weight, float tan1Weight, float tan2Weight) {
|
||||
assert(weight!=0.0f or tan1Weight!=0.0f or tan2Weight!=0.0f);
|
||||
int & size = this->_sizes[protoStencil];
|
||||
Index idx = protoStencil*this->_maxsize;
|
||||
if (size < (this->_maxsize-1)) {
|
||||
idx += size;
|
||||
this->_indices[idx] = vert;
|
||||
this->_weights[idx] = weight;
|
||||
this->_tan1Weights[idx] = tan1Weight;
|
||||
this->_tan2Weights[idx] = tan2Weight;
|
||||
} 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(protoStencil)==this->_bigStencils.end());
|
||||
this->_bigStencils[protoStencil] = dst;
|
||||
} else {
|
||||
assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end());
|
||||
dst = this->_bigStencils[protoStencil];
|
||||
}
|
||||
dst->_indices.push_back(vert);
|
||||
dst->_weights.push_back(weight);
|
||||
dst->_tan1Weights.push_back(tan1Weight);
|
||||
dst->_tan2Weights.push_back(tan2Weight);
|
||||
}
|
||||
++size;
|
||||
}
|
||||
|
||||
float * GetTan1Weights(Index protoStencil) {
|
||||
if (not this->IsBigStencil(protoStencil)) {
|
||||
return &_tan1Weights[protoStencil*this->_maxsize];
|
||||
} else {
|
||||
assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end());
|
||||
return &this->_bigStencils[protoStencil]->_tan1Weights[0];
|
||||
}
|
||||
}
|
||||
|
||||
float * GetTan2Weights(Index protoStencil) {
|
||||
if (not this->IsBigStencil(protoStencil)) {
|
||||
return &_tan2Weights[protoStencil*this->_maxsize];
|
||||
} else {
|
||||
assert(this->_bigStencils.find(protoStencil)!=this->_bigStencils.end());
|
||||
return &this->_bigStencils[protoStencil]->_tan2Weights[0];
|
||||
}
|
||||
}
|
||||
|
||||
PROTOSTENCIL operator[] (Index protoStencil) {
|
||||
assert(this->GetNumStencils()>0);
|
||||
return PROTOSTENCIL(protoStencil, this);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
int CopyLimitStencil(Index protoStencil,
|
||||
Index * indices, float * weights, float * tan1Weights, float * tan2Weights) {
|
||||
int size = 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;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<float> _tan1Weights,
|
||||
_tan2Weights;
|
||||
};
|
||||
|
||||
//
|
||||
// 'Big' Proto stencil classes
|
||||
//
|
||||
// When proto-stencils exceed _maxsize, fall back to dynamically allocated
|
||||
// "BigStencils" (with 'Limit' specialization to handle tangents)
|
||||
//
|
||||
struct BigStencil {
|
||||
|
||||
BigStencil(int size, Index const * indices,
|
||||
float const * weights) {
|
||||
_indices.reserve(size+5); _indices.resize(size);
|
||||
memcpy(&_indices.at(0), indices, size*sizeof(int));
|
||||
_weights.reserve(size+5); _weights.resize(size);
|
||||
memcpy(&_weights.at(0), weights, size*sizeof(float));
|
||||
}
|
||||
|
||||
std::vector<Index> _indices;
|
||||
std::vector<float> _weights;
|
||||
};
|
||||
struct BigLimitStencil : public BigStencil {
|
||||
|
||||
BigLimitStencil(int 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);
|
||||
memcpy(&_tan2Weights.at(0), tan2Weights, size*sizeof(float));
|
||||
}
|
||||
|
||||
std::vector<float> _tan1Weights,
|
||||
_tan2Weights;
|
||||
};
|
||||
|
||||
//
|
||||
// ProtoStencils
|
||||
//
|
||||
// Proto-stencils are used to interpolate stencils from supporting vertices.
|
||||
// These stencils are backed by a pool allocator to allow for fast push-back
|
||||
// of contributing control-vertices weights & indices as they are discovered.
|
||||
//
|
||||
class ProtoStencil {
|
||||
|
||||
public:
|
||||
|
||||
ProtoStencil(Index id, Allocator<ProtoStencil, BigStencil> * alloc) :
|
||||
_id(id), _alloc(alloc) { }
|
||||
|
||||
void Clear() {
|
||||
// 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) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (src._alloc) {
|
||||
// Stencil contribution
|
||||
int srcSize = src._alloc->GetSize(src._id);
|
||||
Index const * srcIndices = src._alloc->GetIndices(src._id);
|
||||
float const * srcWeights = src._alloc->GetWeights(src._id);
|
||||
|
||||
addWithWeight(weight, srcSize, srcIndices, srcWeights);
|
||||
} else {
|
||||
// Coarse vertex contribution
|
||||
Index n = _alloc->FindVertex(_id, src._id);
|
||||
if (Vtr::IndexIsValid(n)) {
|
||||
_alloc->GetWeights(_id)[n] += weight;
|
||||
assert(_alloc->GetWeights(_id)[n]>0.0f);
|
||||
} else {
|
||||
_alloc->PushBackVertex(_id, src._id, weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Factorize from a finished stencil table
|
||||
void AddWithWeight(StencilTable const & table, Index idx, float weight) {
|
||||
|
||||
assert(idx<table.GetNumStencils());
|
||||
|
||||
if(weight==0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
int 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);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void addWithWeight(float weight, int srcSize,
|
||||
Index const * srcIndices, float const * srcWeights) {
|
||||
|
||||
for (int 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;
|
||||
};
|
||||
|
||||
typedef Allocator<ProtoStencil, BigStencil> StencilAllocator;
|
||||
|
||||
|
||||
//
|
||||
// ProtoLimitStencil
|
||||
//
|
||||
class ProtoLimitStencil {
|
||||
|
||||
public:
|
||||
|
||||
ProtoLimitStencil(Index id,
|
||||
LimitAllocator<ProtoLimitStencil, BigLimitStencil> * alloc) :
|
||||
_id(id), _alloc(alloc) { }
|
||||
|
||||
void Clear() {
|
||||
// Clear() can only ever be called on an empty stencil: nothing to do
|
||||
assert(_alloc->GetSize(_id)==0);
|
||||
}
|
||||
|
||||
void AddWithWeight(Stencil const & src,
|
||||
float weight, float tan1Weight, float tan2Weight) {
|
||||
|
||||
if(weight==0.0f and tan1Weight==0.0f and tan2Weight==0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
int srcSize = *src.GetSizePtr();
|
||||
Index const * srcIndices = src.GetVertexIndices();
|
||||
float const * srcWeights = src.GetWeights();
|
||||
|
||||
for (int i=0; i<srcSize; ++i) {
|
||||
|
||||
float w = srcWeights[i];
|
||||
if (w==0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Index vertIndex = srcIndices[i],
|
||||
n = _alloc->FindVertex(_id, vertIndex);
|
||||
if (Vtr::IndexIsValid(n)) {
|
||||
_alloc->GetWeights(_id)[n] += weight*w;
|
||||
_alloc->GetTan1Weights(_id)[n] += tan1Weight*w;
|
||||
_alloc->GetTan2Weights(_id)[n] += tan2Weight*w;
|
||||
|
||||
} else {
|
||||
_alloc->PushBackVertex(_id, vertIndex,
|
||||
weight*w, tan1Weight*w, tan2Weight*w);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Index _id;
|
||||
LimitAllocator<ProtoLimitStencil, BigLimitStencil> * _alloc;
|
||||
};
|
||||
|
||||
typedef LimitAllocator<ProtoLimitStencil, BigLimitStencil> LimitStencilAllocator;
|
||||
|
||||
|
||||
|
||||
} // end namespace Far
|
||||
|
||||
} // end namespace OPENSUBDIV_VERSION
|
||||
} // end namespace OpenSubdiv
|
||||
|
||||
#endif // OPENSUBDIV3_FAR_PROTOSTENCIL_H
|
453
opensubdiv/far/stencilBuilder.cpp
Normal file
453
opensubdiv/far/stencilBuilder.cpp
Normal file
@ -0,0 +1,453 @@
|
||||
//
|
||||
// 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/stencilBuilder.h"
|
||||
#include "../far/topologyRefiner.h"
|
||||
|
||||
namespace OpenSubdiv {
|
||||
namespace OPENSUBDIV_VERSION {
|
||||
|
||||
namespace Far {
|
||||
namespace Internal {
|
||||
|
||||
struct PointDerivWeight {
|
||||
float p;
|
||||
float du;
|
||||
float dv;
|
||||
|
||||
PointDerivWeight()
|
||||
: p(0.0f), du(0.0f), dv(0.0f)
|
||||
{ }
|
||||
PointDerivWeight(float w)
|
||||
: p(w), du(w), dv(w)
|
||||
{ }
|
||||
PointDerivWeight(float w, float wDu, float wDv)
|
||||
: p(w), du(wDu), dv(wDv)
|
||||
{ }
|
||||
|
||||
friend PointDerivWeight operator*(PointDerivWeight lhs,
|
||||
PointDerivWeight const& rhs) {
|
||||
lhs.p *= rhs.p;
|
||||
lhs.du *= rhs.du;
|
||||
lhs.dv *= rhs.dv;
|
||||
return lhs;
|
||||
}
|
||||
PointDerivWeight& operator+=(PointDerivWeight const& rhs) {
|
||||
p += rhs.p;
|
||||
du += rhs.du;
|
||||
dv += rhs.dv;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
/// Stencil table constructor set.
|
||||
///
|
||||
class WeightTable {
|
||||
public:
|
||||
WeightTable(int coarseVerts,
|
||||
bool genCtrlVertStencils,
|
||||
bool compactWeights)
|
||||
: _size(0)
|
||||
, _lastOffset(0)
|
||||
, _coarseVertCount(coarseVerts)
|
||||
, _compactWeights(compactWeights)
|
||||
{
|
||||
// These numbers were chosen by profiling production assets at uniform
|
||||
// level 3.
|
||||
size_t n = std::max(coarseVerts,
|
||||
std::min(int(5*1024*1024),
|
||||
coarseVerts*2));
|
||||
_dests.reserve(n);
|
||||
_sources.reserve(n);
|
||||
_weights.reserve(n);
|
||||
|
||||
if (!genCtrlVertStencils)
|
||||
return;
|
||||
|
||||
// Generate trivial control vert stencils
|
||||
_sources.resize(coarseVerts);
|
||||
_weights.resize(coarseVerts);
|
||||
_dests.resize(coarseVerts);
|
||||
_indices.resize(coarseVerts);
|
||||
_sizes.resize(coarseVerts);
|
||||
|
||||
for (int i = 0; i < coarseVerts; i++) {
|
||||
_indices[i] = i;
|
||||
_sizes[i] = 1;
|
||||
_dests[i] = i;
|
||||
_sources[i] = i;
|
||||
_weights[i] = 1.0;
|
||||
}
|
||||
|
||||
_size = _sources.size();
|
||||
_lastOffset = _sources.size() - 1;
|
||||
}
|
||||
|
||||
template <class W, class WACCUM>
|
||||
void AddWithWeight(int src, int dest, W weight, WACCUM weights)
|
||||
{
|
||||
// Factorized stencils are expressed purely in terms of the control
|
||||
// mesh verts. Without this flattening, level_i's weights would point
|
||||
// to level_i-1, which would point to level_i-2, until the final level
|
||||
// points to the control verts.
|
||||
//
|
||||
// So here, we check if the incoming vert (src) is in the control mesh,
|
||||
// if it is, we can simply merge it without attempting to resolve it
|
||||
// first.
|
||||
if (src < _coarseVertCount) {
|
||||
merge(src, dest, weight, W(1.0), _lastOffset, _size, weights);
|
||||
return;
|
||||
}
|
||||
|
||||
// src is not in the control mesh, so resolve all contributing coarse
|
||||
// verts (src itself is made up of many control vert weights).
|
||||
//
|
||||
// Find the src stencil and number of contributing CVs.
|
||||
int len = _sizes[src];
|
||||
int start = _indices[src];
|
||||
|
||||
for (int i = start; i < start+len; i++) {
|
||||
// Invariant: by processing each level in order and each vertex in
|
||||
// dependent order, any src stencil vertex reference is guaranteed
|
||||
// to consist only of coarse verts: therefore resolving src verts
|
||||
// must yield verts in the coarse mesh.
|
||||
assert(_sources[i] < _coarseVertCount);
|
||||
|
||||
// Merge each of src's contributing verts into this stencil.
|
||||
merge(_sources[i], dest, weights.Get(i), weight,
|
||||
_lastOffset, _size, weights);
|
||||
}
|
||||
}
|
||||
|
||||
class PointDerivAccumulator {
|
||||
WeightTable* _tbl;
|
||||
public:
|
||||
PointDerivAccumulator(WeightTable* tbl) : _tbl(tbl)
|
||||
{ }
|
||||
void PushBack(PointDerivWeight weight) {
|
||||
_tbl->_weights.push_back(weight.p);
|
||||
_tbl->_duWeights.push_back(weight.du);
|
||||
_tbl->_dvWeights.push_back(weight.dv);
|
||||
}
|
||||
void Add(size_t i, PointDerivWeight weight) {
|
||||
_tbl->_weights[i] += weight.p;
|
||||
_tbl->_duWeights[i] += weight.du;
|
||||
_tbl->_dvWeights[i] += weight.dv;
|
||||
}
|
||||
PointDerivWeight Get(size_t index) {
|
||||
return PointDerivWeight(_tbl->_weights[index],
|
||||
_tbl->_duWeights[index],
|
||||
_tbl->_dvWeights[index]);
|
||||
}
|
||||
};
|
||||
PointDerivAccumulator GetPointDerivAccumulator() {
|
||||
return PointDerivAccumulator(this);
|
||||
};
|
||||
|
||||
class ScalarAccumulator {
|
||||
WeightTable* _tbl;
|
||||
public:
|
||||
ScalarAccumulator(WeightTable* tbl) : _tbl(tbl)
|
||||
{ }
|
||||
void PushBack(PointDerivWeight weight) {
|
||||
_tbl->_weights.push_back(weight.p);
|
||||
}
|
||||
void Add(size_t i, float w) {
|
||||
_tbl->_weights[i] += w;
|
||||
}
|
||||
float Get(size_t index) {
|
||||
return _tbl->_weights[index];
|
||||
}
|
||||
};
|
||||
ScalarAccumulator GetScalarAccumulator() {
|
||||
return ScalarAccumulator(this);
|
||||
};
|
||||
|
||||
std::vector<int> const&
|
||||
GetOffsets() const { return _indices; }
|
||||
|
||||
std::vector<int> const&
|
||||
GetSizes() const { return _sizes; }
|
||||
|
||||
std::vector<int> const&
|
||||
GetSources() const { return _sources; }
|
||||
|
||||
std::vector<float> const&
|
||||
GetWeights() const { return _weights; }
|
||||
|
||||
std::vector<float> const&
|
||||
GetDuWeights() const { return _duWeights; }
|
||||
|
||||
std::vector<float> const&
|
||||
GetDvWeights() const { return _dvWeights; }
|
||||
|
||||
private:
|
||||
|
||||
// Merge a vertex weight into the stencil table, if there is an existing
|
||||
// weight for a given source vertex it will be combined.
|
||||
//
|
||||
// PERFORMANCE: caution, this function is super hot.
|
||||
template <class W, class WACCUM>
|
||||
void merge(int src, int dst, W weight,
|
||||
// Delaying weight*factor multiplication hides memory latency of
|
||||
// accessing weight[i], yielding more stable performance.
|
||||
W weightFactor,
|
||||
// Similarly, passing offset & tableSize as params yields higher
|
||||
// performance than accessing the class members directly.
|
||||
int lastOffset, int tableSize, WACCUM weights)
|
||||
{
|
||||
// The lastOffset is the vertex we're currently processing, by
|
||||
// leveraging this we need not lookup the dest stencil size or offset.
|
||||
//
|
||||
// Additionally, if the client does not want the resulting verts
|
||||
// compacted, do not attempt to combine weights.
|
||||
if (_compactWeights and _dests[lastOffset] == dst) {
|
||||
|
||||
// tableSize is exactly _sources.size(), but using tableSize is
|
||||
// significantly faster.
|
||||
for (int i = lastOffset; i < tableSize; i++) {
|
||||
|
||||
// If we find an existing vertex that matches src, we need to
|
||||
// combine the weights to avoid duplicate entries for src.
|
||||
if (_sources[i] == src) {
|
||||
weights.Add(i, weight*weightFactor);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We haven't seen src yet, insert it as a new vertex weight.
|
||||
add(src, dst, weight*weightFactor, weights);
|
||||
}
|
||||
|
||||
// Add a new vertex weight to the stencil table.
|
||||
template <class W, class WACCUM>
|
||||
void add(int src, int dst, W weight, WACCUM weights)
|
||||
{
|
||||
// The _dests array has num(weights) elements mapping each individual
|
||||
// element back to a specific stencil. The array is constructed in such
|
||||
// a way that the current stencil being built is always at the end of
|
||||
// the array, so if the dests array is empty or back() doesn't match
|
||||
// dst, then we just started building a new stencil.
|
||||
if (_dests.empty() or dst != _dests.back()) {
|
||||
// _indices and _sizes always have num(stencils) elements so that
|
||||
// stencils can be directly looked up by their index in these
|
||||
// arrays. So here, ensure that they are large enough to hold the
|
||||
// new stencil about to be built.
|
||||
if (dst+1 > (int)_indices.size()) {
|
||||
_indices.resize(dst+1);
|
||||
_sizes.resize(dst+1);
|
||||
}
|
||||
// Initialize the new stencil's meta-data (offset, size).
|
||||
_indices[dst] = _sources.size();
|
||||
_sizes[dst] = 0;
|
||||
// Keep track of where the current stencil begins, which lets us
|
||||
// avoid having to look it up later.
|
||||
_lastOffset = _sources.size();
|
||||
}
|
||||
// Cache the number of elements as an optimization, it's faster than
|
||||
// calling size() on any of the vectors.
|
||||
_size++;
|
||||
|
||||
// Increment the current stencil element size.
|
||||
_sizes[dst]++;
|
||||
// Track this element as belonging to the stencil "dst".
|
||||
_dests.push_back(dst);
|
||||
|
||||
// Store the actual stencil data.
|
||||
_sources.push_back(src);
|
||||
weights.PushBack(weight);
|
||||
}
|
||||
|
||||
// The following vectors are explicitly stored as non-interleaved elements
|
||||
// to reduce cache misses.
|
||||
|
||||
// Stencil to destination vertex map.
|
||||
std::vector<int> _dests;
|
||||
|
||||
// The actual stencil data.
|
||||
std::vector<int> _sources;
|
||||
std::vector<float> _weights;
|
||||
std::vector<float> _duWeights;
|
||||
std::vector<float> _dvWeights;
|
||||
|
||||
// Index data used to recover stencil-to-vertex mapping.
|
||||
std::vector<int> _indices;
|
||||
std::vector<int> _sizes;
|
||||
|
||||
// Acceleration members to avoid pointer chasing and reverse loops.
|
||||
int _size;
|
||||
int _lastOffset;
|
||||
int _coarseVertCount;
|
||||
bool _compactWeights;
|
||||
};
|
||||
|
||||
StencilBuilder::StencilBuilder(int coarseVertCount,
|
||||
bool isVarying,
|
||||
bool genCtrlVertStencils,
|
||||
bool compactWeights)
|
||||
: _weightTable(new WeightTable(coarseVertCount,
|
||||
genCtrlVertStencils,
|
||||
compactWeights))
|
||||
, _isVarying(isVarying)
|
||||
{
|
||||
}
|
||||
|
||||
StencilBuilder::~StencilBuilder()
|
||||
{
|
||||
delete _weightTable;
|
||||
}
|
||||
|
||||
size_t
|
||||
StencilBuilder::GetNumVerticesTotal() const
|
||||
{
|
||||
return _weightTable->GetWeights().size();
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
StencilBuilder::GetNumVertsInStencil(size_t stencilIndex) const
|
||||
{
|
||||
if (stencilIndex > _weightTable->GetSizes().size() - 1)
|
||||
return 0;
|
||||
|
||||
return (int)_weightTable->GetSizes()[stencilIndex];
|
||||
}
|
||||
|
||||
std::vector<int> const&
|
||||
StencilBuilder::GetStencilOffsets() const {
|
||||
return _weightTable->GetOffsets();
|
||||
}
|
||||
|
||||
std::vector<int> const&
|
||||
StencilBuilder::GetStencilSizes() const {
|
||||
return _weightTable->GetSizes();
|
||||
}
|
||||
|
||||
std::vector<int> const&
|
||||
StencilBuilder::GetStencilSources() const {
|
||||
return _weightTable->GetSources();
|
||||
}
|
||||
|
||||
std::vector<float> const&
|
||||
StencilBuilder::GetStencilWeights() const {
|
||||
return _weightTable->GetWeights();
|
||||
}
|
||||
|
||||
std::vector<float> const&
|
||||
StencilBuilder::GetStencilDuWeights() const {
|
||||
return _weightTable->GetDuWeights();
|
||||
}
|
||||
|
||||
std::vector<float> const&
|
||||
StencilBuilder::GetStencilDvWeights() const {
|
||||
return _weightTable->GetDvWeights();
|
||||
}
|
||||
|
||||
void
|
||||
StencilBuilder::Index::AddWithWeight(Index const & src, float weight)
|
||||
{
|
||||
if (_owner->_isVarying)
|
||||
return;
|
||||
// Ignore no-op weights.
|
||||
if (weight == 0)
|
||||
return;
|
||||
_owner->_weightTable->AddWithWeight(src._index, _index, weight,
|
||||
_owner->_weightTable->GetScalarAccumulator());
|
||||
}
|
||||
|
||||
void
|
||||
StencilBuilder::Index::AddWithWeight(Stencil const& src, float weight)
|
||||
{
|
||||
if(weight == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
int srcSize = *src.GetSizePtr();
|
||||
Vtr::Index const * srcIndices = src.GetVertexIndices();
|
||||
float const * srcWeights = src.GetWeights();
|
||||
|
||||
for (int i = 0; i < srcSize; ++i) {
|
||||
float w = srcWeights[i];
|
||||
if (w == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vtr::Index srcIndex = srcIndices[i];
|
||||
|
||||
float wgt = weight * w;
|
||||
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
|
||||
_owner->_weightTable->GetScalarAccumulator());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StencilBuilder::Index::AddWithWeight(Stencil const& src,
|
||||
float weight, float du, float dv)
|
||||
{
|
||||
if(weight == 0.0f and du == 0.0f and dv == 0.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
int srcSize = *src.GetSizePtr();
|
||||
Vtr::Index const * srcIndices = src.GetVertexIndices();
|
||||
float const * srcWeights = src.GetWeights();
|
||||
|
||||
for (int i = 0; i < srcSize; ++i) {
|
||||
float w = srcWeights[i];
|
||||
if (w == 0.0f) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vtr::Index srcIndex = srcIndices[i];
|
||||
|
||||
PointDerivWeight wgt = PointDerivWeight(weight, du, dv) * w;
|
||||
_owner->_weightTable->AddWithWeight(srcIndex, _index, wgt,
|
||||
_owner->_weightTable->GetPointDerivAccumulator());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StencilBuilder::Index::AddVaryingWithWeight(Index const &src, float weight)
|
||||
{
|
||||
if (not _owner->_isVarying)
|
||||
return;
|
||||
// Ignore no-op weights.
|
||||
if (weight == 0)
|
||||
return;
|
||||
_owner->_weightTable->AddWithWeight(src._index, _index, weight,
|
||||
_owner->_weightTable->GetScalarAccumulator());
|
||||
}
|
||||
|
||||
void
|
||||
StencilBuilder::Index::AddFaceVaryingWithWeight(Index const &, float)
|
||||
{
|
||||
// Not supported.
|
||||
}
|
||||
|
||||
} // end namespace Internal
|
||||
} // end namespace Far
|
||||
} // end namespace OPENSUBDIV_VERSION
|
||||
} // end namespace OpenSubdiv
|
||||
|
110
opensubdiv/far/stencilBuilder.h
Normal file
110
opensubdiv/far/stencilBuilder.h
Normal file
@ -0,0 +1,110 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifndef OPENSUBDIV3_FAR_STENCILBUILDER_H
|
||||
#define OPENSUBDIV3_FAR_STENCILBUILDER_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../version.h"
|
||||
#include "../far/stencilTable.h"
|
||||
|
||||
namespace OpenSubdiv {
|
||||
namespace OPENSUBDIV_VERSION {
|
||||
|
||||
namespace Far {
|
||||
namespace Internal {
|
||||
|
||||
class WeightTable;
|
||||
|
||||
class StencilBuilder {
|
||||
public:
|
||||
StencilBuilder(int coarseVertCount,
|
||||
bool isVarying=false,
|
||||
bool genCtrlVertStencils=true,
|
||||
bool compactWeights=true);
|
||||
~StencilBuilder();
|
||||
|
||||
// TODO: noncopyable.
|
||||
|
||||
size_t GetNumVerticesTotal() const;
|
||||
|
||||
int GetNumVertsInStencil(size_t stencilIndex) const;
|
||||
|
||||
// Mapping from stencil[i] to it's starting offset in the sources[] and weights[] arrays;
|
||||
std::vector<int> const& GetStencilOffsets() const;
|
||||
|
||||
// The number of contributing sources and weights in stencil[i]
|
||||
std::vector<int> const& GetStencilSizes() const;
|
||||
|
||||
// The absolute source vertex offsets.
|
||||
std::vector<int> const& GetStencilSources() const;
|
||||
|
||||
// The individual vertex weights, each weight is paired with one source.
|
||||
std::vector<float> const& GetStencilWeights() const;
|
||||
std::vector<float> const& GetStencilDuWeights() const;
|
||||
std::vector<float> const& GetStencilDvWeights() const;
|
||||
|
||||
// Vertex Facade.
|
||||
class Index {
|
||||
public:
|
||||
Index(StencilBuilder* owner, int index)
|
||||
: _owner(owner)
|
||||
, _index(index)
|
||||
{}
|
||||
|
||||
// Add with point/vertex weight only.
|
||||
void AddWithWeight(Index const & src, float weight);
|
||||
void AddWithWeight(Stencil const& src, float weight);
|
||||
|
||||
// Add with first derivative.
|
||||
void AddWithWeight(Stencil const& src,
|
||||
float weight, float du, float dv);
|
||||
|
||||
void AddVaryingWithWeight(Index const &, float);
|
||||
void AddFaceVaryingWithWeight(Index const &, float);
|
||||
|
||||
Index operator[](int index) const {
|
||||
return Index(_owner, index+_index);
|
||||
}
|
||||
|
||||
int GetOffset() const { return _index; }
|
||||
|
||||
void Clear() {/*nothing to do here*/}
|
||||
private:
|
||||
StencilBuilder* _owner;
|
||||
int _index;
|
||||
};
|
||||
|
||||
private:
|
||||
WeightTable* _weightTable;
|
||||
bool _isVarying;
|
||||
};
|
||||
|
||||
} // end namespace Internal
|
||||
} // end namespace Far
|
||||
} // end namespace OPENSUBDIV_VERSION
|
||||
} // end namespace OpenSubdiv
|
||||
|
||||
#endif // FAR_STENCILBUILDER_H
|
@ -30,13 +30,91 @@
|
||||
#include "../far/types.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenSubdiv {
|
||||
namespace OPENSUBDIV_VERSION {
|
||||
|
||||
namespace Far {
|
||||
|
||||
namespace {
|
||||
void
|
||||
copyStencilData(int numControlVerts,
|
||||
bool includeCoarseVerts,
|
||||
size_t firstOffset,
|
||||
std::vector<int> const* offsets,
|
||||
std::vector<int> * _offsets,
|
||||
std::vector<int> const* sizes,
|
||||
std::vector<int> * _sizes,
|
||||
std::vector<int> const* sources,
|
||||
std::vector<int> * _sources,
|
||||
std::vector<float> const* weights,
|
||||
std::vector<float> * _weights,
|
||||
std::vector<float> const* duWeights=NULL,
|
||||
std::vector<float> * _duWeights=NULL,
|
||||
std::vector<float> const* dvWeights=NULL,
|
||||
std::vector<float> * _dvWeights=NULL)
|
||||
{
|
||||
size_t off = includeCoarseVerts ? 0 : firstOffset;
|
||||
|
||||
_offsets->resize(offsets->size());
|
||||
_sizes->resize(sizes->size());
|
||||
_sources->resize(sources->size());
|
||||
_weights->resize(weights->size());
|
||||
if (_duWeights)
|
||||
_duWeights->resize(duWeights->size());
|
||||
if (_dvWeights)
|
||||
_dvWeights->resize(dvWeights->size());
|
||||
|
||||
// The stencils are probably not in order, so we must copy/sort them.
|
||||
// Note here that loop index 'i' represents stencil_i for vertex_i.
|
||||
int curOffset = 0;
|
||||
|
||||
size_t stencilCount = 0,
|
||||
weightCount = 0;
|
||||
|
||||
for (size_t i = off; i < offsets->size(); i++) {
|
||||
// Once we've copied out all the control verts, jump to the offset
|
||||
// where the actual stencils begin.
|
||||
if ((int)i == numControlVerts)
|
||||
i = firstOffset;
|
||||
|
||||
// Copy the stencil.
|
||||
int sz = (*sizes)[i];
|
||||
int off = (*offsets)[i];
|
||||
(*_offsets)[stencilCount] = curOffset;
|
||||
(*_sizes)[stencilCount] = sz;
|
||||
std::memcpy(&(*_sources)[curOffset],
|
||||
&(*sources)[off], sz*sizeof(int));
|
||||
std::memcpy(&(*_weights)[curOffset],
|
||||
&(*weights)[off], sz*sizeof(float));
|
||||
|
||||
if (_duWeights) {
|
||||
std::memcpy(&(*_duWeights)[curOffset],
|
||||
&(*duWeights)[off], sz*sizeof(float));
|
||||
}
|
||||
if (_dvWeights) {
|
||||
std::memcpy(&(*_dvWeights)[curOffset],
|
||||
&(*dvWeights)[off], sz*sizeof(float));
|
||||
}
|
||||
|
||||
curOffset += sz;
|
||||
stencilCount++;
|
||||
weightCount += sz;
|
||||
}
|
||||
|
||||
_offsets->resize(stencilCount);
|
||||
_sizes->resize(stencilCount);
|
||||
_sources->resize(weightCount);
|
||||
if (_duWeights)
|
||||
_duWeights->resize(weightCount);
|
||||
if (_dvWeights)
|
||||
_dvWeights->resize(weightCount);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Vertex stencil descriptor
|
||||
///
|
||||
/// Allows access and manipulation of a single stencil in a StencilTable.
|
||||
@ -121,6 +199,23 @@ protected:
|
||||
/// control vertices.
|
||||
///
|
||||
class StencilTable {
|
||||
StencilTable(int numControlVerts,
|
||||
std::vector<int> const& offsets,
|
||||
std::vector<int> const& sizes,
|
||||
std::vector<int> const& sources,
|
||||
std::vector<float> const& weights,
|
||||
bool includeCoarseVerts,
|
||||
size_t firstOffset)
|
||||
: _numControlVertices(numControlVerts)
|
||||
{
|
||||
copyStencilData(numControlVerts,
|
||||
includeCoarseVerts,
|
||||
firstOffset,
|
||||
&offsets, &_offsets,
|
||||
&sizes, &_sizes,
|
||||
&sources, &_indices,
|
||||
&weights, &_weights);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@ -203,6 +298,9 @@ protected:
|
||||
|
||||
protected:
|
||||
StencilTable() : _numControlVertices(0) {}
|
||||
StencilTable(int numControlVerts)
|
||||
: _numControlVertices(numControlVerts)
|
||||
{ }
|
||||
|
||||
friend class StencilTableFactory;
|
||||
// XXX: temporarily, GregoryBasis class will go away.
|
||||
@ -281,6 +379,29 @@ class LimitStencilTable : public StencilTable {
|
||||
|
||||
public:
|
||||
|
||||
// TODO: share construction logic
|
||||
LimitStencilTable(int numControlVerts,
|
||||
std::vector<int> const& offsets,
|
||||
std::vector<int> const& sizes,
|
||||
std::vector<int> const& sources,
|
||||
std::vector<float> const& weights,
|
||||
std::vector<float> const& duWeights,
|
||||
std::vector<float> const& dvWeights,
|
||||
bool includeCoarseVerts,
|
||||
size_t firstOffset)
|
||||
: StencilTable(numControlVerts)
|
||||
{
|
||||
copyStencilData(numControlVerts,
|
||||
includeCoarseVerts,
|
||||
firstOffset,
|
||||
&offsets, &_offsets,
|
||||
&sizes, &_sizes,
|
||||
&sources, &_indices,
|
||||
&weights, &_weights,
|
||||
&duWeights, &_duWeights,
|
||||
&dvWeights, &_dvWeights);
|
||||
}
|
||||
|
||||
/// \brief Returns the 'u' derivative stencil interpolation weights
|
||||
std::vector<float> const & GetDuWeights() const {
|
||||
return _duWeights;
|
||||
|
@ -23,15 +23,16 @@
|
||||
//
|
||||
|
||||
#include "../far/stencilTableFactory.h"
|
||||
#include "../far/stencilBuilder.h"
|
||||
#include "../far/endCapGregoryBasisPatchFactory.h"
|
||||
#include "../far/patchTable.h"
|
||||
#include "../far/patchTableFactory.h"
|
||||
#include "../far/patchMap.h"
|
||||
#include "../far/protoStencil.h"
|
||||
#include "../far/topologyRefiner.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
namespace OpenSubdiv {
|
||||
namespace OPENSUBDIV_VERSION {
|
||||
@ -60,133 +61,52 @@ StencilTable const *
|
||||
StencilTableFactory::Create(TopologyRefiner const & refiner,
|
||||
Options options) {
|
||||
|
||||
StencilTable * result = new StencilTable;
|
||||
|
||||
// always initialize numControlVertices (useful for torus case)
|
||||
result->_numControlVertices = refiner.GetLevel(0).GetNumVertices();
|
||||
|
||||
int maxlevel = std::min(int(options.maxLevel), refiner.GetMaxLevel());
|
||||
if (maxlevel==0 and (not options.generateControlVerts)) {
|
||||
StencilTable * result = new StencilTable;
|
||||
result->_numControlVertices = refiner.GetLevel(0).GetNumVertices();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 'maxsize' reflects the size of the default supporting basis factorized
|
||||
// in the stencils, with a little bit of head-room. Each subdivision scheme
|
||||
// has a set valence for 'regular' vertices, which drives the size of the
|
||||
// supporting basis of control-vertices. The goal is to reduce the number
|
||||
// of incidences where the pool allocator has to switch to dynamically
|
||||
// allocated heap memory when encountering extraordinary vertices that
|
||||
// require a larger supporting basis.
|
||||
//
|
||||
// The maxsize settings we use follow the assumption that the vast
|
||||
// majority of the vertices in a mesh are regular, and that the valence
|
||||
// of the extraordinary vertices is only higher by 1 edge.
|
||||
int maxsize = 0;
|
||||
bool interpolateVarying = false;
|
||||
switch (options.interpolationMode) {
|
||||
case INTERPOLATE_VERTEX: {
|
||||
Sdc::SchemeType type = refiner.GetSchemeType();
|
||||
switch (type) {
|
||||
case Sdc::SCHEME_BILINEAR : maxsize = 5; break;
|
||||
case Sdc::SCHEME_CATMARK : maxsize = 17; break;
|
||||
case Sdc::SCHEME_LOOP : maxsize = 10; break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
} break;
|
||||
case INTERPOLATE_VARYING: maxsize = 5; interpolateVarying=true; break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
std::vector<StencilAllocator> allocators(
|
||||
options.generateIntermediateLevels ? maxlevel+1 : 2,
|
||||
StencilAllocator(maxsize, interpolateVarying));
|
||||
|
||||
StencilAllocator * srcAlloc = &allocators[0],
|
||||
* dstAlloc = &allocators[1];
|
||||
bool interpolateVarying = options.interpolationMode==INTERPOLATE_VARYING;
|
||||
Internal::StencilBuilder builder(refiner.GetLevel(0).GetNumVertices(),
|
||||
interpolateVarying,
|
||||
/*genControlVerts*/ true,
|
||||
/*compactWeights*/ true);
|
||||
|
||||
//
|
||||
// Interpolate stencils for each refinement level using
|
||||
// TopologyRefiner::InterpolateLevel<>()
|
||||
//
|
||||
for (int level=1;level<=maxlevel; ++level) {
|
||||
|
||||
dstAlloc->Resize(refiner.GetLevel(level).GetNumVertices());
|
||||
|
||||
if (options.interpolationMode==INTERPOLATE_VERTEX) {
|
||||
refiner.Interpolate(level, *srcAlloc, *dstAlloc);
|
||||
Internal::StencilBuilder::Index srcIndex(&builder, 0);
|
||||
Internal::StencilBuilder::Index dstIndex(&builder,
|
||||
refiner.GetLevel(0).GetNumVertices());
|
||||
for (int level=1; level<=maxlevel; ++level) {
|
||||
if (not interpolateVarying) {
|
||||
refiner.Interpolate(level, srcIndex, dstIndex);
|
||||
} else {
|
||||
refiner.InterpolateVarying(level, *srcAlloc, *dstAlloc);
|
||||
refiner.InterpolateVarying(level, srcIndex, dstIndex);
|
||||
}
|
||||
|
||||
if (options.generateIntermediateLevels) {
|
||||
if (level<maxlevel) {
|
||||
if (options.factorizeIntermediateLevels) {
|
||||
srcAlloc = &allocators[level];
|
||||
} else {
|
||||
// if the stencils are dependent on the previous level of
|
||||
// subdivision, pass an empty allocator to treat all parent
|
||||
// vertices as control vertices
|
||||
assert(allocators[0].GetNumStencils()==0);
|
||||
}
|
||||
dstAlloc = &allocators[level+1];
|
||||
}
|
||||
} else {
|
||||
std::swap(srcAlloc, dstAlloc);
|
||||
}
|
||||
srcIndex = dstIndex;
|
||||
dstIndex = dstIndex[refiner.GetLevel(level).GetNumVertices()];
|
||||
}
|
||||
|
||||
// Copy stencils from the pool allocator into the table
|
||||
{
|
||||
// Add total number of stencils, weights & indices
|
||||
int nelems = 0, nstencils=0;
|
||||
if (options.generateIntermediateLevels) {
|
||||
for (int level=0; level<=maxlevel; ++level) {
|
||||
nstencils += allocators[level].GetNumStencils();
|
||||
nelems += allocators[level].GetNumVerticesTotal();
|
||||
}
|
||||
} else {
|
||||
nstencils = (int)srcAlloc->GetNumStencils();
|
||||
nelems = srcAlloc->GetNumVerticesTotal();
|
||||
}
|
||||
|
||||
// Allocate
|
||||
result->_numControlVertices = refiner.GetLevel(0).GetNumVertices();
|
||||
|
||||
if (options.generateControlVerts) {
|
||||
nstencils += result->_numControlVertices;
|
||||
nelems += result->_numControlVertices;
|
||||
}
|
||||
result->resize(nstencils, nelems);
|
||||
|
||||
// Copy stencils
|
||||
Stencil dst(&result->_sizes.at(0),
|
||||
&result->_indices.at(0), &result->_weights.at(0));
|
||||
|
||||
if (options.generateControlVerts) {
|
||||
generateControlVertStencils(result->_numControlVertices, dst);
|
||||
}
|
||||
|
||||
if (options.generateIntermediateLevels) {
|
||||
for (int level=1; level<=maxlevel; ++level) {
|
||||
for (int i=0; i<allocators[level].GetNumStencils(); ++i) {
|
||||
*dst._size = allocators[level].CopyStencil(i, dst._indices, dst._weights);
|
||||
dst.Next();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i=0; i<srcAlloc->GetNumStencils(); ++i) {
|
||||
*dst._size = srcAlloc->CopyStencil(i, dst._indices, dst._weights);
|
||||
dst.Next();
|
||||
}
|
||||
}
|
||||
|
||||
if (options.generateOffsets) {
|
||||
result->generateOffsets();
|
||||
}
|
||||
}
|
||||
|
||||
size_t firstOffset = refiner.GetLevel(0).GetNumVertices();
|
||||
if (not options.generateIntermediateLevels)
|
||||
firstOffset = srcIndex.GetOffset();
|
||||
|
||||
// Copy stencils from the pool allocator into the tables
|
||||
// always initialize numControlVertices (useful for torus case)
|
||||
StencilTable * result =
|
||||
new StencilTable(refiner.GetLevel(0).GetNumVertices(),
|
||||
builder.GetStencilOffsets(),
|
||||
builder.GetStencilSizes(),
|
||||
builder.GetStencilSources(),
|
||||
builder.GetStencilWeights(),
|
||||
options.generateControlVerts,
|
||||
firstOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -261,8 +181,8 @@ StencilTableFactory::Create(int numTables, StencilTable const ** tables) {
|
||||
StencilTable const *
|
||||
StencilTableFactory::AppendEndCapStencilTable(
|
||||
TopologyRefiner const &refiner,
|
||||
StencilTable const *baseStencilTable,
|
||||
StencilTable const *endCapStencilTable,
|
||||
StencilTable const * baseStencilTable,
|
||||
StencilTable const * endCapStencilTable,
|
||||
bool factorize) {
|
||||
|
||||
// factorize and append.
|
||||
@ -330,34 +250,37 @@ StencilTableFactory::AppendEndCapStencilTable(
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// copy all endcap stencils to proto stencils, and factorize if needed.
|
||||
int nEndCapStencils = endCapStencilTable->GetNumStencils();
|
||||
int nEndCapStencilsElements = 0;
|
||||
|
||||
// we exclude zero weight stencils. the resulting number of
|
||||
// stencils of endcap may be different from input.
|
||||
StencilAllocator allocator(16);
|
||||
allocator.Resize(nEndCapStencils);
|
||||
Internal::StencilBuilder builder(refiner.GetLevel(0).GetNumVertices(),
|
||||
/*isVarying*/ false,
|
||||
/*genControlVerts*/ false,
|
||||
/*compactWeights*/ factorize);
|
||||
Internal::StencilBuilder::Index origin(&builder, 0);
|
||||
Internal::StencilBuilder::Index dst = origin;
|
||||
Internal::StencilBuilder::Index srcIdx = origin;
|
||||
|
||||
for (int i = 0 ; i < nEndCapStencils; ++i) {
|
||||
Stencil src = endCapStencilTable->GetStencil(i);
|
||||
allocator[i].Clear();
|
||||
dst = origin[i];
|
||||
for (int j = 0; j < src.GetSize(); ++j) {
|
||||
Index index = src.GetVertexIndices()[j];
|
||||
float weight = src.GetWeights()[j];
|
||||
if (weight == 0.0) continue;
|
||||
|
||||
if (factorize) {
|
||||
allocator[i].AddWithWeight(*baseStencilTable,
|
||||
index + stencilsIndexOffset,
|
||||
weight);
|
||||
dst.AddWithWeight(
|
||||
baseStencilTable->GetStencil(index+stencilsIndexOffset),
|
||||
weight);
|
||||
} else {
|
||||
allocator.PushBackVertex(i,
|
||||
index + controlVertsIndexOffset,
|
||||
weight);
|
||||
srcIdx = origin[index + controlVertsIndexOffset];
|
||||
dst.AddWithWeight(srcIdx, weight);
|
||||
}
|
||||
}
|
||||
nEndCapStencilsElements += allocator.GetSize(i);
|
||||
nEndCapStencilsElements += builder.GetNumVertsInStencil(i);
|
||||
}
|
||||
|
||||
// create new stencil table
|
||||
@ -384,10 +307,11 @@ StencilTableFactory::AppendEndCapStencilTable(
|
||||
|
||||
// endcap stencils second
|
||||
for (int i = 0 ; i < nEndCapStencils; ++i) {
|
||||
int size = allocator.GetSize(i);
|
||||
int size = builder.GetNumVertsInStencil(i);
|
||||
int idx = builder.GetStencilOffsets()[i];
|
||||
for (int j = 0; j < size; ++j) {
|
||||
*indices++ = allocator.GetIndices(i)[j];
|
||||
*weights++ = allocator.GetWeights(i)[j];
|
||||
*indices++ = builder.GetStencilSources()[idx+j];
|
||||
*weights++ = builder.GetStencilWeights()[idx+j];
|
||||
}
|
||||
*sizes++ = size;
|
||||
}
|
||||
@ -416,7 +340,7 @@ LimitStencilTableFactory::Create(TopologyRefiner const & refiner,
|
||||
|
||||
bool uniform = refiner.IsUniform();
|
||||
|
||||
int maxlevel = refiner.GetMaxLevel(), maxsize=17;
|
||||
int maxlevel = refiner.GetMaxLevel();
|
||||
|
||||
StencilTable const * cvstencils = cvStencilsIn;
|
||||
if (not cvstencils) {
|
||||
@ -430,9 +354,9 @@ LimitStencilTableFactory::Create(TopologyRefiner const & refiner,
|
||||
options.generateControlVerts = true;
|
||||
options.generateOffsets = true;
|
||||
|
||||
// XXXX (manuelk) We could potentially save some mem-copies by not
|
||||
// instanciating the stencil table and work directly off the pool
|
||||
// allocators.
|
||||
// PERFORMANCE: We could potentially save some mem-copies by not
|
||||
// instanciating the stencil tables and work directly off the source
|
||||
// data.
|
||||
cvstencils = StencilTableFactory::Create(refiner, options);
|
||||
} else {
|
||||
// Sanity checks
|
||||
@ -489,35 +413,32 @@ LimitStencilTableFactory::Create(TopologyRefiner const & refiner,
|
||||
// Generate limit stencils for locations
|
||||
//
|
||||
|
||||
// Create a pool allocator to accumulate ProtoLimitStencils
|
||||
LimitStencilAllocator alloc(maxsize);
|
||||
alloc.Resize(numStencils);
|
||||
Internal::StencilBuilder builder(refiner.GetLevel(0).GetNumVertices(),
|
||||
/*isVarying*/ false,
|
||||
/*genControlVerts*/ false,
|
||||
/*compactWeights*/ true);
|
||||
Internal::StencilBuilder::Index origin(&builder, 0);
|
||||
Internal::StencilBuilder::Index dst = origin;
|
||||
|
||||
// XXXX (manuelk) we can make uniform (bilinear) stencils faster with a
|
||||
// dedicated code path that does not use PatchTable or the PatchMap
|
||||
float wP[20], wDs[20], wDt[20];
|
||||
|
||||
for (int i=0; i<(int)locationArrays.size(); ++i) {
|
||||
|
||||
for (size_t i=0; i<locationArrays.size(); ++i) {
|
||||
LocationArray const & array = locationArrays[i];
|
||||
|
||||
assert(array.ptexIdx>=0);
|
||||
|
||||
for (int j=0; j<array.numLocations; ++j) {
|
||||
|
||||
float s = array.s[j],
|
||||
t = array.t[j];
|
||||
|
||||
PatchMap::Handle const * handle = patchmap.FindPatch(array.ptexIdx, s, t);
|
||||
|
||||
PatchMap::Handle const * handle =
|
||||
patchmap.FindPatch(array.ptexIdx, s, t);
|
||||
if (handle) {
|
||||
|
||||
ConstIndexArray cvs = patchtable->GetPatchVertices(*handle);
|
||||
|
||||
patchtable->EvaluateBasis(*handle, s, t, wP, wDs, wDt);
|
||||
|
||||
StencilTable const & src = *cvstencils;
|
||||
ProtoLimitStencil dst = alloc[numLimitStencils];
|
||||
dst = origin[numLimitStencils];
|
||||
|
||||
dst.Clear();
|
||||
for (int k = 0; k < cvs.size(); ++k) {
|
||||
@ -540,30 +461,18 @@ LimitStencilTableFactory::Create(TopologyRefiner const & refiner,
|
||||
//
|
||||
// Copy the proto-stencils into the limit stencil table
|
||||
//
|
||||
LimitStencilTable * result = new LimitStencilTable;
|
||||
|
||||
int nelems = alloc.GetNumVerticesTotal();
|
||||
if (nelems>0) {
|
||||
|
||||
// Allocate
|
||||
result->resize(numLimitStencils, nelems);
|
||||
|
||||
// Copy stencils
|
||||
LimitStencil dst(&result->_sizes.at(0), &result->_indices.at(0),
|
||||
&result->_weights.at(0), &result->_duWeights.at(0),
|
||||
&result->_dvWeights.at(0));
|
||||
|
||||
for (int i = 0; i < numLimitStencils; ++i) {
|
||||
*dst._size = alloc.CopyLimitStencil(i, dst._indices, dst._weights,
|
||||
dst._duWeights, dst._dvWeights);
|
||||
dst.Next();
|
||||
}
|
||||
|
||||
// XXXX manuelk should offset creation be optional ?
|
||||
result->generateOffsets();
|
||||
}
|
||||
result->_numControlVertices = refiner.GetLevel(0).GetNumVertices();
|
||||
size_t firstOffset = refiner.GetLevel(0).GetNumVertices();
|
||||
|
||||
LimitStencilTable * result = new LimitStencilTable(
|
||||
refiner.GetLevel(0).GetNumVertices(),
|
||||
builder.GetStencilOffsets(),
|
||||
builder.GetStencilSizes(),
|
||||
builder.GetStencilSources(),
|
||||
builder.GetStencilWeights(),
|
||||
builder.GetStencilDuWeights(),
|
||||
builder.GetStencilDvWeights(),
|
||||
/*ctrlVerts*/false,
|
||||
firstOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user