1
0
Fork 0

Add ViewPartionNode to the scenegraph

ViewPartitionNode addresses Z-fighting issues by rendering near and far
parts of the scene seperately.
This commit is contained in:
timoore 2008-01-04 07:35:43 +00:00
parent 9f9d2f934a
commit 4208cf8226
4 changed files with 315 additions and 10 deletions

View file

@ -67,6 +67,7 @@ libMain_a_SOURCES = \
viewer.cxx viewer.hxx \
viewmgr.cxx viewmgr.hxx \
FGManipulator.cxx FGManipulator.hxx \
ViewPartitionNode.cxx ViewPartitionNode.hxx \
$(GFX_CODE)
fgfs_SOURCES = bootstrap.cxx

View file

@ -0,0 +1,236 @@
// Copyright (C) 2008 Tim Moore
//
// Much of this file was copied directly from the osgdepthpartition
// example in the OpenSceneGraph distribution, so I feel it is right
// to preserve the license in that code:
/*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <osg/CullSettings>
#include <osg/Depth>
#include <osg/StateSet>
#include <osgUtil/CullVisitor>
// For DotOsgWrapperProxy
#include <osgDB/Registry>
#include <osgDB/Input>
#include <osgDB/Output>
#include <simgear/scene/util/RenderConstants.hxx>
#include "ViewPartitionNode.hxx"
using namespace osg;
const double ViewPartitionNode::nearCameraFar = 100.0;
ViewPartitionNode::ViewPartitionNode():
cameras(3), visibility(40000.0)
{
const GLbitfield inheritanceMask = (CullSettings::ALL_VARIABLES
& ~CullSettings::COMPUTE_NEAR_FAR_MODE
& ~CullSettings::NEAR_FAR_RATIO
& ~CullSettings::CULLING_MODE);
int i = 1;
for (CameraList::iterator iter = cameras.begin();
iter != cameras.end();
++iter, ++i) {
Camera* camera = new Camera;
camera->setReferenceFrame(Transform::RELATIVE_RF);
camera->setInheritanceMask(inheritanceMask);
camera->setComputeNearFarMode(CullSettings::DO_NOT_COMPUTE_NEAR_FAR);
camera->setCullingMode(CullSettings::VIEW_FRUSTUM_CULLING);
camera->setRenderOrder(Camera::POST_RENDER, i);
*iter = camera;
}
Camera* backgroundCamera = cameras[BACKGROUND_CAMERA].get();
backgroundCamera->setClearMask(GL_DEPTH_BUFFER_BIT);
// Turn off depth test for the background
Depth* depth = new Depth(Depth::LESS, 0.0, 1.0, false);
StateSet* backgroundSS = new StateSet;
backgroundSS->setAttribute(depth);
backgroundSS->setMode(GL_DEPTH_TEST, StateAttribute::OFF);
backgroundCamera->setStateSet(backgroundSS);
backgroundCamera->setInheritanceMask(backgroundCamera->getInheritanceMask()
& ~CullSettings::CULL_MASK);
backgroundCamera->setCullMask(simgear::BACKGROUND_BIT);
cameras[NEAR_CAMERA]->setClearMask(GL_DEPTH_BUFFER_BIT);
// Background camera will have cleared the buffers and doesn't
// touch the depth buffer
cameras[FAR_CAMERA]->setClearMask(0);
}
ViewPartitionNode::ViewPartitionNode(const ViewPartitionNode& rhs,
const CopyOp& copyop):
cameras(3), visibility(rhs.visibility)
{
for (int i = 0; i < 3; i++)
cameras[i] = static_cast<Camera*>(copyop(rhs.cameras[i].get()));
}
void ViewPartitionNode::traverse(NodeVisitor& nv)
{
osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(&nv);
if(!cv) {
Group::traverse(nv);
return;
}
RefMatrix& modelview = *(cv->getModelViewMatrix());
RefMatrix& projection = *(cv->getProjectionMatrix());
double left, right, bottom, top, parentNear, parentFar;
projection.getFrustum(left, right, bottom, top, parentNear, parentFar);
double farCameraFar = visibility + 1000.0;
double nearPlanes[3], farPlanes[3];
nearPlanes[0] = farCameraFar; farPlanes[0] = parentFar;
nearPlanes[2] = parentNear;
// If visibility is low, only use two cameras
if (farCameraFar < nearCameraFar) {
nearPlanes[1] = farPlanes[1] = 0.0;
farPlanes[2] = farCameraFar;
} else {
nearPlanes[1] = farPlanes[2] = nearCameraFar;
farPlanes[1] = farCameraFar;
farPlanes[2] = nearCameraFar;
}
for (int i = 0; i < 3; ++i) {
if (farPlanes[i] >0.0) {
ref_ptr<RefMatrix> newProj = new RefMatrix();
makeNewProjMat(projection, nearPlanes[i], farPlanes[i],
*newProj.get());
cv->pushProjectionMatrix(newProj.get());
cameras[i]->accept(nv);
cv->popProjectionMatrix();
}
}
}
bool ViewPartitionNode::addChild(osg::Node *child)
{
return insertChild(_children.size(), child);
}
bool ViewPartitionNode::insertChild(unsigned int index, osg::Node *child)
{
if(!Group::insertChild(index, child))
return false;
// Insert child into each Camera
for (CameraList::iterator iter = cameras.begin();
iter != cameras.end();
++iter) {
(*iter)->insertChild(index, child);
}
return true;
}
bool ViewPartitionNode::removeChildren(unsigned int pos, unsigned int numRemove)
{
if (!Group::removeChildren(pos, numRemove))
return false;
// Remove child from each Camera
for (CameraList::iterator iter = cameras.begin();
iter != cameras.end();
++iter) {
(*iter)->removeChildren(pos, numRemove);
}
return true;
}
bool ViewPartitionNode::setChild(unsigned int i, osg::Node *node)
{
if(!Group::setChild(i, node)) return false; // Set child
// Set child for each Camera
for (CameraList::iterator iter = cameras.begin();
iter != cameras.end();
++iter) {
(*iter)->setChild(i, node);
}
return true;
}
// Given a projection matrix, return a new one with the same frustum
// sides and new near / far values.
void ViewPartitionNode::makeNewProjMat(Matrixd& oldProj, double znear,
double zfar, Matrixd& projection)
{
projection = oldProj;
// Slightly inflate the near & far planes to avoid objects at the
// extremes being clipped out.
znear *= 0.999;
zfar *= 1.001;
// Clamp the projection matrix z values to the range (near, far)
double epsilon = 1.0e-6;
if (fabs(projection(0,3)) < epsilon &&
fabs(projection(1,3)) < epsilon &&
fabs(projection(2,3)) < epsilon) {
// Projection is Orthographic
epsilon = -1.0/(zfar - znear); // Used as a temp variable
projection(2,2) = 2.0*epsilon;
projection(3,2) = (zfar + znear)*epsilon;
} else {
// Projection is Perspective
double trans_near = (-znear*projection(2,2) + projection(3,2)) /
(-znear*projection(2,3) + projection(3,3));
double trans_far = (-zfar*projection(2,2) + projection(3,2)) /
(-zfar*projection(2,3) + projection(3,3));
double ratio = fabs(2.0/(trans_near - trans_far));
double center = -0.5*(trans_near + trans_far);
projection.postMult(osg::Matrixd(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, ratio, 0.0,
0.0, 0.0, center*ratio, 1.0));
}
}
namespace
{
bool ViewPartitionNode_readLocalData(osg::Object& obj, osgDB::Input& fr)
{
ViewPartitionNode& node = static_cast<ViewPartitionNode&>(obj);
if (fr[0].matchWord("visibility")) {
++fr;
double visibility;
if (fr[0].getFloat(visibility))
++fr;
else
return false;
node.setVisibility(visibility);
}
return true;
}
bool ViewPartitionNode_writeLocalData(const Object& obj, osgDB::Output& fw)
{
const ViewPartitionNode& node = static_cast<const ViewPartitionNode&>(obj);
fw.indent() << "visibility " << node.getVisibility() << std::endl;
return true;
}
osgDB::RegisterDotOsgWrapperProxy viewPartitionNodeProxy
(
new ViewPartitionNode,
"ViewPartitionNode",
"Object Node ViewPartitionNode Group",
&ViewPartitionNode_readLocalData,
&ViewPartitionNode_writeLocalData
);
}

View file

@ -0,0 +1,60 @@
// Copyright (C) 2008 Tim Moore
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program 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 GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef VIEWPARTITIONNODE_HXX
#define VIEWPARTITIONNODE_HXX 1
// The ViewPartitionNode splits the viewing frustum inherited from a
// camera higher in the scene graph (usually the Viewer master or
// slave camera) into 3 parts: from the parent near plane to a
// intermediate far plane (100m), then out to the current visibility,
// and then beyond where background stuff is rendered. The depth
// buffer and depth testing are disabled for the background.
#include <osg/Camera>
#include <osg/Group>
#include <osg/Matrix>
class ViewPartitionNode : public osg::Group
{
public:
ViewPartitionNode();
ViewPartitionNode(const ViewPartitionNode& rhs,
const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
META_Node(flightgear, ViewPartitionNode);
/** Catch child management functions so the Cameras can be informed
of added or removed children. */
virtual bool addChild(osg::Node *child);
virtual bool insertChild(unsigned int index, osg::Node *child);
virtual bool removeChildren(unsigned int pos, unsigned int numRemove = 1);
virtual bool setChild(unsigned int i, osg::Node *node);
virtual void traverse(osg::NodeVisitor &nv);
void setVisibility(double vis) { visibility = vis; }
double getVisibility() const { return visibility; }
protected:
void makeNewProjMat(osg::Matrixd& oldProj, double znear, double zfar,
osg::Matrixd& projection);
typedef std::vector< osg::ref_ptr<osg::Camera> > CameraList;
CameraList cameras;
enum CameraNum {
BACKGROUND_CAMERA = 0,
FAR_CAMERA = 1,
NEAR_CAMERA = 2
};
double visibility;
static const double nearCameraFar;
};
#endif

View file

@ -104,6 +104,7 @@
#include "splash.hxx"
#include "renderer.hxx"
#include "main.hxx"
#include "ViewPartitionNode.hxx"
// XXX Make this go away when OSG 2.2 is released.
#if (FG_OSG_VERSION >= 21004)
@ -392,7 +393,7 @@ static osg::ref_ptr<osg::Group> mRealRoot = new osg::Group;
static osg::ref_ptr<osg::Group> mRoot = new osg::Group;
static osg::ref_ptr<osg::Camera> mBackGroundCamera = new osg::Camera;
static osg::ref_ptr<ViewPartitionNode> viewPartition = new ViewPartitionNode;
FGRenderer::FGRenderer()
{
@ -491,6 +492,7 @@ FGRenderer::init( void ) {
stateSet->setAttribute(hint);
// this is the topmost scenegraph node for osg
#if 0
mBackGroundCamera->addChild(thesky->getPreRoot());
mBackGroundCamera->setClearMask(0);
@ -505,10 +507,12 @@ FGRenderer::init( void ) {
stateSet = mBackGroundCamera->getOrCreateStateSet();
stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
#endif
osg::Group* sceneGroup = new osg::Group;
sceneGroup->addChild(globals->get_scenery()->get_scene_graph());
sceneGroup->addChild(thesky->getCloudRoot());
sceneGroup->setNodeMask(~simgear::BACKGROUND_BIT);
//sceneGroup->addChild(thesky->getCloudRoot());
stateSet = sceneGroup->getOrCreateStateSet();
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
@ -520,11 +524,10 @@ FGRenderer::init( void ) {
// relative because of CameraView being just a clever transform node
lightSource->setReferenceFrame(osg::LightSource::RELATIVE_RF);
lightSource->setLocalStateSetModes(osg::StateAttribute::ON);
mRoot->addChild(lightSource);
lightSource->addChild(mBackGroundCamera.get());
lightSource->addChild(sceneGroup);
lightSource->addChild(thesky->getPreRoot());
mRoot->addChild(lightSource);
stateSet = globals->get_scenery()->get_scene_graph()->getOrCreateStateSet();
stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
@ -553,7 +556,7 @@ FGRenderer::init( void ) {
osg::Camera* guiCamera = new osg::Camera;
guiCamera->setRenderOrder(osg::Camera::POST_RENDER, 100);
guiCamera->setClearMask(0);
inheritanceMask = osg::CullSettings::ALL_VARIABLES;
GLbitfield inheritanceMask = osg::CullSettings::ALL_VARIABLES;
inheritanceMask &= ~osg::CullSettings::COMPUTE_NEAR_FAR_MODE;
inheritanceMask &= ~osg::CullSettings::CULLING_MODE;
guiCamera->setInheritanceMask(inheritanceMask);
@ -567,8 +570,10 @@ FGRenderer::init( void ) {
osg::Switch* sw = new osg::Switch;
sw->setUpdateCallback(new FGScenerySwitchCallback);
sw->addChild(mRoot.get());
viewPartition->addChild(sw);
viewPartition->addChild(thesky->getCloudRoot());
mRealRoot->addChild(sw);
mRealRoot->addChild(viewPartition.get());
mRealRoot->addChild(FGCreateRedoutNode());
mRealRoot->addChild(guiCamera);
}
@ -811,14 +816,17 @@ FGRenderer::update( bool refresh_camera_settings ) {
l->adj_fog_color(),
l->get_sun_angle()*SGD_RADIANS_TO_DEGREES);
mUpdateVisitor->setVisibility(actual_visibility);
viewPartition->setVisibility(actual_visibility);
simgear::GroundLightManager::instance()->update(mUpdateVisitor.get());
bool hotspots = fgGetBool("/sim/panel-hotspots");
osg::Node::NodeMask cullMask = ~simgear::LIGHTS_BITS & ~simgear::PICK_BIT;
osg::Node::NodeMask cullMask = (~simgear::LIGHTS_BITS & ~simgear::PICK_BIT
& ~simgear::BACKGROUND_BIT);
cullMask |= simgear::GroundLightManager::instance()
->getLightNodeMask(mUpdateVisitor.get());
if (hotspots)
cullMask |= simgear::PICK_BIT;
camera->setCullMask(cullMask);
// XXX
for (int i = 0; i < viewer->getNumSlaves(); ++i)
viewer->getSlave(i)._camera->setCullMask(cullMask);
}