From 4208cf8226798a7d5c40060f2cc5c8e3349487c4 Mon Sep 17 00:00:00 2001 From: timoore Date: Fri, 4 Jan 2008 07:35:43 +0000 Subject: [PATCH] Add ViewPartionNode to the scenegraph ViewPartitionNode addresses Z-fighting issues by rendering near and far parts of the scene seperately. --- src/Main/Makefile.am | 1 + src/Main/ViewPartitionNode.cxx | 236 +++++++++++++++++++++++++++++++++ src/Main/ViewPartitionNode.hxx | 60 +++++++++ src/Main/renderer.cxx | 28 ++-- 4 files changed, 315 insertions(+), 10 deletions(-) create mode 100644 src/Main/ViewPartitionNode.cxx create mode 100644 src/Main/ViewPartitionNode.hxx diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index 636dbadcb..8e26d217a 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -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 diff --git a/src/Main/ViewPartitionNode.cxx b/src/Main/ViewPartitionNode.cxx new file mode 100644 index 000000000..506576def --- /dev/null +++ b/src/Main/ViewPartitionNode.cxx @@ -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 +#include +#include +#include +// For DotOsgWrapperProxy +#include +#include +#include + +#include +#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(copyop(rhs.cameras[i].get())); +} + +void ViewPartitionNode::traverse(NodeVisitor& nv) +{ + osgUtil::CullVisitor* cv = dynamic_cast(&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 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(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(obj); + fw.indent() << "visibility " << node.getVisibility() << std::endl; + return true; +} + +osgDB::RegisterDotOsgWrapperProxy viewPartitionNodeProxy +( + new ViewPartitionNode, + "ViewPartitionNode", + "Object Node ViewPartitionNode Group", + &ViewPartitionNode_readLocalData, + &ViewPartitionNode_writeLocalData + ); +} diff --git a/src/Main/ViewPartitionNode.hxx b/src/Main/ViewPartitionNode.hxx new file mode 100644 index 000000000..9e3d52492 --- /dev/null +++ b/src/Main/ViewPartitionNode.hxx @@ -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 +#include +#include + +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 > CameraList; + CameraList cameras; + enum CameraNum { + BACKGROUND_CAMERA = 0, + FAR_CAMERA = 1, + NEAR_CAMERA = 2 + }; + double visibility; + static const double nearCameraFar; +}; +#endif diff --git a/src/Main/renderer.cxx b/src/Main/renderer.cxx index faada3757..ab119852d 100644 --- a/src/Main/renderer.cxx +++ b/src/Main/renderer.cxx @@ -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 mRealRoot = new osg::Group; static osg::ref_ptr mRoot = new osg::Group; -static osg::ref_ptr mBackGroundCamera = new osg::Camera; +static osg::ref_ptr 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); } -- 2.39.5