/* -*-c++-*- */
/* osgEarth - Dynamic map generation toolkit for OpenSceneGraph
* Copyright 2008-2012 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/
#ifndef OSGEARTH_SPLAT_SPLAT_TERRAIN_EFFECT_H
#define OSGEARTH_SPLAT_SPLAT_TERRAIN_EFFECT_H

#include "SplatCatalog"
#include "SplatCoverageLegend"
#include "Biome"
#include "BiomeSelector"
#include "SplatShaders"

#include <osgEarth/TerrainEffect>
#include <osgEarth/ImageLayer>
#include <osg/Image>
#include <osg/Uniform>
#include <osg/Texture2DArray>

using namespace osgEarth;

namespace osgEarth { namespace Splat
{
    /**
     * Effect that applies texture splatting to the terrain.
     */
    class SplatTerrainEffect : public TerrainEffect
    {
    public:
        /** construct a new terrain effect. */
        SplatTerrainEffect(
            const BiomeVector&    biomes,
            SplatCoverageLegend*  legend,
            const osgDB::Options* dbOptions);

        /**
         * Sets the image layer that supplies the coverage values. This value
         * is mandatory, and you must call it prior to installing the effect.
         */
        void setCoverageLayer(const ImageLayer* layer) { _coverageLayer = layer; }
        const ImageLayer* getCoverageLayer() { return _coverageLayer.get(); }

        /**
         * Sets the draw priority of the splat effect. Default is -1.0, which means
         * the splat will render before the terrain engine renders any image layers.
         * You can set it to 1.0 to splat after drawing image layers.
         *
         * This is options, but if you call it you must do so prior to installing
         * the effect.
         */
        void setRenderOrder(float value) { _renderOrder = value; }
        float getRenderOrder() const { return _renderOrder; }

        /**
         * Uniform that governs coverage sample warping. The value is type FLOAT.
         * This warps the texture coordinates used to sample the coverage data
         * using a simplex noise function. This can help to mitigate the "blockiness"
         * that somes from low resolution coverage data.
         * Reasonable values are in the range [0 .. 0.01]
         */
        osg::Uniform* getCoverageWarpUniform() { return _warpUniform.get(); }

        /**
         * Uniform that governs the bilinear sampling of the coverage texture.
         * The default is 1.0, at which normal bilinear sampling occurs.
         * Higher values will blur the splatting, and lower values will sharpen it.
         */
        osg::Uniform* getCoverageBlurUniform() { return _blurUniform.get(); }

        /**
         * Uniform that governs the scale level-of-detail offset when sampling 
         * splat textures. Defaults to 0. This is an integer such that for a
         * value of N, the texture coordinates are scaled by 2^N when sampling
         * splat textures.
         */
        osg::Uniform* getScaleLevelOffsetUniform() { return _scaleOffsetUniform.get(); }


    public: // TerrainEffect interface

        void onInstall(TerrainEngineNode* engine);

        void onUninstall(TerrainEngineNode* engine);


    protected:
        virtual ~SplatTerrainEffect() { }

        void installCoverageSamplingFunction(SplatTextureDef& textureDef);
        osg::Texture* createNoiseTexture() const;

        // these 2 vectors are index-aligned:
        BiomeVector                         _biomes;
        SplatTextureDefVector               _textureDefs;

        bool                                _ok;
        int                                 _splatTexUnit;
        osg::ref_ptr<osg::Uniform>          _splatTexUniform;
        osg::ref_ptr<osg::Uniform>          _coverageTexUniform;
        osg::ref_ptr<osg::Uniform>          _scaleOffsetUniform;
        osg::ref_ptr<osg::Uniform>          _warpUniform;
        osg::ref_ptr<osg::Uniform>          _blurUniform;
        osg::ref_ptr<SplatCoverageLegend>   _legend;
        osg::observer_ptr<const ImageLayer> _coverageLayer;
        float                               _renderOrder;
        int                                 _noiseTexUnit;
        osg::ref_ptr<osg::Texture>          _noiseTex;
        osg::ref_ptr<osg::Uniform>          _noiseTexUniform;
        osg::ref_ptr<osg::Uniform>          _noiseScaleUniform;
        osg::ref_ptr<osg::Uniform>          _useBilinearUniform;

        osg::ref_ptr<BiomeSelector>         _biomeSelector;

        bool                                _editMode;
        bool                                _gpuNoise;

    };

} } // namespace osgEarth::Splat

#endif // OSGEARTH_SPLAT_SPLAT_TERRAIN_EFFECT_H
