Files
OpenSceneGraph/src/osgSim/OverlayNode.cpp
Robert Osfield 0726624a86 From Jose Delport: "have been using the new VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY mode
of the OverlayNode.

I change the overlay subgraph dynamically and when I remove all the
subgraph nodes that is inside the current main camera FOV (others
outside still exist), the overlay texture does not update because of the
early return in the traversal. I then get a kind of ghost texture moving
around the terrain.

The attached file fixed the problem for me, but I'm not sure if it is
the best way to address the problem."
2007-09-24 09:35:59 +00:00

1868 lines
64 KiB
C++

/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
*
* This library is open source and may be redistributed and/or modified under
* the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
* (at your option) any later version. The full license is in LICENSE file
* included with this distribution, and on the openscenegraph.org website.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* OpenSceneGraph Public License for more details.
*/
#include <osg/ComputeBoundsVisitor>
#include <osg/Texture2D>
#include <osg/CoordinateSystemNode>
#include <osg/TexEnv>
#include <osg/Geometry>
#include <osg/io_utils>
#include <osgDB/FileUtils>
#include <osgUtil/CullVisitor>
#include <osgSim/OverlayNode>
using namespace osgSim;
using namespace osg;
namespace osgSim
{
class CustomPolytope
{
public:
CustomPolytope() {}
typedef std::vector<Vec3d> Vertices;
struct Face
{
std::string name;
osg::Plane plane;
Vertices vertices;
};
Face& createFace() { _faces.push_back(Face()); return _faces.back(); }
/** Create a Polytope which is a cube, centered at 0,0,0, with sides of 2 units.*/
void setToUnitFrustum(bool withNear=true, bool withFar=true)
{
const osg::Vec3d v000(-1.0,-1.0,-1.0);
const osg::Vec3d v010(-1.0,1.0,-1.0);
const osg::Vec3d v001(-1.0,-1.0,1.0);
const osg::Vec3d v011(-1.0,1.0,1.0);
const osg::Vec3d v100(1.0,-1.0,-1.0);
const osg::Vec3d v110(1.0,1.0,-1.0);
const osg::Vec3d v101(1.0,-1.0,1.0);
const osg::Vec3d v111(1.0,1.0,1.0);
_faces.clear();
{ // left plane.
Face& face = createFace();
face.name = "left";
face.plane.set(1.0,0.0,0.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v001);
face.vertices.push_back(v011);
face.vertices.push_back(v010);
}
{ // right plane.
Face& face = createFace();
face.name = "right";
face.plane.set(-1.0,0.0,0.0,1.0);
face.vertices.push_back(v100);
face.vertices.push_back(v110);
face.vertices.push_back(v111);
face.vertices.push_back(v101);
}
{ // bottom plane.
Face& face = createFace();
face.name = "bottom";
face.plane.set(0.0,1.0,0.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v100);
face.vertices.push_back(v101);
face.vertices.push_back(v001);
}
{ // top plane.
Face& face = createFace();
face.name = "top";
face.plane.set(0.0,-1.0,0.0,1.0);
face.vertices.push_back(v111);
face.vertices.push_back(v011);
face.vertices.push_back(v010);
face.vertices.push_back(v110);
}
if (withNear)
{ // near plane
Face& face = createFace();
face.name = "near";
face.plane.set(0.0,0.0,-1.0,1.0);
face.vertices.push_back(v000);
face.vertices.push_back(v010);
face.vertices.push_back(v110);
face.vertices.push_back(v100);
}
if (withFar)
{ // far plane
Face& face = createFace();
face.name = "far";
face.plane.set(0.0,0.0,1.0,1.0);
face.vertices.push_back(v001);
face.vertices.push_back(v101);
face.vertices.push_back(v111);
face.vertices.push_back(v011);
}
}
void setToBoundingBox(const osg::BoundingBox& bb)
{
#if 0
osg::notify(osg::NOTICE)<<"setToBoundingBox xrange "<<bb.xMin()<<" "<<bb.xMax()<<std::endl;
osg::notify(osg::NOTICE)<<" "<<bb.yMin()<<" "<<bb.yMax()<<std::endl;
osg::notify(osg::NOTICE)<<" "<<bb.zMin()<<" "<<bb.zMax()<<std::endl;
#endif
const osg::Vec3d v000(bb.xMin(),bb.yMin(), bb.zMin());
const osg::Vec3d v010(bb.xMin(),bb.yMax(), bb.zMin());
const osg::Vec3d v001(bb.xMin(),bb.yMin(), bb.zMax());
const osg::Vec3d v011(bb.xMin(),bb.yMax(), bb.zMax());
const osg::Vec3d v100(bb.xMax(),bb.yMin(), bb.zMin());
const osg::Vec3d v110(bb.xMax(),bb.yMax(), bb.zMin());
const osg::Vec3d v101(bb.xMax(),bb.yMin(), bb.zMax());
const osg::Vec3d v111(bb.xMax(),bb.yMax(), bb.zMax());
_faces.clear();
{ // x min plane
Face& face = createFace();
face.name = "xMin";
face.plane.set(1.0,0.0,0.0,-bb.xMin());
face.vertices.push_back(v000);
face.vertices.push_back(v001);
face.vertices.push_back(v011);
face.vertices.push_back(v010);
}
{ // x max plane.
Face& face = createFace();
face.name = "xMax";
face.plane.set(-1.0,0.0,0.0,bb.xMax());
face.vertices.push_back(v100);
face.vertices.push_back(v110);
face.vertices.push_back(v111);
face.vertices.push_back(v101);
}
{ // y min plane.
Face& face = createFace();
face.name = "yMin";
face.plane.set(0.0,1.0,0.0,-bb.yMin());
face.vertices.push_back(v000);
face.vertices.push_back(v100);
face.vertices.push_back(v101);
face.vertices.push_back(v001);
}
{ // y max plane.
Face& face = createFace();
face.name = "yMax";
face.plane.set(0.0,-1.0,0.0,bb.yMax());
face.vertices.push_back(v111);
face.vertices.push_back(v011);
face.vertices.push_back(v010);
face.vertices.push_back(v110);
}
{ // z min plane
Face& face = createFace();
face.name = "zMin";
face.plane.set(0.0,0.0,1.0,-bb.zMin());
face.vertices.push_back(v000);
face.vertices.push_back(v010);
face.vertices.push_back(v110);
face.vertices.push_back(v100);
}
{ // z max plane
Face& face = createFace();
face.name = "zMax";
face.plane.set(0.0,0.0,-1.0,bb.zMax());
face.vertices.push_back(v001);
face.vertices.push_back(v101);
face.vertices.push_back(v111);
face.vertices.push_back(v011);
}
}
void transform(const osg::Matrix& matrix, const osg::Matrix& inverse)
{
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
face.plane.transformProvidingInverse(inverse);
for(Vertices::iterator vitr = face.vertices.begin();
vitr != face.vertices.end();
++vitr)
{
*vitr = *vitr * matrix;
}
}
}
void insertVertex(const osg::Vec3d& vertex, osg::EllipsoidModel* em=0, double minHeight=0.0)
{
// osg::notify(osg::NOTICE)<<"Inserting vertex "<<vertex<<std::endl;
Faces removedFaces;
Faces::iterator itr;
for(itr = _faces.begin();
itr != _faces.end();
)
{
Face& face = *itr;
if (face.plane.distance(vertex)<0.0)
{
removedFaces.push_back(face);
itr = _faces.erase(itr);
}
else
{
++itr;
}
}
if (removedFaces.empty()) return;
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::map<Edge,int> Edges;
Edges edges;
double numVerticesAdded=0.0;
osg::Vec3d center;
for(itr = removedFaces.begin();
itr != removedFaces.end();
++itr)
{
Face& face = *itr;
Vertices& vertices = face.vertices;
for(unsigned int i=0; i<vertices.size(); ++i)
{
osg::Vec3d& a = vertices[i];
osg::Vec3d& b = vertices[ (i+1) % vertices.size()];
if (a<b) ++edges[Edge(a,b)];
else ++edges[Edge(b,a)];
center += a;
numVerticesAdded += 1.0;
}
}
if (numVerticesAdded==0.0) return;
center /= numVerticesAdded;
typedef std::set<osg::Vec3> VertexSet;
VertexSet uniqueVertices;
for(Edges::iterator eitr = edges.begin();
eitr != edges.end();
++eitr)
{
if (eitr->second==1)
{
// osg::notify(osg::NOTICE)<<" edge Ok"<<std::endl;
const Edge& edge = eitr->first;
Face face;
face.name = "baseSide";
face.plane.set(vertex, edge.first, edge.second);
face.vertices.push_back(vertex);
face.vertices.push_back(edge.first);
face.vertices.push_back(edge.second);
if (face.plane.distance(center)<0.0) face.plane.flip();
_faces.push_back(face);
uniqueVertices.insert(edge.first);
uniqueVertices.insert(edge.second);
}
}
// now trim the new polytope back the desired height
if (em)
{
// compute the base vertices at the new height
Vertices baseVertices;
for(VertexSet::iterator itr = uniqueVertices.begin();
itr != uniqueVertices.end();
++itr)
{
const osg::Vec3d& point = *itr;
double latitude, longitude, height;
em->convertXYZToLatLongHeight(point.x(), point.y(), point.z(), latitude, longitude, height);
osg::Vec3d normal(point);
normal.normalize();
baseVertices.push_back(point - normal * (height - minHeight));
}
//compute centroid of the base vertices
osg::Vec3d center;
double totalArea = 0;
for(unsigned int i=0; i<baseVertices.size()-1; ++i)
{
const osg::Vec3d& a = baseVertices[i];
const osg::Vec3d& b = baseVertices[i+1];
const osg::Vec3d& c = baseVertices[(i+2)%baseVertices.size()];
double area = ((a-b)^(b-c)).length()*0.5;
osg::Vec3d localCenter = (a+b+c)/3.0;
center += localCenter*area;
totalArea += area;
}
center /= totalArea;
osg::Vec3d normal(center);
normal.normalize();
osg::Plane basePlane(normal, center);
cut(basePlane,"basePlane");
}
// osg::notify(osg::NOTICE)<<" Removed faces "<<removedFaces.size()<<std::endl;
}
void projectDowntoBase(const osg::Vec3d& control, const osg::Vec3d& normal)
{
// osg::notify(osg::NOTICE)<<"CustomPolytope::projectDowntoBase not implementated yet."<<std::endl;
Faces removedFaces;
Faces::iterator itr;
for(itr = _faces.begin();
itr != _faces.end();
)
{
Face& face = *itr;
if ((face.plane.getNormal()*normal)>=0.0)
{
removedFaces.push_back(face);
itr = _faces.erase(itr);
}
else
{
++itr;
}
}
if (removedFaces.empty()) return;
typedef std::pair<osg::Vec3d, osg::Vec3d> Edge;
typedef std::map<Edge,int> Edges;
Edges edges;
double numVerticesAdded=0.0;
osg::Vec3d center;
for(itr = removedFaces.begin();
itr != removedFaces.end();
++itr)
{
Face& face = *itr;
Vertices& vertices = face.vertices;
for(unsigned int i=0; i<vertices.size(); ++i)
{
osg::Vec3d& a = vertices[i];
osg::Vec3d& b = vertices[ (i+1) % vertices.size()];
if (a<b) ++edges[Edge(a,b)];
else ++edges[Edge(b,a)];
center += a;
numVerticesAdded += 1.0;
}
}
if (numVerticesAdded==0.0) return;
center /= numVerticesAdded;
typedef std::set<osg::Vec3> VertexSet;
VertexSet uniqueVertices;
for(Edges::iterator eitr = edges.begin();
eitr != edges.end();
++eitr)
{
if (eitr->second==1)
{
// osg::notify(osg::NOTICE)<<" edge Ok"<<std::endl;
const Edge& edge = eitr->first;
double h_first = (edge.first-control) * normal;
osg::Vec3d projected_first = edge.first - normal * h_first;
double h_second = (edge.second-control) * normal;
osg::Vec3d projected_second = edge.second - normal * h_second;
Face face;
face.name = "baseSide";
face.plane.set(projected_first, edge.first, edge.second);
face.vertices.push_back(projected_first);
face.vertices.push_back(projected_second);
face.vertices.push_back(edge.second);
face.vertices.push_back(edge.first);
if (face.plane.distance(center)<0.0) face.plane.flip();
_faces.push_back(face);
uniqueVertices.insert(projected_first);
uniqueVertices.insert(projected_second);
}
}
Face newFace;
newFace.name = "basePlane";
newFace.plane.set(normal,control);
osg::Vec3d side = ( fabs(normal.x()) < fabs(normal.y()) ) ?
osg::Vec3(1.0, 0.0, 0.0) :
osg::Vec3(0.0, 1.0, 0.0);
osg::Vec3 v = normal ^ side;
v.normalize();
osg::Vec3 u = v ^ normal;
u.normalize();
typedef std::map<double, Vec3d> AnglePositions;
AnglePositions anglePositions;
for(VertexSet::iterator vitr = uniqueVertices.begin();
vitr != uniqueVertices.end();
++vitr)
{
osg::Vec3d delta = *vitr - center;
double angle = atan2(delta * u, delta * v);
if (angle<0.0) angle += 2.0*osg::PI;
anglePositions[angle] = *vitr;
}
for(AnglePositions::iterator aitr = anglePositions.begin();
aitr != anglePositions.end();
++aitr)
{
newFace.vertices.push_back(aitr->second);
}
_faces.push_back(newFace);
}
void computeSilhoette(const osg::Vec3d& normal, Vertices& vertices)
{
typedef std::pair<osg::Vec3d, osg::Vec3d> EdgePair;
typedef std::vector<Face*> EdgeFaces;
typedef std::map<EdgePair, EdgeFaces> EdgeMap;
EdgeMap edgeMap;
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
for(unsigned int i=0; i<face.vertices.size(); ++i)
{
osg::Vec3d& va = face.vertices[i];
osg::Vec3d& vb = face.vertices[(i+1)%face.vertices.size()];
if (va < vb) edgeMap[EdgePair(va,vb)].push_back(&face);
else edgeMap[EdgePair(vb,va)].push_back(&face);
}
}
typedef std::set<osg::Vec3> VertexSet;
VertexSet uniqueVertices;
for(EdgeMap::iterator eitr = edgeMap.begin();
eitr != edgeMap.end();
++eitr)
{
const EdgePair& edge = eitr->first;
const EdgeFaces& edgeFaces = eitr->second;
if (edgeFaces.size()==1)
{
// osg::notify(osg::NOTICE)<<"Open edge found "<<edgeFaces.front()->name<<std::endl;
}
else if (edgeFaces.size()==2)
{
double dotProduct0 = edgeFaces[0]->plane.getNormal() * normal;
double dotProduct1 = edgeFaces[1]->plane.getNormal() * normal;
if (dotProduct0 * dotProduct1 <0.0)
{
// osg::notify(osg::NOTICE)<<" Silhoette edge found "<<edgeFaces[0]->name<<" "<<edgeFaces[1]->name<<std::endl;
uniqueVertices.insert(edge.first);
uniqueVertices.insert(edge.second);
}
else
{
// osg::notify(osg::NOTICE)<<" Non silhoette edge found "<<edgeFaces[0]->name<<" "<<edgeFaces[1]->name<<std::endl;
}
}
else
{
// osg::notify(osg::NOTICE)<<"Confused edge found "<<edgeFaces.size()<<std::endl;
}
}
// compute center
osg::Vec3d center;
VertexSet::iterator vitr;
for(vitr = uniqueVertices.begin();
vitr != uniqueVertices.end();
++vitr)
{
center += *vitr;
}
center /= (double)(uniqueVertices.size());
// compute the ordered points around silhoette
osg::Vec3d side = ( fabs(normal.x()) < fabs(normal.y()) ) ?
osg::Vec3(1.0, 0.0, 0.0) :
osg::Vec3(0.0, 1.0, 0.0);
osg::Vec3 v = normal ^ side;
v.normalize();
osg::Vec3 u = v ^ normal;
u.normalize();
typedef std::map<double, Vec3d> AnglePositions;
AnglePositions anglePositions;
for(vitr = uniqueVertices.begin();
vitr != uniqueVertices.end();
++vitr)
{
osg::Vec3d delta = *vitr - center;
double angle = atan2(delta * u, delta * v);
if (angle<0.0) angle += 2.0*osg::PI;
anglePositions[angle] = *vitr;
}
for(AnglePositions::iterator aitr = anglePositions.begin();
aitr != anglePositions.end();
++aitr)
{
vertices.push_back(aitr->second);
}
}
void cut(const osg::Polytope& polytope)
{
// osg::notify(osg::NOTICE)<<"Cutting with polytope "<<std::endl;
for(osg::Polytope::PlaneList::const_iterator itr = polytope.getPlaneList().begin();
itr != polytope.getPlaneList().end();
++itr)
{
cut(*itr);
}
}
void cut(const CustomPolytope& polytope)
{
// osg::notify(osg::NOTICE)<<"Cutting with polytope "<<std::endl;
for(Faces::const_iterator itr = polytope._faces.begin();
itr != polytope._faces.end();
++itr)
{
cut(itr->plane, itr->name);
}
}
void cut(const osg::Plane& plane, const std::string& name=std::string())
{
// osg::notify(osg::NOTICE)<<" Cutting plane "<<plane<<std::endl;
Face newFace;
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
)
{
Face& face = *itr;
int intersect = plane.intersect(face.vertices);
if (intersect==1)
{
// osg::notify(osg::NOTICE)<<" Face inside"<<std::endl;
++itr;
}
else if (intersect==0)
{
// osg::notify(osg::NOTICE)<<" Face intersecting - before "<<face.vertices.size()<<std::endl;
Vertices& vertices = face.vertices;
Vertices newVertices;
typedef std::vector<double> Distances;
Distances distances;
distances.reserve(face.vertices.size());
for(Vertices::iterator vitr = vertices.begin();
vitr != vertices.end();
++vitr)
{
distances.push_back(plane.distance(*vitr));
}
for(unsigned int i=0; i<vertices.size(); ++i)
{
osg::Vec3d& va = vertices[i];
osg::Vec3d& vb = vertices[(i+1)%vertices.size()];
double distance_a = distances[i];
double distance_b = distances[(i+1)%vertices.size()];
// is first edge point inside plane?
if (distance_a>=0.0) newVertices.push_back(vertices[i]);
// does edge intersect plane
if (distance_a * distance_b<0.0)
{
// inserting vertex
osg::Vec3d intersection = (vb*distance_a - va*distance_b)/(distance_a-distance_b);
newVertices.push_back(intersection);
newFace.vertices.push_back(intersection);
// osg::notify(osg::NOTICE)<<" intersection distance "<<plane.distance(intersection)<<std::endl;
}
}
vertices.swap(newVertices);
// osg::notify(osg::NOTICE)<<" intersecting - after "<<face.vertices.size()<<std::endl;
++itr;
}
else if (intersect==-1)
{
// osg::notify(osg::NOTICE)<<" Face outside"<<_faces.size()<<std::endl;
itr = _faces.erase(itr);
}
}
if (!newFace.vertices.empty())
{
// osg::notify(osg::NOTICE)<<" inserting newFace intersecting "<<newFace.vertices.size()<<std::endl;
newFace.name = name;
newFace.plane = plane;
Vertices& vertices = newFace.vertices;
osg::Vec3d side = ( fabs(plane.getNormal().x()) < fabs(plane.getNormal().y()) ) ?
osg::Vec3(1.0, 0.0, 0.0) :
osg::Vec3(0.0, 1.0, 0.0);
osg::Vec3 v = plane.getNormal() ^ side;
v.normalize();
osg::Vec3 u = v ^ plane.getNormal();
u.normalize();
osg::Vec3d center;
Vertices::iterator vitr;
for(vitr = vertices.begin();
vitr != vertices.end();
++vitr)
{
center += *vitr;
}
center /= vertices.size();
typedef std::map<double, Vec3d> AnglePositions;
AnglePositions anglePositions;
for(vitr = vertices.begin();
vitr != vertices.end();
++vitr)
{
osg::Vec3d delta = *vitr - center;
double angle = atan2(delta * u, delta * v);
if (angle<0.0) angle += 2.0*osg::PI;
anglePositions[angle] = *vitr;
}
Vertices newVertices;
for(AnglePositions::iterator aitr = anglePositions.begin();
aitr != anglePositions.end();
++aitr)
{
newVertices.push_back(aitr->second);
}
newVertices.swap(vertices);
// osg::notify(osg::NOTICE)<<" after angle sort "<<newFace.vertices.size()<<std::endl;
_faces.push_back(newFace);
}
}
void getPolytope(osg::Polytope& polytope)
{
for(Faces::const_iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
polytope.add(itr->plane);
}
}
void getPoints(Vertices& vertices)
{
typedef std::set<osg::Vec3d> VerticesSet;
VerticesSet verticesSet;
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
for(Vertices::iterator vitr = face.vertices.begin();
vitr != face.vertices.end();
++vitr)
{
verticesSet.insert(*vitr);
}
}
for(VerticesSet::iterator sitr = verticesSet.begin();
sitr != verticesSet.end();
++sitr)
{
vertices.push_back(*sitr);
}
}
osg::Drawable* createDrawable(const osg::Vec4d& colour)
{
osg::Geometry* geometry = new osg::Geometry;
osg::Vec3Array* vertices = new osg::Vec3Array;
geometry->setVertexArray(vertices);
for(Faces::iterator itr = _faces.begin();
itr != _faces.end();
++itr)
{
Face& face = *itr;
geometry->addPrimitiveSet( new osg::DrawArrays(GL_LINE_LOOP, vertices->size(), face.vertices.size()) );
for(Vertices::iterator vitr = face.vertices.begin();
vitr != face.vertices.end();
++vitr)
{
vertices->push_back(*vitr);
}
}
osg::Vec4Array* colours = new osg::Vec4Array;
colours->push_back(colour);
geometry->setColorArray(colours);
geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
osg::StateSet* stateset = geometry->getOrCreateStateSet();
stateset->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
stateset->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::OFF);
stateset->setTextureMode(1, GL_TEXTURE_2D, osg::StateAttribute::OFF);
return geometry;
}
protected:
typedef std::list<Face> Faces;
Faces _faces;
};
}
OverlayNode::OverlayNode(OverlayTechnique technique):
_overlayTechnique(technique),
_texEnvMode(GL_DECAL),
_textureUnit(1),
_textureSizeHint(1024),
_overlayClearColor(0.0f,0.0f,0.0f,0.0f),
_continuousUpdate(false),
_overlayBaseHeight(-100.0),
_updateCamera(false),
_renderTargetImpl(osg::Camera::FRAME_BUFFER_OBJECT)
{
setNumChildrenRequiringUpdateTraversal(1);
init();
}
OverlayNode::OverlayNode(const OverlayNode& copy, const osg::CopyOp& copyop):
osg::Group(copy,copyop),
_overlayTechnique(copy._overlayTechnique),
_overlaySubgraph(copy._overlaySubgraph),
_texEnvMode(copy._texEnvMode),
_textureUnit(copy._textureUnit),
_textureSizeHint(copy._textureSizeHint),
_overlayClearColor(copy._overlayClearColor),
_continuousUpdate(copy._continuousUpdate),
_overlayBaseHeight(copy._overlayBaseHeight),
_renderTargetImpl(copy._renderTargetImpl)
{
setNumChildrenRequiringUpdateTraversal(getNumChildrenRequiringUpdateTraversal()+1);
init();
}
void OverlayNode::OverlayData::setThreadSafeRefUnref(bool threadSafe)
{
if (_camera.valid()) _camera->setThreadSafeRefUnref(threadSafe);
if (_texgenNode.valid()) _texgenNode->setThreadSafeRefUnref(threadSafe);
if (_overlayStateSet.valid()) _overlayStateSet->setThreadSafeRefUnref(threadSafe);
if (_mainSubgraphStateSet.valid()) _mainSubgraphStateSet->setThreadSafeRefUnref(threadSafe);
if (_texture.valid()) _texture->setThreadSafeRefUnref(threadSafe);
}
void OverlayNode::OverlayData::resizeGLObjectBuffers(unsigned int maxSize)
{
if (_camera.valid()) _camera->resizeGLObjectBuffers(maxSize);
if (_texgenNode.valid()) _texgenNode->resizeGLObjectBuffers(maxSize);
if (_overlayStateSet.valid()) _overlayStateSet->resizeGLObjectBuffers(maxSize);
if (_mainSubgraphStateSet.valid()) _mainSubgraphStateSet->resizeGLObjectBuffers(maxSize);
if (_texture.valid()) _texture->resizeGLObjectBuffers(maxSize);
}
void OverlayNode::OverlayData::releaseGLObjects(osg::State* state) const
{
if (_camera.valid()) _camera->releaseGLObjects(state);
if (_texgenNode.valid()) _texgenNode->releaseGLObjects(state);
if (_overlayStateSet.valid()) _overlayStateSet->releaseGLObjects(state);
if (_mainSubgraphStateSet.valid()) _mainSubgraphStateSet->releaseGLObjects(state);
if (_texture.valid()) _texture->releaseGLObjects(state);
}
void OverlayNode::setThreadSafeRefUnref(bool threadSafe)
{
osg::Group::setThreadSafeRefUnref(threadSafe);
if (_overlaySubgraph.valid()) _overlaySubgraph->setThreadSafeRefUnref(threadSafe);
for(OverlayDataMap::iterator itr = _overlayDataMap.begin();
itr != _overlayDataMap.end();
++itr)
{
itr->second.setThreadSafeRefUnref(threadSafe);
}
}
void OverlayNode::resizeGLObjectBuffers(unsigned int maxSize)
{
osg::Group::resizeGLObjectBuffers(maxSize);
if (_overlaySubgraph.valid()) _overlaySubgraph->resizeGLObjectBuffers(maxSize);
for(OverlayDataMap::iterator itr = _overlayDataMap.begin();
itr != _overlayDataMap.end();
++itr)
{
itr->second.resizeGLObjectBuffers(maxSize);
}
}
void OverlayNode::releaseGLObjects(osg::State* state) const
{
osg::Group::releaseGLObjects(state);
if (_overlaySubgraph.valid()) _overlaySubgraph->releaseGLObjects(state);
for(OverlayDataMap::const_iterator itr = _overlayDataMap.begin();
itr != _overlayDataMap.end();
++itr)
{
itr->second.releaseGLObjects(state);
}
}
void OverlayNode::setOverlayTechnique(OverlayTechnique technique)
{
if (_overlayTechnique==technique) return;
_overlayTechnique = technique;
init();
}
void OverlayNode::setRenderTargetImplementation(osg::Camera::RenderTargetImplementation impl)
{
if(_renderTargetImpl==impl) return;
_renderTargetImpl = impl;
init();
}
OverlayNode::OverlayData& OverlayNode::getOverlayData(osgUtil::CullVisitor* cv)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_overlayDataMapMutex);
OverlayDataMap::iterator itr = _overlayDataMap.find(cv);
if (itr != _overlayDataMap.end()) return itr->second;
OverlayData& overlayData = _overlayDataMap[cv];
unsigned int tex_width = _textureSizeHint;
unsigned int tex_height = _textureSizeHint;
if (!overlayData._texture)
{
// osg::notify(osg::NOTICE)<<" setting up texture"<<std::endl;
osg::Texture2D* texture = new osg::Texture2D;
texture->setTextureSize(tex_width, tex_height);
texture->setInternalFormat(GL_RGBA);
texture->setFilter(osg::Texture2D::MIN_FILTER,osg::Texture2D::LINEAR);
texture->setFilter(osg::Texture2D::MAG_FILTER,osg::Texture2D::LINEAR);
texture->setWrap(osg::Texture2D::WRAP_S,osg::Texture2D::CLAMP_TO_BORDER);
texture->setWrap(osg::Texture2D::WRAP_T,osg::Texture2D::CLAMP_TO_BORDER);
#if 1
texture->setBorderColor(osg::Vec4(_overlayClearColor));
#else
texture->setBorderColor(osg::Vec4(1.0,0.0,0.0,0.5));
#endif
overlayData._texture = texture;
}
// set up the render to texture camera.
if (!overlayData._camera || overlayData._camera->getRenderTargetImplementation() != _renderTargetImpl)
{
// osg::notify(osg::NOTICE)<<" setting up camera"<<std::endl;
// create the camera
overlayData._camera = new osg::Camera;
overlayData._camera->setClearColor(_overlayClearColor);
overlayData._camera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
// set viewport
overlayData._camera->setViewport(0,0,tex_width,tex_height);
overlayData._camera->setComputeNearFarMode(osg::CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
// set the camera to render before the main camera.
overlayData._camera->setRenderOrder(osg::Camera::PRE_RENDER);
// tell the camera to use OpenGL frame buffer object where supported.
overlayData._camera->setRenderTargetImplementation(_renderTargetImpl);
// attach the texture and use it as the color buffer.
overlayData._camera->attach(osg::Camera::COLOR_BUFFER, overlayData._texture.get());
if (_overlaySubgraph.valid()) overlayData._camera->addChild(_overlaySubgraph.get());
}
if (!overlayData._texgenNode)
{
overlayData._texgenNode = new osg::TexGenNode;
overlayData._texgenNode->setTextureUnit(_textureUnit);
}
if (!overlayData._y0) overlayData._y0 = new osg::Uniform("y0",0.0f);
if (!overlayData._lightingEnabled) overlayData._lightingEnabled = new osg::Uniform("lightingEnabled",true);
if (!overlayData._overlayStateSet)
{
overlayData._overlayStateSet = new osg::StateSet;
overlayData._overlayStateSet->addUniform(overlayData._y0.get());
overlayData._overlayStateSet->addUniform(overlayData._lightingEnabled.get());
osg::Program* program = new osg::Program;
overlayData._overlayStateSet->setAttribute(program);
// get shaders from source
std::string vertexShaderFile = osgDB::findDataFile("shaders/overlay_perspective_rtt.vert");
if (!vertexShaderFile.empty())
{
program->addShader(osg::Shader::readShaderFile(osg::Shader::VERTEX, vertexShaderFile));
}
else
{
char vertexShaderSource[] =
"uniform float y0; \n"
"uniform bool lightingEnabled; \n"
" \n"
"vec4 warp(in vec4 source) \n"
"{ \n"
" float divisor = source.y + y0; \n"
" return vec4(source.x * (1.0 + y0 ), source.y * y0 + 1.0, (source.z * y0 + 1.0)*0.01, source.w * divisor); \n"
"} \n"
" \n"
"vec3 fnormal(void) \n"
"{ \n"
" //Compute the normal \n"
" vec3 normal = gl_NormalMatrix * gl_Normal; \n"
" normal = normalize(normal); \n"
" return normal; \n"
"} \n"
" \n"
"void directionalLight(in int i, \n"
" in vec3 normal, \n"
" inout vec4 ambient, \n"
" inout vec4 diffuse, \n"
" inout vec4 specular) \n"
"{ \n"
" float nDotVP; // normal . light direction \n"
" float nDotHV; // normal . light half vector \n"
" float pf; // power factor \n"
" \n"
" nDotVP = max(0.0, dot(normal, normalize(vec3 (gl_LightSource[i].position)))); \n"
" nDotHV = max(0.0, dot(normal, vec3 (gl_LightSource[i].halfVector))); \n"
" \n"
" if (nDotVP == 0.0) \n"
" { \n"
" pf = 0.0; \n"
" } \n"
" else \n"
" { \n"
" pf = pow(nDotHV, gl_FrontMaterial.shininess); \n"
" \n"
" } \n"
" ambient += gl_LightSource[i].ambient; \n"
" diffuse += gl_LightSource[i].diffuse * nDotVP; \n"
" specular += gl_LightSource[i].specular * pf; \n"
"} \n"
"void main() \n"
"{ \n"
" gl_Position = warp(ftransform()); \n"
" \n"
" if (lightingEnabled) \n"
" { \n"
" vec4 ambient = vec4(0.0); \n"
" vec4 diffuse = vec4(0.0); \n"
" vec4 specular = vec4(0.0); \n"
" \n"
" vec3 normal = fnormal(); \n"
" \n"
" directionalLight(0, normal, ambient, diffuse, specular); \n"
" \n"
" vec4 color = gl_FrontLightModelProduct.sceneColor + \n"
" ambient * gl_FrontMaterial.ambient + \n"
" diffuse * gl_FrontMaterial.diffuse + \n"
" specular * gl_FrontMaterial.specular; \n"
" \n"
" gl_FrontColor = color; \n"
" \n"
" } \n"
" else \n"
" { \n"
" gl_FrontColor = gl_Color; \n"
" } \n"
" \n"
"} \n";
osg::Shader* vertex_shader = new osg::Shader(osg::Shader::VERTEX, vertexShaderSource);
program->addShader(vertex_shader);
}
}
if (!overlayData._mainSubgraphProgram)
{
overlayData._mainSubgraphProgram = new osg::Program;
// get shaders from source
std::string fragmentShaderFile = osgDB::findDataFile("shaders/overlay_perspective_main.frag");
if (!fragmentShaderFile.empty())
{
overlayData._mainSubgraphProgram->addShader(osg::Shader::readShaderFile(osg::Shader::FRAGMENT, fragmentShaderFile));
}
else
{
char fragmentShaderSource[] =
"uniform sampler2D texture_0; \n"
"uniform sampler2D texture_1; \n"
" \n"
"uniform float y0; \n"
" \n"
"vec2 warp(in vec2 source) \n"
"{ \n"
" float inv_divisor = 1.0 / (source.y + y0); \n"
" return vec2(source.x * (1.0 + y0 ) * inv_divisor , (source.y * y0 + 1.0 ) * inv_divisor); \n"
"} \n"
" \n"
"void main() \n"
"{ \n"
" vec2 coord = gl_TexCoord[1].xy; \n"
" coord.x = coord.x*2.0 - 1.0; \n"
" coord.y = coord.y*2.0 - 1.0; \n"
" \n"
" vec2 warped = warp(coord); \n"
" warped.x = (warped.x + 1.0)*0.5; \n"
" warped.y = (warped.y + 1.0)*0.5; \n"
" \n"
" vec4 base_color = texture2D(texture_0, gl_TexCoord[0].xy); \n"
" vec4 overlay_color = texture2D(texture_1, warped ); \n"
" vec3 mixed_color = mix(base_color.rgb, overlay_color.rgb, overlay_color.a); \n"
" gl_FragColor = vec4(mixed_color, base_color.a); \n"
"} \n";
osg::Shader* fragment_shader = new osg::Shader(osg::Shader::FRAGMENT, fragmentShaderSource);
overlayData._mainSubgraphProgram->addShader(fragment_shader);
}
}
if (!overlayData._mainSubgraphStateSet)
{
overlayData._mainSubgraphStateSet = new osg::StateSet;
overlayData._mainSubgraphStateSet->addUniform(overlayData._y0.get());
overlayData._mainSubgraphStateSet->addUniform(new osg::Uniform("texture_0",0));
overlayData._mainSubgraphStateSet->addUniform(new osg::Uniform("texture_1",1));
overlayData._mainSubgraphStateSet->setTextureAttributeAndModes(_textureUnit, overlayData._texture.get(), osg::StateAttribute::ON);
overlayData._mainSubgraphStateSet->setTextureMode(_textureUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON);
overlayData._mainSubgraphStateSet->setTextureMode(_textureUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON);
overlayData._mainSubgraphStateSet->setTextureMode(_textureUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON);
overlayData._mainSubgraphStateSet->setTextureMode(_textureUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON);
if (_texEnvMode!=GL_NONE)
{
overlayData._mainSubgraphStateSet->setTextureAttribute(_textureUnit, new osg::TexEnv((osg::TexEnv::Mode)_texEnvMode));
}
}
return overlayData;
}
void OverlayNode::init()
{
switch(_overlayTechnique)
{
case(OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY):
init_OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY();
break;
case(VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY):
init_VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY();
break;
case(VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY):
init_VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY();
break;
}
}
void OverlayNode::init_OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY()
{
osg::notify(osg::INFO)<<"OverlayNode::init() - OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY"<<std::endl;
// force initialization of _overlayDataMap for the key 0 (or NULL)
getOverlayData(0);
}
void OverlayNode::init_VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY()
{
osg::notify(osg::INFO)<<"OverlayNode::init() - VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY"<<std::endl;
}
void OverlayNode::init_VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY()
{
osg::notify(osg::INFO)<<"OverlayNode::init() - VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY"<<std::endl;
}
void OverlayNode::traverse(osg::NodeVisitor& nv)
{
switch(_overlayTechnique)
{
case(OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY):
traverse_OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY(nv);
break;
case(VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY):
traverse_VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY(nv);
break;
case(VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY):
traverse_VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY(nv);
break;
}
}
void OverlayNode::traverse_OBJECT_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY(osg::NodeVisitor& nv)
{
OverlayData& overlayData = getOverlayData(0);
osg::Camera* camera = overlayData._camera.get();
if (nv.getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR)
{
Group::traverse(nv);
if (_continuousUpdate || _updateCamera)
{
// now compute the camera's view and projection matrix to point at the shadower (the camera's children)
osg::BoundingSphere bs;
for(unsigned int i=0; i<camera->getNumChildren(); ++i)
{
bs.expandBy(camera->getChild(i)->getBound());
}
if (bs.valid())
{
// see if we are within a coordinate system node.
osg::CoordinateSystemNode* csn = 0;
osg::NodePath& nodePath = nv.getNodePath();
for(osg::NodePath::reverse_iterator itr = nodePath.rbegin();
itr != nodePath.rend() && csn==0;
++itr)
{
csn = dynamic_cast<osg::CoordinateSystemNode*>(*itr);
}
osg::EllipsoidModel* em = csn ? csn->getEllipsoidModel() : 0;
if (em)
{
osg::Vec3d eyePoint(0.0,0.0,0.0); // center of the planet
double centerDistance = (eyePoint-osg::Vec3d(bs.center())).length();
double znear = centerDistance-bs.radius();
double zfar = centerDistance+bs.radius();
double zNearRatio = 0.001f;
if (znear<zfar*zNearRatio) znear = zfar*zNearRatio;
double top = (bs.radius()/centerDistance)*znear;
double right = top;
camera->setProjectionMatrixAsFrustum(-right,right,-top,top,znear,zfar);
camera->setViewMatrixAsLookAt(eyePoint, bs.center(), osg::Vec3(0.0f,1.0f,0.0f));
}
else
{
osg::Vec3d upDirection(0.0,1.0,0.0);
osg::Vec3d viewDirection(0.0,0.0,1.0);
double viewDistance = 2.0*bs.radius();
osg::Vec3d center = bs.center();
osg::Vec3d eyePoint = center+viewDirection*viewDistance;
double znear = viewDistance-bs.radius();
double zfar = viewDistance+bs.radius();
float top = bs.radius();
float right = top;
camera->setProjectionMatrixAsOrtho(-right,right,-top,top,znear,zfar);
camera->setViewMatrixAsLookAt(eyePoint,center,upDirection);
}
// compute the matrix which takes a vertex from local coords into tex coords
// will use this later to specify osg::TexGen..
osg::Matrix MVP = camera->getViewMatrix() *
camera->getProjectionMatrix();
osg::Matrix MVPT = MVP *
osg::Matrix::translate(1.0,1.0,1.0) *
osg::Matrix::scale(0.5,0.5,0.5);
overlayData._texgenNode->getTexGen()->setMode(osg::TexGen::EYE_LINEAR);
overlayData._texgenNode->getTexGen()->setPlanesFromMatrix(MVPT);
overlayData._textureFrustum.setToUnitFrustum(false,false);
overlayData._textureFrustum.transformProvidingInverse(MVP);
}
_updateCamera = false;
}
return;
}
if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)
{
Group::traverse(nv);
return;
}
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
if (!cv)
{
Group::traverse(nv);
return;
}
unsigned int contextID = cv->getState()!=0 ? cv->getState()->getContextID() : 0;
// if we need to redraw then do cull traversal on camera.
if (!_textureObjectValidList[contextID] || _continuousUpdate)
{
camera->setClearColor(_overlayClearColor);
camera->accept(*cv);
_textureObjectValidList[contextID] = 1;
}
// now set up the drawing of the main scene.
{
overlayData._texgenNode->accept(*cv);
const osg::Matrix modelView = *(cv->getModelViewMatrix());
osg::Polytope viewTextureFrustum;
viewTextureFrustum.setAndTransformProvidingInverse(overlayData._textureFrustum, osg::Matrix::inverse(modelView));
cv->getProjectionCullingStack().back().addStateFrustum(overlayData._mainSubgraphStateSet.get(), viewTextureFrustum);
cv->getCurrentCullingSet().addStateFrustum(overlayData._mainSubgraphStateSet.get(), overlayData._textureFrustum);
// push the stateset.
// cv->pushStateSet(_mainSubgraphStateSet.get());
Group::traverse(nv);
// cv->popStateSet();
cv->getCurrentCullingSet().getStateFrustumList().pop_back();
cv->getProjectionCullingStack().back().getStateFrustumList().pop_back();
}
}
void OverlayNode::traverse_VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY(osg::NodeVisitor& nv)
{
// osg::notify(osg::NOTICE)<<"OverlayNode::traverse() - VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY"<<std::endl;
if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR)
{
Group::traverse(nv);
return;
}
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
if (!cv)
{
Group::traverse(nv);
return;
}
OverlayData& overlayData = getOverlayData(cv);
osg::Camera* camera = overlayData._camera.get();
if (_overlaySubgraph.valid())
{
#if 0
if (!overlayData._geode)
{
overlayData._geode = new osg::Geode;
}
#endif
// see if we are within a coordinate system node.
osg::CoordinateSystemNode* csn = 0;
osg::NodePath& nodePath = nv.getNodePath();
for(osg::NodePath::reverse_iterator itr = nodePath.rbegin();
itr != nodePath.rend() && csn==0;
++itr)
{
csn = dynamic_cast<osg::CoordinateSystemNode*>(*itr);
}
osg::EllipsoidModel* em = csn ? csn->getEllipsoidModel() : 0;
osg::BoundingSphere bs = _overlaySubgraph->getBound();
// push the stateset.
cv->pushStateSet(overlayData._mainSubgraphStateSet.get());
Group::traverse(nv);
cv->popStateSet();
osg::Matrix pm = *(cv->getProjectionMatrix());
double znear = cv->getCalculatedNearPlane();
double zfar = cv->getCalculatedFarPlane();
// osg::notify(osg::NOTICE)<<" before znear ="<<znear<<"\t zfar ="<<zfar<<std::endl;
cv->computeNearPlane();
znear = cv->getCalculatedNearPlane();
zfar = cv->getCalculatedFarPlane();
// osg::notify(osg::NOTICE)<<" after znear ="<<znear<<"\t zfar ="<<zfar<<std::endl;
// osg::notify(osg::NOTICE)<<" before clamp pm="<<pm<<std::endl;
cv->clampProjectionMatrixImplementation(pm, znear,zfar);
// osg::notify(osg::NOTICE)<<" after clamp pm="<<pm<<std::endl;
osg::Matrix MVP = *(cv->getModelViewMatrix()) * pm;
osg::Matrix inverseMVP;
inverseMVP.invert(MVP);
// create polytope for the view frustum in local coords
CustomPolytope frustum;
#if 0
frustum.setToUnitFrustum(false, false);
#else
frustum.setToUnitFrustum(true, true);
#endif
frustum.transform(inverseMVP, MVP);
// create polytope for the overlay subgraph in local coords
CustomPolytope overlayPolytope;
// get the bounds of the model.
osg::ComputeBoundsVisitor cbbv(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
_overlaySubgraph->accept(cbbv);
overlayPolytope.setToBoundingBox(cbbv.getBoundingBox());
if (em)
{
overlayPolytope.insertVertex(osg::Vec3d(0.0,0.0,0.0), em, _overlayBaseHeight);
}
else
{
overlayPolytope.projectDowntoBase(osg::Vec3d(0.0,0.0,_overlayBaseHeight), osg::Vec3d(0.0,0.0,1.0));
}
if (overlayData._geode.valid() && overlayData._geode->getNumDrawables()>100)
{
overlayData._geode->removeDrawables(0, 3);
}
if (overlayData._geode.valid())
{
overlayData._geode->addDrawable(overlayPolytope.createDrawable(osg::Vec4d(1.0f,1.0f,0.0f,1.0f)));
overlayData._geode->addDrawable(frustum.createDrawable(osg::Vec4d(0.0f,0.0f,1.0f,1.0f)));
}
// osg::notify(osg::NOTICE)<<"AFTER CUT corners = "<<corners.size()<<std::endl;
osg::Vec3d center = _overlaySubgraph->getBound().center();
osg::Vec3d lookVector(0.0,0.0,-1.0);
if (em)
{
lookVector = -center;
lookVector.normalize();
}
osg::Vec3d sideVectorLV = lookVector ^ cv->getLookVectorLocal();
osg::Vec3d sideVectorUP = lookVector ^ cv->getUpLocal();
osg::Vec3d sideVector = sideVectorLV.length() > sideVectorUP.length() ?
sideVectorLV :
sideVectorUP;
sideVector.normalize();
osg::Vec3d upVector = sideVector ^ lookVector;
upVector.normalize();
osg::Vec3d overlayLookVector = upVector ^ sideVector;
overlayLookVector.normalize();
overlayPolytope.cut(frustum);
CustomPolytope::Vertices corners;
#if 0
overlayPolytope.getPoints(corners);
#else
overlayPolytope.computeSilhoette(lookVector, corners);
#endif
if (corners.empty())
{
camera->setClearColor(_overlayClearColor);
//camera->setStateSet(overlayData._overlayStateSet.get());
camera->accept(*cv);
return;
}
if (overlayData._geode.valid())
{
overlayData._geode->addDrawable(overlayPolytope.createDrawable(osg::Vec4d(1.0f,1.0f,1.0f,1.0f)));
}
// osg::notify(osg::NOTICE)<<" lookVector ="<<lookVector<<std::endl;
double min_side = DBL_MAX;
double max_side = -DBL_MAX;
double min_up = DBL_MAX;
double max_up = -DBL_MAX;
double min_distanceEye = DBL_MAX;
double max_distanceEye = -DBL_MAX;
double zNear = -bs.radius();
double zFar = bs.radius();
typedef std::vector<osg::Vec2d> ProjectedVertices;
ProjectedVertices projectedVertices;
osg::Vec3 eyeLocal = cv->getEyeLocal();
// computed the expected near/far ratio
unsigned int i;
for(i=0; i< corners.size(); ++i)
{
double distanceEye = (corners[i] - eyeLocal).length();
if (distanceEye < min_distanceEye) min_distanceEye = distanceEye;
if (distanceEye > max_distanceEye) max_distanceEye = distanceEye;
osg::Vec3d delta = corners[i] - center;
double distance_side = delta * sideVector;
double distance_up = delta * upVector;
projectedVertices.push_back(osg::Vec2d(distance_side, distance_up));
if (distance_side<min_side)
{
min_side = distance_side;
}
if (distance_side>max_side)
{
max_side = distance_side;
}
if (distance_up<min_up) min_up = distance_up;
if (distance_up>max_up) max_up = distance_up;
}
double mid_side = (min_side + max_side) * 0.5;
double ratio = min_distanceEye / max_distanceEye;
bool usePerspectiveShaders = (_overlayTechnique==VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY);
if (usePerspectiveShaders)
{
// osg::notify(osg::NOTICE)<<"ratio = "<<ratio<<std::endl;
// double original_width = max_side-min_side;
double minRatio = 0.02;
if (ratio<minRatio) ratio = minRatio;
double base_up = min_up - (max_up - min_up) * ratio / (1.0 - ratio);
double max_side_over_up = 0.0;
for(i=0; i< projectedVertices.size(); ++i)
{
double delta_side = fabs(projectedVertices[i].x() - mid_side);
double delta_up = projectedVertices[i].y() - base_up;
double side_over_up = delta_side / delta_up;
if (side_over_up > max_side_over_up) max_side_over_up = side_over_up;
}
osg::Vec3d v000 = osg::Vec3d(-1.0, -1.0, -1.0) * inverseMVP;
osg::Vec3d v010 = osg::Vec3d(-1.0, 1.0, -1.0) * inverseMVP;
osg::Vec3d v100 = osg::Vec3d(1.0, -1.0, -1.0) * inverseMVP;
osg::Vec3d v110 = osg::Vec3d(1.0, 1.0, -1.0) * inverseMVP;
osg::Vec3d v001 = osg::Vec3d(-1.0, -1.0, 1.0) * inverseMVP;
osg::Vec3d v011 = osg::Vec3d(-1.0, 1.0, 1.0) * inverseMVP;
osg::Vec3d v101 = osg::Vec3d(1.0, -1.0, 1.0) * inverseMVP;
osg::Vec3d v111 = osg::Vec3d(1.0, 1.0, 1.0) * inverseMVP;
osg::Vec3d edgeBottomLeft = v001-v000;
osg::Vec3d edgeBottomRight = v101-v100;
osg::Vec3d edgeTopRight = v111-v110;
osg::Vec3d edgeTopLeft = v011-v010;
edgeBottomLeft.normalize();
edgeBottomRight.normalize();
edgeTopLeft.normalize();
edgeTopRight.normalize();
double frustumDiagonal = osg::RadiansToDegrees(acos(edgeBottomLeft * edgeBottomRight));
//osg::notify(osg::NOTICE)<<" width ratio = "<<new_width/original_width<<std::endl;
//osg::notify(osg::NOTICE)<<" near ratio = "<<ratio * new_width/original_width<<std::endl;
double angle = 2.0*osg::RadiansToDegrees(atan(max_side_over_up));
if (angle > frustumDiagonal)
{
double maxHalfAngle = osg::DegreesToRadians(30.0);
// move ratio back
max_side_over_up = tan(maxHalfAngle);
double lowest_up = min_up;
for(i=0; i< projectedVertices.size(); ++i)
{
double delta_side = fabs(projectedVertices[i].x() - mid_side);
double delta_up = delta_side / max_side_over_up;
double local_base_up = projectedVertices[i].y() - delta_up;
if (local_base_up < lowest_up)
{
lowest_up = local_base_up;
// double side_over_up = delta_side / delta_up;
}
}
double new_ratio = (min_up-lowest_up)/(max_up-lowest_up);
//osg::notify(osg::NOTICE)<<" originalRatio = "<<ratio<<" new_ratio="<<new_ratio<<std::endl;
if (new_ratio > ratio) ratio = new_ratio;
base_up = lowest_up;
}
double max_half_width = max_side_over_up*(max_up - base_up);
min_side = mid_side - max_half_width;
max_side = mid_side + max_half_width;
// double new_width = max_side-min_side;
double y0 = (1.0 + ratio) / (1.0 - ratio);
overlayData._y0->set(static_cast<float>(y0));
// osg::notify(osg::NOTICE)<<"y0 = "<<y0<<std::endl;
overlayData._mainSubgraphStateSet->setAttribute(overlayData._mainSubgraphProgram.get());
osg::Matrixd projection;
projection.makeOrtho(min_side,max_side,min_up,max_up,zNear ,zFar);
camera->setProjectionMatrix(projection);
}
else
{
overlayData._mainSubgraphStateSet->removeAttribute(osg::StateAttribute::PROGRAM);
camera->setProjectionMatrixAsOrtho(min_side, max_side, min_up, max_up, zNear ,zFar);
}
#if 0
double width = max_side-min_side;
double height = max_up-min_up;
double area = width*height;
osg::notify(osg::NOTICE)<<"width = "<<width<<"\t height = "<<height<<"\t area = "<<area<<std::endl;
osg::notify(osg::NOTICE)<<"a min_side = "<<min_side<<std::endl;
osg::notify(osg::NOTICE)<<"a max_side = "<<max_side<<std::endl;
osg::notify(osg::NOTICE)<<"a min_up = "<<min_up<<std::endl;
osg::notify(osg::NOTICE)<<"a max_up = "<<max_up<<std::endl;
#endif
if (em)
{
camera->setViewMatrixAsLookAt(bs.center(), osg::Vec3d(0.0f,0.0f,0.0f), upVector);
}
else
{
camera->setViewMatrixAsLookAt(bs.center(), bs.center()+overlayLookVector, upVector);
}
// compute the matrix which takes a vertex from local coords into tex coords
// will use this later to specify osg::TexGen..
MVP = camera->getViewMatrix() * camera->getProjectionMatrix();
osg::Matrix MVPT = MVP *
osg::Matrix::translate(1.0,1.0,1.0) *
osg::Matrix::scale(0.5,0.5,0.5);
//overlayData._texgenNode->setReferenceFrame(osg::TexGenNode::ABSOLUTE_RF);
overlayData._texgenNode->getTexGen()->setMode(osg::TexGen::EYE_LINEAR);
overlayData._texgenNode->getTexGen()->setPlanesFromMatrix(MVPT);
overlayData._textureFrustum.setToUnitFrustum(false,false);
overlayData._textureFrustum.transformProvidingInverse(MVP);
// osg::notify(osg::NOTICE)<<std::endl;
unsigned int contextID = cv->getState()!=0 ? cv->getState()->getContextID() : 0;
if (usePerspectiveShaders)
{
cv->pushStateSet(overlayData._overlayStateSet.get());
typedef std::list<const osg::StateSet*> StateSetStack;
StateSetStack statesetStack;
osgUtil::StateGraph* sg = cv->getCurrentStateGraph();
while(sg)
{
const osg::StateSet* stateset = sg->getStateSet();
if (stateset)
{
statesetStack.push_front(stateset);
}
sg = sg->_parent;
}
osg::StateAttribute::GLModeValue base_mode = osg::StateAttribute::ON;
for(StateSetStack::iterator itr = statesetStack.begin();
itr != statesetStack.end();
++itr)
{
osg::StateAttribute::GLModeValue mode = (*itr)->getMode(GL_LIGHTING);
if ((mode & ~osg::StateAttribute::INHERIT)!=0)
{
if ((mode & osg::StateAttribute::PROTECTED)!=0 ||
(base_mode & osg::StateAttribute::OVERRIDE)==0)
{
base_mode = mode;
}
}
}
overlayData._lightingEnabled->set((base_mode & osg::StateAttribute::ON)!=0);
}
// if we need to redraw then do cull traversal on camera.
camera->setClearColor(_overlayClearColor);
//camera->setStateSet(overlayData._overlayStateSet.get());
camera->accept(*cv);
if (usePerspectiveShaders) cv->popStateSet();
_textureObjectValidList[contextID] = 1;
overlayData._texgenNode->accept(*cv);
if (overlayData._geode.valid())
{
overlayData._geode->accept(*cv);
}
}
else
{
Group::traverse(nv);
}
// osg::notify(osg::NOTICE)<<" "<<&overlayData<<std::endl;
}
void OverlayNode::traverse_VIEW_DEPENDENT_WITH_PERSPECTIVE_OVERLAY(osg::NodeVisitor& nv)
{
traverse_VIEW_DEPENDENT_WITH_ORTHOGRAPHIC_OVERLAY(nv);
}
void OverlayNode::setOverlaySubgraph(osg::Node* node)
{
if (_overlaySubgraph == node) return;
_overlaySubgraph = node;
for(OverlayDataMap::iterator itr = _overlayDataMap.begin();
itr != _overlayDataMap.end();
++itr)
{
osg::Camera* camera = itr->second._camera.get();
if (camera)
{
camera->removeChildren(0, camera->getNumChildren());
camera->addChild(node);
}
}
dirtyOverlayTexture();
}
void OverlayNode::dirtyOverlayTexture()
{
_textureObjectValidList.setAllElementsTo(0);
_updateCamera = true;
}
void OverlayNode::setTexEnvMode(GLenum mode)
{
_texEnvMode = mode;
updateMainSubgraphStateSet();
}
void OverlayNode::setOverlayTextureUnit(unsigned int unit)
{
_textureUnit = unit;
updateMainSubgraphStateSet();
}
void OverlayNode::setOverlayTextureSizeHint(unsigned int size)
{
if (_textureSizeHint == size) return;
_textureSizeHint = size;
for(OverlayDataMap::iterator itr = _overlayDataMap.begin();
itr != _overlayDataMap.end();
++itr)
{
if (itr->second._texture.valid()) itr->second._texture->setTextureSize(_textureSizeHint, _textureSizeHint);
if (itr->second._camera.valid()) itr->second._camera->setViewport(0,0,_textureSizeHint,_textureSizeHint);
}
//_texture->dirtyTextureObject();
}
void OverlayNode::updateMainSubgraphStateSet()
{
osg::notify(osg::INFO)<<"OverlayNode::updateMainSubgraphStateSet()"<<std::endl;
for(OverlayDataMap::iterator itr = _overlayDataMap.begin();
itr != _overlayDataMap.end();
++itr)
{
osg::TexGenNode* texgenNode = itr->second._texgenNode.get();
if (texgenNode) texgenNode->setTextureUnit(_textureUnit);
osg::StateSet* mainSubgraphStateSet = itr->second._mainSubgraphStateSet.get();
if (mainSubgraphStateSet)
{
mainSubgraphStateSet->clear();
mainSubgraphStateSet->setTextureAttributeAndModes(_textureUnit, itr->second._texture.get(), osg::StateAttribute::ON);
mainSubgraphStateSet->setTextureMode(_textureUnit, GL_TEXTURE_GEN_S, osg::StateAttribute::ON);
mainSubgraphStateSet->setTextureMode(_textureUnit, GL_TEXTURE_GEN_T, osg::StateAttribute::ON);
mainSubgraphStateSet->setTextureMode(_textureUnit, GL_TEXTURE_GEN_R, osg::StateAttribute::ON);
mainSubgraphStateSet->setTextureMode(_textureUnit, GL_TEXTURE_GEN_Q, osg::StateAttribute::ON);
if (_texEnvMode!=GL_NONE)
{
mainSubgraphStateSet->setTextureAttribute(_textureUnit, new osg::TexEnv((osg::TexEnv::Mode)_texEnvMode));
}
}
}
}