]> git.mxchange.org Git - simgear.git/blobdiff - simgear/scene/util/SGUpdateVisitor.hxx
Work around apparent OSG 3.2.0 normal binding bug.
[simgear.git] / simgear / scene / util / SGUpdateVisitor.hxx
index 74d60e995169c474d38bc6b84940fb48687a7f8c..8f16a270353d21478b08c77dab3d9ae61163120a 100644 (file)
@@ -1,6 +1,6 @@
 /* -*-c++-*-
  *
- * Copyright (C) 2006 Mathias Froehlich 
+ * Copyright (C) 2006-2009 Mathias Froehlich 
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License as
 #define SG_SCENE_UPDATEVISITOR_HXX
 
 #include <osg/NodeVisitor>
+#include <osg/PagedLOD>
 #include <osgUtil/UpdateVisitor>
 
-#include "simgear/math/SGMath.hxx"
+#include "OsgMath.hxx"
 
 class SGUpdateVisitor : public osgUtil::UpdateVisitor {
 public:
   SGUpdateVisitor() :
     mVisibility(-1)
   {
-    // Need to traverse all children, else some LOD nodes do not get updated
-    // Note that the broad number of updates is not done due to
-    // the update callback in the global position node.
-    setTraversalMode(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN);
+    setTraversalMode(osg::NodeVisitor::TRAVERSE_ACTIVE_CHILDREN);
     setVisibility(10000);
   }
   void setViewData(const SGVec3d& globalEyePos,
                    const SGQuatd& globalViewOrientation)
   {
     mGlobalGeodEyePos = SGGeod::fromCart(globalEyePos);
+    _currentEyePos = toOsg(globalEyePos);
     mGlobalEyePos = globalEyePos;
     mGlobalViewOr = globalViewOrientation;
     mGlobalHorizLocalOr = SGQuatd::fromLonLat(mGlobalGeodEyePos);
@@ -135,7 +134,72 @@ public:
   double getSunAngleDeg() const
   { return mSunAngleDeg; }
 
+  virtual void apply(osg::Node& node)
+  {
+    if (!needToEnterNode(node))
+      return;
+    osgUtil::UpdateVisitor::apply(node);
+  }
+  // To avoid expiry of LOD nodes that are in range and that are updated,
+  // mark them with the last traversal number, even if they are culled away
+  // by the cull frustum.
+  virtual void apply(osg::PagedLOD& pagedLOD)
+  {
+    if (!needToEnterNode(pagedLOD))
+      return;
+    if (getFrameStamp())
+      pagedLOD.setFrameNumberOfLastTraversal(getFrameStamp()->getFrameNumber());
+    osgUtil::UpdateVisitor::apply(pagedLOD);
+  }
+  // To be able to traverse correctly only the active children, we need to
+  // track the model view matrices during update.
+  virtual void apply(osg::Transform& transform)
+  {
+    if (!needToEnterNode(transform))
+      return;
+    osg::Matrix matrix = _matrix;
+    transform.computeLocalToWorldMatrix(_matrix, this);
+    osgUtil::UpdateVisitor::apply(transform);
+    _matrix = matrix;
+  }
+  virtual void apply(osg::Camera& camera)
+  {
+    if (!needToEnterNode(camera))
+      return;
+    if (camera.getReferenceFrame() == osg::Camera::ABSOLUTE_RF) {
+      osg::Vec3d currentEyePos = _currentEyePos;
+      _currentEyePos = osg::Vec3d(0, 0, 0);
+      apply(static_cast<osg::Transform&>(camera));
+      _currentEyePos = currentEyePos;
+    } else {
+      apply(static_cast<osg::Transform&>(camera));
+    }
+  }
+  // Function to make the LOD traversal only enter that children that
+  // are visible on the screen.
+  virtual float getDistanceToViewPoint(const osg::Vec3& pos, bool) const
+  { return (_currentEyePos - _matrix.preMult(osg::Vec3d(pos))).length(); }
+
+protected:
+  bool needToEnterNode(const osg::Node& node) const
+  {
+    if (!node.isCullingActive())
+      return true;
+    return isSphereInRange(node.getBound());
+  }
+  bool isSphereInRange(const osg::BoundingSphere& sphere) const
+  {
+    if (!sphere.valid())
+      return false;
+    float maxDist = mVisibility + sphere._radius;
+    osg::Vec3d center = _matrix.preMult(osg::Vec3d(sphere._center));
+    return (_currentEyePos - center).length2() <= maxDist*maxDist;
+  }
+    
 private:
+  osg::Matrix _matrix;
+  osg::Vec3d _currentEyePos;
+
   SGGeod mGlobalGeodEyePos;
   SGVec3d mGlobalEyePos;
   SGQuatd mGlobalViewOr;