// Generates the main shader code for rendering the terrain. void MPTerrainEngineNode::updateState() { if ( _batchUpdateInProgress ) { _stateUpdateRequired = true; } else { if ( _elevationTextureUnit < 0 && elevationTexturesRequired() ) { getResources()->reserveTextureImageUnit( _elevationTextureUnit, "MP Engine Elevation" ); } osg::StateSet* terrainStateSet = getTerrainStateSet(); // required for multipass tile rendering to work terrainStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); // activate standard mix blending. terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); // install shaders, if we're using them. if ( Registry::capabilities().supportsGLSL() ) { VirtualProgram* vp = new VirtualProgram(); vp->setName( "osgEarth.engine_mp.TerrainNode" ); terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON ); // bind the vertex attributes generated by the tile compiler. vp->addBindAttribLocation( "oe_terrain_attr", osg::Drawable::ATTRIBUTE_6 ); vp->addBindAttribLocation( "oe_terrain_attr2", osg::Drawable::ATTRIBUTE_7 ); Shaders package; package.replace( "$MP_PRIMARY_UNIT", Stringify() << _primaryUnit ); package.replace( "$MP_SECONDARY_UNIT", Stringify() << (_secondaryUnit>=0?_secondaryUnit:0) ); package.define( "MP_USE_BLENDING", (_terrainOptions.enableBlending() == true) ); package.loadFunction( vp, package.VertexModel ); package.loadFunction( vp, package.VertexView ); package.loadFunction( vp, package.Fragment ); // terrain background color; negative means use the vertex color. Color terrainColor = _terrainOptions.color().getOrUse( Color(1,1,1,-1) ); terrainStateSet->addUniform(new osg::Uniform("oe_terrain_color", terrainColor)); // assemble color filter code snippets. bool haveColorFilters = false; { // Color filter frag function: std::string fs_colorfilters = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "uniform int oe_layer_uid; \n" "$COLOR_FILTER_HEAD" "void oe_mp_apply_filters(inout vec4 color) \n" "{ \n" "$COLOR_FILTER_BODY" "} \n"; std::stringstream cf_head; std::stringstream cf_body; const char* I = " "; // second, install the per-layer color filter functions AND shared layer bindings. bool ifStarted = false; int numImageLayers = _update_mapf->imageLayers().size(); for( int i=0; i<numImageLayers; ++i ) { ImageLayer* layer = _update_mapf->getImageLayerAt(i); if ( layer->getEnabled() ) { // install Color Filter function calls: const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { haveColorFilters = true; if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(color);\n"; filter->install( terrainStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } if ( haveColorFilters ) { std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs_colorfilters, "$COLOR_FILTER_HEAD", cf_head_str ); replaceIn( fs_colorfilters, "$COLOR_FILTER_BODY", cf_body_str ); vp->setFunction( "oe_mp_apply_filters", fs_colorfilters, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.5f ); } } // binding for the terrain texture terrainStateSet->getOrCreateUniform( "oe_layer_tex", osg::Uniform::SAMPLER_2D )->set( _primaryUnit ); // binding for the secondary texture (for LOD blending) if ( parentTexturesRequired() ) { terrainStateSet->getOrCreateUniform( "oe_layer_tex_parent", osg::Uniform::SAMPLER_2D )->set( _secondaryUnit ); // binding for the default secondary texture matrix osg::Matrixf parent_mat; parent_mat(0,0) = 0.0f; terrainStateSet->getOrCreateUniform( "oe_layer_parent_matrix", osg::Uniform::FLOAT_MAT4 )->set( parent_mat ); } // uniform for accessing the elevation texture sampler. if ( elevationTexturesRequired() ) { terrainStateSet->getOrCreateUniform( "oe_terrain_tex", osg::Uniform::SAMPLER_2D)->set( _elevationTextureUnit ); } // uniform that controls per-layer opacity terrainStateSet->getOrCreateUniform( "oe_layer_opacity", osg::Uniform::FLOAT )->set( 1.0f ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) // UID -1 => no image layer (no texture) terrainStateSet->getOrCreateUniform( "oe_layer_uid", osg::Uniform::INT )->set( -1 ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->getOrCreateUniform( "oe_layer_order", osg::Uniform::INT )->set( 0 ); // default min/max range uniforms. (max < min means ranges are disabled) terrainStateSet->addUniform( new osg::Uniform("oe_layer_minRange", 0.0f) ); terrainStateSet->addUniform( new osg::Uniform("oe_layer_maxRange", -1.0f) ); terrainStateSet->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); // special object ID that denotes the terrain surface. terrainStateSet->addUniform( new osg::Uniform( Registry::objectIndex()->getObjectIDUniformName().c_str(), OSGEARTH_OBJECTID_TERRAIN) ); } _stateUpdateRequired = false; } }
// Generates the main shader code for rendering the terrain. void MPTerrainEngineNode::updateState() { if ( _batchUpdateInProgress ) { _stateUpdateRequired = true; } else { osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); // required for multipass tile rendering to work terrainStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); // activate standard mix blending. terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); // install shaders, if we're using them. if ( Registry::capabilities().supportsGLSL() ) { VirtualProgram* vp = new VirtualProgram(); vp->setName( "osgEarth.engine_mp.TerrainNode" ); terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON ); // bind the vertex attributes generated by the tile compiler. vp->addBindAttribLocation( "oe_terrain_attr", osg::Drawable::ATTRIBUTE_6 ); vp->addBindAttribLocation( "oe_terrain_attr2", osg::Drawable::ATTRIBUTE_7 ); // Vertex shader: std::string vs = Stringify() << "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 oe_layer_texc;\n" "varying vec4 oe_layer_tilec;\n" "void oe_mp_setup_coloring(inout vec4 VertexModel) \n" "{ \n" " oe_layer_texc = gl_MultiTexCoord" << _primaryUnit << ";\n" " oe_layer_tilec = gl_MultiTexCoord" << _secondaryUnit << ";\n" "}\n"; bool useTerrainColor = _terrainOptions.color().isSet(); bool useBlending = _terrainOptions.enableBlending() == true; // Fragment Shader for normal blending: std::string fs = Stringify() << "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 oe_layer_texc; \n" "uniform sampler2D oe_layer_tex; \n" "uniform int oe_layer_uid; \n" "uniform int oe_layer_order; \n" "uniform float oe_layer_opacity; \n" << (useTerrainColor ? "uniform vec4 oe_terrain_color; \n" : "" ) << "void oe_mp_apply_coloring(inout vec4 color) \n" "{ \n" << (useTerrainColor ? " color = oe_terrain_color; \n" : "" ) << //" color = vec4(1,1,1,1); \n" " vec4 texel; \n" " if ( oe_layer_uid >= 0 ) { \n" " texel = texture2D(oe_layer_tex, oe_layer_texc.st); \n" " texel.a *= oe_layer_opacity; \n" " } \n" " else \n" " texel = color; \n" " " << (useBlending ? " if ( oe_layer_order == 0 ) \n" " color = texel*texel.a + color*(1.0-texel.a); \n" // simulate src_alpha, 1-src_alpha blens " else \n" : "" ) << " color = texel; \n" "} \n"; // Color filter frag function: std::string fs_colorfilters = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "uniform int oe_layer_uid; \n" "__COLOR_FILTER_HEAD__" "void oe_mp_apply_filters(inout vec4 color) \n" "{ \n" "__COLOR_FILTER_BODY__" "} \n"; vp->setFunction( "oe_mp_setup_coloring", vs, ShaderComp::LOCATION_VERTEX_MODEL, 0.0 ); vp->setFunction( "oe_mp_apply_coloring", fs, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 ); // assemble color filter code snippets. bool haveColorFilters = false; { std::stringstream cf_head; std::stringstream cf_body; const char* I = " "; // second, install the per-layer color filter functions AND shared layer bindings. bool ifStarted = false; int numImageLayers = _update_mapf->imageLayers().size(); for( int i=0; i<numImageLayers; ++i ) { ImageLayer* layer = _update_mapf->getImageLayerAt(i); if ( layer->getEnabled() ) { // install Color Filter function calls: const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { haveColorFilters = true; if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(color);\n"; filter->install( terrainStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } if ( haveColorFilters ) { std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs_colorfilters, "__COLOR_FILTER_HEAD__", cf_head_str ); replaceIn( fs_colorfilters, "__COLOR_FILTER_BODY__", cf_body_str ); vp->setFunction( "oe_mp_apply_filters", fs_colorfilters, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 ); } } // binding for the terrain texture terrainStateSet->getOrCreateUniform( "oe_layer_tex", osg::Uniform::SAMPLER_2D )->set( _primaryUnit ); // binding for the secondary texture (for LOD blending) terrainStateSet->getOrCreateUniform( "oe_layer_tex_parent", osg::Uniform::SAMPLER_2D )->set( _secondaryUnit ); // binding for the default secondary texture matrix osg::Matrixf parent_mat; parent_mat(0,0) = 0.0f; terrainStateSet->getOrCreateUniform( "oe_layer_parent_matrix", osg::Uniform::FLOAT_MAT4 )->set( parent_mat ); // uniform that controls per-layer opacity terrainStateSet->getOrCreateUniform( "oe_layer_opacity", osg::Uniform::FLOAT )->set( 1.0f ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) // UID -1 => no image layer (no texture) terrainStateSet->getOrCreateUniform( "oe_layer_uid", osg::Uniform::INT )->set( -1 ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->getOrCreateUniform( "oe_layer_order", osg::Uniform::INT )->set( 0 ); // base terrain color. if ( useTerrainColor ) { terrainStateSet->getOrCreateUniform( "oe_terrain_color", osg::Uniform::FLOAT_VEC4 )->set( *_terrainOptions.color() ); } } _stateUpdateRequired = false; } }
// Generates the main shader code for rendering the terrain. void RexTerrainEngineNode::updateState() { if ( _batchUpdateInProgress ) { _stateUpdateRequired = true; } else { osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); // everything osg::StateSet* surfaceStateSet = getSurfaceStateSet(); // just the surface // required for multipass tile rendering to work surfaceStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); // activate standard mix blending. terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); // install patch param if we are tessellation on the GPU. if ( _terrainOptions.gpuTessellation() == true ) { #ifdef HAVE_PATCH_PARAMETER terrainStateSet->setAttributeAndModes( new osg::PatchParameter(3) ); #endif } // install shaders, if we're using them. if ( Registry::capabilities().supportsGLSL() ) { Shaders package; VirtualProgram* terrainVP = VirtualProgram::getOrCreate(terrainStateSet); terrainVP->setName( "Rex Terrain" ); package.load(terrainVP, package.ENGINE_VERT_MODEL); //moved to CTOR so it's always available //package.load(terrainVP, package.SDK); bool useTerrainColor = _terrainOptions.color().isSet(); package.define("OE_REX_USE_TERRAIN_COLOR", useTerrainColor); if ( useTerrainColor ) { surfaceStateSet->addUniform(new osg::Uniform("oe_terrain_color", _terrainOptions.color().get())); } bool useBlending = _terrainOptions.enableBlending().get(); package.define("OE_REX_GL_BLENDING", useBlending); bool morphImagery = _terrainOptions.morphImagery().get(); package.define("OE_REX_MORPH_IMAGERY", morphImagery); // Funtions that affect only the terrain surface: VirtualProgram* surfaceVP = VirtualProgram::getOrCreate(surfaceStateSet); surfaceVP->setName("Rex Surface"); // Functions that affect the terrain surface only: package.load(surfaceVP, package.ENGINE_VERT_VIEW); package.load(surfaceVP, package.ENGINE_FRAG); // Normal mapping shaders: if ( this->normalTexturesRequired() ) { package.load(surfaceVP, package.NORMAL_MAP_VERT); package.load(surfaceVP, package.NORMAL_MAP_FRAG); } // Morphing? if (_terrainOptions.morphTerrain() == true || _terrainOptions.morphImagery() == true) { package.define("OE_REX_VERTEX_MORPHING", (_terrainOptions.morphTerrain() == true)); package.load(surfaceVP, package.MORPHING_VERT); } for(LandCoverZones::iterator zone = _landCoverData._zones.begin(); zone != _landCoverData._zones.end(); ++zone) { for(LandCoverBins::iterator bin = zone->_bins.begin(); bin != zone->_bins.end(); ++bin) { osg::StateSet* landCoverStateSet = bin->_binProto->getStateSet(); // enable alpha-to-coverage multisampling for vegetation. landCoverStateSet->setMode(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB, 1); // uniform that communicates the availability of multisampling. landCoverStateSet->addUniform( new osg::Uniform( "oe_terrain_hasMultiSamples", osg::DisplaySettings::instance()->getMultiSamples()) ); landCoverStateSet->setAttributeAndModes( new osg::BlendFunc(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO), osg::StateAttribute::OVERRIDE ); #ifdef HAVE_OSG_PATCH_PARAMETER landCoverStateSet->setAttributeAndModes( new osg::PatchParameter(3) ); #endif } } // assemble color filter code snippets. bool haveColorFilters = false; { // Color filter frag function: std::string fs_colorfilters = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "uniform int oe_layer_uid; \n" "$COLOR_FILTER_HEAD" "void oe_rexEngine_applyFilters(inout vec4 color) \n" "{ \n" "$COLOR_FILTER_BODY" "} \n"; std::stringstream cf_head; std::stringstream cf_body; const char* I = " "; // second, install the per-layer color filter functions AND shared layer bindings. bool ifStarted = false; int numImageLayers = _update_mapf->imageLayers().size(); for( int i=0; i<numImageLayers; ++i ) { ImageLayer* layer = _update_mapf->getImageLayerAt(i); if ( layer->getEnabled() ) { // install Color Filter function calls: const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { haveColorFilters = true; if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(color);\n"; filter->install( surfaceStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } if ( haveColorFilters ) { std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs_colorfilters, "$COLOR_FILTER_HEAD", cf_head_str ); replaceIn( fs_colorfilters, "$COLOR_FILTER_BODY", cf_body_str ); surfaceVP->setFunction( "oe_rexEngine_applyFilters", fs_colorfilters, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 ); } } // Apply uniforms for sampler bindings: OE_DEBUG << LC << "Render Bindings:\n"; for(RenderBindings::const_iterator b = _renderBindings.begin(); b != _renderBindings.end(); ++b) { if ( b->isActive() ) { terrainStateSet->addUniform( new osg::Uniform(b->samplerName().c_str(), b->unit()) ); OE_DEBUG << LC << " > Bound \"" << b->samplerName() << "\" to unit " << b->unit() << "\n"; } } // uniform that controls per-layer opacity terrainStateSet->addUniform( new osg::Uniform("oe_layer_opacity", 1.0f) ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) // UID -1 => no image layer (no texture) terrainStateSet->addUniform( new osg::Uniform("oe_layer_uid", (int)-1 ) ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->addUniform( new osg::Uniform("oe_layer_order", (int)0) ); // default min/max range uniforms. (max < min means ranges are disabled) terrainStateSet->addUniform( new osg::Uniform("oe_layer_minRange", 0.0f) ); terrainStateSet->addUniform( new osg::Uniform("oe_layer_maxRange", -1.0f) ); terrainStateSet->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); terrainStateSet->addUniform(new osg::Uniform("oe_tile_size", (float)_terrainOptions.tileSize().get())); // special object ID that denotes the terrain surface. surfaceStateSet->addUniform( new osg::Uniform( Registry::objectIndex()->getObjectIDUniformName().c_str(), OSGEARTH_OBJECTID_TERRAIN) ); } _stateUpdateRequired = false; } }
void RexTerrainEngineNode::addTileLayer(Layer* tileLayer) { if ( tileLayer && tileLayer->getEnabled() ) { // Install the image layer stateset on this layer. // Later we will refactor this into an ImageLayerRenderer or something similar. //osg::StateSet* stateSet = tileLayer->getOrCreateStateSet(); //stateSet->merge(*getSurfaceStateSet()); ImageLayer* imageLayer = dynamic_cast<ImageLayer*>(tileLayer); if (imageLayer) { // for a shared layer, allocate a shared image unit if necessary. if ( imageLayer->isShared() ) { optional<int>& unit = imageLayer->shareImageUnit(); if ( !unit.isSet() ) { int temp; if ( getResources()->reserveTextureImageUnit(temp) ) { imageLayer->shareImageUnit() = temp; OE_INFO << LC << "Image unit " << temp << " assigned to shared layer " << imageLayer->getName() << std::endl; } else { OE_WARN << LC << "Insufficient GPU image units to share layer " << imageLayer->getName() << std::endl; } } // Build a sampler binding for the shared layer. if ( unit.isSet() ) { // Find the next empty SHARED slot: unsigned newIndex = SamplerBinding::SHARED; while (_renderBindings[newIndex].isActive()) ++newIndex; // Put the new binding there: SamplerBinding& newBinding = _renderBindings[newIndex]; newBinding.usage() = SamplerBinding::SHARED; newBinding.sourceUID() = imageLayer->getUID(); newBinding.unit() = unit.get(); newBinding.samplerName() = imageLayer->shareTexUniformName().get(); newBinding.matrixName() = imageLayer->shareTexMatUniformName().get(); OE_INFO << LC << " .. Sampler=\"" << newBinding.samplerName() << "\", " << "Matrix=\"" << newBinding.matrixName() << ", " << "unit=" << newBinding.unit() << "\n"; } } } else { // non-image tile layer. Keep track of these.. } refresh(); } }
// Generates the main shader code for rendering the terrain. void RexTerrainEngineNode::updateState() { if ( _batchUpdateInProgress ) { _stateUpdateRequired = true; } else { osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); // everything osg::StateSet* surfaceStateSet = getSurfaceStateSet(); // just the surface //terrainStateSet->setRenderBinDetails(0, "SORT_FRONT_TO_BACK"); // required for multipass tile rendering to work surfaceStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); surfaceStateSet->setAttributeAndModes( new osg::CullFace(), osg::StateAttribute::ON); // activate standard mix blending. terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); // install patch param if we are tessellation on the GPU. if ( _terrainOptions.gpuTessellation() == true ) { #ifdef HAVE_PATCH_PARAMETER terrainStateSet->setAttributeAndModes( new osg::PatchParameter(3) ); #endif } // install shaders, if we're using them. if ( Registry::capabilities().supportsGLSL() ) { Shaders package; VirtualProgram* terrainVP = VirtualProgram::getOrCreate(terrainStateSet); terrainVP->setName( "Rex Terrain" ); package.load(terrainVP, package.ENGINE_VERT_MODEL); surfaceStateSet->addUniform(new osg::Uniform("oe_terrain_color", _terrainOptions.color().get())); if (_terrainOptions.enableBlending() == true) { surfaceStateSet->setDefine("OE_TERRAIN_BLEND_IMAGERY"); } // Funtions that affect only the terrain surface: VirtualProgram* surfaceVP = VirtualProgram::getOrCreate(surfaceStateSet); surfaceVP->setName("Rex Surface"); // Functions that affect the terrain surface only: package.load(surfaceVP, package.ENGINE_VERT_VIEW); package.load(surfaceVP, package.ENGINE_FRAG); // Elevation? if (this->elevationTexturesRequired()) { surfaceStateSet->setDefine("OE_TERRAIN_RENDER_ELEVATION"); } // Normal mapping shaders: if ( this->normalTexturesRequired() ) { package.load(surfaceVP, package.NORMAL_MAP_VERT); package.load(surfaceVP, package.NORMAL_MAP_FRAG); surfaceStateSet->setDefine("OE_TERRAIN_RENDER_NORMAL_MAP"); } // Morphing? if (_terrainOptions.morphTerrain() == true || _terrainOptions.morphImagery() == true) { package.load(surfaceVP, package.MORPHING_VERT); if (_terrainOptions.morphImagery() == true) { surfaceStateSet->setDefine("OE_TERRAIN_MORPH_IMAGERY"); } if (_terrainOptions.morphTerrain() == true) { surfaceStateSet->setDefine("OE_TERRAIN_MORPH_GEOMETRY"); } } // assemble color filter code snippets. bool haveColorFilters = false; { // Color filter frag function: std::string fs_colorfilters = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "uniform int oe_layer_uid; \n" "$COLOR_FILTER_HEAD" "void oe_rexEngine_applyFilters(inout vec4 color) \n" "{ \n" "$COLOR_FILTER_BODY" "} \n"; std::stringstream cf_head; std::stringstream cf_body; const char* I = " "; // second, install the per-layer color filter functions AND shared layer bindings. bool ifStarted = false; ImageLayerVector imageLayers; _update_mapf->getLayers(imageLayers); for( int i=0; i<imageLayers.size(); ++i ) { ImageLayer* layer = imageLayers.at(i); if ( layer->getEnabled() ) { // install Color Filter function calls: const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { haveColorFilters = true; if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(color);\n"; filter->install( surfaceStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } if ( haveColorFilters ) { std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs_colorfilters, "$COLOR_FILTER_HEAD", cf_head_str ); replaceIn( fs_colorfilters, "$COLOR_FILTER_BODY", cf_body_str ); surfaceVP->setFunction( "oe_rexEngine_applyFilters", fs_colorfilters, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.6 ); } } // Apply uniforms for sampler bindings: OE_DEBUG << LC << "Render Bindings:\n"; osg::ref_ptr<osg::Texture> tex = new osg::Texture2D(ImageUtils::createEmptyImage(1,1)); for (unsigned i = 0; i < _renderBindings.size(); ++i) { SamplerBinding& b = _renderBindings[i]; if (b.isActive()) { osg::Uniform* u = new osg::Uniform(b.samplerName().c_str(), b.unit()); terrainStateSet->addUniform( u ); OE_DEBUG << LC << " > Bound \"" << b.samplerName() << "\" to unit " << b.unit() << "\n"; terrainStateSet->setTextureAttribute(b.unit(), tex.get()); } } // uniform that controls per-layer opacity terrainStateSet->addUniform( new osg::Uniform("oe_layer_opacity", 1.0f) ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) // UID -1 => no image layer (no texture) terrainStateSet->addUniform( new osg::Uniform("oe_layer_uid", (int)-1 ) ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->addUniform( new osg::Uniform("oe_layer_order", (int)0) ); // default min/max range uniforms. (max < min means ranges are disabled) terrainStateSet->addUniform( new osg::Uniform("oe_layer_minRange", 0.0f) ); terrainStateSet->addUniform( new osg::Uniform("oe_layer_maxRange", -1.0f) ); terrainStateSet->addUniform( new osg::Uniform("oe_layer_attenuationRange", _terrainOptions.attentuationDistance().get()) ); terrainStateSet->getOrCreateUniform( "oe_min_tile_range_factor", osg::Uniform::FLOAT)->set( *_terrainOptions.minTileRangeFactor() ); terrainStateSet->addUniform(new osg::Uniform("oe_tile_size", (float)_terrainOptions.tileSize().get())); // special object ID that denotes the terrain surface. surfaceStateSet->addUniform( new osg::Uniform( Registry::objectIndex()->getObjectIDUniformName().c_str(), OSGEARTH_OBJECTID_TERRAIN) ); } _stateUpdateRequired = false; } }
// Generates the main shader code for rendering the terrain. void MPTerrainEngineNode::updateShaders() { if ( _batchUpdateInProgress ) { _shaderUpdateRequired = true; } else { osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); VirtualProgram* vp = new VirtualProgram(); vp->setName( "engine_mp:TerrainNode" ); terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON ); // bind the vertex attributes generated by the tile compiler. vp->addBindAttribLocation( "oe_terrain_attr", osg::Drawable::ATTRIBUTE_6 ); vp->addBindAttribLocation( "oe_terrain_attr2", osg::Drawable::ATTRIBUTE_7 ); // Vertex shader template: std::string vs = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 oe_layer_texc;\n" "varying vec4 oe_layer_tilec;\n" "void oe_mp_setup_coloring(inout vec4 VertexModel) \n" "{ \n" " oe_layer_texc = __GL_MULTITEXCOORD1__;\n" " oe_layer_tilec = __GL_MULTITEXCOORD2__;\n" "}\n"; // Fragment shader for normal blending: std::string fs = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 oe_layer_texc; \n" "uniform sampler2D oe_layer_tex; \n" "uniform int oe_layer_uid; \n" "uniform int oe_layer_order; \n" "uniform float oe_layer_opacity; \n" "void oe_mp_apply_coloring( inout vec4 color ) \n" "{ \n" " vec4 texel; \n" " if ( oe_layer_uid >= 0 ) { \n" " texel = texture2D(oe_layer_tex, oe_layer_texc.st); \n" " texel.a *= oe_layer_opacity; \n" " } \n" " else \n" " texel = color; \n" " if (oe_layer_order == 0 ) \n" " color = texel*texel.a + color*(1.0-texel.a); \n" // simulate src_alpha, 1-src_alpha blens " else \n" " color = texel; \n" "} \n"; // Fragment shader with pre-multiplied alpha blending: std::string fs_pma = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 oe_layer_texc; \n" "uniform sampler2D oe_layer_tex; \n" "uniform int oe_layer_uid; \n" "uniform int oe_layer_order; \n" "uniform float oe_layer_opacity; \n" "void oe_mp_apply_coloring_pma( inout vec4 color ) \n" "{ \n" " vec4 texelpma; \n" // a UID < 0 means no texture. " if ( oe_layer_uid >= 0 ) \n" " texelpma = texture2D(oe_layer_tex, oe_layer_texc.st) * oe_layer_opacity; \n" " else \n" " texelpma = color * color.a * oe_layer_opacity; \n" // to PMA. // first layer must PMA-blend with the globe color. " if (oe_layer_order == 0) { \n" " color.rgb *= color.a; \n" " color = texelpma + color*(1.0-texelpma.a); \n" // simulate one, 1-src_alpha blend " } \n" " else { \n" " color = texelpma; \n" " } \n" "} \n"; // Color filter frag function: std::string fs_colorfilters = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "uniform int oe_layer_uid; \n" "__COLOR_FILTER_HEAD__" "void oe_mp_apply_filters(inout vec4 color) \n" "{ \n" "__COLOR_FILTER_BODY__" "} \n"; // install the gl_MultiTexCoord* variable that uses the proper texture // image unit: replaceIn( vs, "__GL_MULTITEXCOORD1__", Stringify() << "gl_MultiTexCoord" << _primaryUnit ); replaceIn( vs, "__GL_MULTITEXCOORD2__", Stringify() << "gl_MultiTexCoord" << _secondaryUnit ); vp->setFunction( "oe_mp_setup_coloring", vs, ShaderComp::LOCATION_VERTEX_MODEL, 0.0 ); if ( _terrainOptions.premultipliedAlpha() == true ) vp->setFunction( "oe_mp_apply_coloring_pma", fs_pma, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 ); else vp->setFunction( "oe_mp_apply_coloring", fs, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 ); // assemble color filter code snippets. bool haveColorFilters = false; { std::stringstream cf_head; std::stringstream cf_body; const char* I = " "; if ( _terrainOptions.premultipliedAlpha() == true ) { // un-PMA the color before passing it to the color filters. cf_body << I << "if (color.a > 0.0) color.rgb /= color.a; \n"; } // second, install the per-layer color filter functions AND shared layer bindings. bool ifStarted = false; int numImageLayers = _update_mapf->imageLayers().size(); for( int i=0; i<numImageLayers; ++i ) { ImageLayer* layer = _update_mapf->getImageLayerAt(i); if ( layer->getEnabled() ) { // install Color Filter function calls: const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { haveColorFilters = true; if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(color);\n"; filter->install( terrainStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } if ( _terrainOptions.premultipliedAlpha() == true ) { // re-PMA the color after it passes through the color filters. cf_body << I << "color.rgb *= color.a; \n"; } if ( haveColorFilters ) { std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs_colorfilters, "__COLOR_FILTER_HEAD__", cf_head_str ); replaceIn( fs_colorfilters, "__COLOR_FILTER_BODY__", cf_body_str ); vp->setFunction( "oe_mp_apply_filters", fs_colorfilters, ShaderComp::LOCATION_FRAGMENT_COLORING, 0.0 ); } } if ( _terrainOptions.premultipliedAlpha() == true ) { // activate PMA blending. terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); } else { // activate standard mix blending. terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), osg::StateAttribute::ON ); } // required for multipass tile rendering to work terrainStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); // binding for the terrain texture terrainStateSet->getOrCreateUniform( "oe_layer_tex", osg::Uniform::SAMPLER_2D )->set( _primaryUnit ); // binding for the secondary texture (for LOD blending) terrainStateSet->getOrCreateUniform( "oe_layer_tex_parent", osg::Uniform::SAMPLER_2D )->set( _secondaryUnit ); // uniform that controls per-layer opacity terrainStateSet->getOrCreateUniform( "oe_layer_opacity", osg::Uniform::FLOAT )->set( 1.0f ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) // UID -1 => no image layer (no texture) terrainStateSet->getOrCreateUniform( "oe_layer_uid", osg::Uniform::INT )->set( -1 ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->getOrCreateUniform( "oe_layer_order", osg::Uniform::INT )->set( 0 ); _shaderUpdateRequired = false; } }
osg::Node* OSGTileFactory::createPopulatedTile(const MapFrame& mapf, Terrain* terrain, const TileKey& key, bool wrapInPagedLOD, bool fallback, bool& validData ) { const MapInfo& mapInfo = mapf.getMapInfo(); bool isPlateCarre = !mapInfo.isGeocentric() && mapInfo.isGeographicSRS(); typedef std::vector<GeoImageData> GeoImageDataVector; GeoImageDataVector image_tiles; // Collect the image layers bool empty_map = mapf.imageLayers().size() == 0 && mapf.elevationLayers().size() == 0; // Create the images for the tile for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) { ImageLayer* layer = i->get(); GeoImageData imageData; // Only try to create images if the key is valid if ( layer->isKeyValid( key ) ) { imageData._image = layer->createImage( key ); imageData._layerUID = layer->getUID(); imageData._imageTileKey = key; } // always push images, even it they are empty, so that the image_tiles vector is one-to-one // with the imageLayers() vector. image_tiles.push_back( imageData ); } bool hasElevation = false; //Create the heightfield for the tile osg::ref_ptr<osg::HeightField> hf; if ( mapf.elevationLayers().size() > 0 ) { mapf.getHeightField( key, false, hf, 0L, _terrainOptions.elevationInterpolation().value()); } //If we are on the first LOD and we couldn't get a heightfield tile, just create an empty one. Otherwise you can run into the situation //where you could have an inset heightfield on one hemisphere and the whole other hemisphere won't show up. if ( mapInfo.isGeocentric() && key.getLevelOfDetail() <= 1 && !hf.valid()) { hf = createEmptyHeightField( key ); } hasElevation = hf.valid(); //Determine if we've created any images unsigned int numValidImages = 0; for (unsigned int i = 0; i < image_tiles.size(); ++i) { if (image_tiles[i]._image.valid()) numValidImages++; } //If we couldn't create any imagery or heightfields, bail out if (!hf.valid() && (numValidImages == 0) && !empty_map) { OE_DEBUG << LC << "Could not create any imagery or heightfields for " << key.str() <<". Not building tile" << std::endl; validData = false; //If we're not asked to fallback on previous LOD's and we have no data, return NULL if (!fallback) { return NULL; } } else { validData = true; } //Try to interpolate any missing image layers from parent tiles for (unsigned int i = 0; i < mapf.imageLayers().size(); i++ ) { if (!image_tiles[i]._image.valid()) { if (mapf.getImageLayerAt(i)->isKeyValid(key)) { //If the key was valid and we have no image, then something possibly went wrong with the image creation such as a server being busy. createValidGeoImage(mapf.getImageLayerAt(i), key, image_tiles[i]._image, image_tiles[i]._imageTileKey); } //If we still couldn't create an image, either something is really wrong or the key wasn't valid, so just create a transparent placeholder image if (!image_tiles[i]._image.valid()) { //If the image is not valid, create an empty texture as a placeholder image_tiles[i]._image = GeoImage(ImageUtils::createEmptyImage(), key.getExtent()); image_tiles[i]._imageTileKey = key; } } } //Fill in missing heightfield information from parent tiles if (!hf.valid()) { //We have no heightfield sources, if ( mapf.elevationLayers().size() == 0 ) { hf = createEmptyHeightField( key ); } else { //Try to get a heightfield again, but this time fallback on parent tiles if ( mapf.getHeightField( key, true, hf, 0L, _terrainOptions.elevationInterpolation().value() ) ) { hasElevation = true; } else { //We couldn't get any heightfield, so just create an empty one. hf = createEmptyHeightField( key ); } } } // In a Plate Carre tesselation, scale the heightfield elevations from meters to degrees if ( isPlateCarre ) { HeightFieldUtils::scaleHeightFieldToDegrees( hf.get() ); } osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( key, mapInfo ); osgTerrain::HeightFieldLayer* hf_layer = new osgTerrain::HeightFieldLayer(); hf_layer->setLocator( locator.get() ); hf_layer->setHeightField( hf.get() ); bool isStreaming = _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_SEQUENTIAL || _terrainOptions.loadingPolicy()->mode() == LoadingPolicy::MODE_PREEMPTIVE; Tile* tile = terrain->createTile( key, locator.get() ); tile->setTerrainTechnique( terrain->cloneTechnique() ); tile->setVerticalScale( _terrainOptions.verticalScale().value() ); //tile->setLocator( locator.get() ); tile->setElevationLayer( hf_layer ); //tile->setRequiresNormals( true ); tile->setDataVariance(osg::Object::DYNAMIC); #if 0 //Attach an updatecallback to normalize the edges of TerrainTiles. if (hasElevation && _terrainOptions.normalizeEdges().get() ) { tile->setUpdateCallback(new TerrainTileEdgeNormalizerUpdateCallback()); tile->setDataVariance(osg::Object::DYNAMIC); } #endif //Assign the terrain system to the TerrainTile. //It is very important the terrain system is set while the MapConfig's sourceMutex is locked. //This registers the terrain tile so that adding/removing layers are always in sync. If you don't do this //you can end up with a situation where the database pager is waiting to merge a tile, then a layer is added, then //the tile is finally merged and is out of sync. double min_units_per_pixel = DBL_MAX; #if 0 // create contour layer: if (map->getContourTransferFunction() != NULL) { osgTerrain::ContourLayer* contourLayer(new osgTerrain::ContourLayer(map->getContourTransferFunction())); contourLayer->setMagFilter(_terrainOptions.getContourMagFilter().value()); contourLayer->setMinFilter(_terrainOptions.getContourMinFilter().value()); tile->setCustomColorLayer(layer,contourLayer); //TODO: need layerUID, not layer index here -GW ++layer; } #endif for (unsigned int i = 0; i < image_tiles.size(); ++i) { if (image_tiles[i]._image.valid()) { const GeoImage& geo_image = image_tiles[i]._image; double img_xmin, img_ymin, img_xmax, img_ymax; geo_image.getExtent().getBounds( img_xmin, img_ymin, img_xmax, img_ymax ); //Specify a new locator for the color with the coordinates of the TileKey that was actually used to create the image osg::ref_ptr<GeoLocator> img_locator = key.getProfile()->getSRS()->createLocator( img_xmin, img_ymin, img_xmax, img_ymax, isPlateCarre ); if ( mapInfo.isGeocentric() ) img_locator->setCoordinateSystemType( osgTerrain::Locator::GEOCENTRIC ); tile->setCustomColorLayer( CustomColorLayer( mapf.getImageLayerAt(i), geo_image.getImage(), img_locator.get(), key.getLevelOfDetail(), key) ); double upp = geo_image.getUnitsPerPixel(); // Scale the units per pixel to degrees if the image is mercator (and the key is geo) if ( geo_image.getSRS()->isMercator() && key.getExtent().getSRS()->isGeographic() ) upp *= 1.0f/111319.0f; min_units_per_pixel = osg::minimum(upp, min_units_per_pixel); } } osg::BoundingSphere bs = tile->getBound(); double max_range = 1e10; double radius = bs.radius(); #if 1 double min_range = radius * _terrainOptions.minTileRangeFactor().get(); //osg::LOD::RangeMode mode = osg::LOD::DISTANCE_FROM_EYE_POINT; #else double width = key.getExtent().width(); if (min_units_per_pixel == DBL_MAX) min_units_per_pixel = width/256.0; double min_range = (width / min_units_per_pixel) * _terrainOptions.getMinTileRangeFactor(); //osg::LOD::RangeMode mode = osg::LOD::PIXEL_SIZE_ON_SCREEN; #endif // a skirt hides cracks when transitioning between LODs: hf->setSkirtHeight(radius * _terrainOptions.heightFieldSkirtRatio().get() ); // for now, cluster culling does not work for CUBE rendering //bool isCube = mapInfo.isCube(); //map->getMapOptions().coordSysType() == MapOptions::CSTYPE_GEOCENTRIC_CUBE; if ( mapInfo.isGeocentric() && !mapInfo.isCube() ) { //TODO: Work on cluster culling computation for cube faces osg::ClusterCullingCallback* ccc = createClusterCullingCallback(tile, locator->getEllipsoidModel() ); tile->setCullCallback( ccc ); } // Wait until now, when the tile is fully baked, to assign the terrain to the tile. // Placeholder tiles might try to locate this tile as an ancestor, and access its layers // and locators...so they must be intact before making this tile available via setTerrain. // // If there's already a placeholder tile registered, this will be ignored. If there isn't, // this will register the new tile. tile->attachToTerrain( terrain ); //tile->setTerrain( terrain ); //terrain->registerTile( tile ); if ( isStreaming && key.getLevelOfDetail() > 0 ) { static_cast<StreamingTile*>(tile)->setHasElevationHint( hasElevation ); } osg::Node* result = 0L; if (wrapInPagedLOD) { // create a PLOD so we can keep subdividing: osg::PagedLOD* plod = new osg::PagedLOD(); plod->setCenter( bs.center() ); plod->addChild( tile, min_range, max_range ); std::string filename = createURI( _engineId, key ); //map->getId(), key ); //Only add the next tile if it hasn't been blacklisted bool isBlacklisted = osgEarth::Registry::instance()->isBlacklisted( filename ); if (!isBlacklisted && key.getLevelOfDetail() < (unsigned int)getTerrainOptions().maxLOD().value() && validData ) { plod->setFileName( 1, filename ); plod->setRange( 1, 0.0, min_range ); } else { plod->setRange( 0, 0, FLT_MAX ); } #if USE_FILELOCATIONCALLBACK osgDB::Options* options = new osgDB::Options; options->setFileLocationCallback( new FileLocationCallback() ); plod->setDatabaseOptions( options ); #endif result = plod; if ( isStreaming ) result->addCullCallback( new PopulateStreamingTileDataCallback( _cull_thread_mapf ) ); } else { result = tile; } return result; }
// called from the UPDATE TRAVERSAL, because this method can potentially alter // the scene graph. bool StreamingTile::serviceCompletedRequests( const MapFrame& mapf, bool tileTableLocked ) { //Don't do anything until we have been added to the scene graph if (!_hasBeenTraversed) return false; bool tileModified = false; if ( !_requestsInstalled ) return false; // First service the tile generator: if ( _tileGenRequest.valid() && _tileGenRequest->isCompleted() ) { CustomTerrainTechnique* tech = dynamic_cast<CustomTerrainTechnique*>( getTerrainTechnique() ); if ( tech ) { //TODO: consider waiting to apply if there are still more tile updates in the queue. if ( _tileUpdates.size() == 0 ) { tileModified = tech->applyTileUpdates(); } } _tileGenRequest = 0L; } // now deal with imagery. const LoadingPolicy& lp = getStreamingTerrain()->getLoadingPolicy(); StreamingTerrainNode* terrain = getStreamingTerrain(); //Check each layer independently. for( ImageLayerVector::const_iterator i = mapf.imageLayers().begin(); i != mapf.imageLayers().end(); ++i ) { ImageLayer* imageLayer = i->get(); bool checkForFinalImagery = false; CustomColorLayer colorLayer; if ( getCustomColorLayer( imageLayer->getUID(), colorLayer ) ) { if ( lp.mode() == LoadingPolicy::MODE_PREEMPTIVE ) { // in preemptive mode, always check for the final imagery - there are no intermediate // placeholders. checkForFinalImagery = true; } else if (lp.mode() == LoadingPolicy::MODE_SEQUENTIAL && readyForNewImagery(imageLayer, colorLayer.getLevelOfDetail()) ) { // in sequential mode, we have to incrementally increase imagery resolution by // creating placeholders based of parent tiles, one LOD at a time. if ( colorLayer.getLevelOfDetail() + 1 < (int)_key.getLevelOfDetail() ) { // if the parent's image LOD is higher than ours, replace ours with the parent's // since it is a higher-resolution placeholder: if ( _family[Relative::PARENT].getImageLOD(colorLayer.getUID()) > colorLayer.getLevelOfDetail() ) { osg::ref_ptr<Tile> parentTile; getStreamingTerrain()->getTile( _family[Relative::PARENT].tileID, parentTile, !tileTableLocked ); // Set the color layer to the parent color layer as a placeholder. CustomColorLayer parentColorLayer; if ( parentTile->getCustomColorLayer( colorLayer.getUID(), parentColorLayer ) ) { this->setCustomColorLayer( parentColorLayer ); } // ... and queue up an update request. queueTileUpdate( TileUpdate::UPDATE_IMAGE_LAYER, colorLayer.getUID() ); } } else { // we've gone as far as we can with placeholders; time to check for the // final imagery tile. checkForFinalImagery = true; } } } if ( checkForFinalImagery ) { // Then the image requests: for( TaskRequestList::iterator itr = _requests.begin(); itr != _requests.end(); ) { bool increment = true; TileColorLayerRequest* r = static_cast<TileColorLayerRequest*>( itr->get() ); //We only care about the current layer we are checking if ( r->_layerUID == imageLayer->getUID() ) { if ( itr->get()->isCompleted() ) { if ( r->wasCanceled() ) { //Reset the cancelled task to IDLE and give it a new progress callback. r->setState( TaskRequest::STATE_IDLE ); r->setProgressCallback( new StampedProgressCallback( r, terrain->getImageryTaskService( r->_layerUID ))); r->reset(); } else // success.. { //See if we even care about the request if ( !mapf.getImageLayerByUID( r->_layerUID ) ) { //The maplayer was probably deleted OE_DEBUG << "Layer uid=" << r->_layerUID << " no longer exists, ignoring TileColorLayerRequest " << std::endl; itr = _requests.erase(itr); increment = false; } else { CustomColorLayerRef* result = static_cast<CustomColorLayerRef*>( r->getResult() ); if ( result ) { this->setCustomColorLayer( result->_layer ); queueTileUpdate( TileUpdate::UPDATE_IMAGE_LAYER, r->_layerUID ); //OE_NOTICE << "Complete IR (" << _key.str() << ") layer=" << r->_layerId << std::endl; // remove from the list (don't reference "r" after this!) itr = _requests.erase( itr ); increment = false; } else { if (r->_numTries > r->_maxTries) { CustomColorLayer oldLayer; if ( this->getCustomColorLayer( r->_layerUID, oldLayer ) ) { // apply the old color layer but with a new LOD. this->setCustomColorLayer( CustomColorLayer( oldLayer.getMapLayer(), oldLayer.getImage(), oldLayer.getLocator(), _key.getLevelOfDetail(), _key )); itr = _requests.erase( itr ); increment = false; OE_DEBUG << "Tried (" << _key.str() << ") (layer uid=" << r->_layerUID << "), too many times, moving on...." << std::endl; } } else { OE_DEBUG << "IReq error (" << _key.str() << ") (layer uid=" << r->_layerUID << "), retrying" << std::endl; //The color layer request failed, probably due to a server error. Reset it. r->setState( TaskRequest::STATE_IDLE ); r->reset(); } } } } } } if ( increment ) ++itr; } } } // Finally, the elevation requests: if ( _hasElevation && !_elevationLayerUpToDate && _elevRequest.valid() && _elevPlaceholderRequest.valid() ) { // First, check is the Main elevation request is done. If so, we will now have the final HF data // and can shut down the elevation requests for this tile. if ( _elevRequest->isCompleted() ) { if ( _elevRequest->wasCanceled() ) { // If the request was canceled, reset it to IDLE and reset the callback. On the next _elevRequest->setState( TaskRequest::STATE_IDLE ); _elevRequest->setProgressCallback( new ProgressCallback() ); _elevRequest->reset(); } else // success: { // if the elevation request succeeded, install the new elevation layer! TileElevationLayerRequest* r = static_cast<TileElevationLayerRequest*>( _elevRequest.get() ); osg::ref_ptr<osgTerrain::HeightFieldLayer> newHFLayer = static_cast<osgTerrain::HeightFieldLayer*>( r->getResult() ); if ( newHFLayer.valid() && newHFLayer->getHeightField() != NULL ) { newHFLayer->getHeightField()->setSkirtHeight( terrain->getTileFactory()->getTerrainOptions().heightFieldSkirtRatio().get() * this->getBound().radius() ); // need to write-lock the layer data since we'll be changing it: { Threading::ScopedWriteLock lock( _tileLayersMutex ); this->setElevationLayer( newHFLayer.get() ); this->dirtyBound(); } // the tile needs rebuilding. This will kick off a TileGenRequest. queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); // finalize the LOD marker for this tile, so other tiles can see where we are. _elevationLOD = _key.getLevelOfDetail(); #ifdef PREEMPTIVE_DEBUG OE_NOTICE << "Tile (" << _key.str() << ") final HF, LOD (" << _elevationLOD << ")" << std::endl; #endif // this was the final elev request, so mark elevation as DONE. _elevationLayerUpToDate = true; // GW- just reset these and leave them alone and let cancelRequests() take care of cleanup later. // done with our Elevation requests! //_elevRequest = 0L; //_elevPlaceholderRequest = 0L; } else { //We've tried to get the tile's elevation but couldn't. Just mark the elevation layer as up to date and move on. _elevationLOD = _key.getLevelOfDetail(); _elevationLayerUpToDate = true; //This code will retry indefinitely. We need to have a way to limit the number of retries since //it will block neighbor tiles from loading. //_elevRequest->setState( TaskRequest::STATE_IDLE ); //_elevRequest->reset(); } } } else if ( _elevPlaceholderRequest->isCompleted() ) { TileElevationPlaceholderLayerRequest* r = static_cast<TileElevationPlaceholderLayerRequest*>(_elevPlaceholderRequest.get()); if ( r->wasCanceled() ) { r->setState( TaskRequest::STATE_IDLE ); r->setProgressCallback( new ProgressCallback() ); r->reset(); } else // success: { osg::ref_ptr<osgTerrain::HeightFieldLayer> newPhLayer = static_cast<osgTerrain::HeightFieldLayer*>( r->getResult() ); if ( newPhLayer.valid() && newPhLayer->getHeightField() != NULL ) { // install the new elevation layer. { Threading::ScopedWriteLock lock( _tileLayersMutex ); this->setElevationLayer( newPhLayer.get() ); this->dirtyBound(); } // tile needs to be recompiled. queueTileUpdate( TileUpdate::UPDATE_ELEVATION ); // update the elevation LOD for this tile, now that the new HF data is installed. This will // allow other tiles to see where this tile's HF data is. _elevationLOD = r->_nextLOD; #ifdef PREEMPTIVE_DEBUG OE_NOTICE << "..tile (" << _key.str() << ") is now at (" << _elevationLOD << ")" << std::endl; #endif } _elevPlaceholderRequest->setState( TaskRequest::STATE_IDLE ); _elevPlaceholderRequest->reset(); } } } // if we have a new TileGenRequest, queue it up now. if ( _tileUpdates.size() > 0 && !_tileGenRequest.valid() ) // _tileGenNeeded && !_tileGenRequest.valid()) { _tileGenRequest = new TileGenRequest( this, _tileUpdates.front() ); _tileUpdates.pop(); //OE_NOTICE << "tile (" << _key.str() << ") queuing new tile gen" << std::endl; getStreamingTerrain()->getTileGenerationTaskService()->add( _tileGenRequest.get() ); } return tileModified; }
void RexTerrainEngineNode::addTileLayer(Layer* tileLayer) { if ( tileLayer && tileLayer->getEnabled() ) { ImageLayer* imageLayer = dynamic_cast<ImageLayer*>(tileLayer); if (imageLayer) { // for a shared layer, allocate a shared image unit if necessary. if ( imageLayer->isShared() ) { if (!imageLayer->shareImageUnit().isSet()) { int temp; if ( getResources()->reserveTextureImageUnit(temp, imageLayer->getName().c_str()) ) { imageLayer->shareImageUnit() = temp; //OE_INFO << LC << "Image unit " << temp << " assigned to shared layer " << imageLayer->getName() << std::endl; } else { OE_WARN << LC << "Insufficient GPU image units to share layer " << imageLayer->getName() << std::endl; } } // Build a sampler binding for the shared layer. if ( imageLayer->shareImageUnit().isSet() ) { // Find the next empty SHARED slot: unsigned newIndex = SamplerBinding::SHARED; while (_renderBindings[newIndex].isActive()) ++newIndex; // Put the new binding there: SamplerBinding& newBinding = _renderBindings[newIndex]; newBinding.usage() = SamplerBinding::SHARED; newBinding.sourceUID() = imageLayer->getUID(); newBinding.unit() = imageLayer->shareImageUnit().get(); newBinding.samplerName() = imageLayer->shareTexUniformName().get(); newBinding.matrixName() = imageLayer->shareTexMatUniformName().get(); OE_INFO << LC << "Shared Layer \"" << imageLayer->getName() << "\" : sampler=\"" << newBinding.samplerName() << "\", " << "matrix=\"" << newBinding.matrixName() << "\", " << "unit=" << newBinding.unit() << "\n"; } } } else { // non-image tile layer. Keep track of these.. } if (_terrain) { // Update the existing render models, and trigger a data reload. // Later we can limit the reload to an update of only the new data. UpdateRenderModels updateModels(_mapFrame); #if 0 // This uses the loaddata filter approach which will only request // data for one layer. It mostly works but not 100%; see hires-insets // as an example. Removing the world layer and re-adding it while // zoomed in doesn't result in all tiles reloading. Possibly a // synchronization issue. ImageLayerVector imageLayers; _mapFrame.getLayers(imageLayers); if (imageLayers.size() == 1) updateModels.setReloadData(true); else updateModels.layersToLoad().insert(tileLayer->getUID()); #else updateModels.setReloadData(true); #endif _terrain->accept(updateModels); } } }
// Generates the main shader code for rendering the terrain. void MPTerrainEngineNode::updateShaders() { if ( _batchUpdateInProgress ) { _shaderUpdateRequired = true; } else { osg::StateSet* terrainStateSet = _terrain->getOrCreateStateSet(); VirtualProgram* vp = new VirtualProgram(); vp->setName( "engine_mp:TerrainNode" ); terrainStateSet->setAttributeAndModes( vp, osg::StateAttribute::ON ); // Vertex shader template: std::string vs = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 osg_FrontColor; \n" "varying vec4 osg_FrontSecondaryColor; \n" "varying vec4 oe_layer_tc;\n" "void osgearth_vert_setupColoring() \n" "{ \n" " osg_FrontColor = gl_Color; \n" " osg_FrontSecondaryColor = vec4(0.0);\n" " oe_layer_tc = __GL_MULTITEXCOORD__;\n" "}\n"; // Fragment shader template: std::string fs = "#version " GLSL_VERSION_STR "\n" GLSL_DEFAULT_PRECISION_FLOAT "\n" "varying vec4 oe_layer_tc; \n" "uniform sampler2D oe_layer_tex; \n" "uniform int oe_layer_uid; \n" "uniform int oe_layer_order; \n" "uniform float oe_layer_opacity; \n" "__COLOR_FILTER_HEAD__" "void osgearth_frag_applyColoring( inout vec4 color ) \n" "{ \n" " vec4 texel = texture2D(oe_layer_tex, oe_layer_tc.st);\n" " float alpha = texel.a * oe_layer_opacity; \n" " if (oe_layer_order == 0) \n" " color = vec4(color.rgb * (1.0 - alpha) + (texel.rgb * alpha), 1.0); \n" " else \n" " color = vec4(texel.rgb, color.a * alpha); \n" " __COLOR_FILTER_BODY__" "} \n"; // install the gl_MultiTexCoord* variable that uses the proper texture // image unit: replaceIn( vs, "__GL_MULTITEXCOORD__", Stringify() << "gl_MultiTexCoord" << _textureImageUnit ); // assemble color filter code snippets. { std::stringstream cf_head; std::stringstream cf_body; // second, install the per-layer color filter functions. bool ifStarted = false; const char* I = " "; int numImageLayers = _update_mapf->imageLayers().size(); for( int i=0; i<numImageLayers; ++i ) { ImageLayer* layer = _update_mapf->getImageLayerAt(i); if ( layer->getEnabled() ) { const ColorFilterChain& chain = layer->getColorFilters(); if ( chain.size() > 0 ) { if ( ifStarted ) cf_body << I << "else if "; else cf_body << I << "if "; cf_body << "(oe_layer_uid == " << layer->getUID() << ") {\n"; for( ColorFilterChain::const_iterator j = chain.begin(); j != chain.end(); ++j ) { const ColorFilter* filter = j->get(); cf_head << "void " << filter->getEntryPointFunctionName() << "(in int slot, inout vec4 color);\n"; cf_body << I << I << filter->getEntryPointFunctionName() << "(" << _textureImageUnit << ", color);\n"; filter->install( terrainStateSet ); } cf_body << I << "}\n"; ifStarted = true; } } } std::string cf_head_str, cf_body_str; cf_head_str = cf_head.str(); cf_body_str = cf_body.str(); replaceIn( fs, "__COLOR_FILTER_HEAD__", cf_head_str ); replaceIn( fs, "__COLOR_FILTER_BODY__", cf_body_str ); } vp->setShader( "osgearth_vert_setupColoring", new osg::Shader( osg::Shader::VERTEX, vs ), osg::StateAttribute::ON | osg::StateAttribute::PROTECTED ); vp->setShader( "osgearth_frag_applyColoring", new osg::Shader( osg::Shader::FRAGMENT, fs ), osg::StateAttribute::ON | osg::StateAttribute::PROTECTED ); // required for multipass tile rendering to work terrainStateSet->setAttributeAndModes( new osg::Depth(osg::Depth::LEQUAL, 0, 1, true) ); // blend multipass image layers terrainStateSet->setAttributeAndModes( new osg::BlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA), 1); // binding for the terrain texture terrainStateSet->getOrCreateUniform( "oe_layer_tex", osg::Uniform::SAMPLER_2D )->set( _textureImageUnit ); // uniform that controls per-layer opacity terrainStateSet->getOrCreateUniform( "oe_layer_opacity", osg::Uniform::FLOAT )->set( 1.0f ); // uniform that conveys the layer UID to the shaders; necessary // for per-layer branching (like color filters) terrainStateSet->getOrCreateUniform( "oe_layer_uid", osg::Uniform::INT )->set( 0 ); // uniform that conveys the render order, since the shaders // need to know which is the first layer in order to blend properly terrainStateSet->getOrCreateUniform( "oe_layer_order", osg::Uniform::INT )->set( 0 ); _shaderUpdateRequired = false; } }