Permalink
Browse files

Rotate texture values when filtering for tangent-space textures

  • Loading branch information...
bmwang authored and Brent Burley committed Oct 8, 2013
1 parent fd01d0f commit 7bff9132f919c88b96fb82f753530cbd50ca5233
View
@@ -67,6 +67,7 @@ struct PtexIO : public Ptex {
uint64_t lmddatasize;
uint64_t editdatasize;
uint64_t editdatapos;
EdgeFilterMode edgefiltermode;
};
struct LevelInfo {
uint64_t leveldatasize;
View
@@ -92,6 +92,12 @@ bool PtexReader::open(const char* path, Ptex::String& error)
memset(&_extheader, 0, sizeof(_extheader));
readBlock(&_extheader, PtexUtils::min(uint32_t(ExtHeaderSize), _header.extheadersize));
// file version 1.4 added edgefiltermode into ExtHeader
// previous versions have behavior equivalent to edgefiltermode = efm_none
if (_header.version == 1 && _header.minorversion < 4) {
_extheader.edgefiltermode = efm_none;
}
// compute offsets of various blocks
FilePos pos = HeaderSize + _header.extheadersize;
_faceinfopos = pos; pos += _header.faceinfosize;
View
@@ -82,6 +82,7 @@ class PtexReader : public PtexCachedFile, public PtexTexture, public PtexIO {
virtual Ptex::DataType dataType() { return DataType(_header.datatype); }
virtual Ptex::BorderMode uBorderMode() { return BorderMode(_extheader.ubordermode); }
virtual Ptex::BorderMode vBorderMode() { return BorderMode(_extheader.vbordermode); }
virtual Ptex::EdgeFilterMode edgeFilterMode() { return EdgeFilterMode(_extheader.edgefiltermode); }
virtual int alphaChannel() { return _header.alphachan; }
virtual int numChannels() { return _header.nchannels; }
virtual int numFaces() { return _header.nfaces; }
@@ -54,6 +54,7 @@ void PtexSeparableFilter::eval(float* result, int firstChan, int nChannels,
if (faceid < 0 || faceid >= _tx->numFaces()) return;
_ntxchan = _tx->numChannels();
_dt = _tx->dataType();
_efm = _tx->edgeFilterMode();
_firstChanOffset = firstChan*DataSize(_dt);
_nchan = PtexUtils::min(nChannels, _ntxchan-firstChan);
@@ -355,7 +356,7 @@ void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::
if (!dh) return;
if (dh->isConstant()) {
k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan);
k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _efm, _nchan);
}
else if (dh->isTiled()) {
Ptex::Res tileres = dh->tileRes();
@@ -377,14 +378,14 @@ void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::
PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) );
if (th) {
if (th->isConstant())
kt.applyConst(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan);
kt.applyConst(_result, (char*)th->getData()+_firstChanOffset, _dt, _efm, _nchan);
else
kt.apply(_result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
kt.apply(_result, (char*)th->getData()+_firstChanOffset, _dt, _efm, _nchan, _ntxchan);
}
}
}
}
else {
k.apply(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
k.apply(_result, (char*)dh->getData()+_firstChanOffset, _dt, _efm, _nchan, _ntxchan);
}
}
@@ -53,7 +53,8 @@ class PtexSeparableFilter : public PtexFilter, public Ptex
PtexSeparableFilter(PtexTexture* tx, const PtexFilter::Options& opts ) :
_tx(tx), _options(opts), _result(0), _weight(0),
_firstChanOffset(0), _nchan(0), _ntxchan(0),
_dt((DataType)0), _uMode(tx->uBorderMode()), _vMode(tx->vBorderMode()) {}
_dt((DataType)0), _uMode(tx->uBorderMode()), _vMode(tx->vBorderMode()),
_efm(tx->edgeFilterMode()) {}
virtual ~PtexSeparableFilter() {}
virtual void buildKernel(PtexSeparableKernel& k, float u, float v, float uw, float vw,
@@ -75,6 +76,7 @@ class PtexSeparableFilter : public PtexFilter, public Ptex
int _ntxchan; // number of channels in texture
DataType _dt; // data type of texture
BorderMode _uMode, _vMode; // border modes (clamp,black,periodic)
EdgeFilterMode _efm; // edge filter mode (rotate when kernel is rotated or not)
};
#endif
@@ -38,6 +38,87 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
#include "PtexSeparableKernel.h"
namespace {
// Vector Accumulate with a rotation function for the first 2 channels
// (to rotate based on rotation of face)
template<typename T, int n>
struct VecAccumTV {
void operator()(float* dst, const T* val, float weight,
void (*rotFn)(float*, const T*, float))
{
rotFn(dst, val, weight);
PtexUtils::VecAccum<T,n-2>()(dst+2, val+2, weight);
}
};
// for nchan=1,0, don't rotate
template<typename T>
struct VecAccumTV<T,1> {
void operator()(float* dst, const T* val, float weight,
void (*)(float*, const T*, float))
{
PtexUtils::VecAccum<T,1>()(dst, val, weight);
}
};
template<typename T>
struct VecAccumTV<T,0> {
void operator()(float* dst, const T* val, float weight,
void (*)(float*, const T*, float))
{
PtexUtils::VecAccum<T,0>()(dst, val, weight);
}
};
// generic rotated vec accum.
template<typename T>
struct VecAccumNTV {
void operator()(float* dst, const T* val, int nchan, float weight,
void (*rotFn)(float*, const T*, float))
{
if (nchan >= 2)
{
rotFn(dst, val, weight);
PtexUtils::VecAccumN<T>()(dst+2, val+2, nchan-2, weight);
}
else {
PtexUtils::VecAccumN<T>()(dst, val, nchan, weight);
}
}
};
// Rotation functions
// Similar to the rotations for UV sampling coordinates
// R0/1/2/3 are the number of rotations CCW (called from rotate()
template<typename T>
void VecAccumTV_R0(float* dst, const T* val, float weight)
{
dst[0] += val[0] * weight;
dst[1] += val[1] * weight;
}
template<typename T>
void VecAccumTV_R1(float* dst, const T* val, float weight)
{
dst[0] -= val[1] * weight;
dst[1] += val[0] * weight;
}
template<typename T>
void VecAccumTV_R2(float* dst, const T* val, float weight)
{
dst[0] -= val[0] * weight;
dst[1] -= val[1] * weight;
}
template<typename T>
void VecAccumTV_R3(float* dst, const T* val, float weight)
{
dst[0] += val[1] * weight;
dst[1] -= val[0] * weight;
}
typedef void (*AccumRotFn)(float* dst, const float* src, float weight);
static AccumRotFn accumRotFunctions[4] = {
VecAccumTV_R0<float>, VecAccumTV_R1<float>,
VecAccumTV_R2<float>, VecAccumTV_R3<float>,
};
// apply to 1..4 channels (unrolled channel loop) of packed data (nTxChan==nChan)
template<class T, int nChan>
void Apply(PtexSeparableKernel& k, float* result, void* data, int /*nChan*/, int /*nTxChan*/)
@@ -49,6 +130,7 @@ namespace {
float* kvp = k.kv;
T* p = (T*)data + (k.v * k.res.u() + k.u) * nChan;
T* pEnd = p + k.vw * rowlen;
AccumRotFn rot = accumRotFunctions[k.rot&3];
while (p != pEnd)
{
float* kup = k.ku;
@@ -63,7 +145,7 @@ namespace {
p += nChan;
}
// result[i] += rowResult[i] * kv[v] for i in {0..n-1}
PtexUtils::VecAccum<float,nChan>()(result, rowResult, *kvp++);
VecAccumTV<float,nChan>()(result, rowResult, *kvp++, rot);
p += rowskip;
}
}
@@ -79,6 +161,7 @@ namespace {
float* kvp = k.kv;
T* p = (T*)data + (k.v * k.res.u() + k.u) * nTxChan;
T* pEnd = p + k.vw * rowlen;
AccumRotFn rot = accumRotFunctions[k.rot&3];
while (p != pEnd)
{
float* kup = k.ku;
@@ -93,7 +176,7 @@ namespace {
p += nTxChan;
}
// result[i] += rowResult[i] * kv[v] for i in {0..n-1}
PtexUtils::VecAccum<float,nChan>()(result, rowResult, *kvp++);
VecAccumTV<float,nChan>()(result, rowResult, *kvp++, rot);
p += rowskip;
}
}
@@ -109,6 +192,7 @@ namespace {
float* kvp = k.kv;
T* p = (T*)data + (k.v * k.res.u() + k.u) * nTxChan;
T* pEnd = p + k.vw * rowlen;
AccumRotFn rot = accumRotFunctions[k.rot&3];
while (p != pEnd)
{
float* kup = k.ku;
@@ -123,10 +207,42 @@ namespace {
p += nTxChan;
}
// result[i] += rowResult[i] * kv[v] for i in {0..n-1}
PtexUtils::VecAccumN<float>()(result, rowResult, nChan, *kvp++);
VecAccumNTV<float>()(result, rowResult, nChan, *kvp++, rot);
p += rowskip;
}
}
// apply const with consideration to rot
template<class T>
void ApplyConstNTV(float weight, float* dst, void* data, int nChan, int rot)
{
typedef void (*RotConstFn)(float* dst, const T* val, float weight);
RotConstFn rotFn;
switch (rot&3)
{
default: rotFn = VecAccumTV_R0<T>; break;
case 1: rotFn = VecAccumTV_R1<T>; break;
case 2: rotFn = VecAccumTV_R2<T>; break;
case 3: rotFn = VecAccumTV_R3<T>; break;
}
VecAccumNTV<T>()(dst, (T*) data, nChan, weight, rotFn);
}
template<class T, int nChan>
void ApplyConstTV(float weight, float* dst, void* data, int /*nChan*/, int rot)
{
typedef void (*RotConstFn)(float* dst, const T* val, float weight);
RotConstFn rotFn;
switch (rot&3)
{
default: rotFn = VecAccumTV_R0<T>; break;
case 1: rotFn = VecAccumTV_R1<T>; break;
case 2: rotFn = VecAccumTV_R2<T>; break;
case 3: rotFn = VecAccumTV_R3<T>; break;
}
VecAccumTV<T, nChan>()(dst, (T*) data, weight, rotFn);
}
}
@@ -146,4 +262,14 @@ PtexSeparableKernel::applyFunctions[] = {
ApplyS<uint8_t,2>, ApplyS<uint16_t,2>, ApplyS<PtexHalf,2>, ApplyS<float,2>,
ApplyS<uint8_t,3>, ApplyS<uint16_t,3>, ApplyS<PtexHalf,3>, ApplyS<float,3>,
ApplyS<uint8_t,4>, ApplyS<uint16_t,4>, ApplyS<PtexHalf,4>, ApplyS<float,4>,
};
PtexSeparableKernel::ApplyConstFn
PtexSeparableKernel::applyConstFunctions[] = {
ApplyConstNTV<uint8_t>, ApplyConstNTV<uint16_t>, ApplyConstNTV<PtexHalf>, ApplyConstNTV<float>,
ApplyConstTV<uint8_t,1>, ApplyConstTV<uint16_t,1>, ApplyConstTV<PtexHalf,1>, ApplyConstTV<float,1>,
ApplyConstTV<uint8_t,2>, ApplyConstTV<uint16_t,2>, ApplyConstTV<PtexHalf,2>, ApplyConstTV<float,2>,
ApplyConstTV<uint8_t,3>, ApplyConstTV<uint16_t,3>, ApplyConstTV<PtexHalf,3>, ApplyConstTV<float,3>,
ApplyConstTV<uint8_t,4>, ApplyConstTV<uint16_t,4>, ApplyConstTV<PtexHalf,4>, ApplyConstTV<float,4>,
};
@@ -53,25 +53,26 @@ class PtexSeparableKernel : public Ptex {
static const int kmax = 10; // max kernel width
float kubuff[kmax];
float kvbuff[kmax];
int rot;
PtexSeparableKernel()
: res(0), u(0), v(0), uw(0), vw(0), ku(kubuff), kv(kvbuff) {}
: res(0), u(0), v(0), uw(0), vw(0), ku(kubuff), kv(kvbuff), rot(0) {}
PtexSeparableKernel(const PtexSeparableKernel& k)
{
set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv);
set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv, k.rot);
}
PtexSeparableKernel& operator= (const PtexSeparableKernel& k)
{
set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv);
set(k.res, k.u, k.v, k.uw, k.vw, k.ku, k.kv, k.rot);
return *this;
}
void set(Res resVal,
int uVal, int vVal,
int uwVal, int vwVal,
const float* kuVal, const float* kvVal)
const float* kuVal, const float* kvVal, int rotVal=0)
{
assert(uwVal <= kmax && vwVal <= kmax);
res = resVal;
@@ -83,6 +84,7 @@ class PtexSeparableKernel : public Ptex {
memcpy(kvbuff, kvVal, sizeof(*kv)*vw);
ku = kubuff;
kv = kvbuff;
rot = rotVal;
}
void stripZeros()
@@ -243,15 +245,16 @@ class PtexSeparableKernel : public Ptex {
std::swap(ku, kv);
}
void rotate(int rot)
void rotate(int rotVal)
{
// rotate kernel 'rot' steps ccw
switch (rot & 3) {
switch (rotVal & 3) {
default: return;
case 1: flipU(); swapUV(); break;
case 2: flipU(); flipV(); break;
case 3: flipV(); swapUV(); break;
}
rot = (rot + rotVal)&3;
}
bool adjustMainToSubface(int eid)
@@ -445,21 +448,26 @@ class PtexSeparableKernel : public Ptex {
return newWeight;
}
void apply(float* dst, void* data, DataType dt, int nChan, int nTxChan)
void apply(float* dst, void* data, DataType dt, EdgeFilterMode efm, int nChan, int nTxChan)
{
// dispatch specialized apply function
if (efm == efm_none)
rot = 0;
ApplyFn fn = applyFunctions[(nChan!=nTxChan)*20 + ((unsigned)nChan<=4)*nChan*4 + dt];
fn(*this, dst, data, nChan, nTxChan);
}
void applyConst(float* dst, void* data, DataType dt, int nChan)
void applyConst(float* dst, void* data, DataType dt, EdgeFilterMode efm, int nChan)
{
PtexUtils::applyConst(weight(), dst, data, dt, nChan);
if (efm == efm_none)
rot = 0;
ApplyConstFn fn = applyConstFunctions[((unsigned)nChan<=4)*nChan*4 + dt];
fn(weight(), dst, data, nChan, rot);
}
private:
typedef void (*ApplyFn)(PtexSeparableKernel& k, float* dst, void* data, int nChan, int nTxChan);
typedef void (*ApplyConstFn)(float weight, float* dst, void* data, int nChan);
typedef void (*ApplyConstFn)(float weight, float* dst, void* data, int nChan, int rot);
static ApplyFn applyFunctions[40];
static ApplyConstFn applyConstFunctions[20];
static inline float accumulate(const float* p, int n)
View
@@ -68,6 +68,14 @@ const char* Ptex::BorderModeName(BorderMode m)
return names[m];
}
const char* Ptex::EdgeFilterModeName(EdgeFilterMode m)
{
static const char* names[] = { "none", "tanvec" };
if (m < 0 || m >= int(sizeof(names)/sizeof(const char*)))
return "(invalid edge filter mode)";
return names[m];
}
const char* Ptex::EdgeIdName(EdgeId eid)
{
View
@@ -760,6 +760,9 @@ PtexMainWriter::PtexMainWriter(const char* path, PtexTexture* tex,
// copy border modes
setBorderModes(tex->uBorderMode(), tex->vBorderMode());
// copy edge filter mode
setEdgeFilterMode(tex->edgeFilterMode());
// copy meta data from existing file
PtexPtr<PtexMetaData> meta ( _reader->getMetaData() );
writeMeta(meta);
View
@@ -53,6 +53,10 @@ class PtexWriterBase : public PtexWriter, public PtexIO {
_extheader.ubordermode = uBorderMode;
_extheader.vbordermode = vBorderMode;
}
virtual void setEdgeFilterMode(Ptex::EdgeFilterMode edgeFilterMode)
{
_extheader.edgefiltermode = edgeFilterMode;
}
virtual void writeMeta(const char* key, const char* value);
virtual void writeMeta(const char* key, const int8_t* value, int count);
virtual void writeMeta(const char* key, const int16_t* value, int count);
Oops, something went wrong.

0 comments on commit 7bff913

Please sign in to comment.