#include #include #include #include #include #include #include #include #include #include #include "ShaderTerrain.h" using namespace osgTerrain; 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::Geometry* 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::Vec2Array; geometry->setTexCoordArray(0, texcoords.get(), osg::Array::BIND_PER_VERTEX); geometry->setTexCoordArray(1, texcoords.get(), osg::Array::BIND_PER_VERTEX); geometry->setTexCoordArray(2, texcoords.get(), osg::Array::BIND_PER_VERTEX); geometry->setTexCoordArray(3, texcoords.get(), osg::Array::BIND_PER_VERTEX); int nx = key.nx; int ny = key.nx; int numVertices = nx * ny; vertices->reserve(numVertices); normals->reserve(numVertices); texcoords->reserve(numVertices); double r_mult = 1.0/static_cast(ny-1); double c_mult = 1.0/static_cast(nx-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); for(int r=0; r(r)*r_mult; for(int c=0; c(c)*c_mult; vertices->push_back(pos); normals->push_back(normal); texcoords->push_back(osg::Vec2(pos.x(), pos.y())); locationCoords.push_back(osg::Vec2d(pos.x(), pos.y())); } } 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); geometry->addPrimitiveSet(elements.get()); for(int r=0; raddElement(i); elements->addElement(i+1); elements->addElement(i+nx+1); elements->addElement(i+nx); } } osg::Matrixd matrix; 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); const osgTerrain::Locator* locator = computeMasterLocator(tile); if (locator) { matrix = locator->getTransform(); 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(), 0.0, pos.x(), pos.y(),pos.z()); 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 = "< 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.release(); } osg::Program* 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 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("terrain.vert"); program->addShader(vertex_shader.get()); osg::ref_ptr fragment_shader = osgDB::readShaderFile("terrain.frag"); program->addShader(fragment_shader.get()); return program.get(); } 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->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::Program* program = getOrCreateProgram(layerTypes); if (program) { stateset->setAttribute(program); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////// // // 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()"<getTileSubgraph(_terrainTile); // set tile as no longer dirty. _terrainTile->setDirtyMask(0); } void ShaderTerrain::update(osgUtil::UpdateVisitor* uv) { if (_terrainTile) _terrainTile->osg::Group::traverse(*uv); if (_transform.valid()) _transform->accept(*uv); } void ShaderTerrain::cull(osgUtil::CullVisitor* cv) { 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; } } if (_terrainTile->getDirty()) { // OSG_INFO<<"******* Doing init ***********"<init(_terrainTile->getDirtyMask(), false); } if (_transform.valid()) { _transform->accept(nv); } } void ShaderTerrain::cleanSceneGraph() { } void ShaderTerrain::releaseGLObjects(osg::State* state) const { _transform->releaseGLObjects(state); }