OpenSceneGraph/src/osgUtil/SceneView.cpp

395 lines
12 KiB
C++
Raw Normal View History

#include <osgUtil/SceneView>
2001-09-22 10:42:08 +08:00
#include <osgUtil/AppVisitor>
#include <osgUtil/DisplayListVisitor>
#include <osg/Notify>
#include <osg/Texture>
#include <osg/AlphaFunc>
#include <osg/TexEnv>
#include <osg/GLU>
2001-01-11 00:32:10 +08:00
using namespace osg;
using namespace osgUtil;
SceneView::SceneView()
{
_calc_nearfar = true;
_backgroundColor.set(0.2f, 0.2f, 0.4f, 1.0f);
_near_plane = 1.0f;
_far_plane = 1.0f;
2001-01-11 00:32:10 +08:00
_lodBias = 1.0f;
_lightingMode=HEADLIGHT;
_prioritizeTextures = false;
2001-09-22 10:42:08 +08:00
_viewport = new Viewport;
_initCalled = false;
2001-01-11 00:32:10 +08:00
}
2001-01-11 00:32:10 +08:00
SceneView::~SceneView()
{
}
2001-01-11 00:32:10 +08:00
void SceneView::setDefaults()
{
_globalState = new osg::StateSet;
2001-01-11 00:32:10 +08:00
_lightingMode=HEADLIGHT;
_light = new osg::Light;
_light->setAmbient(Vec4(0.00f,0.0f,0.00f,1.0f));
_light->setDiffuse(Vec4(0.8f,0.8f,0.8f,1.0f));
_light->setSpecular(Vec4(1.0f,1.0f,1.0f,1.0f));
2001-01-11 00:32:10 +08:00
_state = new State;
_camera = new Camera;
_stereoMode = MONO;
_leftEye.set(-0.03f,0.0f,0.0f);
_rightEye.set(0.03f,0.0f,0.0f);
_focalLength = 1.0f;
_screenDistance = 1.0f;
_rendergraph = new RenderGraph;
_renderStage = new RenderStage;
2001-09-22 10:42:08 +08:00
DisplayListVisitor* dlv = new DisplayListVisitor();
dlv->setState(_state.get());
_initVisitor = dlv;
_appVisitor = new AppVisitor;
_cullVisitor = new CullVisitor;
_cullVisitor->setRenderGraph(_rendergraph.get());
_cullVisitor->setRenderStage(_renderStage.get());
2001-01-11 00:32:10 +08:00
_globalState->setGlobalDefaults();
// enable lighting by default.
_globalState->setMode(GL_LIGHTING, osg::StateAttribute::ON);
// enable depth testing by default.
_globalState->setMode(GL_DEPTH_TEST, osg::StateAttribute::ON);
// set up an alphafunc by default to speed up blending operations.
osg::AlphaFunc* alphafunc = new osg::AlphaFunc();
alphafunc->setFunction(osg::AlphaFunc::GREATER,0.0f);
_globalState->setAttributeAndModes(alphafunc, osg::StateAttribute::ON);
// set up an alphafunc by default to speed up blending operations.
osg::TexEnv* texenv = new osg::TexEnv();
texenv->setMode(osg::TexEnv::MODULATE);
_globalState->setAttributeAndModes(texenv, osg::StateAttribute::ON);
2001-01-11 00:32:10 +08:00
_backgroundColor.set(0.2f, 0.2f, 0.4f, 1.0f);
}
void SceneView::init()
{
_initCalled = true;
if (_sceneData.valid() && _initVisitor.valid())
{
_initVisitor->reset();
_initVisitor->setFrameStamp(_frameStamp.get());
if (_frameStamp.valid())
{
_initVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
}
_sceneData->accept(*_initVisitor.get());
}
}
2001-01-11 00:32:10 +08:00
void SceneView::app()
2001-01-11 00:32:10 +08:00
{
if (!_initCalled) init();
if (_sceneData.valid() && _appVisitor.valid())
{
_appVisitor->reset();
2001-09-22 10:42:08 +08:00
_appVisitor->setFrameStamp(_frameStamp.get());
// use the frame number for the traversal number.
if (_frameStamp.valid())
{
_appVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
}
_sceneData->accept(*_appVisitor.get());
2001-01-11 00:32:10 +08:00
}
2001-09-22 10:42:08 +08:00
2001-01-11 00:32:10 +08:00
}
void SceneView::cull()
2001-01-11 00:32:10 +08:00
{
2001-01-11 00:32:10 +08:00
if (!_sceneData) return;
if (!_initCalled) init();
2001-09-22 10:42:08 +08:00
_camera->adjustAspectRatio(_viewport->aspectRatio());
_rendergraph->clean();
_cullVisitor->reset();
2001-01-11 00:32:10 +08:00
2001-09-22 10:42:08 +08:00
_cullVisitor->setFrameStamp(_frameStamp.get());
// use the frame number for the traversal number.
if (_frameStamp.valid())
{
_cullVisitor->setTraversalNumber(_frameStamp->getFrameNumber());
}
// comment out reset of rendergraph since clean is more efficient.
// _rendergraph->reset();
2001-01-11 00:32:10 +08:00
// use clean of the rendergraph rather than reset, as it is able to
// reuse the structure on the rendergraph in the next frame. This
// achieves a certain amount of frame cohereancy of memory allocation.
2001-01-11 00:32:10 +08:00
_cullVisitor->setLODBias(_lodBias);
_cullVisitor->setCamera(*_camera);
2001-09-22 10:42:08 +08:00
_cullVisitor->setViewport(_viewport.get());
_cullVisitor->setEarthSky(NULL); // reset earth sky on each frame.
2001-01-11 00:32:10 +08:00
_renderStage->reset();
2001-01-11 00:32:10 +08:00
2001-09-22 10:42:08 +08:00
_renderStage->setViewport(_viewport.get());
_renderStage->setCamera(_camera.get());
_renderStage->setClearColor(_backgroundColor);
_renderStage->setLight(_light.get());
2001-01-11 00:32:10 +08:00
switch(_lightingMode)
2001-01-11 00:32:10 +08:00
{
case(HEADLIGHT):
_renderStage->setLightingMode(RenderStageLighting::HEADLIGHT);
break;
case(SKY_LIGHT):
_renderStage->setLightingMode(RenderStageLighting::SKY_LIGHT);
break;
case(NO_SCENEVIEW_LIGHT):
_renderStage->setLightingMode(RenderStageLighting::NO_SCENEVIEW_LIGHT);
break;
}
2001-01-11 00:32:10 +08:00
if (_globalState.valid()) _cullVisitor->pushStateSet(_globalState.get());
// traverse the scene graph to generate the rendergraph.
_sceneData->accept(*_cullVisitor);
if (_globalState.valid()) _cullVisitor->popStateSet();
2001-01-11 00:32:10 +08:00
// do any state sorting required.
_renderStage->sort();
2001-01-11 00:32:10 +08:00
const osg::EarthSky* earthSky = _cullVisitor->getEarthSky();
if (earthSky)
{
if (earthSky->getRequiresClear())
{
_renderStage->setClearColor(earthSky->getClearColor());
_renderStage->setClearMask(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
// really should set clear mask here, but what to? Need
// to consider the stencil and accumulation buffers..
// will defer to later. Robert Osfield. October 2001.
}
else
{
// we have an earth sky implementation to do the work for use
// so we don't need to clear.
_renderStage->setClearMask(0);
}
}
// prune out any empty RenderGraph children.
// note, this would be not required if the _renderGraph had been
// reset at the start of each frame (see top of this method) but
// a clean has been used instead to try to minimize the amount of
// allocation and deleteing of the RenderGraph nodes.
_rendergraph->prune();
if (_calc_nearfar)
{
_near_plane = _cullVisitor->getCalculatedNearPlane();
_far_plane = _cullVisitor->getCalculatedFarPlane();
if (_near_plane<=_far_plane)
{
// shift the far plane slight further away from the eye point.
// and shift the near plane slightly near the eye point, this
// will give a little space betwenn the near and far planes
// and the model, crucial when the naer and far planes are
// coincedent.
_far_plane *= 1.05;
_near_plane *= 0.95;
// if required clamp the near plane to prevent negative or near zero
// near planes.
float min_near_plane = _far_plane*0.0005f;
if (_near_plane<min_near_plane) _near_plane=min_near_plane;
}
else
{
_near_plane = 1.0f;
_far_plane = 1000.0f;
}
_camera->setNearFar(_near_plane,_far_plane);
}
2001-01-11 00:32:10 +08:00
}
2001-01-11 00:32:10 +08:00
void SceneView::draw()
{
if (!_sceneData) return;
2001-01-11 00:32:10 +08:00
if (!_state)
{
osg::notify(osg::WARN) << "Warning: no valid osgUtil::SceneView::_state"<<endl;
osg::notify(osg::WARN) << " creating a state automatically."<<endl;
2001-01-11 00:32:10 +08:00
// note the constructor for osg::State will set ContextID to 0.
_state = new osg::State;
}
// we in theory should be able to
_state->reset();
2001-09-22 10:42:08 +08:00
_state->setFrameStamp(_frameStamp.get());
// note, to support multi-pipe systems the deletion of OpenGL display list
// and texture objects is deferred until the OpenGL context is the correct
// context for when the object were originally created. Here we know what
// context we are in so can flush the appropriate caches.
osg::Drawable::flushDeletedDisplayLists(_state->getContextID());
osg::Texture::flushDeletedTextureObjects(_state->getContextID());
RenderLeaf* previous = NULL;
switch(getStereoMode())
{
case(MONO):
{
_renderStage->draw(*_state,previous);
}
break;
case(QUAD_BUFFER_STEREO):
{
osg::ref_ptr<osg::Camera> left_camera = new osg::Camera(*_camera);
osg::ref_ptr<osg::Camera> right_camera = new osg::Camera(*_camera);
float iod = 0.05f;
left_camera->adjustEyeOffsetForStereo(osg::Vec3(-iod*0.5,0.0f,0.0f),_screenDistance);
right_camera->adjustEyeOffsetForStereo(osg::Vec3(iod*0.5,0.0f,0.0f),_screenDistance);
glDrawBuffer(GL_BACK_LEFT);
_renderStage->setCamera(left_camera.get());
_renderStage->draw(*_state,previous);
glDrawBuffer(GL_BACK_RIGHT);
_renderStage->setCamera(right_camera.get());
_renderStage->_stageDrawnThisFrame = false;
_renderStage->draw(*_state,previous);
}
break;
case(ANAGLYPHIC_STEREO):
{
osg::ref_ptr<osg::Camera> left_camera = new osg::Camera(*_camera);
osg::ref_ptr<osg::Camera> right_camera = new osg::Camera(*_camera);
float iod = 0.05f;
left_camera->adjustEyeOffsetForStereo(osg::Vec3(-iod*0.5,0.0f,0.0f),_screenDistance);
right_camera->adjustEyeOffsetForStereo(osg::Vec3(iod*0.5,0.0f,0.0f),_screenDistance);
osg::ColorMask* red = new osg::ColorMask;
osg::ColorMask* green = new osg::ColorMask;
red->setMask(true,false,false,true);
_renderStage->setColorMask(red);
_renderStage->setCamera(left_camera.get());
_renderStage->draw(*_state,previous);
green->setMask(false,true,true,true);
_renderStage->setColorMask(green);
_renderStage->_stageDrawnThisFrame = false;
_renderStage->setCamera(right_camera.get());
_renderStage->draw(*_state,previous);
}
break;
default:
{
osg::notify(osg::NOTICE)<<"Warning: stereo camera mode not implemented yet."<<endl;
_renderStage->draw(*_state,previous);
}
break;
}
GLenum errorNo = glGetError();
if (errorNo!=GL_NO_ERROR)
{
osg::notify(WARN)<<"Warning: detected OpenGL error '"<<gluErrorString(errorNo)<<"'"<<endl;
}
2001-01-11 00:32:10 +08:00
}
2001-01-11 00:32:10 +08:00
/** Calculate, via glUnProject, the object coordinates of a window point.
Note, current implementation requires that SceneView::draw() has been previously called
for projectWindowIntoObject to produce valid values. As per OpenGL
windows coordinates are calculated relative to the bottom left of the window.*/
bool SceneView::projectWindowIntoObject(const osg::Vec3& window,osg::Vec3& object) const
{
2001-09-22 10:42:08 +08:00
return _camera->unproject(window,*_viewport,object);
2001-01-11 00:32:10 +08:00
}
2001-01-11 00:32:10 +08:00
/** Calculate, via glUnProject, the object coordinates of a window x,y
when projected onto the near and far planes.
Note, current implementation requires that SceneView::draw() has been previously called
for projectWindowIntoObject to produce valid values. As per OpenGL
windows coordinates are calculated relative to the bottom left of the window.*/
bool SceneView::projectWindowXYIntoObject(int x,int y,osg::Vec3& near_point,osg::Vec3& far_point) const
{
2001-09-22 10:42:08 +08:00
bool result_near = _camera->unproject(Vec3(x,y,0.0f),*_viewport,near_point);
bool result_far = _camera->unproject(Vec3(x,y,1.0f),*_viewport,far_point);
return result_near & result_far;
2001-01-11 00:32:10 +08:00
}
2001-01-11 00:32:10 +08:00
/** Calculate, via glProject, the object coordinates of a window.
Note, current implementation requires that SceneView::draw() has been previously called
for projectWindowIntoObject to produce valid values. As per OpenGL
windows coordinates are calculated relative to the bottom left of the window.*/
bool SceneView::projectObjectIntoWindow(const osg::Vec3& object,osg::Vec3& window) const
{
2001-09-22 10:42:08 +08:00
return _camera->project(object,*_viewport,window);
2001-01-11 00:32:10 +08:00
}