ripimage_texture.hpp

Back to Image-based texture using rip-map resampling

pastel/gfx/texture/

#ifndef PASTELGFX_RIPIMAGE_TEXTURE_HPP
#define PASTELGFX_RIPIMAGE_TEXTURE_HPP

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

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

#include "pastel/sys/vector/vector_tools.h"

namespace Pastel
{

    template <typename Type, integer N>
    RipImage_Texture<Type, N>::RipImage_Texture()
        : ripMap_(0)
        , extender_()
    {
    }

    template <typename Type, integer N>
    RipImage_Texture<Type, N>::RipImage_Texture(
        const RipMap<Type, N>& ripMap,
        const ArrayExtender_& extender)
        : ripMap_(&ripMap)
        , extender_(extender)
    {
    }

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

        integer n = uv.size();

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

        Vector<real, N> radius =
            max(abs(m)) * Vector<real, N>(mostDetailedImage.extent());

        if (allLessEqual(radius, 1))
        {
            // Magnification: just do linear interpolation.

            return sampleLinear(
                evaluate(uv * Vector<real, N>(mostDetailedImage.extent())),
                mostDetailedImage, extender_);
        }

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

        const Vector<real, N> level(max(evaluate(log(radius) * invLn2), 0));

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

        if (anyGreaterEqual(level, Vector<real, N>(ripMap_->levels() - 1)))
        {
            // Return the coarsest ripmap pixel.

            return ripMap_->coarsest()(0);
        }

        // Gather samples from all of the neighboring 
        // 2^n ripmaps.

        Vector<integer, N> p(floor(level));
        Vector<real, N> tDetail = level - Vector<real, N>(p);

        integer samples = (integer)1 << n;

        Tuple<Type, ModifyN<N, 1 << N>::Result> valueSet(ofDimension(samples));
        Tuple<bool, N> s(ofDimension(n), false);
        for (integer i = 0;i < samples;++i)
        {

            const Array<Type, N>& image = (*ripMap_)(p);

            valueSet[i] = sampleLinear(
                evaluate(uv * Vector<real, N>(image.extent())),
                image, extender_);

            integer axis = 0;
            while(axis < n)
            {
                if (s[axis])
                {
                    s[axis] = false;
                    --p[axis];
                }
                else
                {
                    s[axis] = true;
                    ++p[axis];
                    break;
                }
                ++axis;
            }
        }

        // Linearly interpolate between those samples.

        return linear(tDetail, 
            range(valueSet.begin(), valueSet.end()));
    }

}

#endif