void TileStack::drawAll(GWindow& window) const { TileNode* p = front; while (p!= NULL) { p->draw(window); p = p->next; } }
void TilePagedLOD::traverse(osg::NodeVisitor& nv) { // Only traverse the TileNode if our neighbors (the other members of // our group of four) are ready as well. if ( _children.size() > 0 ) { _children[0]->setNodeMask( _familyReady ? ~0 : 0 ); // find our tile node: TileNode* tilenode = dynamic_cast<TileGroup*>(_children[0].get()) ? static_cast<TileGroup*>(_children[0].get())->getTileNode() : static_cast<TileNode*>(_children[0].get()); // Check whether the TileNode is marked dirty. If so, install a new pager request // to reload and replace the TileNode. if (nv.getVisitorType() == nv.CULL_VISITOR && this->getNumFileNames() < 2 && tilenode->isOutOfDate() ) { // lock keeps multiple CullVisitors from doing the same thing Threading::ScopedMutexLock exclusive( _updateMutex ); if ( this->getNumFileNames() < 2 ) // double-check pattern { //OE_WARN << LC << "Queuing request for replacement: " << _container->getTileNode()->getKey().str() << std::endl; this->setFileName( 1, Stringify() << _prefix << ".osgearth_engine_mp_standalone_tile" ); this->setRange( 1, 0, FLT_MAX ); } } } osg::PagedLOD::traverse( nv ); }
osg::Node* SingleKeyNodeFactory::createTile(TileModel* model, bool setupChildrenIfNecessary) { // compile the model into a node: TileNode* tileNode = _modelCompiler->compile( model, _frame ); // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { //Compute the min range based on the 2D size of the tile osg::BoundingSphere bs = tileNode->getBound(); GeoExtent extent = model->_tileKey.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()); TilePagedLOD* plod = new TilePagedLOD( _engineUID, _liveTiles, _deadTiles ); plod->setCenter ( bs.center() ); plod->addChild ( tileNode ); plod->setRange ( 0, minRange, FLT_MAX ); plod->setFileName( 1, Stringify() << tileNode->getKey().str() << "." << _engineUID << ".osgearth_engine_mp_tile" ); plod->setRange ( 1, 0, minRange ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = Registry::instance()->cloneOrCreateOptions(); options->setFileLocationCallback( new FileLocationCallback() ); plod->setDatabaseOptions( options ); #endif result = plod; // this one rejects back-facing tiles: if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { osg::HeightField* hf = model->_elevationData.getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); } } else { result = tileNode; } return result; }
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 ); } }
void TileNode::createChildren(EngineContext* context) { // NOTE: Ensure that _mutex is locked before calling this fucntion! // Create the four child nodes. for(unsigned quadrant=0; quadrant<4; ++quadrant) { TileNode* node = new TileNode(); if (context->getOptions().minExpiryFrames().isSet()) { node->setMinimumExpiryFrames( *context->getOptions().minExpiryFrames() ); } if (context->getOptions().minExpiryTime().isSet()) { node->setMinimumExpiryTime( *context->getOptions().minExpiryTime() ); } // Build the surface geometry: node->create( getTileKey().createChildKey(quadrant), context ); // Add to the scene graph. addChild( node ); // Inherit the samplers with new scale/bias information. node->inheritState( context ); } }
void TileNode::loadChildren() { _mutex.lock(); if ( !_childrenReady ) { // Create the children createChildren( _context.get() ); _childrenReady = true; int numChildren = getNumChildren(); if ( numChildren > 0 ) { for(int i=0; i<numChildren; ++i) { TileNode* child = getSubTile(i); if (child) { // Load the children's data. child->loadSync(); } } } } _mutex.unlock(); }
// The osgDB::DatabasePager will call this method when merging a new child // into the scene graph. bool TilePagedLOD::addChild(osg::Node* node) { if ( node ) { // if we see an invalid tile marker, disable the paged lod slot. if ( dynamic_cast<InvalidTileNode*>(node) ) { this->setFileName( 1, "" ); this->setRange( 1, 0, 0 ); this->setRange( 0, 0.0f, FLT_MAX ); return true; } // If it's a TileNode, this is the simple first addition of the // static TileNode child (not from the pager). TileNode* tilenode = dynamic_cast<TileNode*>( node ); if ( tilenode && _live.get() ) { _live->add( tilenode ); // Listen for out east and south neighbors. const TileKey& key = tilenode->getKey(); _live->listenFor( key.createNeighborKey(1, 0), tilenode ); _live->listenFor( key.createNeighborKey(0, 1), tilenode ); } return osg::PagedLOD::addChild( node ); } return false; }
osg::Node* SerialKeyNodeFactory::createTile(TileModel* model, bool tileHasRealData) { // compile the model into a node: TileNode* tileNode = _modelCompiler->compile( model ); // see if this tile might have children. bool prepareForChildren = (tileHasRealData || (_options.minLOD().isSet() && model->_tileKey.getLOD() < *_options.minLOD())) && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { //Compute the min range based on the 2D size of the tile osg::BoundingSphere bs = tileNode->getBound(); GeoExtent extent = model->_tileKey.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()); osgDB::Options* dbOptions = Registry::instance()->cloneOrCreateOptions(); TileGroup* plod = new TileGroup(tileNode, _engineUID, _liveTiles.get(), _deadTiles.get(), dbOptions); plod->setSubtileRange( minRange ); #if USE_FILELOCATIONCALLBACK dbOptions->setFileLocationCallback( new FileLocationCallback() ); #endif result = plod; } else { result = tileNode; } // this one rejects back-facing tiles: if ( _mapInfo.isGeocentric() && _options.clusterCulling() == true ) { osg::HeightField* hf = model->_elevationData.getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); } return result; }
void PathGenerator::addToOpenNode(int x, int y, adjType adjtype, TileNode* parent) { if (x < 0 || y < 0) { return; } if (!canPass(x, y)) { return; } TileNode* node = _nodes[x][y]; if (inPassedNodes(node)) { return; } else if (!inOpenNodes(node)) { _opened_nodes.push_back(node); int incf; if (adjtype == Straight) incf = 10; else incf = 14; node->setG(parent->getG() + incf); node->setH(descarteDistance(Point(node->getX(), node->getY()), _end) * 10); node->setF(node->getG() + node->getH()); node->setParent(parent); } }
std::vector<Point>* PathGenerator::generatePath(Point start, Point end) { this->resetTileNodes(); _start = start; _end = end; vector<Point>* path = new vector<Point>(); TileNode* startNode = _nodes[int(start.x)][int(start.y)]; startNode->setParent(NULL); _opened_nodes.push_back(startNode); TileNode* pathNode = NULL; while (true) { pathNode = getMinFNode(); addAdjacentOpenNodes(pathNode); _passed_nodes.push_back(pathNode); deletePassedNode(pathNode); if (pathNode->getX() == int(end.x) && pathNode->getY() == int(end.y)) { break; } } while (pathNode) { path->push_back(convertCoordinate2Pixel(pathNode->getX(), pathNode->getY(), _map_height)); pathNode = pathNode->getParent(); } std::reverse(path->begin(), path->end()); return path; }
// The osgDB::DatabasePager will call this automatically to purge expired // tiles from the scene graph. bool TilePagedLOD::removeExpiredChildren(double expiryTime, unsigned expiryFrame, osg::NodeList& removedChildren) { if (_children.size()>_numChildrenThatCannotBeExpired) { unsigned cindex = _children.size() - 1; double minExpiryTime = 0.0; unsigned minExpiryFrames = 0; // these were added in osg 3.1.0+ #if OSG_VERSION_GREATER_OR_EQUAL(3,1,0) minExpiryTime = _perRangeDataList[cindex]._minExpiryTime; minExpiryFrames = _perRangeDataList[cindex]._minExpiryFrames; #endif if (!_perRangeDataList[cindex]._filename.empty() && _perRangeDataList[cindex]._timeStamp + minExpiryTime < expiryTime && _perRangeDataList[cindex]._frameNumber + minExpiryFrames < expiryFrame) { osg::Node* nodeToRemove = _children[cindex].get(); removedChildren.push_back(nodeToRemove); ExpirationCollector collector( _live.get() ); nodeToRemove->accept( collector ); _releaser->push( collector._toRelease ); if ( _debug ) { TileNode* tileNode = getTileNode(); std::string key = tileNode ? tileNode->getKey().str() : "unk"; OE_NOTICE << LC << "Tile " << key << " : expiring " << collector._count << " children; " << "TS = " << _perRangeDataList[cindex]._timeStamp << ", MET = " << minExpiryTime << ", ET = " << expiryTime << "; FN = " << _perRangeDataList[cindex]._frameNumber << ", MEF = " << minExpiryFrames << ", EF = " << expiryFrame << "\n"; } return Group::removeChildren(cindex,1); } } return false; }
void Level::FillInTiles(Vector2 a_levelSize, std::vector<int>* a_idMap) { m_levelSize = a_levelSize; m_tiles.reserve(a_levelSize.x / TILE_WIDTH * a_levelSize.y / TILE_HEIGHT); for(int tileY = 0; tileY < a_levelSize.y / TILE_HEIGHT; tileY++) { for(int tileX = 0; tileX < a_levelSize.x / TILE_WIDTH; tileX++) { int index = tileX + tileY * (a_levelSize.x / TILE_WIDTH); TileNode* t = new TileNode(tileY, tileX, (*a_idMap)[index]); m_tiles.push_back(t); t->SetSurface(t->m_iTileId); } } }
void TileNode::createChildren(EngineContext* context) { // Create the four child nodes. for(unsigned quadrant=0; quadrant<4; ++quadrant) { TileNode* node = new TileNode(); // Build the surface geometry: node->create( getTileKey().createChildKey(quadrant), context ); // Add to the scene graph. addChild( node ); // Inherit the samplers with new scale/bias information. node->inheritState( context ); } OE_DEBUG << LC << "Creating children of: " << getTileKey().str() << "; count = " << (++_count) << "\n"; }
void PathGenerator::resetTileNodes() { for (int i = 0; i < _map_width; i++) { for (int j = 0; j < _map_height; j++) { TileNode* node = _nodes[i][j]; node->setF(0); node->setG(0); node->setH(0); node->setX(i); node->setY(j); node->setParent(NULL); } } _opened_nodes.clear(); _passed_nodes.clear(); }
void SerialKeyNodeFactory::addTile(TileModel* model, bool tileHasRealData, bool tileHasLodBlending, osg::Group* parent ) { // create a node: TileNode* tileNode = new TileNode( model->_tileKey, model->_tileLocator ); // install the tile model and compile it: tileNode->setTileModel( model ); tileNode->compile( _modelCompiler.get() ); // assemble a URI for this tile's child group: std::string uri = Stringify() << model->_tileKey.str() << "." << _engineUID << ".osgearth_engine_mp_tile"; osg::Node* result = 0L; // Only add the next tile if all the following are true: // 1. Either there's real tile data, or a minLOD is explicity set in the options; // 2. The tile isn't blacklisted; and // 3. We are still below the maximim LOD. bool wrapInPagedLOD = (tileHasRealData || (_options.minLOD().isSet() && model->_tileKey.getLOD() < *_options.minLOD())) && !osgEarth::Registry::instance()->isBlacklisted( uri ) && model->_tileKey.getLOD() < *_options.maxLOD(); if ( wrapInPagedLOD ) { osg::BoundingSphere bs = tileNode->getBound(); float maxRange = FLT_MAX; //Compute the min range based on the 2D size of the tile GeoExtent extent = model->_tileKey.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()); // create a PLOD so we can keep subdividing: osg::PagedLOD* plod = new CustomPagedLOD( _liveTiles.get(), _deadTiles.get() ); plod->setCenter( bs.center() ); plod->addChild( tileNode ); plod->setRangeMode( *_options.rangeMode() ); plod->setFileName( 1, uri ); if (plod->getRangeMode() == osg::LOD::PIXEL_SIZE_ON_SCREEN) { static const float sqrt2 = sqrt(2.0f); minRange = 0; maxRange = (*_options.tilePixelSize()) * sqrt2; plod->setRange( 0, minRange, maxRange ); plod->setRange( 1, maxRange, FLT_MAX ); } else { plod->setRange( 0, minRange, maxRange ); plod->setRange( 1, 0, minRange ); } plod->setUserData( new MapNode::TileRangeData(minRange, maxRange) ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = Registry::instance()->cloneOrCreateOptions(); options->setFileLocationCallback( new FileLocationCallback() ); plod->setDatabaseOptions( options ); #endif result = plod; if ( tileHasLodBlending ) { // Make the LOD transition distance, and a measure of how // close the tile is to an LOD change, to shaders. result->addCullCallback(new LODFactorCallback); } } else { result = tileNode; } // this cull callback dynamically adjusts the LOD scale based on distance-to-camera: if ( _options.lodFallOff().isSet() && *_options.lodFallOff() > 0.0 ) { result->addCullCallback( new DynamicLODScaleCallback(*_options.lodFallOff()) ); } // this one rejects back-facing tiles: if ( _mapInfo.isGeocentric() && _options.clusterCulling() == true ) { osg::HeightField* hf = model->_elevationData.getHFLayer()->getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getLocator()->getEllipsoidModel(), *_options.verticalScale() ) ); } parent->addChild( result ); }
void TileManager::ProcessTileMap(std::string a_sFileName) { m_sCurrentLevel = a_sFileName; int rows = m_pBaseSprite[a_sFileName]->GetHeight() / TILE_HEIGHT; int columns = m_pBaseSprite[a_sFileName]->GetWidth() / TILE_WIDTH; int idCounter = 0; int id = 0; int counter = 0; for(int row = 0; row < rows; row++) { for(int col = 0; col < columns; col++) { Tmpl8::Surface* surface = new Tmpl8::Surface(TILE_WIDTH, TILE_HEIGHT); m_pBaseSprite[a_sFileName]->CopyPartTo(surface, 0, 0, col * TILE_WIDTH, row * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT); if(row == 0 && col == 0) { TileNode* tileNode = new TileNode(row, col, 0); m_pTiles[a_sFileName].push_back(tileNode); m_pTileSurfaces[a_sFileName].push_back(new TileSurface(idCounter,surface)); tileNode->SetSurface(tileNode->m_iTileId); counter++; idCounter++; continue; } bool isUnique = true; for(std::vector<TileSurface*>::iterator iter = m_pTileSurfaces[a_sFileName].begin(); iter != m_pTileSurfaces[a_sFileName].end();) { if(!(*iter)->CompareSurfaces(surface)) { isUnique = false; id = (*iter)->m_iTileId; iter = m_pTileSurfaces[a_sFileName].end(); } else { iter++; } } if(isUnique) { m_pTileSurfaces[a_sFileName].push_back(new TileSurface(idCounter,surface)); id = idCounter; idCounter++; } else { delete surface; } TileNode* tileNode = new TileNode(row, col, id); m_pTiles[a_sFileName].push_back(tileNode); tileNode->SetSurface(tileNode->m_iTileId); counter++; } } FIBITMAP* bitmap = FreeImage_Allocate(512, 1024, 24); int tilesInRow = 512 / TILE_WIDTH; int rowCounter = 0; int colCounter = 0; for(std::vector<TileSurface*>::iterator iter = m_pTileSurfaces[a_sFileName].begin(); iter != m_pTileSurfaces[a_sFileName].end(); iter++) { Tmpl8::Pixel* buffer = (*iter)->m_pTileSurface->GetBuffer(); int pitch = (*iter)->m_pTileSurface->GetPitch(); for(int x = 0; x < TILE_WIDTH; x++) { for(int y = 0; y < TILE_HEIGHT; y++) { RGBQUAD color; color.rgbRed = (buffer[x + y*pitch] & 0xFF0000) >> 16; color.rgbGreen = (buffer[x + y*pitch] & 0x00FF00) >> 8; color.rgbBlue = buffer[x + y*pitch] & 0x0000FF; FreeImage_SetPixelColor(bitmap, colCounter*TILE_WIDTH + x, 1023 - rowCounter*TILE_HEIGHT - y, &color); } } colCounter++; if(colCounter >= tilesInRow) { colCounter = 0; rowCounter++; } } FreeImage_Save(FIF_PNG, bitmap, std::string("assets/tilesets/"+a_sFileName+"_tilemap.png").c_str(), 0); std::string tileMap; std::stringstream ss; counter = 0; for(int row = 0; row < rows; row++) { for(int col = 0; col < columns; col++) { TileNode* currentNode = m_pTiles[a_sFileName][counter]; counter++; ss.str(""); ss << currentNode->m_iTileId; tileMap += ss.str(); if(col+1 != columns) tileMap += ","; } if(row+1 != rows) tileMap += "\n"; } std::ofstream tilemapFile; tilemapFile.open (std::string("assets/tilesets/"+a_sFileName+".dat").c_str()); tilemapFile << tileMap; tilemapFile.close(); std::ofstream metaFile; metaFile.open (std::string("assets/tilesets/"+a_sFileName+"_metadata.dat").c_str()); ss.str(""); ss << m_pTileSurfaces[a_sFileName].size() << "," << TILE_WIDTH << "," << TILE_HEIGHT << "," << m_pBaseSprite[a_sFileName]->GetWidth() << "," << m_pBaseSprite[a_sFileName]->GetHeight(); metaFile << ss.str(); metaFile.close(); for(std::vector<TileSurface*>::iterator iter = m_pTileSurfaces[a_sFileName].begin(); iter != m_pTileSurfaces[a_sFileName].end(); iter++) { delete *iter; } m_pTileSurfaces[a_sFileName].clear(); for(std::vector<TileNode*>::iterator iter = m_pTiles[a_sFileName].begin(); iter != m_pTiles[a_sFileName].end(); iter++) { delete *iter; } m_pTiles[a_sFileName].clear(); delete m_pBaseSprite[a_sFileName]; }
TileNode* TileGroupFactory::createTileNode(TerrainTileModel* model, ProgressCallback* progress) { TileNode* tileNode = new TileNode(model); for(TerrainTileImageLayerModelVector::const_iterator i = model->colorLayers().begin(); i != model->colorLayers().end(); ++i) { const TerrainTileLayerModel* layer = i->get(); if ( layer->getTexture() ) { osg::StateSet* stateSet = tileNode->getOrCreateStateSet(); stateSet->setTextureAttribute( _renderBindings.color().unit(), layer->getTexture() ); // (note: sampler uniform is set at the top level by the engine) } if ( layer->getMatrix() ) { osg::StateSet* stateSet = tileNode->getOrCreateStateSet(); stateSet->addUniform( new osg::Uniform( _renderBindings.color().matrixName().c_str(), *(layer->getMatrix())) ); } } if ( model->elevationModel() ) { const TerrainTileElevationModel* em = model->elevationModel(); if ( em->getTexture() ) { osg::StateSet* stateSet = tileNode->getOrCreateStateSet(); stateSet->setTextureAttribute( _renderBindings.elevation().unit(), em->getTexture() ); osg::Matrixf elevMatrix; if ( em->getMatrix() ) elevMatrix = *(em->getMatrix()); stateSet->addUniform( new osg::Uniform( _renderBindings.elevation().matrixName().c_str(), elevMatrix )); // (note: sampler uniform is set at the top level by the engine) } } for(TerrainTileImageLayerModelVector::const_iterator i = model->sharedLayers().begin(); i != model->sharedLayers().end(); ++i) { const TerrainTileImageLayerModel* layerModel = i->get(); const ImageLayer* imageLayer = layerModel->getImageLayer(); if ( imageLayer ) { if ( layerModel->getTexture() ) { osg::StateSet* stateSet = tileNode->getOrCreateStateSet(); stateSet->setTextureAttribute( imageLayer->shareImageUnit().get(), layerModel->getTexture() ); //TODO: don't really need this if we set it up in the Engine // once at the top level when adding the layer. stateSet->addUniform( new osg::Uniform( imageLayer->shareSamplerName()->c_str(), imageLayer->shareImageUnit().get() )); } if ( layerModel->getMatrix() ) { osg::StateSet* stateSet = tileNode->getOrCreateStateSet(); stateSet->addUniform( new osg::Uniform( imageLayer->shareMatrixName()->c_str(), *(layerModel->getMatrix())) ); } } } return tileNode; }
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); }
void RexTerrainEngineNode::dirtyTerrain() { //TODO: scrub the geometry pool? // clear the loader: _loader->clear(); if ( _terrain ) { this->removeChild( _terrain ); } // New terrain _terrain = new osg::Group(); this->addChild( _terrain ); // are we LOD blending? bool setupParentData = _terrainOptions.morphImagery() == true || // gw: redundant? this->parentTexturesRequired(); // reserve GPU unit for the main color texture: if ( _renderBindings.empty() ) { setupRenderBindings(); } // recalculate the LOD morphing parameters: destroySelectionInfo(); buildSelectionInfo(); // clear out the tile registry: if ( _liveTiles.valid() ) { _liveTiles->moveAll( _deadTiles.get() ); } // Factory to create the root keys: EngineContext* context = getEngineContext(); // Build the first level of the terrain. // Collect the tile keys comprising the root tiles of the terrain. std::vector<TileKey> keys; _update_mapf->getProfile()->getAllKeysAtLOD( *_terrainOptions.firstLOD(), keys ); // create a root node for each root tile key. OE_INFO << LC << "Creating " << keys.size() << " root keys.." << std::endl; unsigned child = 0; for( unsigned i=0; i<keys.size(); ++i ) { TileNode* tileNode = new TileNode(); // Next, build the surface geometry for the node. tileNode->create( keys[i], context ); _terrain->addChild( tileNode ); } updateState(); // Call the base class TerrainEngineNode::dirtyTerrain(); }
void RexTerrainEngineNode::dirtyTerrain() { if ( _terrain ) { this->removeChild( _terrain ); } // clear the loader: _loader->clear(); // clear out the tile registry: if ( _liveTiles.valid() ) { _liveTiles->releaseAll(_releaser.get()); } // scrub the geometry pool: _geometryPool->clear(); // New terrain _terrain = new osg::Group(); this->addChild( _terrain ); // Build the first level of the terrain. // Collect the tile keys comprising the root tiles of the terrain. std::vector<TileKey> keys; _mapFrame.getProfile()->getAllKeysAtLOD( *_terrainOptions.firstLOD(), keys ); // create a root node for each root tile key. OE_DEBUG << LC << "Creating " << keys.size() << " root keys." << std::endl; // We need to take a self-ref here to ensure that the TileNode's data loader // can use its observer_ptr back to the terrain engine. this->ref(); for( unsigned i=0; i<keys.size(); ++i ) { TileNode* tileNode = new TileNode(); if (_terrainOptions.minExpiryFrames().isSet()) { tileNode->setMinimumExpirationFrames(_terrainOptions.minExpiryFrames().get()); } if (_terrainOptions.minExpiryTime().isSet()) { tileNode->setMinimumExpirationTime(_terrainOptions.minExpiryTime().get()); } // Next, build the surface geometry for the node. tileNode->create( keys[i], 0L, _engineContext.get() ); // Add it to the scene graph _terrain->addChild( tileNode ); // And load the tile's data synchronously (only for root tiles). tileNode->loadSync(); } // release the self-ref. this->unref_nodelete(); // Set up the state sets. updateState(); // Call the base class TerrainEngineNode::dirtyTerrain(); }
std::vector<Vec2> TMXPathFinding::getPath(Vec2 startPos, Vec2 endPos, std::vector<int> walkableGIDs, std::vector<int> obstacleGIDs) { open.clear(); close.clear(); // inizialize a goal node goalNode = new TileNode(); goalNode->setLocX(endPos.x); goalNode->setLocY(endPos.y); // inizialize a start node startNode = new TileNode(); startNode->setLocX(startPos.x); startNode->setLocY(startPos.y); startNode->setCostFromStart(0); startNode->setParent(nullptr); int cost = getDistance(startNode, goalNode); startNode->setCostToGoal(cost); startNode->setTotalCost(); open.emplace(startNode, startNode->getTotalCost()); while (open.size() != 0) { // Fix a Cost to check the values min = 32767; // std::numeric_limits<int>::max() TileNode *minNode; // Find minNode from open QUEUE for (auto kv : open) { extractNode = kv.first; iCost = kv.second; if (iCost < min) { min = iCost; // Change min to the New Cost got from the open QUEUE minNode = extractNode; } } extractNode = minNode; open.erase(minNode); // pop node from open // if it's a goal, we're done if (extractNode->getLocation() == goalNode->getLocation()) { // 1- retrieve all extractNode's parents // 2- save into Vec2 vector // 3- reverse Vec2 vector // 4- return Vec2 vector std::vector<Vec2> points; points.push_back(extractNode->getLocation()); int size = extractNode->getCostFromStart(); for (int i = 0; i < size; i++) { points.push_back(Vec2(extractNode->getParent()->getLocation().x, extractNode->getParent()->getLocation().y)); extractNode = extractNode->getParent(); } std::reverse(points.begin(), points.end()); return points; } else { for (int i = 0; i < dir; i++) { costToOpen = 0; costToClose = 0; inOpen = false; inClose = false; newNode = new TileNode(); newNode->setLocX(extractNode->getLocation().x); newNode->setLocY(extractNode->getLocation().y); switch (i) { case 0: // left newNode->setLocX(-1); newNode->setLocY(0); break; case 1: // right newNode->setLocX(1); newNode->setLocY(0); break; case 2: // up newNode->setLocX(0); newNode->setLocY(1); break; case 3: // down newNode->setLocX(0); newNode->setLocY(-1); break; case 4: // top-left newNode->setLocX(-1); newNode->setLocY(1); break; case 5: // bottom-left newNode->setLocX(-1); newNode->setLocY(-1); break; case 6: // bottom-right newNode->setLocX(1); newNode->setLocY(-1); break; case 7: // top-right newNode->setLocX(1); newNode->setLocY(1); break; } if (newNode->getLocation() != goalNode->getLocation()) { if (newNode->getLocation().x < 0 || newNode->getLocation().y < 0 || newNode->getLocation().x >= tileMap->getMapSize().width || newNode->getLocation().y >= tileMap->getMapSize().height) { // newNode is invalid, outside tileMap so ignore continue; } bool isNeedToContinue = false; // check newNode in given/all layers for (auto layer : tileLayers) { for (int gid : obstacleGIDs) { if (layer->getTileGIDAt(newNode->getLocation()) == gid) { // newNode is obstacle so ignore isNeedToContinue = true; break; } } if (isNeedToContinue) break; if (walkableGIDs.size() > 0) { isNeedToContinue = true; for (int gid : walkableGIDs) { if (layer->getTileGIDAt(newNode->getLocation()) == gid) { // newNode is walkable isNeedToContinue = false; break; } } if (isNeedToContinue) // newNode is not walkable so ignore break; } } if (isNeedToContinue) continue; } cost = getDistance(newNode, goalNode); newNode->setCostFromStart(extractNode->getCostFromStart() + 1); newNode->setCostToGoal(cost); newNode->setParent(extractNode); newNode->setTotalCost(); inOpen = false; inClose = false; for (auto kv : open) { TileNode *node = kv.first; if (newNode->getLocation() == node->getLocation()) { costToOpen = node->getTotalCost(); inOpen = true; break; } } for (auto kv : close) { TileNode *node = kv.first; if (newNode->getLocation() == node->getLocation()) { costToClose = node->getTotalCost(); inClose = true; break; } } if ((inOpen && (newNode->getTotalCost() >= costToOpen)) || (inClose && (newNode->getTotalCost() >= costToClose))) { // newNode is already in open or close QUEUE with lower cost so ignore continue; } else { if (inClose) { close.erase(newNode); // reinsert newNode in open open.emplace(newNode, newNode->getTotalCost()); } if (inOpen) { // adjust newNode's location in Open iCost = costToOpen; modifiedNode = newNode; // remove from open open.erase(newNode); modifiedNode->setTotalCost(newNode->getTotalCost()); // updated node reinsert in open open.emplace(modifiedNode, modifiedNode->getTotalCost()); } else { // if its neither in OPEN nor in CLOSE insert it in OPEN open.emplace(newNode, newNode->getTotalCost()); } } } } close.emplace(extractNode, extractNode->getTotalCost()); } std::vector<Vec2> dummy; return dummy; }
osg::Node* SingleKeyNodeFactory::createTile(TileModel* model, bool setupChildrenIfNecessary, ProgressCallback* progress) { #ifdef EXPERIMENTAL_TILE_NODE_CACHE osg::ref_ptr<TileNode> tileNode; TileNodeCache::Record rec; cache.get(model->_tileKey, rec); if ( rec.valid() ) { tileNode = rec.value().get(); } else { tileNode = _modelCompiler->compile( model, _frame ); cache.insert(model->_tileKey, tileNode); } #else // compile the model into a node: TileNode* tileNode = _modelCompiler->compile(model, _frame, progress); tileNode->setEngineUID( _engineUID ); #endif // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { osg::BoundingSphere bs = tileNode->getBound(); TilePagedLOD* plod = new TilePagedLOD( _engineUID, _liveTiles, _deadTiles ); plod->setCenter ( bs.center() ); plod->addChild ( tileNode ); plod->setFileName( 1, Stringify() << tileNode->getKey().str() << "." << _engineUID << ".osgearth_engine_mp_tile" ); plod->setDebug ( _debug ); 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->_tileKey.getExtent(); double radius = 0.0; #if 0 // Test code to use the equitorial radius so that all of the tiles at the same level // have the same range. This will make the poles page in more appropriately. if (_frame.getMapInfo().isGeocentric()) { GeoExtent equatorialExtent( extent.getSRS(), extent.west(), -extent.height()/2.0, extent.east(), extent.height()/2.0 ); radius = equatorialExtent.getBoundingGeoCircle().getRadius(); } else #endif { 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 ); 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 { // the *2 is because we page in 4-tile sets, not individual tiles. float size = 2.0f * _options.tilePixelSize().value(); plod->setRange( 0, 0.0f, size ); plod->setRange( 1, size, FLT_MAX ); plod->setRangeMode( osg::LOD::PIXEL_SIZE_ON_SCREEN ); } // Install a tile-aligned bounding box in the pager node itself so we can do // visibility testing before paging in subtiles. plod->setChildBoundingBoxAndMatrix( 1, tileNode->getTerrainBoundingBox(), tileNode->getMatrix() ); // DBPager will set a priority based on the ratio range/maxRange. // This will offset that number with a full LOD #, giving LOD precedence. // Experimental. //plod->setPriorityScale( 1, model->_tileKey.getLOD()+1 ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = plod->getOrCreateDBOptions(); options->setFileLocationCallback( new FileLocationCallback() ); #endif result = plod; // this one rejects back-facing tiles: if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { #if 1 osg::HeightField* hf = model->_elevationData.getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); #else // This works, but isn't quite as tight at the cluster culler. // Re-evaluate down the road. result->addCullCallback( new HorizonTileCuller( _frame.getMapInfo().getSRS(), tileNode->getTerrainBoundingBox(), tileNode->getMatrix(), tileNode->getKey() ) ); #endif } } else { result = tileNode; } return result; }
bool operator<(const TileNode& a, const TileNode& b) { return a.f() < b.f(); }
osg::Node* SingleKeyNodeFactory::createTile(TileModel* model, bool setupChildrenIfNecessary, ProgressCallback* progress) { #ifdef EXPERIMENTAL_TILE_NODE_CACHE osg::ref_ptr<TileNode> tileNode; TileNodeCache::Record rec; cache.get(model->_tileKey, rec); if ( rec.valid() ) { tileNode = rec.value().get(); } else { tileNode = _modelCompiler->compile( model, _frame ); cache.insert(model->_tileKey, tileNode); } #else // compile the model into a node: TileNode* tileNode = _modelCompiler->compile(model, _frame, progress); #endif // see if this tile might have children. bool prepareForChildren = setupChildrenIfNecessary && model->_tileKey.getLOD() < *_options.maxLOD(); osg::Node* result = 0L; if ( prepareForChildren ) { osg::BoundingSphere bs = tileNode->getBound(); TilePagedLOD* plod = new TilePagedLOD( _engineUID, _liveTiles, _deadTiles ); plod->setCenter ( bs.center() ); plod->addChild ( tileNode ); plod->setFileName( 1, Stringify() << tileNode->getKey().str() << "." << _engineUID << ".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->_tileKey.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 ); } // DBPager will set a priority based on the ratio range/maxRange. // This will offset that number with a full LOD #, giving LOD precedence. // Experimental. //plod->setPriorityScale( 1, model->_tileKey.getLOD()+1 ); #if USE_FILELOCATIONCALLBACK osgDB::Options* options = plod->getOrCreateDBOptions(); options->setFileLocationCallback( new FileLocationCallback() ); #endif result = plod; // this one rejects back-facing tiles: if ( _frame.getMapInfo().isGeocentric() && _options.clusterCulling() == true ) { osg::HeightField* hf = model->_elevationData.getHeightField(); result->addCullCallback( HeightFieldUtils::createClusterCullingCallback( hf, tileNode->getKey().getProfile()->getSRS()->getEllipsoid(), *_options.verticalScale() ) ); } } else { result = tileNode; } return result; }
// The osgDB::DatabasePager will call this method when merging a new child // into the scene graph. bool TilePagedLOD::addChild(osg::Node* node) { // First check whether this is a new TileGroup (a group that contains a TileNode // and children paged LODs). If so, add it normally and inform our parent. TileGroup* subtilegroup = dynamic_cast<TileGroup*>(node); if ( subtilegroup ) { TileNode* subtile = subtilegroup->getTileNode(); _isUpsampled = !subtile->getTileModel()->hasRealData(); _live->add( subtile ); return osg::PagedLOD::addChild( node ); } // If that fails, check whether this is a simple TileNode. This means that // this is a leaf node in the graph (no children), or it's a replacement // tile for our existing tileNode, or possibly that it has no data at all // (and we need to create an upsampled child to complete the required set of // four). TileNode* subtile = dynamic_cast<TileNode*>(node); if ( subtile ) { if ( subtile->getTileModel()->getMapRevision() < _live->getMapRevision() ) { //OE_NOTICE << LC << "Tile " << subtile->getKey().str() << " received data but it's already out of date...requeuing" // << std::endl; return false; } // If it's a legit tile, add it normally and inform our parent. // TODO: "invalid" tiles are deprecated. Remove this. else if ( subtile->isValid() ) { if ( _children.size() == 0 ) { _isUpsampled = !subtile->getTileModel()->hasRealData(); // The initial valid tile node we've been waiting for. Insert it. _live->add( subtile ); return osg::PagedLOD::addChild( node ); } else { // A replacement tile. Replace the tile node we have with this // new version and update the registry. _isUpsampled = !subtile->getTileModel()->hasRealData(); //if ( _isUpsampled ) // OE_NOTICE << LC << "Replaced UPSAMPLED leaf at " << _prefix << std::endl; if ( dynamic_cast<TileGroup*>(_children[0].get()) ) { subtile->setCullCallback( 0L ); static_cast<TileGroup*>(_children[0].get())->setTileNode( subtile ); } else // TileNode { this->setChild(0, subtile); } // remove the tile-replacement filename. _rangeList.resize( 1 ); _perRangeDataList.resize( 1 ); // update the registry. don't need to remove the old entry since add() will // replace the existing entry (they have the same key) _live->add( subtile ); return true; } } } // Getting here means the Tile dies somewhere in the pager while the pager was // trying to add it. From what I can tell, this is normal and just happens sometimes if ( !node ) { OE_DEBUG << LC << "TilePagedLOD: got an addChild(NULL) on " << _prefix << std::endl; _isCanceled = true; } return false; }
void RexTerrainEngineNode::dirtyTerrain() { //TODO: scrub the geometry pool? // clear the loader: _loader->clear(); if ( _terrain ) { this->removeChild( _terrain ); } // New terrain _terrain = new osg::Group(); this->addChild( _terrain ); // are we LOD blending? bool setupParentData = _terrainOptions.morphImagery() == true || // gw: redundant? this->parentTexturesRequired(); // reserve GPU unit for the main color texture: if ( _renderBindings.empty() ) { setupRenderBindings(); } // Calculate the LOD morphing parameters: unsigned maxLOD = _terrainOptions.maxLOD().getOrUse(DEFAULT_MAX_LOD); _selectionInfo.initialize( 0u, // always zero, not the terrain options firstLOD std::min( _terrainOptions.maxLOD().get(), maxLOD ), _terrainOptions.tileSize().get(), _update_mapf->getMapInfo().getProfile(), _terrainOptions.minTileRangeFactor().get() ); // clear out the tile registry: if ( _liveTiles.valid() ) { _liveTiles->releaseAll(_releaser.get()); } // Factory to create the root keys: EngineContext* context = getEngineContext(); // Build the first level of the terrain. // Collect the tile keys comprising the root tiles of the terrain. std::vector<TileKey> keys; _update_mapf->getProfile()->getAllKeysAtLOD( *_terrainOptions.firstLOD(), keys ); // create a root node for each root tile key. OE_INFO << LC << "Creating " << keys.size() << " root keys.." << std::endl; unsigned child = 0; for( unsigned i=0; i<keys.size(); ++i ) { TileNode* tileNode = new TileNode(); if (context->getOptions().minExpiryFrames().isSet()) { tileNode->setMinimumExpirationFrames( *context->getOptions().minExpiryFrames() ); } if (context->getOptions().minExpiryTime().isSet()) { tileNode->setMinimumExpirationTime( *context->getOptions().minExpiryTime() ); } // Next, build the surface geometry for the node. tileNode->create( keys[i], 0L, context ); // Add it to the scene graph _terrain->addChild( tileNode ); // And load the tile's data synchronously (only for root tiles). tileNode->loadSync( context ); } updateState(); // Call the base class TerrainEngineNode::dirtyTerrain(); }
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; }
bool TileNode::inheritState(EngineContext* context) { // Find the parent node. It will only be null if this is a "first LOD" tile. TileNode* parent = getNumParents() > 0 ? dynamic_cast<TileNode*>(getParent(0)) : 0L; bool changesMade = false; // which quadrant is this tile in? unsigned quadrant = getTileKey().getQuadrant(); // default inheritance of the elevation data for bounding purposes: osg::ref_ptr<const osg::Image> elevRaster; osg::Matrixf elevMatrix; if ( parent ) { elevRaster = parent->getElevationRaster(); elevMatrix = parent->getElevationMatrix(); elevMatrix.preMult( scaleBias[quadrant] ); } // Find all the sampler matrix uniforms and scale/bias them to the current quadrant. // This will inherit textures and use the proper sub-quadrant until new data arrives (later). for( RenderBindings::const_iterator binding = context->getRenderBindings().begin(); binding != context->getRenderBindings().end(); ++binding ) { if ( binding->usage().isSetTo(binding->COLOR) ) { if ( parent && parent->getStateSet() ) { MPTexture* parentMPTex = parent->getMPTexture(); _mptex->inheritState( parentMPTex, scaleBias[quadrant] ); changesMade = true; } } else if ( binding->usage().isSetTo(binding->COLOR_PARENT) ) { //nop -- this is handled as part of the COLOR binding } else { osg::StateAttribute* sa = getStateSet()->getTextureAttribute(binding->unit(), osg::StateAttribute::TEXTURE); // If the attribute isn't present, that means we are inheriting it from above. // So construct a new scale/bias matrix. if ( sa == 0L ) { osg::Matrixf matrix; // Find the parent's matrix and scale/bias it to this quadrant: if ( parent && parent->getStateSet() ) { const osg::Uniform* matrixUniform = parent->getStateSet()->getUniform( binding->matrixName() ); if ( matrixUniform ) { matrixUniform->get( matrix ); matrix.preMult( scaleBias[quadrant] ); } } // Add a new uniform with the scale/bias'd matrix: osg::StateSet* stateSet = getOrCreateStateSet(); stateSet->removeUniform( binding->matrixName() ); stateSet->addUniform( context->getOrCreateMatrixUniform(binding->matrixName(), matrix) ); changesMade = true; } // If this is elevation data, record the new raster so we can apply it to the node. else if ( binding->usage().isSetTo(binding->ELEVATION) ) { osg::Texture* t = static_cast<osg::Texture*>(sa); elevRaster = t->getImage(0); elevMatrix = osg::Matrixf::identity(); } } } // If we found one, communicate it to the node and its children. if (elevRaster.valid()) { if (elevRaster.get() != getElevationRaster() || elevMatrix != getElevationMatrix() ) { setElevationRaster( elevRaster.get(), elevMatrix ); changesMade = true; } } // finally, update the uniforms for terrain morphing updateTileUniforms( context->getSelectionInfo() ); if ( !changesMade ) { OE_INFO << LC << _key.str() << ", good, no changes :)\n"; } else { dirtyBound(); } return changesMade; }
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); }