PtexSeparableFilter.cpp
Go to the documentation of this file.
/*
PTEX SOFTWARE
Copyright 2014 Disney Enterprises, Inc. All rights reserved
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* The names "Disney", "Walt Disney Pictures", "Walt Disney Animation
Studios" or the names of its contributors may NOT be used to
endorse or promote products derived from this software without
specific prior written permission from Walt Disney Pictures.
Disclaimer: THIS SOFTWARE IS PROVIDED BY WALT DISNEY PICTURES AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE, NONINFRINGEMENT AND TITLE ARE DISCLAIMED.
IN NO EVENT SHALL WALT DISNEY PICTURES, THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND BASED ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*/
#include "PtexPlatform.h"
#include <cmath>
#include <assert.h>
#include "PtexSeparableFilter.h"
#include "PtexSeparableKernel.h"
#include "PtexUtils.h"
PTEX_NAMESPACE_BEGIN
void PtexSeparableFilter::eval(float* result, int firstChan, int nChannels,
int faceid, float u, float v,
float uw1, float vw1, float uw2, float vw2,
float width, float blur)
{
// init
if (!_tx || nChannels <= 0) return;
if (faceid < 0 || faceid >= _tx->numFaces()) return;
_firstChanOffset = firstChan*DataSize(_dt);
_nchan = PtexUtils::min(nChannels, _ntxchan-firstChan);
// get face info
const FaceInfo& f = _tx->getFaceInfo(faceid);
// if neighborhood is constant, just return constant value of face
if (f.isNeighborhoodConstant()) {
PtexPtr<PtexFaceData> data ( _tx->getData(faceid, 0) );
if (data) {
char* d = (char*) data->getData() + _firstChanOffset;
Ptex::ConvertToFloat(result, d, _dt, _nchan);
}
return;
}
// find filter width as bounding box of vectors w1 and w2
float uw = PtexUtils::abs(uw1) + PtexUtils::abs(uw2), vw = PtexUtils::abs(vw1) + PtexUtils::abs(vw2);
// handle border modes
bool return_black = false;
switch (_uMode) {
case m_clamp: u = PtexUtils::clamp(u, 0.0f, 1.0f); break;
case m_periodic: u = u-PtexUtils::floor(u); break;
case m_black: if (u <= -1.0f || u >= 2.0f) return_black = true; break;
}
switch (_vMode) {
case m_clamp: v = PtexUtils::clamp(v, 0.0f, 1.0f); break;
case m_periodic: v = v-PtexUtils::floor(v); break;
case m_black: if (v <= -1.0f || v >= 2.0f) return_black = true; break;
}
if (return_black) {
memset(result, 0, sizeof(float)*_nchan);
return;
}
// build kernel
PtexSeparableKernel k;
if (f.isSubface()) {
// for a subface, build the kernel as if it were on a main face and then downres
uw = uw * width + blur * 2.0f;
vw = vw * width + blur * 2.0f;
buildKernel(k, u*.5f, v*.5f, uw*.5f, vw*.5f,
Ptex::Res((int8_t)(f.res.ulog2+1),(int8_t)(f.res.vlog2+1)));
if (k.res.ulog2 == 0) k.upresU();
if (k.res.vlog2 == 0) k.upresV();
k.res.ulog2--; k.res.vlog2--;
}
else {
uw = uw * width + blur;
vw = vw * width + blur;
buildKernel(k, u, v, uw, vw, f.res);
}
k.stripZeros();
// check kernel (debug only)
assert(k.uw > 0 && k.vw > 0);
assert(k.uw <= PtexSeparableKernel::kmax && k.vw <= PtexSeparableKernel::kmax);
_weight = k.weight();
// allocate temporary result
_result = (float*) alloca(sizeof(float)*_nchan);
memset(_result, 0, sizeof(float)*_nchan);
// apply to faces
splitAndApply(k, faceid, f);
// normalize (both for data type and cumulative kernel weight applied)
// and output result
float scale = 1.0f / (_weight * OneValue(_dt));
for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale);
// clear temp result
_result = 0;
}
void PtexSeparableFilter::splitAndApply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
{
// do we need to split? (i.e. does kernel span an edge?)
bool splitR = (k.u+k.uw > k.res.u()), splitL = (k.u < 0);
bool splitT = (k.v+k.vw > k.res.v()), splitB = (k.v < 0);
if (_options.noedgeblend) {
if (splitR) k.mergeR(_uMode);
if (splitL) k.mergeL(_uMode);
if (splitT) k.mergeT(_vMode);
if (splitB) k.mergeB(_vMode);
apply(k, faceid, f);
return;
}
if (splitR || splitL || splitT || splitB) {
PtexSeparableKernel ka, kc;
if (splitR) {
if (f.adjface(e_right) >= 0) {
k.splitR(ka);
if (splitT) {
if (f.adjface(e_top) >= 0) {
ka.splitT(kc);
applyToCorner(kc, faceid, f, e_top);
}
else ka.mergeT(_vMode);
}
if (splitB) {
if (f.adjface(e_bottom) >= 0) {
ka.splitB(kc);
applyToCorner(kc, faceid, f, e_right);
}
else ka.mergeB(_vMode);
}
applyAcrossEdge(ka, faceid, f, e_right);
}
else k.mergeR(_uMode);
}
if (splitL) {
if (f.adjface(e_left) >= 0) {
k.splitL(ka);
if (splitT) {
if (f.adjface(e_top) >= 0) {
ka.splitT(kc);
applyToCorner(kc, faceid, f, e_left);
}
else ka.mergeT(_vMode);
}
if (splitB) {
if (f.adjface(e_bottom) >= 0) {
ka.splitB(kc);
applyToCorner(kc, faceid, f, e_bottom);
}
else ka.mergeB(_vMode);
}
applyAcrossEdge(ka, faceid, f, e_left);
}
else k.mergeL(_uMode);
}
if (splitT) {
if (f.adjface(e_top) >= 0) {
k.splitT(ka);
applyAcrossEdge(ka, faceid, f, e_top);
}
else k.mergeT(_vMode);
}
if (splitB) {
if (f.adjface(e_bottom) >= 0) {
k.splitB(ka);
applyAcrossEdge(ka, faceid, f, e_bottom);
}
else k.mergeB(_vMode);
}
}
// do local face
apply(k, faceid, f);
}
void PtexSeparableFilter::applyAcrossEdge(PtexSeparableKernel& k,
int faceid, const Ptex::FaceInfo& f, int eid)
{
int afid = f.adjface(eid), aeid = f.adjedge(eid);
const Ptex::FaceInfo* af = &_tx->getFaceInfo(afid);
int rot = eid - aeid + 2;
// adjust uv coord and res for face/subface boundary
bool fIsSubface = f.isSubface(), afIsSubface = af->isSubface();
if (fIsSubface != afIsSubface) {
if (afIsSubface) {
// main face to subface transition
// adjust res and offset uv coord for primary subface
bool primary = k.adjustMainToSubface(eid);
if (!primary) {
// advance ajacent face and edge id to secondary subface
int neid = (aeid + 3) % 4;
afid = af->adjface(neid);
aeid = af->adjedge(neid);
af = &_tx->getFaceInfo(afid);
rot += neid - aeid + 2;
}
}
else {
// subface to main face transition
// Note: the transform depends on which subface the kernel is
// coming from. The "primary" subface is the one the main
// face is pointing at. The secondary subface adjustment
// happens to be the same as for the primary subface for the
// next edge, so the cases can be combined.
bool primary = (af->adjface(aeid) == faceid);
k.adjustSubfaceToMain(eid - primary);
}
}
// rotate and apply (resplit if going to a subface)
k.rotate(rot);
if (afIsSubface) splitAndApply(k, afid, *af);
else apply(k, afid, *af);
}
void PtexSeparableFilter::applyToCorner(PtexSeparableKernel& k, int faceid,
const Ptex::FaceInfo& f, int eid)
{
// traverse clockwise around corner vertex and gather corner faces
int afid = faceid, aeid = eid;
const FaceInfo* af = &f;
bool prevIsSubface = af->isSubface();
const int MaxValence = 10;
int cfaceId[MaxValence];
int cedgeId[MaxValence];
const FaceInfo* cface[MaxValence];
int numCorners = 0;
for (int i = 0; i < MaxValence; i++) {
// advance to next face
int prevFace = afid;
afid = af->adjface(aeid);
aeid = (af->adjedge(aeid) + 1) % 4;
// we hit a boundary or reached starting face
// note: we need to check edge id too because we might have
// a periodic texture (w/ toroidal topology) where all 4 corners
// are from the same face
if (afid < 0 || (afid == faceid && aeid == eid)) {
numCorners = i - 2;
break;
}
// record face info
af = &_tx->getFaceInfo(afid);
cfaceId[i] = afid;
cedgeId[i] = aeid;
cface[i] = af;
// check to see if corner is a subface "tee"
bool isSubface = af->isSubface();
if (prevIsSubface && !isSubface && af->adjface((aeid+3)%4) == prevFace)
{
// adjust the eid depending on whether we started from
// the primary or secondary subface.
bool primary = (i==1);
k.adjustSubfaceToMain(eid + primary * 2);
k.rotate(eid - aeid + 3 - primary);
splitAndApply(k, afid, *af);
return;
}
prevIsSubface = isSubface;
}
if (numCorners == 1) {
// regular case (valence 4)
applyToCornerFace(k, f, eid, cfaceId[1], *cface[1], cedgeId[1]);
}
else if (numCorners > 1) {
// valence 5+, make kernel symmetric and apply equally to each face
// first, rotate to standard orientation, u=v=0
k.rotate(eid + 2);
float initialWeight = k.weight();
float newWeight = k.makeSymmetric(initialWeight);
for (int i = 1; i <= numCorners; i++) {
PtexSeparableKernel kc = k;
applyToCornerFace(kc, f, 2, cfaceId[i], *cface[i], cedgeId[i]);
}
// adjust weight for symmetrification and for additional corners
_weight += newWeight * (float)numCorners - initialWeight;
}
else {
// valence 2 or 3, ignore corner face (just adjust weight)
_weight -= k.weight();
}
}
void PtexSeparableFilter::applyToCornerFace(PtexSeparableKernel& k, const Ptex::FaceInfo& f, int eid,
int cfid, const Ptex::FaceInfo& cf, int ceid)
{
// adjust uv coord and res for face/subface boundary
bool fIsSubface = f.isSubface(), cfIsSubface = cf.isSubface();
if (fIsSubface != cfIsSubface) {
if (cfIsSubface) k.adjustMainToSubface(eid + 3);
else k.adjustSubfaceToMain(eid + 3);
}
// rotate and apply (resplit if going to a subface)
k.rotate(eid - ceid + 2);
if (cfIsSubface) splitAndApply(k, cfid, cf);
else apply(k, cfid, cf);
}
void PtexSeparableFilter::apply(PtexSeparableKernel& k, int faceid, const Ptex::FaceInfo& f)
{
assert(k.u >= 0 && k.u + k.uw <= k.res.u());
assert(k.v >= 0 && k.v + k.vw <= k.res.v());
if (k.uw <= 0 || k.vw <= 0) return;
// downres kernel if needed
while (k.res.u() > f.res.u()) k.downresU();
while (k.res.v() > f.res.v()) k.downresV();
// get face data, and apply
PtexPtr<PtexFaceData> dh ( _tx->getData(faceid, k.res) );
if (!dh) return;
if (dh->isConstant()) {
k.applyConst(_result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan);
return;
}
// allocate temporary result for tanvec mode (if needed)
bool tanvecMode = (_efm == efm_tanvec) && (_nchan >= 2) && (k.rot > 0);
float* result = tanvecMode ? (float*) alloca(sizeof(float)*_nchan) : _result;
if (tanvecMode) memset(result, 0, sizeof(float)*_nchan);
if (dh->isTiled()) {
Ptex::Res tileres = dh->tileRes();
PtexSeparableKernel kt;
kt.res = tileres;
int tileresu = tileres.u();
int tileresv = tileres.v();
int ntilesu = k.res.u() / tileresu;
for (int v = k.v, vw = k.vw; vw > 0; vw -= kt.vw, v += kt.vw) {
int tilev = v / tileresv;
kt.v = v % tileresv;
kt.vw = PtexUtils::min(vw, tileresv - kt.v);
kt.kv = k.kv + v - k.v;
for (int u = k.u, uw = k.uw; uw > 0; uw -= kt.uw, u += kt.uw) {
int tileu = u / tileresu;
kt.u = u % tileresu;
kt.uw = PtexUtils::min(uw, tileresu - kt.u);
kt.ku = k.ku + u - k.u;
PtexPtr<PtexFaceData> th ( dh->getTile(tilev * ntilesu + tileu) );
if (th) {
if (th->isConstant())
kt.applyConst(result, (char*)th->getData()+_firstChanOffset, _dt, _nchan);
else
kt.apply(result, (char*)th->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
}
}
}
}
else {
k.apply(result, (char*)dh->getData()+_firstChanOffset, _dt, _nchan, _ntxchan);
}
if (tanvecMode) {
// rotate tangent-space vector data and update main result
switch (k.rot) {
case 0: // rot==0 included for completeness, but tanvecMode should be false in this case
_result[0] += result[0];
_result[1] += result[1];
break;
case 1:
_result[0] -= result[1];
_result[1] += result[0];
break;
case 2:
_result[0] -= result[0];
_result[1] -= result[1];
break;
case 3:
_result[0] += result[1];
_result[1] -= result[0];
break;
}
for (int i = 2; i < _nchan; i++) _result[i] += result[i];
}
}
PTEX_NAMESPACE_END
PtexPlatform.h
Platform-specific classes, functions, and includes.
PtexSeparableFilter.h
PtexSeparableKernel.h
PtexUtils.h
PTEX_NAMESPACE_END
#define PTEX_NAMESPACE_END
Definition PtexVersion.h:62
PtexPtr
Smart-pointer for acquiring and releasing API objects.
Definition Ptexture.h:1032
PtexSeparableFilter::splitAndApply
void splitAndApply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
Definition PtexSeparableFilter.cpp:135
PtexSeparableFilter::applyToCornerFace
void applyToCornerFace(PtexSeparableKernel &k, const Ptex::FaceInfo &f, int eid, int cfaceid, const Ptex::FaceInfo &cf, int ceid)
Definition PtexSeparableFilter.cpp:331
PtexSeparableFilter::eval
virtual void eval(float *result, int firstchan, int nchannels, int faceid, float u, float v, float uw1, float vw1, float uw2, float vw2, float width, float blur)
Apply filter to a ptex data file.
Definition PtexSeparableFilter.cpp:47
PtexSeparableFilter::apply
void apply(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f)
Definition PtexSeparableFilter.cpp:348
PtexSeparableFilter::applyAcrossEdge
void applyAcrossEdge(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
Definition PtexSeparableFilter.cpp:215
PtexSeparableFilter::_result
float * _result
Definition PtexSeparableFilter.h:80
PtexSeparableFilter::_tx
PtexTexture * _tx
Definition PtexSeparableFilter.h:78
PtexSeparableFilter::_options
Options _options
Definition PtexSeparableFilter.h:79
PtexSeparableFilter::_uMode
BorderMode _uMode
Definition PtexSeparableFilter.h:86
PtexSeparableFilter::applyToCorner
void applyToCorner(PtexSeparableKernel &k, int faceid, const Ptex::FaceInfo &f, int eid)
Definition PtexSeparableFilter.cpp:257
PtexSeparableFilter::_ntxchan
int _ntxchan
Definition PtexSeparableFilter.h:84
PtexSeparableFilter::_weight
float _weight
Definition PtexSeparableFilter.h:81
PtexSeparableFilter::_firstChanOffset
int _firstChanOffset
Definition PtexSeparableFilter.h:82
PtexSeparableFilter::_efm
EdgeFilterMode _efm
Definition PtexSeparableFilter.h:87
PtexSeparableFilter::_dt
DataType _dt
Definition PtexSeparableFilter.h:85
PtexSeparableFilter::_vMode
BorderMode _vMode
Definition PtexSeparableFilter.h:86
PtexSeparableFilter::buildKernel
virtual void buildKernel(PtexSeparableKernel &k, float u, float v, float uw, float vw, Res faceRes)=0
PtexSeparableFilter::_nchan
int _nchan
Definition PtexSeparableFilter.h:83
PtexSeparableKernel
Definition PtexSeparableKernel.h:48
PtexSeparableKernel::mergeT
void mergeT(BorderMode mode)
Definition PtexSeparableKernel.h:139
PtexSeparableKernel::mergeB
void mergeB(BorderMode mode)
Definition PtexSeparableKernel.h:129
PtexSeparableKernel::stripZeros
void stripZeros()
Definition PtexSeparableKernel.h:96
PtexSeparableKernel::apply
void apply(float *dst, void *data, DataType dt, int nChan, int nTxChan)
Definition PtexSeparableKernel.h:457
PtexSeparableKernel::makeSymmetric
float makeSymmetric(float initialWeight)
Definition PtexSeparableKernel.h:397
PtexSeparableKernel::u
int u
Definition PtexSeparableKernel.h:51
PtexSeparableKernel::kv
float * kv
Definition PtexSeparableKernel.h:54
PtexSeparableKernel::splitT
void splitT(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:214
PtexSeparableKernel::rot
int rot
Definition PtexSeparableKernel.h:58
PtexSeparableKernel::res
Res res
Definition PtexSeparableKernel.h:50
PtexSeparableKernel::adjustSubfaceToMain
void adjustSubfaceToMain(int eid)
Definition PtexSeparableKernel.h:302
PtexSeparableKernel::splitB
void splitB(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:192
PtexSeparableKernel::vw
int vw
Definition PtexSeparableKernel.h:52
PtexSeparableKernel::v
int v
Definition PtexSeparableKernel.h:51
PtexSeparableKernel::splitL
void splitL(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:148
PtexSeparableKernel::downresV
void downresV()
Definition PtexSeparableKernel.h:342
PtexSeparableKernel::ku
float * ku
Definition PtexSeparableKernel.h:53
PtexSeparableKernel::uw
int uw
Definition PtexSeparableKernel.h:52
PtexSeparableKernel::mergeR
void mergeR(BorderMode mode)
Definition PtexSeparableKernel.h:120
PtexSeparableKernel::applyConst
void applyConst(float *dst, void *data, DataType dt, int nChan)
Definition PtexSeparableKernel.h:464
PtexSeparableKernel::adjustMainToSubface
bool adjustMainToSubface(int eid)
Definition PtexSeparableKernel.h:266
PtexSeparableKernel::mergeL
void mergeL(BorderMode mode)
Definition PtexSeparableKernel.h:110
PtexSeparableKernel::upresV
void upresV()
Definition PtexSeparableKernel.h:384
PtexSeparableKernel::upresU
void upresU()
Definition PtexSeparableKernel.h:371
PtexSeparableKernel::splitR
void splitR(PtexSeparableKernel &k)
Definition PtexSeparableKernel.h:171
PtexSeparableKernel::kmax
static const int kmax
Definition PtexSeparableKernel.h:55
PtexSeparableKernel::rotate
void rotate(int rotVal)
Definition PtexSeparableKernel.h:254
PtexSeparableKernel::weight
float weight() const
Definition PtexSeparableKernel.h:105
PtexSeparableKernel::downresU
void downresU()
Definition PtexSeparableKernel.h:313
PtexTexture::getData
virtual void getData(int faceid, void *buffer, int stride)=0
Access texture data for a face at highest-resolution.
PtexTexture::numFaces
virtual int numFaces()=0
Number of faces stored in file.
PtexTexture::getFaceInfo
virtual const Ptex::FaceInfo & getFaceInfo(int faceid)=0
Access resolution and adjacency information about a face.
PTEX_NAMESPACE_BEGIN
Definition PtexSeparableKernel.cpp:42
PtexUtils::clamp
T clamp(T x, T lo, T hi)
Definition PtexUtils.h:154
PtexUtils::abs
T abs(T x)
Definition PtexUtils.h:134
PtexUtils::min
T min(T a, T b)
Definition PtexUtils.h:148
Ptex::ConvertToFloat
void ConvertToFloat(float *dst, const void *src, Ptex::DataType dt, int numChannels)
Convert a number of data values from the given data type to float.
PtexFilter::Options::noedgeblend
bool noedgeblend
Disable cross-face filtering. Useful for debugging or rendering on polys.
Definition Ptexture.h:961
Ptex::FaceInfo
Information about a face, as stored in the Ptex file header.
Definition Ptexture.h:229
Ptex::FaceInfo::isSubface
bool isSubface() const
Determine if face is a subface (by checking a flag).
Definition Ptexture.h:271
Ptex::FaceInfo::res
Res res
Resolution of face.
Definition Ptexture.h:230
Ptex::FaceInfo::adjedge
EdgeId adjedge(int eid) const
Access an adjacent edge id. The eid value must be 0..3.
Definition Ptexture.h:256
Ptex::FaceInfo::adjface
int adjface(int eid) const
Access an adjacent face id. The eid value must be 0..3.
Definition Ptexture.h:259
Ptex::Res
Pixel resolution of a given texture.
Definition Ptexture.h:159
Ptex::Res::v
int v() const
V resolution in texels.
Definition Ptexture.h:176
Ptex::Res::u
int u() const
U resolution in texels.
Definition Ptexture.h:173