]> git.mxchange.org Git - flightgear.git/commitdiff
Add ViewPartionNode to the scenegraph
authortimoore <timoore>
Fri, 4 Jan 2008 07:35:43 +0000 (07:35 +0000)
committertimoore <timoore>
Fri, 4 Jan 2008 07:35:43 +0000 (07:35 +0000)
ViewPartitionNode addresses Z-fighting issues by rendering near and far
parts of the scene seperately.

src/Main/Makefile.am
src/Main/ViewPartitionNode.cxx [new file with mode: 0644]
src/Main/ViewPartitionNode.hxx [new file with mode: 0644]
src/Main/renderer.cxx

index 636dbadcbf5449f0016a3fb3827d174597ee8665..8e26d217acfcd3d47c3b46f8d1bf15826f26ec52 100644 (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
diff --git a/src/Main/ViewPartitionNode.cxx b/src/Main/ViewPartitionNode.cxx
new file mode 100644 (file)
index 0000000..506576d
--- /dev/null
@@ -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
+    );
+}
diff --git a/src/Main/ViewPartitionNode.hxx b/src/Main/ViewPartitionNode.hxx
new file mode 100644 (file)
index 0000000..9e3d524
--- /dev/null
@@ -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
index faada37573dc768e7928c3731265e93158e63df7..ab119852d1327a9802c1f66ea1e6e5035f7cd89f 100644 (file)
 #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);
 }