#include #include #include #include #include #include #include #include #include #include #include "ShaderTerrain.h" using namespace osgTerrain; #if 0 #define LOCK(mutex) OpenThreads::ScopedLock lock(mutex); #else #define LOCK(mutex) /* OpenThreads::Thread::microSleep(1);*/ #endif const osgTerrain::Locator* osgTerrain::computeMasterLocator(const osgTerrain::TerrainTile* tile) { const osgTerrain::Layer* elevationLayer = tile->getElevationLayer(); const osgTerrain::Layer* colorLayer = tile->getColorLayer(0); const Locator* elevationLocator = elevationLayer ? elevationLayer->getLocator() : 0; const Locator* colorLocator = colorLayer ? colorLayer->getLocator() : 0; const Locator* masterLocator = elevationLocator ? elevationLocator : colorLocator; if (!masterLocator) { OSG_NOTICE<<"Problem, no locator found in any of the terrain layers"<getTransform(); osg::Vec3d bottom_left = osg::Vec3d(0.0,0.0,0.0) * matrix; osg::Vec3d bottom_right = osg::Vec3d(1.0,0.0,0.0) * matrix; osg::Vec3d top_left = osg::Vec3d(1.0,1.0,0.0) * matrix; key.sx = static_cast((bottom_right-bottom_left).length()); key.sy = static_cast((top_left-bottom_left).length()); if (masterLocator->getCoordinateSystemType()==osgTerrain::Locator::GEOCENTRIC) { // need to differentiate between tiles based of latitude, so use y position of bottom left corner. key.y = static_cast(bottom_left.y()); } else { // when the projection is linear there is no need to differentiate tiles according to their latitude key.y = 0.0; } } osgTerrain::HeightFieldLayer* layer = dynamic_cast(tile->getElevationLayer()); if (layer) { osg::HeightField* hf = layer->getHeightField(); if (hf) { key.nx = hf->getNumColumns(); key.ny = hf->getNumRows(); } } return true; } static int numberGeometryCreated = 0; static int numberSharedGeometry = 0; osg::ref_ptr GeometryPool::getOrCreateGeometry(osgTerrain::TerrainTile* tile) { OpenThreads::ScopedLock lock(_geometryMapMutex); GeometryKey key; createKeyForTile(tile, key); GeometryMap::iterator itr = _geometryMap.find(key); if (itr != _geometryMap.end()) { ++numberSharedGeometry; // OSG_NOTICE<<"Sharing geometry "<second.get()<<", number shared = "<second.get(); } osg::ref_ptr geometry = new osg::Geometry; _geometryMap[key] = geometry; osg::ref_ptr vertices = new osg::Vec3Array; geometry->setVertexArray(vertices.get()); osg::ref_ptr normals = new osg::Vec3Array; geometry->setNormalArray(normals.get(), osg::Array::BIND_PER_VERTEX); osg::ref_ptr colours = new osg::Vec4Array; geometry->setColorArray(colours.get(), osg::Array::BIND_OVERALL); colours->push_back(osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f)); osg::ref_ptr texcoords = new osg::Vec4Array; geometry->setTexCoordArray(0, texcoords.get(), osg::Array::BIND_PER_VERTEX); int nx = key.nx; int ny = key.nx; int numVerticesMainBody = nx * ny; int numVerticesSkirt = (nx)*2 + (ny)*2; int numVertices = numVerticesMainBody + numVerticesSkirt; vertices->reserve(numVertices); normals->reserve(numVertices); texcoords->reserve(numVertices); double c_mult = 1.0/static_cast(nx-1); double r_mult = 1.0/static_cast(ny-1); typedef std::vector LocationCoords; LocationCoords locationCoords; locationCoords.reserve(numVertices); osg::Vec3d pos(0.0, 0.0, 0.0); osg::Vec3d normal(0.0, 0.0, 1.0); osg::Vec2 delta(1.0f/static_cast(nx), 1.0f/static_cast(ny)); // pass in the delta texcoord per texel via the color array (*colours)[0].x() = c_mult; (*colours)[0].y() = r_mult; osg::Matrixd matrix; const osgTerrain::Locator* locator = computeMasterLocator(tile); if (locator) { matrix = locator->getTransform(); } // compute the size of the skirtHeight osg::Vec3d bottom_left(0.0,0.0,0.0); osg::Vec3d top_right(1.0,1.0,0.0); // transform for unit coords to local coords of the tile bottom_left = bottom_left * matrix; top_right = top_right * matrix; // if we have a geocentric database then transform into geocentric coords. const osg::EllipsoidModel* em = locator->getEllipsoidModel(); if (em && locator->getCoordinateSystemType()==osgTerrain::Locator::GEOCENTRIC) { // note y axis maps to latitude, x axis to longitude em->convertLatLongHeightToXYZ(bottom_left.y(), bottom_left.x(), bottom_left.z(), bottom_left.x(), bottom_left.y(), bottom_left.z()); em->convertLatLongHeightToXYZ(top_right.y(), top_right.x(), top_right.z(), top_right.x(), top_right.y(), top_right.z()); } double diagonalLength = (top_right-bottom_left).length(); double skirtRatio = 0.02; double skirtHeight = -diagonalLength*skirtRatio; // set up the vertex data { // bottom row for skirt pos.y () = static_cast(0)*r_mult; pos.z() = skirtHeight; for(int c=0; c(c)*c_mult; vertices->push_back(pos); normals->push_back(normal); texcoords->push_back(osg::Vec4(pos.x(), pos.y(), 1.0f, 1.0f)); locationCoords.push_back(osg::Vec4d(pos.x(), pos.y(),c_mult, r_mult)); } // main body for(int r=0; r(r)*r_mult; // start skirt vertex pos.z() = skirtHeight; { pos.x() = static_cast(0)*c_mult; vertices->push_back(pos); normals->push_back(normal); texcoords->push_back(osg::Vec4(pos.x(), pos.y(), 1.0f, 1.0f)); locationCoords.push_back(osg::Vec4d(pos.x(), pos.y(),c_mult, r_mult)); } pos.z() = 0; for(int c=0; c(c)*c_mult; vertices->push_back(pos); normals->push_back(normal); texcoords->push_back(osg::Vec4(pos.x(), pos.y(), 1.0f, 1.0f)); locationCoords.push_back(osg::Vec4d(pos.x(), pos.y(),c_mult, r_mult)); } // end skirt vertex pos.z() = skirtHeight; { pos.x() = static_cast(nx-1)*c_mult; vertices->push_back(pos); normals->push_back(normal); texcoords->push_back(osg::Vec4(pos.x(), pos.y(), 1.0f, 1.0f)); locationCoords.push_back(osg::Vec4d(pos.x(), pos.y(),c_mult, r_mult)); } } // top row skirt pos.y () = static_cast(ny-1)*r_mult; pos.z() = skirtHeight; for(int c=0; c(c)*c_mult; vertices->push_back(pos); normals->push_back(normal); texcoords->push_back(osg::Vec4(pos.x(), pos.y(), 1.0f, 1.0f)); locationCoords.push_back(osg::Vec4d(pos.x(), pos.y(),c_mult, r_mult)); } } #if 1 bool smallTile = numVertices <= 16384; osg::ref_ptr elements = smallTile ? static_cast(new osg::DrawElementsUShort(GL_TRIANGLE_STRIP)) : static_cast(new osg::DrawElementsUInt(GL_TRIANGLE_STRIP)); elements->reserveElements( (nx-1) * (ny-1) * 2 + (nx-1)*2*2 + (ny-1)*2*2 +(ny)*2); geometry->addPrimitiveSet(elements.get()); // first row containing the skirt int il = 0; int iu = 0; for(int c=0; caddElement(iu); elements->addElement(il); } elements->addElement(il); // center section for(int r=0; raddElement(iu); for(int c=0; caddElement(iu); elements->addElement(il); } elements->addElement(il); } // top row containing skirt il = nx+(ny-1)*(nx+2)+1; iu = il+nx+1; elements->addElement(iu); for(int c=0; caddElement(iu); elements->addElement(il); } #else bool smallTile = numVertices <= 16384; osg::ref_ptr elements = smallTile ? static_cast(new osg::DrawElementsUShort(GL_QUADS)) : static_cast(new osg::DrawElementsUInt(GL_QUADS)); elements->reserveElements( (nx-1) * (ny-1) * 4 + (nx-1)*2*4 + (ny-1)*2*4 ); geometry->addPrimitiveSet(elements.get()); // first row containing the skirt for(int c=0; caddElement(il); elements->addElement(il+1); elements->addElement(iu+1); elements->addElement(iu); } // center section for(int r=0; raddElement(il); elements->addElement(il+1); elements->addElement(iu+1); elements->addElement(iu); } } // top row containing skirt for(int c=0; caddElement(il); elements->addElement(il+1); elements->addElement(iu+1); elements->addElement(iu); } #endif if (locator) { matrix = locator->getTransform(); osg::Vec3d center(0.5, 0.5, 0.0); osg::Vec3d bottom_left(0.0,0.0,0.0); osg::Vec3d bottom_right(1.0,0.0,0.0); osg::Vec3d top_left(0.0,1.0,0.0); center = center * matrix; bottom_left = bottom_left * matrix; bottom_right = bottom_right * matrix; top_left = top_left * matrix; // shift to center.x() to x=0 and carry all the corners with it. bottom_left.x() -= center.x(); bottom_right.x() -= center.x(); top_left.x() -= center.x(); //center.x() = 0.0; // OSG_NOTICE<<" in lat/longs : bottom_left = "<convertLatLongHeightToXYZ(center.y(), center.x(), center.z(), center.x(), center.y(),center.z()); em->convertLatLongHeightToXYZ(bottom_left.y(), bottom_left.x(), bottom_left.z(), bottom_left.x(), bottom_left.y(),bottom_left.z()); em->convertLatLongHeightToXYZ(bottom_right.y(), bottom_right.x(), bottom_right.z(), bottom_right.x(), bottom_right.y(),bottom_right.z()); em->convertLatLongHeightToXYZ(top_left.y(), top_left.x(), top_left.z(), top_left.x(), top_left.y(),top_left.z()); osg::Matrixd worldToLocalTransform; worldToLocalTransform.invert(localToWorldTransform); center = center * worldToLocalTransform; bottom_left = bottom_left * worldToLocalTransform; bottom_right = bottom_right * worldToLocalTransform; top_left = top_left * worldToLocalTransform; for(int i=0; iconvertLatLongHeightToXYZ(pos.y(), pos.x(), height, pos.x(), pos.y(),pos.z()); osg::Vec4& tc = (*texcoords)[i]; osg::Vec3d pos_right = osg::Vec3d(location.x()+location[2], location.y(), 0.0) * matrix; em->convertLatLongHeightToXYZ(pos_right.y(), pos_right.x(), height, pos_right.x(), pos_right.y(),pos_right.z()); osg::Vec3d pos_up = osg::Vec3d(location.x(), location.y()+location[3], 0.0) * matrix; em->convertLatLongHeightToXYZ(pos_up.y(), pos_up.x(), height, pos_up.x(), pos_up.y(),pos_up.z()); double length_right = (pos_right-pos).length(); double length_up = (pos_up-pos).length(); tc[2] = 1.0/length_right; tc[3] = 1.0/length_up; osg::Vec3d normal(pos); normal = osg::Matrixd::transform3x3(localToWorldTransform, normal); normal.normalize(); pos = pos * worldToLocalTransform; pos -= center; (*vertices)[i] = pos; (*normals)[i] = normal; } } } // double tileWidth = (bottom_right-bottom_left).length(); // double skirtHeight = tileWidth*0.05; // OSG_NOTICE<<" in local coords : center = "< GeometryPool::getTileSubgraph(osgTerrain::TerrainTile* tile) { // create or reuse Geometry osg::ref_ptr geometry = getOrCreateGeometry(tile); osg::ref_ptr hfDrawable = new HeightFieldDrawable(); osgTerrain::HeightFieldLayer* hfl = dynamic_cast(tile->getElevationLayer()); osg::HeightField* hf = hfl ? hfl->getHeightField() : 0; hfDrawable->setHeightField(hf); hfDrawable->setGeometry(geometry.get()); // create a transform to place the geometry in the appropriate place osg::ref_ptr transform = new osg::MatrixTransform; // transform->addChild(geometry.get()); transform->addChild(hfDrawable.get()); const osgTerrain::Locator* locator = computeMasterLocator(tile); if (locator) { osg::Matrixd matrix = locator->getTransform(); osg::Vec3d center = osg::Vec3d(0.5, 0.5, 0.0) * matrix; // shift to center.x() to x=0 and carry all the corners with it. const osg::EllipsoidModel* em = locator->getEllipsoidModel(); if (em && locator->getCoordinateSystemType()==osgTerrain::Locator::GEOCENTRIC) { osg::Matrixd localToWorldTransform; // note y axis maps to latitude, x axis to longitude em->computeLocalToWorldTransformFromLatLongHeight(center.y(), center.x(), center.z(), localToWorldTransform); // OSG_NOTICE<<"We have a EllipsoidModel to take account of "<setMatrix(localToWorldTransform); //osgDB::writeNodeFile(*transform, "subgraph.osgt"); } else { transform->setMatrix(locator->getTransform()); } } osg::Vec3Array* vertices = dynamic_cast(geometry->getVertexArray()); osg::Vec3Array* normals = dynamic_cast(geometry->getNormalArray()); if (hf && vertices && normals && (vertices->size()==normals->size())) { unsigned int nr = hf->getNumRows(); unsigned int nc = hf->getNumColumns(); osg::BoundingBox bb; osg::FloatArray* heights = hf->getFloatArray(); for(unsigned int r=0; rsetInitialBound(bb); // OSG_NOTICE<<"Assigning initial bound ("<getBoundingBox(); //OSG_NOTICE<<" getBoundingBox ("< stateset = transform->getOrCreateStateSet(); // apply colour layers applyLayers(tile, stateset.get()); return transform; } osg::ref_ptr GeometryPool::getOrCreateProgram(LayerTypes& layerTypes) { #if 0 OSG_NOTICE<<"getOrCreateProgram("; for(LayerTypes::iterator itr = layerTypes.begin(); itr != layerTypes.end(); ++itr) { if (itr!= layerTypes.begin()) OSG_NOTICE<<", "; switch(*itr) { case(HEIGHTFIELD_LAYER): OSG_NOTICE<<"HeightField"; break; case(COLOR_LAYER): OSG_NOTICE<<"Colour"; break; case(CONTOUR_LAYER): OSG_NOTICE<<"Contour"; break; } } #endif OpenThreads::ScopedLock lock(_programMapMutex); ProgramMap::iterator itr = _programMap.find(layerTypes); if (itr!=_programMap.end()) { // OSG_NOTICE<<") returning exisitng Program "<second.get()<second.get(); } osg::ref_ptr program = new osg::Program; _programMap[layerTypes] = program; // OSG_NOTICE<<") creating new Program "< vertex_shader = osgDB::readShaderFile("shaders/terrain.vert"); program->addShader(vertex_shader.get()); osg::ref_ptr fragment_shader = osgDB::readShaderFile("shaders/terrain.frag"); program->addShader(fragment_shader.get()); return program; } void GeometryPool::applyLayers(osgTerrain::TerrainTile* tile, osg::StateSet* stateset) { typedef std::map LayerToTextureMap; LayerToTextureMap layerToTextureMap; // OSG_NOTICE<<"tile->getNumColorLayers() = "<getNumColorLayers()<(tile->getElevationLayer()); if (hfl) { osg::Texture2D* texture2D = dynamic_cast(layerToTextureMap[hfl]); if (!texture2D) { texture2D = new osg::Texture2D; osg::ref_ptr image = new osg::Image; const void* dataPtr = hfl->getHeightField()->getFloatArray()->getDataPointer(); image->setImage(hfl->getNumRows(), hfl->getNumColumns(), 1, GL_LUMINANCE32F_ARB, GL_LUMINANCE, GL_FLOAT, reinterpret_cast(const_cast(dataPtr)), osg::Image::NO_DELETE); texture2D->setImage(image.get()); texture2D->setFilter(osg::Texture2D::MIN_FILTER, osg::Texture2D::NEAREST); texture2D->setFilter(osg::Texture2D::MAG_FILTER, osg::Texture2D::NEAREST); texture2D->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP); texture2D->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP); texture2D->setBorderColor(osg::Vec4d(0.0,0.0,0.0,0.0)); texture2D->setResizeNonPowerOfTwoHint(false); layerToTextureMap[hfl] = texture2D; } int textureUnit = layerTypes.size(); stateset->setTextureAttributeAndModes(textureUnit, texture2D, osg::StateAttribute::ON); stateset->addUniform(new osg::Uniform("terrainTexture",textureUnit)); layerTypes.push_back(HEIGHTFIELD_LAYER); } #if 1 for(unsigned int layerNum=0; layerNumgetNumColorLayers(); ++layerNum) { osgTerrain::Layer* colorLayer = tile->getColorLayer(layerNum); if (!colorLayer) continue; osgTerrain::SwitchLayer* switchLayer = dynamic_cast(colorLayer); if (switchLayer) { if (switchLayer->getActiveLayer()<0 || static_cast(switchLayer->getActiveLayer())>=switchLayer->getNumLayers()) { continue; } colorLayer = switchLayer->getLayer(switchLayer->getActiveLayer()); if (!colorLayer) continue; } osg::Image* image = colorLayer->getImage(); if (!image) continue; osgTerrain::ImageLayer* imageLayer = dynamic_cast(colorLayer); osgTerrain::ContourLayer* contourLayer = dynamic_cast(colorLayer); if (imageLayer) { osg::Texture2D* texture2D = dynamic_cast(layerToTextureMap[colorLayer]); if (!texture2D) { texture2D = new osg::Texture2D; texture2D->setImage(image); texture2D->setMaxAnisotropy(16.0f); texture2D->setResizeNonPowerOfTwoHint(false); texture2D->setFilter(osg::Texture::MIN_FILTER, colorLayer->getMinFilter()); texture2D->setFilter(osg::Texture::MAG_FILTER, colorLayer->getMagFilter()); texture2D->setWrap(osg::Texture::WRAP_S,osg::Texture::CLAMP_TO_EDGE); texture2D->setWrap(osg::Texture::WRAP_T,osg::Texture::CLAMP_TO_EDGE); bool mipMapping = !(texture2D->getFilter(osg::Texture::MIN_FILTER)==osg::Texture::LINEAR || texture2D->getFilter(osg::Texture::MIN_FILTER)==osg::Texture::NEAREST); bool s_NotPowerOfTwo = image->s()==0 || (image->s() & (image->s() - 1)); bool t_NotPowerOfTwo = image->t()==0 || (image->t() & (image->t() - 1)); if (mipMapping && (s_NotPowerOfTwo || t_NotPowerOfTwo)) { OSG_INFO<<"Disabling mipmapping for non power of two tile size("<s()<<", "<t()<<")"<setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR); } layerToTextureMap[colorLayer] = texture2D; // OSG_NOTICE<<"Creating new ImageLayer texture "<s()="<s()<<" image->t()="<t()<setTextureAttributeAndModes(textureUnit, texture2D, osg::StateAttribute::ON); std::stringstream str; str<<"colorTexture"<addUniform(new osg::Uniform(str.str().c_str(),textureUnit)); layerTypes.push_back(COLOR_LAYER); } else if (contourLayer) { osg::Texture1D* texture1D = dynamic_cast(layerToTextureMap[colorLayer]); if (!texture1D) { texture1D = new osg::Texture1D; texture1D->setImage(image); texture1D->setResizeNonPowerOfTwoHint(false); texture1D->setFilter(osg::Texture::MIN_FILTER, osg::Texture::NEAREST); texture1D->setFilter(osg::Texture::MAG_FILTER, colorLayer->getMagFilter()); layerToTextureMap[colorLayer] = texture1D; } int textureUnit = layerTypes.size(); stateset->setTextureAttributeAndModes(textureUnit, texture1D, osg::StateAttribute::ON); std::stringstream str; str<<"contourTexture"<addUniform(new osg::Uniform(str.str().c_str(),textureUnit)); layerTypes.push_back(CONTOUR_LAYER); } } #endif osg::ref_ptr program = getOrCreateProgram(layerTypes); if (program.valid()) { stateset->setAttribute(program.get()); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////// // // HeightFieldDrawable // HeightFieldDrawable::HeightFieldDrawable() { setSupportsDisplayList(false); } HeightFieldDrawable::HeightFieldDrawable(const HeightFieldDrawable& rhs,const osg::CopyOp& copyop): osg::Drawable(rhs, copyop), _heightField(rhs._heightField), _geometry(rhs._geometry) { setSupportsDisplayList(false); } void HeightFieldDrawable::drawImplementation(osg::RenderInfo& renderInfo) const { if (_geometry.valid()) _geometry->draw(renderInfo); } void HeightFieldDrawable::compileGLObjects(osg::RenderInfo& renderInfo) const { if (_geometry.valid()) _geometry->compileGLObjects(renderInfo); } void HeightFieldDrawable::resizeGLObjectBuffers(unsigned int maxSize) { if (_geometry.valid()) _geometry->resizeGLObjectBuffers(maxSize); } void HeightFieldDrawable::releaseGLObjects(osg::State* state) const { if (_geometry.valid()) _geometry->releaseGLObjects(state); } void HeightFieldDrawable::accept(osg::Drawable::AttributeFunctor& af) { if (_geometry) _geometry->accept(af); } void HeightFieldDrawable::accept(osg::Drawable::ConstAttributeFunctor& caf) const { if (_geometry) _geometry->accept(caf); } void HeightFieldDrawable::accept(osg::PrimitiveFunctor& pf) const { if (_geometry) _geometry->accept(pf); } void HeightFieldDrawable::accept(osg::PrimitiveIndexFunctor& pif) const { if (_geometry) _geometry->accept(pif); } ///////////////////////////////////////////////////////////////////////////////////////////////////////// // // ShaderTerrain // ShaderTerrain::ShaderTerrain() { // OSG_NOTICE<<"ShaderTerrain::ShaderTerrain()"<1) { unsigned int val = _currentTraversalCount; printf("Has a concurrent traversal %i\n",val); //throw "have concurrent traversal happening"; OpenThreads::Thread::YieldCurrentThread(); } else { printf("Single threaded traversal\n"); } #endif //OSG_NOTICE<<"ShaderTerrain::init("<getTileSubgraph(_terrainTile); // set tile as no longer dirty. _terrainTile->setDirtyMask(0); --_currentTraversalCount; } void ShaderTerrain::update(osgUtil::UpdateVisitor* uv) { LOCK(_transformMutex); if (_terrainTile) _terrainTile->osg::Group::traverse(*uv); if (_transform.valid()) _transform->accept(*uv); } void ShaderTerrain::cull(osgUtil::CullVisitor* cv) { LOCK(_transformMutex); if (_transform.valid()) _transform->accept(*cv); } void ShaderTerrain::traverse(osg::NodeVisitor& nv) { if (!_terrainTile) return; // if app traversal update the frame count. if (nv.getVisitorType()==osg::NodeVisitor::UPDATE_VISITOR) { // if (_terrainTile->getDirty()) _terrainTile->init(_terrainTile->getDirtyMask(), false); osgUtil::UpdateVisitor* uv = dynamic_cast(&nv); if (uv) { update(uv); return; } } else if (nv.getVisitorType()==osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = dynamic_cast(&nv); if (cv) { cull(cv); return; } } { LOCK(_transformMutex); if (_transform.valid()) { _transform->accept(nv); } } } void ShaderTerrain::cleanSceneGraph() { } void ShaderTerrain::releaseGLObjects(osg::State* state) const { // LOCK(_transformMutex); if (_transform.valid()) { // OSG_NOTICE<<"ShaderTerrain::releaseGLObjects()"<releaseGLObjects(state); } }