mipimage_texture.hpp

Back to Image-based texture with mip-map resampling

pastel/gfx/texture/

#ifndef PASTELGFX_MIPIMAGE_TEXTURE_HPP
#define PASTELGFX_MIPIMAGE_TEXTURE_HPP

#include "pastel/gfx/texture/mipimage_texture.h"
#include "pastel/gfx/texture/linearimage_texture.h"

#include "pastel/gfx/mipmap/mipmap.h"

#include "pastel/sys/view/arrayview.h"
#include "pastel/sys/view/repeatedconstview.h"
#include "pastel/sys/view/view_tools.h"

namespace Pastel
{

    template <typename Type, integer N>
    MipImage_Texture<Type, N>::MipImage_Texture()
        : mipMap_(0)
        , extender_()
    {
    }

    template <typename Type, integer N>
    MipImage_Texture<Type, N>::MipImage_Texture(
        const MipMap<Type, N>& mipMap,
        const ArrayExtender_& extender)
        : mipMap_(&mipMap)
        , extender_(extender)
    {
    }

    template <typename Type, integer N>
    Type MipImage_Texture<Type, N>::operator()(
        const Vector<real, N>& uv,
        const Matrix<real>& m) const
    {
        if (!mipMap_ || mipMap_->empty())
        {
            return Type();
        }

        integer n = m.height();

        const Array<Type, N>& mostDetailedImage = 
            mipMap_->mostDetailed();

        // Compute detail level.

        // This is done by finding the tangent
        // vector with the greatest length and using
        // that as a isotropic sampling frequency.
        // The one of greatest length is used to ensure
        // we get rid of aliasing. 

        real d = 0;
        for (integer i = 0;i < n;++i)
        {
            const real dotMi = dot(m.cColumn(i) * Vector<real, N>(mostDetailedImage.extent()));
            if (dotMi > d)
            {
                d = dotMi;
            }
        }

        real invLn2 = inverse(constantLn2<real>());

        const real level = 0.5 * std::log(d) * invLn2;

        // Handle the case where no filtering needs to be done.

        if (level <= 0)
        {
            return sampleLinear(
                evaluate(uv * Vector<real, N>(mostDetailedImage.extent())),
                mostDetailedImage, extender_);
        }

        // Handle the case where the image is smaller than
        // a single pixel.

        if (level >= mipMap_->levels() - 1)
        {
            return mipMap_->coarsest()(0);
        }

        // Gather samples from the 2 neighboring mipmaps.

        // First sample from the more detailed image.

        integer detailLevel = std::floor(level);

        const Array<Type, N>& detailImage = 
            (*mipMap_)(detailLevel);

        Type detailSample =
            sampleLinear(
            evaluate(uv * Vector<real, N>(detailImage.extent())),
            detailImage, extender_);

        // Then sample from the less detailed image.

        integer coarseLevel = detailLevel + 1;

        const Array<Type, N>& coarseImage = 
            (*mipMap_)(coarseLevel);

        Type coarseSample =
            sampleLinear(
            evaluate(uv * Vector<real, N>(coarseImage.extent())),
            coarseImage, extender_);

        // Linearly interpolate these samples by the 
        // fractional detail level.

        real tDetail = level - detailLevel;
        return linear(detailSample, coarseSample, tDetail);
    }

}

#endif