DrawTileCommand* TerrainCuller::addDrawCommand(UID uid, const TileRenderModel* model, const RenderingPass* pass, TileNode* tileNode) { SurfaceNode* surface = tileNode->getSurfaceNode(); const RenderBindings& bindings = _context->getRenderBindings(); // skip layers that are not visible: if (pass && pass->_imageLayer.valid() && !pass->_imageLayer->getVisible()) return 0L; // add a new Draw command to the appropriate layer osg::ref_ptr<LayerDrawable> layer = _terrain.layer(uid); if (layer.valid()) { layer->_tiles.push_back(DrawTileCommand()); DrawTileCommand& tile = layer->_tiles.back(); // install everything we need in the Draw Command: tile._colorSamplers = pass ? &pass->_samplers : 0L; tile._sharedSamplers = &model->_sharedSamplers; tile._matrix = surface->getMatrix(); tile._modelViewMatrix = *this->getModelViewMatrix(); tile._keyValue = tileNode->getTileKeyValue(); tile._geom = surface->getDrawable()->_geom.get(); tile._morphConstants = tileNode->getMorphConstants(); tile._key = tileNode->getKey(); #if 1 osg::Vec3 c = surface->getBound().center() * surface->getInverseMatrix(); tile._range = getDistanceToViewPoint(c, true); #else osg::Vec3f eyeWorld = getViewPointLocal() * surface->getMatrix(); tile._range = (eyeWorld - surface->getBound().center()).length(); #endif const osg::Image* elevRaster = tileNode->getElevationRaster(); if (elevRaster) { float bias = _context->getUseTextureBorder() ? 1.5 : 0.5; // Compute an elevation texture sampling scale/bias so we sample elevation data on center // instead of on edge (as we do with color, etc.) // // This starts out as: // scale = (size-1)/size : this shrinks the sample area by one texel since we're sampling on center // bias = 0.5/size : this shifts the sample area over 1/2 texel to the center. // // But, since we also have a 1-texel border, we need to further reduce the scale by 2 texels to // remove the border, and shift an extra texel over as well. Giving us this: float size = (float)elevRaster->s(); tile._elevTexelCoeff.set((size - (2.0*bias)) / size, bias / size); } return &tile; } else if (pass) { // The pass exists but it's layer doesn't - remember this so we can run // a visitor to clean up the rendering models. ++_orphanedPassesDetected; } return 0L; }
void TerrainCuller::apply(osg::Node& node) { // push the node's state. osg::StateSet* node_state = node.getStateSet(); TileNode* tileNode = dynamic_cast<TileNode*>(&node); if (tileNode) { _currentTileNode = tileNode; _currentTileDrawCommands = 0u; if (!_terrain.patchLayers().empty()) { // todo: check for patch/virtual const RenderBindings& bindings = _context->getRenderBindings(); TileRenderModel& renderModel = _currentTileNode->renderModel(); bool pushedMatrix = false; for (PatchLayerVector::const_iterator i = _terrain.patchLayers().begin(); i != _terrain.patchLayers().end(); ++i) { PatchLayer* layer = i->get(); if (layer->getAcceptCallback() == 0L || layer->getAcceptCallback()->acceptKey(_currentTileNode->getKey())) { // Push this tile's matrix if we haven't already done so: if (!pushedMatrix) { SurfaceNode* surface = tileNode->getSurfaceNode(); // push the surface matrix: osg::Matrix mvm = *getModelViewMatrix(); surface->computeLocalToWorldMatrix(mvm, this); pushModelViewMatrix(createOrReuseMatrix(mvm), surface->getReferenceFrame()); pushedMatrix = true; } // Add the draw command: DrawTileCommand* cmd = addDrawCommand(layer->getUID(), &renderModel, 0L, tileNode); if (cmd) { cmd->_drawPatch = true; cmd->_drawCallback = layer->getDrawCallback(); ++_currentTileDrawCommands; } } } if (pushedMatrix) { popModelViewMatrix(); } } } else { SurfaceNode* surface = dynamic_cast<SurfaceNode*>(&node); if (surface) { TileRenderModel& renderModel = _currentTileNode->renderModel(); // push the surface matrix: osg::Matrix mvm = *getModelViewMatrix(); surface->computeLocalToWorldMatrix(mvm, this); pushModelViewMatrix(createOrReuseMatrix(mvm), surface->getReferenceFrame()); // First go through any legit rendering pass data in the Tile and // and add a DrawCommand for each. for (unsigned p = 0; p < renderModel._passes.size(); ++p) { const RenderingPass& pass = renderModel._passes[p]; if (pass._layer.valid() && pass._layer->getRenderType() == Layer::RENDERTYPE_TILE) { if (addDrawCommand(pass._sourceUID, &renderModel, &pass, _currentTileNode)) { ++_currentTileDrawCommands; } } } // Next, add a DrawCommand for each tile layer not represented in the TerrainTileModel // as a rendering pass. for (LayerVector::const_iterator i = _terrain.tileLayers().begin(); i != _terrain.tileLayers().end(); ++i) { Layer* layer = i->get(); if (addDrawCommand(layer->getUID(), &renderModel, 0L, _currentTileNode)) { ++_currentTileDrawCommands; } } // If the culler added no draw commands for this tile... do something! if (_currentTileDrawCommands == 0) { //OE_INFO << LC << "Adding blank render.\n"; addDrawCommand(-1, &renderModel, 0L, _currentTileNode); } popModelViewMatrix(); _terrain._drawState->_bs.expandBy(surface->getBound()); _terrain._drawState->_box.expandBy(_terrain._drawState->_bs); } } traverse(node); }
void TerrainCuller::apply(osg::Node& node) { // push the node's state. osg::StateSet* node_state = node.getStateSet(); TileNode* tileNode = dynamic_cast<TileNode*>(&node); if (tileNode) { _currentTileNode = tileNode; // reset the pointer to the first DrawTileCommand. We keep track of this so // we can set it's "order" member to zero at the end, so the rendering engine // knows to blend it with the terrain geometry color. _firstTileDrawCommandForTile = 0L; //_currentTileDrawCommands = 0u; if (!_terrain.patchLayers().empty()) { // todo: check for patch/virtual const RenderBindings& bindings = _context->getRenderBindings(); TileRenderModel& renderModel = _currentTileNode->renderModel(); bool pushedMatrix = false; for (PatchLayerVector::const_iterator i = _terrain.patchLayers().begin(); i != _terrain.patchLayers().end(); ++i) { PatchLayer* layer = i->get(); if (layer->getAcceptCallback() == 0L || layer->getAcceptCallback()->acceptKey(_currentTileNode->getKey())) { // Push this tile's matrix if we haven't already done so: if (!pushedMatrix) { SurfaceNode* surface = tileNode->getSurfaceNode(); // push the surface matrix: osg::Matrix mvm = *getModelViewMatrix(); surface->computeLocalToWorldMatrix(mvm, this); pushModelViewMatrix(createOrReuseMatrix(mvm), surface->getReferenceFrame()); pushedMatrix = true; } // Add the draw command: DrawTileCommand* cmd = addDrawCommand(layer->getUID(), &renderModel, 0L, tileNode); if (cmd) { cmd->_drawPatch = true; cmd->_drawCallback = layer->getDrawCallback(); } } } if (pushedMatrix) { popModelViewMatrix(); } } } else { SurfaceNode* surface = dynamic_cast<SurfaceNode*>(&node); if (surface) { TileRenderModel& renderModel = _currentTileNode->renderModel(); // push the surface matrix: osg::Matrix mvm = *getModelViewMatrix(); surface->computeLocalToWorldMatrix(mvm, this); pushModelViewMatrix(createOrReuseMatrix(mvm), surface->getReferenceFrame()); int order = 0; // First go through any legit rendering pass data in the Tile and // and add a DrawCommand for each. for (unsigned p = 0; p < renderModel._passes.size(); ++p) { const RenderingPass& pass = renderModel._passes[p]; DrawTileCommand* cmd = addDrawCommand(pass.sourceUID(), &renderModel, &pass, _currentTileNode); if (cmd) { if (_firstTileDrawCommandForTile == 0L) { _firstTileDrawCommandForTile = cmd; } else if (cmd->_order < _firstTileDrawCommandForTile->_order) { //_firstTileDrawCommandForTile->_order = 1; _firstTileDrawCommandForTile = cmd; } } } // If the culler added no draw commands for this tile... we still need // to draw something or else there will be a hole! So draw a blank tile. // UID = -1 is the special UID code for a blank. if (_firstTileDrawCommandForTile == 0L) { //OE_INFO << LC << "Adding blank render for tile " << _currentTileNode->getKey().str() << std::endl; DrawTileCommand* cmd = addDrawCommand(-1, &renderModel, 0L, _currentTileNode); if (cmd) cmd->_order = 0; } // Set the layer order of the first draw command for this tile to zero, // to support proper terrain blending. if (_firstTileDrawCommandForTile) { _firstTileDrawCommandForTile->_order = 0; } // pop the matrix from the cull stack popModelViewMatrix(); // update our bounds _terrain._drawState->_bs.expandBy(surface->getBound()); _terrain._drawState->_box.expandBy(_terrain._drawState->_bs); } } // Handle any CullCallbacks and traverse. osg::Callback* cullCallback = node.getCullCallback(); if (cullCallback) cullCallback->run(&node, this); else traverse(node); }
DrawTileCommand* TerrainCuller::addDrawCommand(UID uid, const TileRenderModel* model, const RenderingPass* pass, TileNode* tileNode) { SurfaceNode* surface = tileNode->getSurfaceNode(); const RenderBindings& bindings = _context->getRenderBindings(); // skip layers that are not visible: if (pass && pass->visibleLayer() && pass->visibleLayer()->getVisible() == false) { //OE_DEBUG << LC << "Skipping " << pass->visibleLayer()->getName() << " because it's not visible." << std::endl; return 0L; } // add a new Draw command to the appropriate layer osg::ref_ptr<LayerDrawable> drawable = _terrain.layer(uid); if (drawable.valid()) { // Layer marked for drawing? if (drawable->_draw) { // Cull based on the layer extent. if (drawable->_layer) { const LayerExtent& le = (*_layerExtents)[drawable->_layer->getUID()]; if (le._computed && le._extent.isValid() && le._extent.intersects(tileNode->getKey().getExtent()) == false) { // culled out! //OE_DEBUG << LC << "Skippping " << drawable->_layer->getName() // << " key " << tileNode->getKey().str() // << " because it was culled by extent." << std::endl; return 0L; } } drawable->_tiles.push_back(DrawTileCommand()); DrawTileCommand* tile = &drawable->_tiles.back(); // install everything we need in the Draw Command: tile->_colorSamplers = pass ? &(pass->samplers()) : 0L; tile->_sharedSamplers = &model->_sharedSamplers; tile->_modelViewMatrix = this->getModelViewMatrix(); tile->_keyValue = tileNode->getTileKeyValue(); tile->_geom = surface->getDrawable()->_geom.get(); tile->_morphConstants = tileNode->getMorphConstants(); tile->_key = &tileNode->getKey(); tile->_order = drawable->_order; // layer order in map tile. osg::Vec3 c = surface->getBound().center() * surface->getInverseMatrix(); tile->_range = getDistanceToViewPoint(c, true); const osg::Image* elevRaster = tileNode->getElevationRaster(); if (elevRaster) { float bias = _context->getUseTextureBorder() ? 1.5 : 0.5; // Compute an elevation texture sampling scale/bias so we sample elevation data on center // instead of on edge (as we do with color, etc.) // // This starts out as: // scale = (size-1)/size : this shrinks the sample area by one texel since we're sampling on center // bias = 0.5/size : this shifts the sample area over 1/2 texel to the center. // // But, since we also have a 1-texel border, we need to further reduce the scale by 2 texels to // remove the border, and shift an extra texel over as well. Giving us this: float size = (float)elevRaster->s(); tile->_elevTexelCoeff.set((size - (2.0*bias)) / size, bias / size); } return tile; } } else if (pass) { // The pass exists but it's layer is not in the render data draw list. // This means that the layer is no longer in the map. Detect and record // this information so we can run a cleanup visitor later on. ++_orphanedPassesDetected; } return 0L; }
TileNode* TileGroupFactory::createTileNodeGraph(TerrainTileModel* model, bool setupChildrenIfNecessary, ProgressCallback* progress) { // TODO: fix this const unsigned tileSize = 17; // Build the surface node. SurfaceNodeFactory factory(model, _frame, _renderBindings, _geometryPool, tileSize, _options); SurfaceNode* surfaceNode = factory.createSurfaceNode(); surfaceNode->setEngineUID( _terrainEngine->getUID() ); // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->getKey().getLOD() < *_options.maxLOD(); // Build the Tile Node that will hold all the textures and texture matrices. TileNode* tileNode = createTileNode( model, progress ); // Build the paging node that will load subtiles, if necessary: if ( prepareForChildren ) { osg::BoundingSphere bs = surfaceNode->getBound(); TilePagedLOD* plod = new TilePagedLOD( _terrainEngine->getUID(), _liveTiles, _deadTiles ); plod->setCenter ( bs.center() ); plod->addChild ( surfaceNode ); plod->setFileName( 1, Stringify() << model->getKey().str() << "." << _terrainEngine->getUID() << ".osgearth_engine_mp_tile" ); if ( _options.rangeMode().value() == osg::LOD::DISTANCE_FROM_EYE_POINT ) { //Compute the min range based on the 2D size of the tile GeoExtent extent = model->getKey().getExtent(); GeoPoint lowerLeft(extent.getSRS(), extent.xMin(), extent.yMin(), 0.0, ALTMODE_ABSOLUTE); GeoPoint upperRight(extent.getSRS(), extent.xMax(), extent.yMax(), 0.0, ALTMODE_ABSOLUTE); osg::Vec3d ll, ur; lowerLeft.toWorld( ll ); upperRight.toWorld( ur ); double radius = (ur - ll).length() / 2.0; float minRange = (float)(radius * _options.minTileRangeFactor().value()); plod->setRange( 0, minRange, FLT_MAX ); plod->setRange( 1, 0, minRange ); plod->setRangeMode( osg::LOD::DISTANCE_FROM_EYE_POINT ); } else { plod->setRange( 0, 0.0f, _options.tilePixelSize().value() ); plod->setRange( 1, _options.tilePixelSize().value(), FLT_MAX ); plod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN ); } #if 0 // TODO: reinstate this! // Install a tile-aligned bounding box in the pager node itself so we can do // visibility testing before paging in subtiles. plod->setChildBoundingBoxAndMatrix( 1, surfaceNode->getTerrainBoundingBox(), surfaceNode->getLocalToWorldMatrix() ); #endif #if USE_FILELOCATIONCALLBACK osgDB::Options* options = plod->getOrCreateDBOptions(); options->setFileLocationCallback( new FileLocationCallback() ); #endif tileNode->addChild( plod ); // Install a callback to reject back-facing tiles. if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { const osg::HeightField* heightField = model->elevationModel()->getHeightField(); if ( heightField ) { tileNode->addCullCallback( HeightFieldUtils::createClusterCullingCallback( heightField, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); } } } else { tileNode->addChild( surfaceNode ); } return tileNode; }