相关类:
/**
* TileNode represents a single tile. TileNode has 5 children:
* one SurfaceNode that renders the actual tile content under a MatrixTransform;
* and four TileNodes representing the LOD+1 quadtree tiles under this tile.
*/
/**
* TileDrawable is an osg::Drawable that represents an individual terrain tile
* for the purposes of scene graph operations (like intersections, bounds
* computation, statistics, etc.)
*
* NOTE: TileDrawable does not actually render anything!
* The TerrainRenderData object does all the rendering of tiles.
*
* Instead, it exposes various osg::Drawable Functors for traversing
* the terrain's geometry. It also hold a pointer to the tile's elevation
* raster so it can properly reflect the elevation data in the texture.
*/
SharedGeometry: 瓦片渲染的最小单元,由GeometryPool::createGeometry创建
SurfaceNode: the node to house the tile drawable:TtileDrawable的持有者
RexTerrainEngine:派生自TerrainEngine,用于生成瓦片
* TerrainEngineNode is the base class and interface for map engine implementations.
*
* A map engine lives under a MapNode and is responsible for generating the
* actual geometry representing the Earth.
RexTerrainEngine有个创建tile的接口:
一个是创建heightmap的瓦片,一个是创建地球瓦片
//! for standalone tile creation outside of a terrain
osg::Node* createTile(const TileKey& key);
//! Create a standalone tile from a tile model (experimental)
osg::Node* createTile(
const TerrainTileModel* model,
int createTileFlags,
unsigned referenceLOD);
LayerDrawable:作为一个layer所拥有的DrawTileCommand,统一进行渲染
/**
* Drawable for single "Layer" i.e. rendering pass.
* It is important that LayerDrawables be rendered in the order in which
* they appear. Since all LayerDrawables share a common bounds, this
* should happen automatically, but let's keep an eye out for trouble.
*/
tile的渲染:
TerrainCuller,在执行cull流程时,遍历SurfaceNode,获取,::addDrawCommand 将可见的tile加入到待渲染列表中
TerrainCuller::apply(SurfaceNode& node)
{
TileRenderModel& renderModel = _currentTileNode->renderModel();
// push the surface matrix:
osg::RefMatrix* matrix = createOrReuseMatrix(*getModelViewMatrix());
node.computeLocalToWorldMatrix(*matrix,this);
_cv->pushModelViewMatrix(matrix, node.getReferenceFrame());
// now test against the local bounding box for tighter culling:
if (!_cv->isCulled(node.getAlignedBoundingBox()))
{
if (!_isSpy)
{
node.setLastFramePassedCull(getFrameStamp()->getFrameNumber());
}
int order = 0;
unsigned count = 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 (_firstDrawCommandForTile == 0L)
{
_firstDrawCommandForTile = cmd;
}
else if (cmd->_layerOrder < _firstDrawCommandForTile->_layerOrder)
{
_firstDrawCommandForTile = 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 (_firstDrawCommandForTile == 0L)
{
//OE_INFO << LC << "Adding blank render for tile " << _currentTileNode->getKey().str() << std::endl;
DrawTileCommand* cmd = addDrawCommand(-1, &renderModel, 0L, _currentTileNode);
if (cmd)
{
_firstDrawCommandForTile = cmd;
}
}
// Set the layer order of the first draw command for this tile to zero,
// to support proper terrain blending.
if (_firstDrawCommandForTile)
{
_firstDrawCommandForTile->_layerOrder = 0;
}
// update our bounds
_terrain._drawState->_bs.expandBy(node.getBound());
_terrain._drawState->_box.expandBy(_terrain._drawState->_bs);
}
// pop the matrix from the cull stack
_cv->popModelViewMatrix();
if (node.getDebugNode())
{
node.accept(*_cv);
}
}
生成DrawTilecommand,保存渲染状态、投影、pass,几何体(SharedGeometry)等参数,并将DrawTileCommand添加到与某个layer相关联的layerDrawable中
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 = _cv->getModelViewMatrix();
tile->_keyValue = tileNode->getTileKeyValue();
tile->_geom = surface->getDrawable()->_geom.get();
tile->_morphConstants = tileNode->getMorphConstants();
tile->_key = &tileNode->getKey();
osg::Vec3 c = surface->getBound().center() * surface->getInverseMatrix();
tile->_range = getDistanceToViewPoint(c, true);
tile->_layerOrder = drawable->_drawOrder;
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;
}
else
{
OE_WARN << "Added nothing for a UID -1 darw command" << std::endl;
}
return 0L;
}
LayerDrawable的渲染,执行真正的瓦片渲染:
LayerDrawable::drawImplementation(osg::RenderInfo& ri) const
{
//OE_INFO << LC << (_layer ? _layer->getName() : "[empty]") << " tiles=" << _tiles.size() << std::endl;
// Get this context's state values:
PerContextDrawState& ds = _drawState->getPCDS(ri.getContextID());
ds.refresh(ri, _drawState->_bindings);
if (ds._layerUidUL >= 0)
{
GLint uid = _layer ? (GLint)_layer->getUID() : (GLint)-1;
ds._ext->glUniform1i(ds._layerUidUL, uid);
}
else
{
// This just means that the fragment shader for this layer doesn't use oe_layer_uid
}
for (DrawTileCommands::const_iterator tile = _tiles.begin(); tile != _tiles.end(); ++tile)
{
tile->draw(ri, *_drawState, 0L);
}
// If set, dirty all OSG state to prevent any leakage - this is sometimes
// necessary when doing custom OpenGL within a Drawable.
if (_clearOsgState)
{
// Dirty the texture attributes so OSG can properly reset them
// NOTE: cannot call state.dirtyAllAttributes, because that would invalidate
// positional state like light sources!
reinterpret_cast<StateEx*>(ri.getState())->dirtyAllTextureAttributes();
// NOTE: this is a NOOP in OSG 3.5.x, but not in 3.4.x ... Later we will need to
// revisit whether to call disableAllVertexArrays() in 3.5.x instead.
ri.getState()->dirtyAllVertexArrays();
// unbind local buffers when finished.
ds._ext->glBindBuffer(GL_ARRAY_BUFFER_ARB,0);
ds._ext->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,0);
// gw: no need to do this, in fact it will cause positional attributes
// (light clip planes and lights) to immediately be reapplied under the
// current MVM, which will by definition be wrong!)
//ri.getState()->apply();
}
}