- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files
- Directories
- Examples

# PtexImporter/ptex/PtexSeparableFilter.cpp

    /*
    PTEX SOFTWARE
    Copyright 2009 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 <math.h>
    #include <assert.h>

    #include "PtexSeparableFilter.h"
    #include "PtexSeparableKernel.h"
    #include "PtexUtils.h"

    //#define NOEDGEBLEND // uncomment to disable filtering across edges (for debugging)

    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::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);

    #ifdef NOEDGEBLEND
        // for debugging only
        if (splitR) k.mergeR(_uMode);
        if (splitL) k.mergeL(_uMode);
        if (splitT) k.mergeT(_vMode);
        if (splitB) k.mergeB(_vMode);
    #else
        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);
        }
        }
    #endif

        // 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);
        k.makeSymmetric();
        for (int i = 1; i <= numCorners; i++) {
            PtexSeparableKernel kc = k;
            applyToCornerFace(kc, f, 2, cfaceId[i], *cface[i], cedgeId[i]);
        }
        // adjust weight for additional corners (one was already counted)
        _weight += k.weight() * (numCorners-1);
        }
        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);
        }
        else 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);
        }
    }