Esempio n. 1
0
void
SplatTerrainEffect::onInstall(TerrainEngineNode* engine)
{
    if ( engine )
    {
        // Check that we have coverage data (required for now - later masking data will be an option)
        if ( !_coverage.valid() || !_coverage->hasLayer() )
        {
            OE_WARN << LC << "ILLEGAL: coverage data is required\n";
            return;
        }

        if ( !_surface.valid() )
        {
            OE_WARN << LC << "Note: no surface information available\n";
        }

        // First, create a surface splatting texture array for each biome region.
        if ( _coverage.valid() && _surface.valid() )
        {
            if ( createSplattingTextures(_coverage.get(), _surface.get(), _textureDefs) == false )
            {
                OE_WARN << LC << "Failed to create any valid splatting textures\n";
                return;
            }
            
            // First install a shared noise texture.
            if ( _gpuNoise == false )
            {
                osg::StateSet* terrainStateSet = engine->getOrCreateStateSet();
                if ( terrainStateSet->getUniform("oe_splat_noiseTex") == 0L )
                {
                    // reserve a texture unit:
                    if (engine->getResources()->reserveTextureImageUnit(_noiseTexUnit, "Splat Noise"))
                    {
                        NoiseTextureFactory noise;
                        terrainStateSet->setTextureAttribute( _noiseTexUnit, noise.create(256u, 4u) );
                        terrainStateSet->addUniform( new osg::Uniform("oe_splat_noiseTex", _noiseTexUnit) );
                    }
                }
            }

            // Set up surface splatting:
            if ( engine->getResources()->reserveTextureImageUnit(_splatTexUnit, "Splat Coverage Data") )
            {
                osg::StateSet* stateset;

                if ( _surface->getBiomeRegions().size() == 1 )
                    stateset = engine->getSurfaceStateSet();
                else
                    stateset = new osg::StateSet();

                // surface splatting texture array:
                _splatTexUniform = stateset->getOrCreateUniform( SPLAT_SAMPLER, osg::Uniform::SAMPLER_2D_ARRAY );
                _splatTexUniform->set( _splatTexUnit );
                stateset->setTextureAttribute( _splatTexUnit, _textureDefs[0]._texture.get() );

                // coverage code sampler:
                osg::ref_ptr<ImageLayer> coverageLayer;
                _coverage->lockLayer( coverageLayer );

                _coverageTexUniform = stateset->getOrCreateUniform( COVERAGE_SAMPLER, osg::Uniform::SAMPLER_2D );
                _coverageTexUniform->set( coverageLayer->shareImageUnit().get() );

                // control uniforms (TODO: simplify and deprecate unneeded uniforms)
                stateset->addUniform( _scaleOffsetUniform.get() );
                stateset->addUniform( _warpUniform.get() );
                stateset->addUniform( _blurUniform.get() );
                stateset->addUniform( _noiseScaleUniform.get() );
                stateset->addUniform( _useBilinearUniform.get() );

                stateset->addUniform(new osg::Uniform("oe_splat_detailRange",  1000000.0f));


                SplattingShaders splatting;

                splatting.define( "SPLAT_EDIT",        _editMode );
                splatting.define( "SPLAT_GPU_NOISE",   _gpuNoise );
                splatting.define( "OE_USE_NORMAL_MAP", engine->normalTexturesRequired() );

                splatting.replace( "$COVERAGE_TEXTURE_MATRIX", coverageLayer->shareTexMatUniformName().get() );
            
                VirtualProgram* vp = VirtualProgram::getOrCreate(stateset);
                splatting.load( vp, splatting.VertModel );
                splatting.load( vp, splatting.VertView );
                splatting.load( vp, splatting.Frag );
                splatting.load( vp, splatting.Util );

                // GPU noise is expensive, so only use it to tweak noise function values that you
                // can later bake into the noise texture generator.
                if ( _gpuNoise )
                {                
                    //osgEarth::replaceIn( fragmentShader, "#undef SPLAT_GPU_NOISE", "#define SPLAT_GPU_NOISE" );

                    // Use --uniform on the command line to tweak these values:
                    stateset->addUniform(new osg::Uniform("oe_splat_freq",   32.0f));
                    stateset->addUniform(new osg::Uniform("oe_splat_pers",    0.8f));
                    stateset->addUniform(new osg::Uniform("oe_splat_lac",     2.2f));
                    stateset->addUniform(new osg::Uniform("oe_splat_octaves", 8.0f));

                    // support shaders
                    std::string noiseShaderSource = ShaderLoader::load( splatting.Noise, splatting );
                    osg::Shader* noiseShader = new osg::Shader(osg::Shader::FRAGMENT, noiseShaderSource);
                    vp->setShader( "oe_splat_noiseshaders", noiseShader );
                }

                // TODO: disabled biome selection temporarily because the callback impl applies the splatting shader
                // to the land cover bin as well as the surface bin, which we do not want -- find another way
                if ( _surface->getBiomeRegions().size() == 1 )
                {
                    // install his biome's texture set:
                    stateset->setTextureAttribute(_splatTexUnit, _textureDefs[0]._texture.get());

                    // install this biome's sampling function. Use cloneOrCreate since each
                    // stateset needs a different shader set in its VP.
                    VirtualProgram* vp = VirtualProgram::cloneOrCreate( stateset );
                    osg::Shader* shader = new osg::Shader(osg::Shader::FRAGMENT, _textureDefs[0]._samplingFunction);
                    vp->setShader( "oe_splat_getRenderInfo", shader );
                }

                else
                {
                    OE_WARN << LC << "Multi-biome setup needs re-implementing (reminder)\n";

                    // install the cull callback that will select the appropriate
                    // state based on the position of the camera.
                    _biomeRegionSelector = new BiomeRegionSelector(
                        _surface->getBiomeRegions(),
                        _textureDefs,
                        stateset,
                        _splatTexUnit );

                    engine->addCullCallback( _biomeRegionSelector.get() );
                }
            }
        }
    }
}
void
GroundCoverLayer::buildStateSets()
{
    // assert we have the necessary TIUs:
    if (_groundCoverTexBinding.valid() == false) {
        OE_DEBUG << LC << "buildStateSets deferred.. bindings not reserved\n";
        return;
    }

    if (!_zonesConfigured) {
        OE_DEBUG << LC << "buildStateSets deferred.. zones not yet configured\n";
        return;
    }
    
    osg::ref_ptr<LandCoverDictionary> landCoverDict;
    if (_landCoverDict.lock(landCoverDict) == false) {
        OE_DEBUG << LC << "buildStateSets deferred.. land cover dictionary not available\n";
        return;
    }
    
    osg::ref_ptr<LandCoverLayer> landCoverLayer;
    if (_landCoverLayer.lock(landCoverLayer) == false) {
        OE_DEBUG << LC << "buildStateSets deferred.. land cover layer not available\n";
        return;
    }

    NoiseTextureFactory noise;
    osg::ref_ptr<osg::Texture> noiseTexture = noise.create(256u, 4u);

    GroundCoverShaders shaders;

    // Layer-wide stateset:
    osg::StateSet* stateset = getOrCreateStateSet();

    // bind the noise sampler.
    stateset->setTextureAttribute(_noiseBinding.unit(), noiseTexture.get());
    stateset->addUniform(new osg::Uniform(NOISE_SAMPLER, _noiseBinding.unit()));

    if (_maskLayer.valid())
    {
        stateset->setDefine("OE_GROUNDCOVER_MASK_SAMPLER", _maskLayer->shareTexUniformName().get());
        stateset->setDefine("OE_GROUNDCOVER_MASK_MATRIX", _maskLayer->shareTexMatUniformName().get());
    }

    // disable backface culling to support shadow/depth cameras,
    // for which the geometry shader renders cross hatches instead of billboards.
    stateset->setMode(GL_CULL_FACE, osg::StateAttribute::PROTECTED);

    // enable alpha-to-coverage multisampling for vegetation.
    stateset->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, 1);

    // uniform that communicates the availability of multisampling.
    if (osg::DisplaySettings::instance()->getMultiSamples())
    {
        stateset->setDefine("OE_GROUNDCOVER_HAS_MULTISAMPLES");
    }

    stateset->setAttributeAndModes(
        new osg::BlendFunc(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO),
        osg::StateAttribute::OVERRIDE);


    for (Zones::iterator z = _zones.begin(); z != _zones.end(); ++z)
    {
        Zone* zone = z->get();
        GroundCover* groundCover = zone->getGroundCover();
        if (groundCover)
        {
            if (!groundCover->getBiomes().empty() || groundCover->getTotalNumBillboards() > 0)
            {
                osg::StateSet* zoneStateSet = groundCover->getOrCreateStateSet();
                            
                // Install the land cover shaders on the state set
                VirtualProgram* vp = VirtualProgram::getOrCreate(zoneStateSet);
                vp->setName("Ground cover (" + groundCover->getName() + ")");
                shaders.loadAll(vp, getReadOptions());

                // Generate the coverage acceptor shader
                osg::Shader* covTest = groundCover->createPredicateShader(_landCoverDict.get(), _landCoverLayer.get());
                covTest->setName(covTest->getName() + "_GEOMETRY");
                covTest->setType(osg::Shader::GEOMETRY);
                vp->setShader(covTest);

                osg::Shader* covTest2 = groundCover->createPredicateShader(_landCoverDict.get(), _landCoverLayer.get());
                covTest->setName(covTest->getName() + "_TESSCONTROL");
                covTest2->setType(osg::Shader::TESSCONTROL);
                vp->setShader(covTest2);

                osg::ref_ptr<osg::Shader> layerShader = groundCover->createShader();
                layerShader->setType(osg::Shader::GEOMETRY);
                vp->setShader(layerShader.get());

                OE_INFO << LC << "Established zone \"" << zone->getName() << "\" at LOD " << getLOD() << "\n";

                osg::Texture* tex = groundCover->createTexture();

                zoneStateSet->setTextureAttribute(_groundCoverTexBinding.unit(), tex);
                zoneStateSet->addUniform(new osg::Uniform(GCTEX_SAMPLER, _groundCoverTexBinding.unit()));

                OE_DEBUG << LC << "buildStateSets completed!\n";
            }
            else
            {
                OE_WARN << LC << "ILLEGAL: ground cover layer with no biomes or no billboards defined\n";
            }
        }
        else
        {
            // not an error.
            OE_DEBUG << LC << "zone contains no ground cover information\n";
        }
    }
}
void
LandCoverTerrainEffect::onInstall(TerrainEngineNode* engine)
{
    // First verify that the land cover definition is legal
    if ( !_landCover.valid() )
        return;

    if ( engine )
    {
        // install a noise texture if we haven't already.
        osg::StateSet* terrainStateSet = engine->getOrCreateStateSet();

        // check whether it's already installed by another extension:
        if ( terrainStateSet->getUniform("oe_splat_noiseTex") == 0L )
        {
            // reserve a texture unit:
            if (engine->getResources()->reserveTextureImageUnit(_noiseTexUnit, "Splat Noise"))
            {
                NoiseTextureFactory noise;
                terrainStateSet->setTextureAttribute( _noiseTexUnit, noise.create(256u, 4u) );
                terrainStateSet->addUniform( new osg::Uniform("oe_splat_noiseTex", _noiseTexUnit) );
            }
        }

        for(LandCoverLayers::const_iterator i = _landCover->getLayers().begin();
            i != _landCover->getLayers().end();
            ++i)
        {
            const LandCoverLayer* layer = i->get();
            if ( layer )
            {
                if ( !layer->getBiomes().empty() )
                {
                    osg::StateSet* stateset = engine->addLandCoverGroup( layer->getName(), layer->getLOD() );
                    if ( stateset )
                    {
                        // Install the land cover shaders on the state set
                        VirtualProgram* vp = VirtualProgram::getOrCreate(stateset);
                        LandCoverShaders shaders;
                        shaders.loadAll( vp, _dbo.get() );

                        // Generate the coverage acceptor shader
                        osg::Shader* covTest = layer->createPredicateShader( getCoverage() );
                        covTest->setType( osg::Shader::TESSCONTROL );
                        vp->setShader( covTest );

                        osg::ref_ptr<osg::Shader> layerShader = layer->createShader();
                        layerShader->setType( osg::Shader::GEOMETRY );
                        vp->setShader( layerShader );

                        OE_INFO << LC << "Adding land cover group: " << layer->getName() << " at LOD " << layer->getLOD() << "\n";

                        // Install the uniforms
                        stateset->addUniform( new osg::Uniform("oe_landcover_windFactor", layer->getWind()) );
                        stateset->addUniform( new osg::Uniform("oe_landcover_noise", 1.0f) );
                        stateset->addUniform( new osg::Uniform("oe_landcover_ao", 0.5f) );
                        stateset->addUniform( new osg::Uniform("oe_landcover_exposure", 1.0f) );
                    
                        stateset->addUniform( new osg::Uniform("oe_landcover_density",     layer->getDensity()) );
                        stateset->addUniform( new osg::Uniform("oe_landcover_fill",        layer->getFill()) );
                        stateset->addUniform( new osg::Uniform("oe_landcover_maxDistance", layer->getMaxDistance()) );

                        stateset->addUniform( new osg::Uniform("oe_landcover_brightness",  layer->getBrightness()) );
                        stateset->addUniform( new osg::Uniform("oe_landcover_contrast",    layer->getContrast()) );

                        stateset->addUniform( new osg::Uniform("oe_landcover_noise", 0.75f) );

                        int unit;
                        if ( engine->getResources()->reserveTextureImageUnit(unit, "LandCover") )
                        {
                            int s=-1, t=-1;

                            osg::Texture2DArray* tex = new osg::Texture2DArray();
                            
                            int arrayIndex = 0;
                            for(int b=0; b<layer->getBiomes().size(); ++b)
                            {
                                const LandCoverBiome* biome = layer->getBiomes().at(b);
                                
                                for(int i=0; i<biome->getBillboards().size(); ++i, ++arrayIndex)
                                {
                                    const LandCoverBillboard& bb = biome->getBillboards().at(i);

                                    osg::ref_ptr<osg::Image> im;

                                    if ( s < 0 )
                                    {
                                        s  = bb._image->s();
                                        t  = bb._image->t();
                                        im = bb._image.get();
                                        tex->setTextureSize(s, t, layer->getTotalNumBillboards());                              
                                    }
                                    else
                                    {
                                        if ( bb._image->s() != s || bb._image->t() != t )
                                        {
                                            ImageUtils::resizeImage( bb._image.get(), s, t, im );
                                        }
                                        else
                                        {
                                            im = bb._image.get();
                                        }
                                    }

                                    tex->setImage( arrayIndex, im.get() );
                                }
                            }
                                    
                            tex->setFilter(tex->MIN_FILTER, tex->NEAREST_MIPMAP_LINEAR);
                            tex->setFilter(tex->MAG_FILTER, tex->LINEAR);
                            tex->setWrap  (tex->WRAP_S, tex->CLAMP_TO_EDGE);
                            tex->setWrap  (tex->WRAP_T, tex->CLAMP_TO_EDGE);
                            tex->setUnRefImageDataAfterApply( true );
                            tex->setMaxAnisotropy( 4.0 );
                            tex->setResizeNonPowerOfTwoHint( false );

                            stateset->setTextureAttribute(unit, tex);
                            stateset->addUniform(new osg::Uniform("oe_landcover_texArray", unit) );
                        }
                        else
                        {
                            OE_WARN << LC << "Terrain engine failed to allocate a texture image unit for a land cover group\n";
                        }
                    }
                    else
                    {
                        OE_WARN << LC << "Terrain engine failed to return a stateset for a land cover group\n";
                    }
                }
                else
                {
                    OE_WARN << LC << "ILLEGAL: land cover layer with no biomes defined\n";
                }
            }
            else
            {
                OE_WARN << LC << "ILLEGAL: empty layer found in land cover layer list\n";
            }
        }
    }
}