- 申请数据:
申请数据主要是在RexTerrainEngineNode::traverse(osg::NodeVisitor& nv)函数中进行cull时,对需要加载的数据提出加载申请:
主要步骤:
- 创建TerrainCuller对象,使用其对terrian进行裁剪遍历
- 遍历terrain的所有layer,保存渲染状态,对每一个layer的数据进行裁剪
- 对geometryPool执行裁剪
- 对loader执行裁剪遍历
- 对unloader执行裁剪遍历
- 对releaser执行裁剪遍历
- 对rasterizer执行裁剪遍历
void
RexTerrainEngineNode::traverse(osg::NodeVisitor& nv)
{
if ( nv.getVisitorType() == nv.CULL_VISITOR )
{
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
// Marks the start of the cull pass
getEngineContext()->startCull( cv );
// Initialize a new culler
TerrainCuller culler(cv, this->getEngineContext());
// Prepare the culler with the set of renderable layers:
culler.setup(getMap(), _cachedLayerExtents, this->getEngineContext()->getRenderBindings());
// Assemble the terrain drawables:
_terrain->accept(culler);
// If we're using geometry pooling, optimize the drawable for shared state
// by sorting the draw commands.
// TODO: benchmark this further to see whether it's worthwhile
unsigned totalTiles = 0L;
if (getEngineContext()->getGeometryPool()->isEnabled())
{
totalTiles = culler._terrain.sortDrawCommands();
}
// The common stateset for the terrain group:
cv->pushStateSet(_terrain->getOrCreateStateSet());
// Push all the layers to draw on to the cull visitor in the order in which
// they appear in the map.
LayerDrawable* lastLayer = 0L;
unsigned order = 0;
bool surfaceStateSetPushed = false;
bool imageLayerStateSetPushed = false;
int layersDrawn = 0;
osg::State::StateSetStack stateSetStack;
for(LayerDrawableList::iterator i = culler._terrain.layers().begin();
i != culler._terrain.layers().end();
++i)
{
// Note: Cannot save lastLayer here because its _tiles may be empty, which can lead to a crash later
if (!i->get()->_tiles.empty())
{
lastLayer = i->get();
// if this is a RENDERTYPE_TERRAIN_SURFACE, we need to activate either the
// default surface state set or the image layer state set.
if (lastLayer->_renderType == Layer::RENDERTYPE_TERRAIN_SURFACE)
{
if (!surfaceStateSetPushed)
{
cv->pushStateSet(_surfaceStateSet.get());
surfaceStateSetPushed = true;
}
if (lastLayer->_imageLayer || lastLayer->_layer == NULL)
{
if (!imageLayerStateSetPushed)
{
cv->pushStateSet(_imageLayerStateSet.get());
imageLayerStateSetPushed = true;
}
}
else
{
if (imageLayerStateSetPushed)
{
cv->popStateSet();
imageLayerStateSetPushed = false;
}
}
}
else
{
if (imageLayerStateSetPushed)
{
cv->popStateSet();
imageLayerStateSetPushed = false;
}
if (surfaceStateSetPushed)
{
cv->popStateSet();
surfaceStateSetPushed = false;
}
}
//OE_INFO << " Apply: " << (lastLayer->_layer ? lastLayer->_layer->getName() : "-1") << "; tiles=" << lastLayer->_tiles.size() << std::endl;
//buf << (lastLayer->_layer ? lastLayer->_layer->getName() : "none") << " (" << lastLayer->_tiles.size() << ")\n";
if (lastLayer->_layer)
{
lastLayer->_layer->apply(lastLayer, cv);
}
else
{
lastLayer->accept(*cv);
}
++layersDrawn;
}
//buf << (lastLayer->_layer ? lastLayer->_layer->getName() : "none") << " (" << lastLayer->_tiles.size() << ")\n";
}
// Uncomment this to see how many layers were drawn
//Registry::instance()->startActivity("Layers", Stringify()<<layersDrawn);
// The last layer to render must clear up the OSG state,
// otherwise it will be corrupt and can lead to crashing.
if (lastLayer)
{
lastLayer->_clearOsgState = true;
}
if (imageLayerStateSetPushed)
{
cv->popStateSet();
imageLayerStateSetPushed = false;
}
if (surfaceStateSetPushed)
{
cv->popStateSet();
surfaceStateSetPushed = false;
}
// pop the common terrain state set
cv->popStateSet();
// marks the end of the cull pass
this->getEngineContext()->endCull( cv );
// If the culler found any orphaned data, we need to update the render model
// during the next update cycle.
if (culler._orphanedPassesDetected > 0u)
{
_renderModelUpdateRequired = true;
OE_INFO << LC << "Detected " << culler._orphanedPassesDetected << " orphaned rendering passes\n";
}
// we don't call this b/c we don't want _terrain
//TerrainEngineNode::traverse(nv);
// traverse all the other children (geometry pool, loader/unloader, etc.)
_geometryPool->accept(nv);
_loader->accept(nv);
_unloader->accept(nv);
_releaser->accept(nv);
if (_rasterizer)
_rasterizer->accept(nv);
}
}
对地形内部进行裁剪,进入到tilenode的裁剪遍历,每个tileNode,都有四个child(quadtree),逐个执行裁剪遍历,如果某个child没有加载,设置dirty状态,执行loader加载待加载的子节点
bool TileNode::cull(TerrainCuller* culler)
{
EngineContext* context = culler->getEngineContext();
// Horizon check the surface first:
if (!_surface->isVisibleFrom(culler->getViewPointLocal()))
{
return false;
}
// determine whether we can and should subdivide to a higher resolution:
bool childrenInRange = shouldSubDivide(culler, context->getSelectionInfo());
// whether it is OK to create child TileNodes is necessary.
bool canCreateChildren = childrenInRange;
// whether it is OK to load data if necessary.
bool canLoadData = true;
// whether to accept the current surface node and not the children.
bool canAcceptSurface = false;
// Don't load data in progressive mode until the parent is up to date
if (context->getOptions().progressive() == true)
{
TileNode* parent = getParentTile();
if ( parent && parent->isDirty() )
{
canLoadData = false;
}
}
// If this is an inherit-viewpoint camera, we don't need it to invoke subdivision
// because we want only the tiles loaded by the true viewpoint.
const osg::Camera* cam = culler->getCamera();
if ( cam && cam->getReferenceFrame() == osg::Camera::ABSOLUTE_RF_INHERIT_VIEWPOINT )
{
canCreateChildren = false;
canLoadData = false;
}
if (childrenInRange)
{
// We are in range of the child nodes. Either draw them or load them.
// If the children don't exist, create them and inherit the parent's data.
if ( !_childrenReady && canCreateChildren )
{
_mutex.lock();
if ( !_childrenReady )
{
OE_START_TIMER(createChildren);
createChildren( context );
REPORT("TileNode::createChildren", createChildren);
_childrenReady = true;
// This means that you cannot start loading data immediately; must wait a frame.
canLoadData = false;
}
_mutex.unlock();
}
// If all are ready, traverse them now.
if ( _childrenReady )
{
for(int i=0; i<4; ++i)
{
TileNode* child = getSubTile(i);
if (child)
child->accept(*culler);
}
}
// If we don't traverse the children, traverse this node's payload.
else
{
canAcceptSurface = true;
}
}
// If children are outside camera range, draw the payload and expire the children.
else
{
canAcceptSurface = true;
}
// accept this surface if necessary.
if ( canAcceptSurface )
{
_surface->accept( *culler );
}
// If this tile is marked dirty, try loading data.
if ( _dirty && canLoadData )
{
load( culler );
}
return true;
}
如果child无效,创建childTile
if ( !_childrenReady )
{
OE_START_TIMER(createChildren);
createChildren( context );
REPORT("TileNode::createChildren", createChildren);
_childrenReady = true;
// This means that you cannot start loading data immediately; must wait a frame.
canLoadData = false;
}
创建子child和节点
void
TileNode::createChildren(EngineContext* context)
{
// NOTE: Ensure that _mutex is locked before calling this function!
//OE_WARN << "Creating children for " << _key.str() << std::endl;
// Create the four child nodes.
for(unsigned quadrant=0; quadrant<4; ++quadrant)
{
TileNode* node = new TileNode();
if (context->getOptions().minExpiryFrames().isSet())
{
node->setMinimumExpirationFrames( *context->getOptions().minExpiryFrames() );
}
if (context->getOptions().minExpiryTime().isSet())
{
node->setMinimumExpirationTime( *context->getOptions().minExpiryTime() );
}
// Build the surface geometry:
node->create( getKey().createChildKey(quadrant), this, context );
// Add to the scene graph.
addChild( node );
}
}
创建子child,只创建了一个平面,没有高程和图片,需要提交,让后台线程加载图片和高程数据并应用到geometry中:
TileNode::create(const TileKey& key, TileNode* parent, EngineContext* context)
{
.............
// Get a shared geometry from the pool that corresponds to this tile key:
osg::ref_ptr<SharedGeometry> geom;
context->getGeometryPool()->getPooledGeometry(
key,
MapInfo(map.get()),
tileSize,
masks.get(),
geom);
// Create the drawable for the terrain surface:
TileDrawable* surfaceDrawable = new TileDrawable(key, geom.get(),context->getOptions().tileSize().get() );
// Give the tile Drawable access to the render model so it can properly
// calculate its bounding box and sphere.
surfaceDrawable->setModifyBBoxCallback(context->getModifyBBoxCallback());
// Create the node to house the tile drawable:
_surface = new SurfaceNode(key,MapInfo(map.get()),context->getRenderBindings(),surfaceDrawable );
// create a data load request for this new tile:
_loadRequest = new LoadTileData( this, context );
_loadRequest->setName( _key.str() );
_loadRequest->setTileKey( _key );
// whether the stitch together normal maps for adjacent tiles.
_stitchNormalMap = context->_options.normalizeEdges() == true;
// Encode the tile key in a uniform
unsigned tw, th;
_key.getProfile()->getNumTiles(_key.getLOD(), tw, th);
double x = (double)_key.getTileX();
double y = (double)(th - _key.getTileY()-1);
_tileKeyValue.set((float)x,(float)y,(float)_key.getLOD(),-1.0f);
// initialize all the per-tile uniforms the shaders will need:
float range, morphStart, morphEnd;
context->getSelectionInfo().get(_key, range, morphStart, morphEnd);
float one_over_end_minus_start = 1.0f/(morphEnd - morphStart);
_morphConstants.set(morphEnd * one_over_end_minus_start, one_over_end_minus_start);
// Make a tilekey to use for testing whether to subdivide.
if (_key.getTileY() <= th/2)
_subdivideTestKey = _key.createChildKey(0);
else
_subdivideTestKey = _key.createChildKey(3);
// Initialize the data model by copying the parent's rendering data
// and scale/biasing the matrices.
if (parent)
{
unsigned quadrant = getKey().getQuadrant();
const RenderBindings& bindings = context->getRenderBindings();
bool setElevation = false;
for (unsigned p = 0; p < parent->_renderModel._passes.size(); ++p)
{
const RenderingPass& parentPass = parent->_renderModel._passes[p];
// If the key is now out of the layer's valid min/max range, skip this pass.
if (!passInLegalRange(parentPass))
continue;
// Copy the parent pass:
_renderModel._passes.push_back(parentPass);
RenderingPass& myPass = _renderModel._passes.back();
// Scale/bias each matrix for this key quadrant.
Samplers& samplers = myPass.samplers();
for (unsigned s = 0; s < samplers.size(); ++s)
{
samplers[s]._matrix.preMult(scaleBias[quadrant]);
}
// Are we using image blending? If so, initialize the color_parent
// to the color texture.
if (bindings[SamplerBinding::COLOR_PARENT].isActive())
{
samplers[SamplerBinding::COLOR_PARENT] = samplers[SamplerBinding::COLOR];
}
}
// Copy the parent's shared samplers and scale+bias each matrix to the new quadrant:
_renderModel._sharedSamplers = parent->_renderModel._sharedSamplers;
for (unsigned s = 0; s<_renderModel._sharedSamplers.size(); ++s)
{
Sampler& sampler = _renderModel._sharedSamplers[s];
sampler._matrix.preMult(scaleBias[quadrant]);
}
// Use the elevation sampler to initialize the elevation raster
// (used for primitive functors, intersection, etc.)
if (!setElevation && bindings[SamplerBinding::ELEVATION].isActive())
{
const Sampler& elevation = _renderModel._sharedSamplers[SamplerBinding::ELEVATION];
if (elevation._texture.valid())
{
setElevationRaster(elevation._texture->getImage(0), elevation._matrix);
setElevation = true;
}
}
}
// need to recompute the bounds after adding payload:
dirtyBound();
// signal the tile to start loading data:
setDirty( true );
// register me.
context->liveTiles()->add( this );
// tell the world.
OE_DEBUG << LC << "notify (create) key " << getKey().str() << std::endl;
context->getEngine()->getTerrain()->notifyTileAdded(getKey(), this);
}
创建一个平面:
SharedGeometry*
GeometryPool::createGeometry(const TileKey& tileKey,
const MapInfo& mapInfo,
unsigned tileSize,
MaskGenerator* maskSet) const
{
// Establish a local reference frame for the tile:
osg::Vec3d centerWorld;
GeoPoint centroid;
tileKey.getExtent().getCentroid( centroid );
centroid.toWorld( centerWorld );
osg::Matrix world2local, local2world;
centroid.createWorldToLocal( world2local );
local2world.invert( world2local );
// Attempt to calculate the number of verts in the surface geometry.
bool createSkirt = _options.heightFieldSkirtRatio() > 0.0f;
unsigned numVertsInSurface = (tileSize*tileSize);
unsigned numVertsInSkirt = createSkirt ? tileSize*4u - 4u : 0;
unsigned numVerts = numVertsInSurface + numVertsInSkirt;
unsigned numIndiciesInSurface = (tileSize-1) * (tileSize-1) * 6;
unsigned numIncidesInSkirt = getNumSkirtElements(tileSize);
// TODO: reconsider this ...
GLenum mode = (_options.gpuTessellation() == true) ? GL_PATCHES : GL_TRIANGLES;
osg::BoundingSphere tileBound;
// the geometry:
osg::ref_ptr<SharedGeometry> geom = new SharedGeometry();
geom->setUseVertexBufferObjects(true);
//geom->setUseDisplayList(false);
osg::ref_ptr<osg::VertexBufferObject> vbo = new osg::VertexBufferObject();
// Pre-allocate enough space for all triangles.
osg::DrawElements* primSet = new osg::DrawElementsUShort(mode);
primSet->setElementBufferObject(new osg::ElementBufferObject());
primSet->reserveElements(numIndiciesInSurface + numIncidesInSkirt);
geom->setDrawElements(primSet);
// the vertex locations:
osg::ref_ptr<osg::Vec3Array> verts = new osg::Vec3Array();
verts->setVertexBufferObject(vbo.get());
verts->reserve( numVerts );
verts->setBinding(verts->BIND_PER_VERTEX);
geom->setVertexArray( verts.get() );
// the surface normals (i.e. extrusion vectors)
osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array();
normals->setVertexBufferObject(vbo.get());
normals->reserve( numVerts );
normals->setBinding(normals->BIND_PER_VERTEX);
geom->setNormalArray( normals.get() );
osg::ref_ptr<osg::Vec3Array> neighbors = 0L;
osg::ref_ptr<osg::Vec3Array> neighborNormals = 0L;
if ( _options.morphTerrain() == true )
{
// neighbor positions (for morphing)
neighbors = new osg::Vec3Array();
neighbors->setBinding(neighbors->BIND_PER_VERTEX);
neighbors->setVertexBufferObject(vbo.get());
neighbors->reserve( numVerts );
geom->setNeighborArray(neighbors.get());
neighborNormals = new osg::Vec3Array();
neighborNormals->setVertexBufferObject(vbo.get());
neighborNormals->reserve( numVerts );
neighborNormals->setBinding(neighborNormals->BIND_PER_VERTEX);
geom->setNeighborNormalArray( neighborNormals.get() );
}
// tex coord is [0..1] across the tile. The 3rd dimension tracks whether the
// vert is masked: 0=yes, 1=no
#ifdef SHARE_TEX_COORDS
bool populateTexCoords = false;
if ( !_sharedTexCoords.valid() )
{
_sharedTexCoords = new osg::Vec3Array();
_sharedTexCoords->reserve( numVerts );
populateTexCoords = true;
}
osg::Vec3Array* texCoords = _sharedTexCoords.get();
#else
bool populateTexCoords = true;
osg::ref_ptr<osg::Vec3Array> texCoords = new osg::Vec3Array();
texCoords->setBinding(texCoords->BIND_PER_VERTEX);
texCoords->setVertexBufferObject(vbo.get());
texCoords->reserve( numVerts );
#endif
geom->setTexCoordArray(texCoords.get());
float delta = 1.0/(tileSize-1);
osg::Vec3d tdelta(delta,0,0);
tdelta.normalize();
osg::Vec3d vZero(0,0,0);
osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( tileKey, mapInfo );
for(unsigned row=0; row<tileSize; ++row)
{
float ny = (float)row/(float)(tileSize-1);
for(unsigned col=0; col<tileSize; ++col)
{
float nx = (float)col/(float)(tileSize-1);
osg::Vec3d model;
locator->unitToModel(osg::Vec3d(nx, ny, 0.0f), model);
osg::Vec3d modelLTP = model*world2local;
verts->push_back( modelLTP );
tileBound.expandBy( verts->back() );
osg::Vec3d modelPlusOne;
// 创建单位几何,是平面还是球面
locator-> unitToModel(osg::Vec3d(nx, ny, 1.0f), modelPlusOne);
osg::Vec3d normal = (modelPlusOne*world2local)-modelLTP;
normal.normalize();
normals->push_back( normal );
}
}
// tessellateSurface
// skirt mesh
....
return geom.release();
}
平面/球面坐标变换:
bool Locator::convertLocalToModel(const osg::Vec3d& local, osg::Vec3d& world) const
{
switch(_coordinateSystemType)
{
case(GEOCENTRIC):
{
osg::Vec3d geographic = local * _transform;
_ellipsoidModel->convertLatLongHeightToXYZ(geographic.y(), geographic.x(), geographic.z(),
world.x(), world.y(), world.z());
return true;
}
case(GEOGRAPHIC):
{
world = local * _transform;
return true;
}
case(PROJECTED):
{
world = local * _transform;
return true;
}
}
return false;
}
LoadData 对象,即为申请数据:
_loadRequest = new LoadTileData( this, context );
loaderd对数据加载,起始是将loadData转换为osg的datapager所使用的加载请求:
bool PagerLoader::load(Loader::Request* request, float priority, osg::NodeVisitor& nv)
{
// check that the request is not already completed but unmerged:
if ( request && !request->isMerging() && !request->isFinished() && nv.getDatabaseRequestHandler() )
{
//OE_INFO << LC << "load (" << request->getTileKey().str() << ")" << std::endl;
unsigned fn = 0;
if ( nv.getFrameStamp() )
{
fn = nv.getFrameStamp()->getFrameNumber();
request->setFrameNumber( fn );
}
bool addToRequestSet = false;
// lock the request since multiple cull traversals might hit this function.
request->lock();
{
request->setState(Request::RUNNING);
// remember the last tick at which this request was submitted
request->_lastTick = osg::Timer::instance()->tick();
// update the priority, scale and bias it, and then normalize it to [0..1] range.
unsigned lod = request->getTileKey().getLOD();
float p = priority * _priorityScales[lod] + _priorityOffsets[lod];
request->_priority = p / (float)(_numLODs+1);
// timestamp it
request->setFrameNumber( fn );
// incremenet the load count.
request->_loadCount++;
// if this is the first load request since idle, we need to remember this request.
addToRequestSet = (request->_loadCount == 1);
}
request->unlock();
char filename[64];
//sprintf(filename, "%u.%u.osgearth_rex_loader", request->_uid, _engineUID);
sprintf(filename, "%u.osgearth_rex_loader", request->_uid);
nv.getDatabaseRequestHandler()->requestNodeFile(
filename,
_myNodePath,
request->_priority,
nv.getFrameStamp(),
request->_internalHandle,
_dboptions.get() );
// remember the request:
if ( true ) // addToRequestSet // Not sure whether we need to keep doing this in order keep it sorted -- check it out.
{
Threading::ScopedMutexLock lock( _requestsMutex );
_requests[request->getUID()] = request;
}
return true;
}
return false;
}
- 加载数据数据:
osgDB::DatabasePager::DatabaseThread::run()线程中加载数据,创建mesh和网络申请图片:
void
LoadTileData::invoke(ProgressCallback* progress)
{
osg::ref_ptr<TileNode> tilenode;
if (!_tilenode.lock(tilenode))
return;
osg::ref_ptr<TerrainEngineNode> engine;
if (!_engine.lock(engine))
return;
osg::ref_ptr<const Map> map;
if (!_map.lock(map))
return;
// Assemble all the components necessary to display this tile
_dataModel = engine->createTileModel(
map.get(),
tilenode->getKey(),
_filter,
_enableCancel? progress : 0L);
// if the operation was canceled, set the request to idle and delete the tile model.
if (progress && progress->isCanceled())
{
_dataModel = 0L;
setState(Request::IDLE);
}
}
-
更新到场景树
- 事件更新中更新数据:
由加载线程加载的数据,需要在eventtraverse的遍历中,多级遍历后调用到 PagerLoader:traverse遍历,对request进行合并
void PagerLoader::traverse(osg::NodeVisitor& nv) { // only called when _mergesPerFrame > 0 if ( nv.getVisitorType() == nv.EVENT_VISITOR ) { if ( nv.getFrameStamp() ) { setFrameStamp(nv.getFrameStamp()); } // process pending merges. { METRIC_BEGIN("loader.merge"); int count; for(count=0; count < _mergesPerFrame && !_mergeQueue.empty(); ++count) { Request* req = _mergeQueue.begin()->get(); if ( req && req->_lastTick >= _checkpoint ) { OE_START_TIMER(req_apply); req->apply( getFrameStamp() ); double s = OE_STOP_TIMER(req_apply); req->setState(Request::FINISHED); } _mergeQueue.erase( _mergeQueue.begin() ); } METRIC_END("loader.merge"); } // cull finished requests. { METRIC_SCOPED("loader.cull"); Threading::ScopedMutexLock lock( _requestsMutex ); unsigned fn = 0; if ( nv.getFrameStamp() ) fn = nv.getFrameStamp()->getFrameNumber(); // Purge expired requests. for(Requests::iterator i = _requests.begin(); i != _requests.end(); ) { Request* req = i->second.get(); const unsigned frameDiff = fn - req->getLastFrameSubmitted(); // Deal with completed requests: if ( req->isFinished() ) { //OE_INFO << LC << req->getName() << "(" << i->second->getUID() << ") finished." << std::endl; req->setState( Request::IDLE ); if ( REPORT_ACTIVITY ) Registry::instance()->endActivity( req->getName() ); _requests.erase( i++ ); } // Discard requests that are no longer required: else if ( !req->isMerging() && frameDiff > 2 ) { //OE_INFO << LC << req->getName() << "(" << i->second->getUID() << ") died waiting after " << frameDiff << " frames" << std::endl; req->setState( Request::IDLE ); if ( REPORT_ACTIVITY ) Registry::instance()->endActivity( req->getName() ); _requests.erase( i++ ); } // Prevent a request from getting stuck in the merge queue: else if ( req->isMerging() && frameDiff > 1800 ) { //OE_INFO << LC << req->getName() << "(" << i->second->getUID() << ") died waiting " << frameDiff << " frames to merge" << std::endl; req->setState( Request::IDLE ); if ( REPORT_ACTIVITY ) Registry::instance()->endActivity( req->getName() ); _requests.erase( i++ ); } else // still valid. { ++i; } } //OE_NOTICE << LC << "PagerLoader: requests=" << _requests.size() << "; mergeQueue=" << _mergeQueue.size() << std::endl; } } LoaderGroup::traverse( nv ); }
LoadTileData进行数据merge
void LoadTileData::apply(const osg::FrameStamp* stamp) { osg::ref_ptr<EngineContext> context; if (!_context.lock(context)) return; osg::ref_ptr<const Map> map; if (!_map.lock(map)) return; // ensure we got an actual datamodel: if (_dataModel.valid()) { // ensure it's in sync with the map revision (not out of date): if (map.valid() && _dataModel->getRevision() == map->getDataModelRevision()) { // ensure the tile node hasn't expired: osg::ref_ptr<TileNode> tilenode; if ( _tilenode.lock(tilenode) ) { const RenderBindings& bindings = context->getRenderBindings(); // Merge the new data into the tile. tilenode->merge(_dataModel.get(), bindings); // Mark as complete. TODO: per-data requests will do something different. tilenode->setDirty( false ); OE_DEBUG << LC << "apply " << _dataModel->getKey().str() << "\n"; } else { OE_DEBUG << LC << "LoadTileData failed; TileNode disappeared\n"; } } else { OE_INFO << LC << "apply " << _dataModel->getKey().str() << " ignored b/c it is out of date\n"; } // Delete the model immediately _dataModel = 0L; } }
然后调用TileNode::merge
void TileNode::merge(const TerrainTileModel* model, const RenderBindings& bindings) { bool newElevationData = false; const SamplerBinding& color = bindings[SamplerBinding::COLOR]; if (color.isActive()) { for(TerrainTileColorLayerModelVector::const_iterator i = model->colorLayers().begin(); i != model->colorLayers().end(); ++i) { TerrainTileImageLayerModel* model = dynamic_cast<TerrainTileImageLayerModel*>(i->get()); if (model) { if (model->getTexture()) { RenderingPass* pass = _renderModel.getPass(model->getImageLayer()->getUID()); if (!pass) { pass = &_renderModel.addPass(); pass->setLayer(model->getLayer()); // This is a new pass that just showed up at this LOD // Since it just arrived at this LOD, make the parent the same as the color. if (bindings[SamplerBinding::COLOR_PARENT].isActive()) { pass->samplers()[SamplerBinding::COLOR_PARENT]._texture = model->getTexture(); pass->samplers()[SamplerBinding::COLOR_PARENT]._matrix = *model->getMatrix(); } } pass->samplers()[SamplerBinding::COLOR]._texture = model->getTexture(); pass->samplers()[SamplerBinding::COLOR]._matrix = *model->getMatrix(); // Handle an RTT image layer: if (model->getImageLayer() && model->getImageLayer()->useCreateTexture()) { // Check the texture's userdata for a Node. If there is one there, // render it to the texture using the Tile Rasterizer service. // TODO: consider hanging on to this texture and not applying it to // the live tile until the RTT is complete. (Prevents unsightly flashing) GeoNode* rttNode = dynamic_cast<GeoNode*>(model->getTexture()->getUserData()); if (rttNode && _context->getTileRasterizer()) { _context->getTileRasterizer()->push(rttNode->_node.get(), model->getTexture(), rttNode->_extent); } } // check to see if this data requires an image update traversal. if (_imageUpdatesActive == false) { for(unsigned i=0; i<model->getTexture()->getNumImages(); ++i) { if (model->getTexture()->getImage(i)->requiresUpdateCall()) { ADJUST_UPDATE_TRAV_COUNT(this, +1); _imageUpdatesActive = true; break; } } } } } else // non-image color layer (like splatting, e.g.) { TerrainTileColorLayerModel* model = i->get(); if (model && model->getLayer()) { RenderingPass* pass = _renderModel.getPass(model->getLayer()->getUID()); if (!pass) { pass = &_renderModel.addPass(); pass->setLayer(model->getLayer()); } } } } } // Elevation: const SamplerBinding& elevation = bindings[SamplerBinding::ELEVATION]; if (elevation.isActive() && model->elevationModel().valid() && model->elevationModel()->getTexture()) { osg::Texture* tex = model->elevationModel()->getTexture(); // always keep the elevation image around because we use it for bounding box computation: tex->setUnRefImageDataAfterApply(false); _renderModel._sharedSamplers[SamplerBinding::ELEVATION]._texture = tex; _renderModel._sharedSamplers[SamplerBinding::ELEVATION]._matrix.makeIdentity(); setElevationRaster(tex->getImage(0), osg::Matrixf::identity()); newElevationData = true; } // Normals: const SamplerBinding& normals = bindings[SamplerBinding::NORMAL]; if (normals.isActive() && model->normalModel().valid() && model->normalModel()->getTexture()) { osg::Texture* tex = model->normalModel()->getTexture(); // keep the normal map around because we might update it later in "ping" tex->setUnRefImageDataAfterApply(false); _renderModel._sharedSamplers[SamplerBinding::NORMAL]._texture = tex; _renderModel._sharedSamplers[SamplerBinding::NORMAL]._matrix.makeIdentity(); updateNormalMap(); } // Other Shared Layers: for (unsigned i = 0; i < model->sharedLayers().size(); ++i) { TerrainTileImageLayerModel* layerModel = model->sharedLayers()[i].get(); if (layerModel->getTexture()) { // locate the shared binding corresponding to this layer: UID uid = layerModel->getImageLayer()->getUID(); unsigned bindingIndex = INT_MAX; for(unsigned i=SamplerBinding::SHARED; i<bindings.size() && bindingIndex==INT_MAX; ++i) { if (bindings[i].isActive() && bindings[i].sourceUID().isSetTo(uid)) { bindingIndex = i; } } if (bindingIndex < INT_MAX) { osg::Texture* tex = layerModel->getTexture(); _renderModel._sharedSamplers[bindingIndex]._texture = tex; _renderModel._sharedSamplers[bindingIndex]._matrix.makeIdentity(); } } } // Patch Layers for (unsigned i = 0; i < model->patchLayers().size(); ++i) { TerrainTilePatchLayerModel* layerModel = model->patchLayers()[i].get(); } if (_childrenReady) { for (int i = 0; i < 4; ++i) { TileNode* child = getSubTile(i); if (child) child->refreshInheritedData(this, bindings); } } if (newElevationData) { _context->getEngine()->getTerrain()->notifyTileAdded(getKey(), this); } }
-
将node添加到场景:
void Scene::updateSceneGraph(osg::NodeVisitor& updateVisitor)
—》void DatabasePager::addLoadedDataToSceneGraph(const osg::FrameStamp &frameStamp)
将节点,添加到父节点上,至此,由申请才转换为正常的场景节点:
```cpp group->addChild(databaseRequest->_loadedModel.get()); ```
进入PagerLoader,将申请,转换为node,挂到PagerLoader对象上:
bool PagerLoader::addChild(osg::Node* node) { osg::ref_ptr<RequestResultNode> result = dynamic_cast<RequestResultNode*>(node); if ( result.valid() ) { Request* req = result->getRequest(); if ( req ) { // Make sure the request is both current (newer than the last checkpoint) // and running (i.e. has not been canceled along the way) if (req->_lastTick >= _checkpoint && req->isRunning()) { if ( _mergesPerFrame > 0 ) { _mergeQueue.insert( req ); req->setState( Request::MERGING ); } else { req->apply( getFrameStamp() ); req->setState( Request::FINISHED ); if ( REPORT_ACTIVITY ) Registry::instance()->endActivity( req->getName() ); } } else { OE_DEBUG << LC << "Request " << req->getName() << " canceled" << std::endl; req->setState( Request::FINISHED ); if ( REPORT_ACTIVITY ) Registry::instance()->endActivity( req->getName() ); } } } else { //OE_WARN << LC << "Internal error: illegal node type in addchild" << std::endl; } return true; }
-
addTile事件通知:
void Terrain::update() { _updateQueue->runOperations(); }
operation执行添加数据:
void Terrain::OnTileAddedOperation::operator()(osg::Object*) { if ( getKeep() == false ) return; if (_delay-- > 0) return; ++_count; osg::ref_ptr<Terrain> terrain; osg::ref_ptr<osg::Node> node; if ( _terrain.lock(terrain) && (!_key.valid() || _node.lock(node)) ) { if (_key.valid()) terrain->fireTileAdded( _key, node.get() ); else terrain->fireTileAdded( _key, 0L ); } else { // nop; tile expired; let it go. OE_DEBUG << "Tile expired before notification: " << _key.str() << std::endl; } this->setKeep( false ); }
添加到节点上
void Terrain::fireTileAdded( const TileKey& key, osg::Node* node ) { Threading::ScopedReadLock sharedLock( _callbacksMutex ); for( CallbackList::iterator i = _callbacks.begin(); i != _callbacks.end(); ) { TerrainCallbackContext context( this ); i->get()->onTileAdded( key, node, context ); // if the callback set the "remove" flag, discard the callback. if ( context.markedForRemoval() ) i = _callbacks.erase( i ); else ++i; } }
处理节点添加回调实践,检测碰撞等
// Callback that notifies the manipulator whenever the terrain changes // around its center point. struct ManipTerrainCallback : public TerrainCallback { ManipTerrainCallback(EarthManipulator* manip) : _manip(manip) { } void onTileAdded(const TileKey& key, osg::Node* graph, TerrainCallbackContext& context) { osg::ref_ptr<EarthManipulator> safe; if ( _manip.lock(safe) ) { safe->handleTileAdded(key, graph, context); } } osg::observer_ptr<EarthManipulator> _manip; };