void PtexTriangleFilter::applyAcrossEdge(PtexTriangleKernel& k, const Ptex::FaceInfo& f, int eid) { int afid = f.adjface(eid), aeid = f.adjedge(eid); const Ptex::FaceInfo& af = _tx->getFaceInfo(afid); k.reorient(eid, aeid); splitAndApply(k, afid, af); }
PTEX_NAMESPACE_BEGIN void PtexTriangleFilter::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; _ntxchan = _tx->numChannels(); _dt = _tx->dataType(); _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; } // clamp u and v u = PtexUtils::clamp(u, 0.0f, 1.0f); v = PtexUtils::clamp(v, 0.0f, 1.0f); // build kernel PtexTriangleKernel k; buildKernel(k, u, v, uw1, vw1, uw2, vw2, width, blur, f.res); // accumulate the weight as we apply _weight = 0; // 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::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::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::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; _ntxchan = _tx->numChannels(); _dt = _tx->dataType(); _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 = fabs(uw1) + fabs(uw2), vw = fabs(vw1) + fabs(vw2); // handle border modes switch (_uMode) { case m_clamp: u = PtexUtils::clamp(u, 0.0f, 1.0f); break; case m_periodic: u = u-floor(u); break; case m_black: break; // do nothing } switch (_vMode) { case m_clamp: v = PtexUtils::clamp(v, 0.0f, 1.0f); break; case m_periodic: v = v-floor(v); case m_black: break; // do nothing } // 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; vw = vw * width + blur * 2; buildKernel(k, u*.5, v*.5, uw*.5, vw*.5, f.res); 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 double-precision result _result = (double*) alloca(sizeof(double)*_nchan); memset(_result, 0, sizeof(double)*_nchan); // apply to faces splitAndApply(k, faceid, f); // normalize (both for data type and cumulative kernel weight applied) // and output result double scale = 1.0 / (_weight * OneValue(_dt)); for (int i = 0; i < _nchan; i++) result[i] = float(_result[i] * scale); // clear temp result _result = 0; }
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); double initialWeight = k.weight(); double 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 * numCorners - initialWeight; } else { // valence 2 or 3, ignore corner face (just adjust weight) _weight -= k.weight(); } }