osg::Node* renderHeightField(const GeoHeightField& geoHF) { osg::MatrixTransform* mt = new osg::MatrixTransform; GeoPoint centroid; geoHF.getExtent().getCentroid(centroid); osg::Matrix world2local, local2world; centroid.createWorldToLocal( world2local ); local2world.invert( world2local ); mt->setMatrix( local2world ); osg::Geometry* geometry = new osg::Geometry; osg::Geode* geode = new osg::Geode; geode->addDrawable( geometry ); mt->addChild( geode ); osg::Vec3Array* verts = new osg::Vec3Array; geometry->setVertexArray( verts ); for (unsigned int c = 0; c < geoHF.getHeightField()->getNumColumns() - 1; c++) { for (unsigned int r = 0; r < geoHF.getHeightField()->getNumRows() - 1; r++) { // Add two triangles verts->push_back( getWorld( geoHF, c, r ) * world2local ); verts->push_back( getWorld( geoHF, c + 1, r ) * world2local ); verts->push_back( getWorld( geoHF, c + 1, r + 1) * world2local ); verts->push_back( getWorld( geoHF, c, r ) * world2local ); verts->push_back( getWorld( geoHF, c + 1, r + 1) * world2local ); verts->push_back( getWorld( geoHF, c, r + 1) * world2local ); } } geode->setCullingActive(false); mt->setCullingActive(false); geometry->addPrimitiveSet(new osg::DrawArrays(GL_TRIANGLES, 0, verts->size())); osg::Vec4ubArray* colors = new osg::Vec4ubArray(); colors->push_back(osg::Vec4ub(255,0,0,255)); geometry->setColorArray(colors, osg::Array::BIND_OVERALL); mt->getOrCreateStateSet()->setMode(GL_LIGHTING, osg::StateAttribute::OFF); mt->getOrCreateStateSet()->setRenderBinDetails(99, "RenderBin"); return mt; }
void GLSkyNode::onSetDateTime() { if ( !getSunLight() ) return; CelestialBody sun = getEphemeris()->getSunPosition(getDateTime()); // If the user set a projected-map reference point, assume we are using // a projected map and set the sun position acordingly. if (getReferencePoint().isValid()) { // pull the ref point: GeoPoint refpoint = getReferencePoint(); // convert to lat/long: GeoPoint refLatLong; osg::ref_ptr<const SpatialReference> wgs84 = SpatialReference::get("wgs84"); refpoint.transform(wgs84.get(), refLatLong); // Matrix to convert the ECEF sun position to the local tangent plane // centered on our reference point: osg::Matrixd world2local; refLatLong.createWorldToLocal(world2local); // convert the sun position: osg::Vec3d sunPosLocal = sun.geocentric * world2local; sunPosLocal.normalize(); getSunLight()->setPosition( osg::Vec4(sunPosLocal, 0.0) ); } else if (_options.coordinateSystem() == SkyOptions::COORDSYS_ECEF) { osg::Vec3d pos = sun.geocentric; pos.normalize(); _light->setPosition(osg::Vec4(pos, 0.0)); // directional light } else if (_options.coordinateSystem() == SkyOptions::COORDSYS_ECI) { osg::Vec3d pos = sun.eci; pos.normalize(); _light->setPosition(osg::Vec4(pos, 0.0)); // directional light } }
void GLSkyNode::onSetDateTime() { if ( !getSunLight() || !_profile.valid() ) return; const DateTime& dt = getDateTime(); osg::Vec3d sunPosECEF = getEphemeris()->getSunPositionECEF( dt ); if ( _profile->getSRS()->isGeographic() ) { sunPosECEF.normalize(); getSunLight()->setPosition( osg::Vec4(sunPosECEF, 0.0) ); } else { // pull the ref point: GeoPoint refpoint = getReferencePoint(); if ( !refpoint.isValid() ) { // not found; use the center of the profile: _profile->getExtent().getCentroid(refpoint); } // convert to lat/long: GeoPoint refLatLong; refpoint.transform(_profile->getSRS()->getGeographicSRS(), refLatLong); // Matrix to convert the ECEF sun position to the local tangent plane // centered on our reference point: osg::Matrixd world2local; refLatLong.createWorldToLocal(world2local); // convert the sun position: osg::Vec3d sunPosLocal = sunPosECEF * world2local; sunPosLocal.normalize(); getSunLight()->setPosition( osg::Vec4(sunPosLocal, 0.0) ); } }
osg::Geometry* GeometryPool::createGeometry(const TileKey& tileKey, const MapInfo& mapInfo, 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(); GLenum mode = (_options.gpuTessellation() == true) ? GL_PATCHES : GL_TRIANGLES; // Pre-allocate enough space for all triangles. osg::DrawElements* primSet = new osg::DrawElementsUShort(mode); primSet->reserveElements(numIndiciesInSurface + numIncidesInSkirt); osg::BoundingSphere tileBound; // the geometry: osg::Geometry* geom = new osg::Geometry(); geom->setUseVertexBufferObjects(true); geom->setUseDisplayList(false); geom->addPrimitiveSet( primSet ); // the vertex locations: osg::Vec3Array* verts = new osg::Vec3Array(); verts->reserve( numVerts ); geom->setVertexArray( verts ); // the surface normals (i.e. extrusion vectors) osg::Vec3Array* normals = new osg::Vec3Array(); normals->reserve( numVerts ); geom->setNormalArray( normals ); geom->setNormalBinding( geom->BIND_PER_VERTEX ); osg::Vec3Array* neighbors = 0L; if ( _options.morphTerrain() == true ) { // neighbor positions (for morphing) neighbors = new osg::Vec3Array(); neighbors->reserve( numVerts ); geom->setTexCoordArray( 1, neighbors ); } // 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::Vec3Array* texCoords = new osg::Vec3Array(); texCoords->reserve( numVerts ); #endif geom->setTexCoordArray( 0, texCoords ); 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() ); if ( populateTexCoords ) { // if masked then set textCoord z-value to 0.0 float marker = maskSet ? maskSet->getMarker(nx, ny) : MASK_MARKER_NORMAL; texCoords->push_back( osg::Vec3f(nx, ny, marker) ); } osg::Vec3d modelPlusOne; locator->unitToModel(osg::Vec3d(nx, ny, 1.0f), modelPlusOne); osg::Vec3d normal = (modelPlusOne*world2local)-modelLTP; normal.normalize(); normals->push_back( normal ); // neighbor: if ( neighbors ) { osg::Vec3d modelNeighborLTP = (*verts)[verts->size() - getMorphNeighborIndexOffset(col, row, _tileSize)]; neighbors->push_back(modelNeighborLTP); } } } // Now tessellate the surface. // TODO: do we really need this?? bool swapOrientation = !locator->orientationOpenGL(); for(unsigned j=0; j<_tileSize-1; ++j) { for(unsigned i=0; i<_tileSize-1; ++i) { int i00; int i01; if (swapOrientation) { i01 = j*_tileSize + i; i00 = i01+_tileSize; } else { i00 = j*_tileSize + i; i01 = i00+_tileSize; } int i10 = i00+1; int i11 = i01+1; // skip any triangles that have a discarded vertex: bool discard = maskSet && ( maskSet->isMasked( (*texCoords)[i00] ) || maskSet->isMasked( (*texCoords)[i11] ) ); if ( !discard ) { discard = maskSet && maskSet->isMasked( (*texCoords)[i01] ); if ( !discard ) { primSet->addElement(i01); primSet->addElement(i00); primSet->addElement(i11); } discard = maskSet && maskSet->isMasked( (*texCoords)[i10] ); if ( !discard ) { primSet->addElement(i00); primSet->addElement(i10); primSet->addElement(i11); } } } } if ( createSkirt ) { // SKIRTS: // calculate the skirt extrusion height double height = tileBound.radius() * _options.heightFieldSkirtRatio().get(); unsigned skirtIndex = verts->size(); // first, create all the skirt verts, normals, and texcoords. for(int c=0; c<(int)_tileSize-1; ++c) addSkirtDataForIndex( c, height ); //top for(int r=0; r<(int)_tileSize-1; ++r) addSkirtDataForIndex( r*_tileSize+(_tileSize-1), height ); //right for(int c=_tileSize-1; c>=0; --c) addSkirtDataForIndex( (_tileSize-1)*_tileSize+c, height ); //bottom for(int r=_tileSize-1; r>=0; --r) addSkirtDataForIndex( r*_tileSize, height ); //left // then create the elements indices: int i; for(i=skirtIndex; i<(int)verts->size()-2; i+=2) addSkirtTriangles( i, i+2 ); addSkirtTriangles( i, skirtIndex ); } // create mask geometry if (maskSet) { osg::ref_ptr<osg::DrawElementsUInt> maskPrim = maskSet->createMaskPrimitives(mapInfo, verts, texCoords, normals, neighbors); if (maskPrim) geom->addPrimitiveSet( maskPrim ); } return geom; }
osg::Geometry* GeometryPool::createGeometry(const TileKey& tileKey, const MapInfo& mapInfo, 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 = createSkirt ? (_tileSize-1) * 4 * 6 : 0; GLenum mode = (_options.gpuTessellation() == true) ? GL_PATCHES : GL_TRIANGLES; // Pre-allocate enough space for all triangles. osg::DrawElements* primSet = new osg::DrawElementsUShort(mode); primSet->reserveElements(numIndiciesInSurface + numIncidesInSkirt); osg::BoundingSphere tileBound; // the geometry: osg::Geometry* geom = new osg::Geometry(); geom->setUseVertexBufferObjects(true); geom->setUseDisplayList(false); geom->addPrimitiveSet( primSet ); // the vertex locations: osg::Vec3Array* verts = new osg::Vec3Array(); verts->reserve( numVerts ); geom->setVertexArray( verts ); // the surface normals (i.e. extrusion vectors) osg::Vec3Array* normals = new osg::Vec3Array(); normals->reserve( numVerts ); geom->setNormalArray( normals ); geom->setNormalBinding( geom->BIND_PER_VERTEX ); // neighbor positions (for morphing) osg::Vec3Array* neighbors = new osg::Vec3Array(); neighbors->reserve( numVerts ); geom->setTexCoordArray( 1, neighbors ); // 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::Vec3Array* texCoords = new osg::Vec3Array(); texCoords->reserve( numVerts ); #endif geom->setTexCoordArray( 0, texCoords ); 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() ); // no masking in the geometry pool, so always write a z=1.0 -gw //bool masked = _maskSet.contains(nx, ny); if ( populateTexCoords ) { texCoords->push_back( osg::Vec3f(nx, ny, 1.0f) ); } osg::Vec3d modelPlusOne; locator->unitToModel(osg::Vec3d(nx, ny, 1.0f), modelPlusOne); osg::Vec3d normal = (modelPlusOne*world2local)-modelLTP; normal.normalize(); normals->push_back( normal ); // neighbor: osg::Vec3d modelNeighborLTP = (*verts)[verts->size() - getMorphNeighborIndexOffset(col, row, _tileSize)]; neighbors->push_back(modelNeighborLTP); } } // Now tessellate the surface. // TODO: do we really need this?? bool swapOrientation = !locator->orientationOpenGL(); for(unsigned j=0; j<_tileSize-1; ++j) { for(unsigned i=0; i<_tileSize-1; ++i) { int i00; int i01; if (swapOrientation) { i01 = j*_tileSize + i; i00 = i01+_tileSize; } else { i00 = j*_tileSize + i; i01 = i00+_tileSize; } int i10 = i00+1; int i11 = i01+1; // If the quad does not intersect a mask, tessellate it; otherwise skip it // since the mask generator will take care of it. bool addTris = true; if ( maskSet ) { addTris = !maskSet->isMasked( (*texCoords)[i00] ) && !maskSet->isMasked( (*texCoords)[i01] ) && !maskSet->isMasked( (*texCoords)[i10] ) && !maskSet->isMasked( (*texCoords)[i11] ) && !maskSet->containedByQuadAtColRow( i, j, _tileSize ); } if ( addTris ) { primSet->addElement(i01); primSet->addElement(i00); primSet->addElement(i11); primSet->addElement(i00); primSet->addElement(i10); primSet->addElement(i11); } } } if ( createSkirt ) { // SKIRTS: // calculate the skirt extrusion height double height = tileBound.radius() * _options.heightFieldSkirtRatio().get(); unsigned skirtIndex = verts->size(); // first, create all the skirt verts, normals, and texcoords. for(int c=0; c<(int)_tileSize-1; ++c) addSkirtDataForIndex( c, height ); //top for(int r=0; r<(int)_tileSize-1; ++r) addSkirtDataForIndex( r*_tileSize+(_tileSize-1), height ); //right for(int c=_tileSize-1; c>=0; --c) addSkirtDataForIndex( (_tileSize-1)*_tileSize+c, height ); //bottom for(int r=_tileSize-1; r>=0; --r) addSkirtDataForIndex( r*_tileSize, height ); //left // then create the elements indices: int i; for(i=skirtIndex; i<(int)verts->size()-2; i+=2) addSkirtTriangles( i, i+2 ); addSkirtTriangles( i, skirtIndex ); } #if 0 // if we're using patches, we must create a "proxy" primitive set that supports // PrimitiveFunctor et al (for intersections, bounds testing, etc.) if ( mode == GL_PATCHES ) { osg::PrimitiveSet* patchesAsTriangles = osg::clone( primSet, osg::CopyOp::SHALLOW_COPY ); patchesAsTriangles->setMode( GL_TRIANGLES ); geom->setPatchTriangles( patchesAsTriangles ); } #endif return geom; }
void ModelSplatter::operator()(const TileKey& key, osg::Node* node) { TerrainTileNode* tile = osgEarth::findTopMostNodeOfType<TerrainTileNode>(node); if ( !tile ) return; if ( key.getLOD() >= _minLOD && _model.valid() ) { // make sure the correct model is loaded establish(); // elevation texture and matrix are required osg::Texture* elevationTex = tile->getElevationTexture(); if ( !elevationTex ) { //OE_WARN << LC << "No elevation texture for key " << key.str() << "\n"; return; } osg::RefMatrix* elevationTexMat = tile->getElevationTextureMatrix(); if ( !elevationTexMat ) { //OE_WARN << LC << "No elevation texture matrix for key " << key.str() << "\n"; return; } tile->addChild( _model.get() ); osg::StateSet* ss = tile->getOrCreateStateSet(); // first, a rotation vector to make trees point up. GeoPoint p; key.getExtent().getCentroid(p); osg::Vec3d up; p.createWorldUpVector(up); osg::Quat q; q.makeRotate(osg::Vec3d(0,0,1), up); osg::Matrixd zup = osg::Matrixd::rotate(q); // matrices to resolve the weird terrain localization into a usable LTP. osg::Matrix tile2world = tile->getMatrix(); osg::Matrix world2ltp; p.createWorldToLocal(world2ltp); osg::Matrix local2ltp = tile2world * world2ltp; osg::Matrix ltp2local; ltp2local.invert(local2ltp); // after inverting the matrix, combine the ZUP (optimization) local2ltp.preMult( zup ); ss->addUniform( new osg::Uniform("oe_trees_local2ltp", osg::Matrixf(local2ltp)) ); ss->addUniform( new osg::Uniform("oe_trees_ltp2local", osg::Matrixf(ltp2local)) ); // calculate the scatter area: float h = key.getExtent().height() * 111320.0f; float w = key.getExtent().width() * 111320.0f * cos(fabs(osg::DegreesToRadians(p.y()))); ss->addUniform( new osg::Uniform("oe_trees_span", osg::Vec2f(w,h)) ); ss->setTextureAttributeAndModes(2, tile->getElevationTexture(), 1); ss->addUniform( new osg::Uniform("oe_terrain_tex_matrix", osg::Matrixf(*elevationTexMat)) ); } }