]> git.mxchange.org Git - flightgear.git/blobdiff - src/Instrumentation/od_gauge.cxx
ADF: code clean-up/documentation
[flightgear.git] / src / Instrumentation / od_gauge.cxx
index dabf87c3700a4909b21713ae0bcc456dfe7461a1..6daab2ba88407eaa2db4d21929793b1f84dfa5b4 100644 (file)
@@ -2,7 +2,13 @@
 //
 // Written by Harald JOHNSEN, started May 2005.
 //
-// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+// Copyright (C) 2005  Harald JOHNSEN
+//
+// Ported to OSG by Tim Moore - Jun 2007
+//
+// Heavily modified to be usable for the 2d Canvas by Thomas Geymayer - April 2012
+// Supports now multisampling/mipmapping, usage of the stencil buffer and placing
+// the texture in the scene by certain filter criteria
 //
 // This program is free software; you can redistribute it and/or
 // modify it under the terms of the GNU General Public License as
 //
 // You should have received a copy of the GNU General Public License
 // along with this program; if not, write to the Free Software
-// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //
 //
 
-#include <plib/ssg.h>
-#include <simgear/screen/extensions.hxx>
-#include <simgear/screen/RenderTexture.h>
-#include <simgear/debug/logstream.hxx>
-#include SG_GLU_H
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <osg/Texture2D>
+#include <osg/AlphaFunc>
+#include <osg/BlendFunc>
+#include <osg/Camera>
+#include <osg/Geode>
+#include <osg/NodeVisitor>
+#include <osg/Matrix>
+#include <osg/PolygonMode>
+#include <osg/ShadeModel>
+#include <osg/StateSet>
+#include <osg/FrameBufferObject> // for GL_DEPTH_STENCIL_EXT on Windows
+
+#include <osgDB/FileNameUtils>
+
+#include <simgear/scene/material/EffectGeode.hxx>
+#include <simgear/scene/util/RenderConstants.hxx>
 
 #include <Main/globals.hxx>
+#include <Viewer/renderer.hxx>
 #include <Scenery/scenery.hxx>
 #include "od_gauge.hxx"
 
-FGODGauge::FGODGauge() :
-    rtAvailable( false ),
-    rt( 0 )
+#include <cassert>
+
+//------------------------------------------------------------------------------
+FGODGauge::FGODGauge():
+  _size_x( -1 ),
+  _size_y( -1 ),
+  _view_width( -1 ),
+  _view_height( -1 ),
+  _use_image_coords( false ),
+  _use_stencil( false ),
+  _use_mipmapping( false ),
+  _coverage_samples( 0 ),
+  _color_samples( 0 ),
+  rtAvailable( false )
 {
 }
 
-// done here and not in init() so we don't allocate a rendering context if it is
-// never used
-void FGODGauge::allocRT () {
-    GLint colorBits = 0;
-    glGetIntegerv( GL_BLUE_BITS, &colorBits );
-    textureWH = 256;
-    rt = new RenderTexture();
-    if( colorBits < 8 )
-        rt->Reset("rgba=5,5,5,1 ctt");
-    else
-        rt->Reset("rgba ctt");
-
-    if( rt->Initialize(256, 256, true) ) {
-        SG_LOG(SG_ALL, SG_INFO, "FGODGauge:Initialize sucessfull");
-        if (rt->BeginCapture())
-        {
-            SG_LOG(SG_ALL, SG_INFO, "FGODGauge:BeginCapture sucessfull, RTT available");
-            rtAvailable = true;
-            glViewport(0, 0, textureWH, textureWH);
-            glMatrixMode(GL_PROJECTION);
-            glLoadIdentity();
-            gluOrtho2D( -256.0, 256.0, -256.0, 256.0 );
-            glMatrixMode(GL_MODELVIEW);
-            glLoadIdentity();
-            glDisable(GL_LIGHTING);
-            glEnable(GL_COLOR_MATERIAL);
-            glDisable(GL_CULL_FACE);
-            glDisable(GL_FOG);
-            glDisable(GL_DEPTH_TEST);
-            glClearColor(0.0, 0.0, 0.0, 0.0);
-            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-            glBindTexture(GL_TEXTURE_2D, 0);
-            glEnable(GL_TEXTURE_2D);
-            glEnable(GL_ALPHA_TEST);
-            glAlphaFunc(GL_GREATER, 0.0f);
-            glDisable(GL_SMOOTH);
-            glEnable(GL_BLEND);
-            glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
-            rt->EndCapture();
-        } else
-            SG_LOG(SG_ALL, SG_WARN, "FGODGauge:BeginCapture failed, RTT not available, using backbuffer");
-    } else
-        SG_LOG(SG_ALL, SG_WARN, "FGODGauge:Initialize failed, RTT not available, using backbuffer");
+//------------------------------------------------------------------------------
+FGODGauge::~FGODGauge()
+{
+  if( camera.valid() )
+    globals->get_renderer()->removeCamera(camera.get());
 }
 
-FGODGauge::~FGODGauge() {
-    delete rt;
+//------------------------------------------------------------------------------
+void FGODGauge::setSize(int size_x, int size_y)
+{
+  _size_x = size_x;
+  _size_y = size_y < 0 ? size_x : size_y;
+
+  if( texture.valid() )
+    texture->setTextureSize(_size_x, _size_x);
 }
 
-void FGODGauge::init () {
+//----------------------------------------------------------------------------
+void FGODGauge::setViewSize(int width, int height)
+{
+  _view_width = width;
+  _view_height = height < 0 ? width : height;
+
+  if( camera )
+    updateCoordinateFrame();
 }
 
-void FGODGauge::update (double dt) {
+//------------------------------------------------------------------------------
+void FGODGauge::useImageCoords(bool use)
+{
+  if( use == _use_image_coords )
+    return;
+
+  _use_image_coords = use;
+
+  if( texture )
+    updateCoordinateFrame();
 }
 
-void FGODGauge::beginCapture(int viewSize) {
-    if( ! rt )
-        allocRT();
-    if(rtAvailable) {
-        rt->BeginCapture();
-    }
-    else
-        set2D();
-     textureWH = viewSize;
-    glViewport(0, 0, textureWH, textureWH);
+//------------------------------------------------------------------------------
+void FGODGauge::useStencil(bool use)
+{
+  if( use == _use_stencil )
+    return;
+
+  _use_stencil = use;
+
+  if( texture )
+    updateStencil();
 }
 
-void FGODGauge::beginCapture(void) {
-    if( ! rt )
-        allocRT();
-    if(rtAvailable) {
-        rt->BeginCapture();
-    }
-    else
-        set2D();
+//------------------------------------------------------------------------------
+void FGODGauge::setSampling( bool mipmapping,
+                             int coverage_samples,
+                             int color_samples )
+{
+  if(    _use_mipmapping == mipmapping
+      && _coverage_samples == coverage_samples
+      && _color_samples == color_samples )
+    return;
+
+  _use_mipmapping = mipmapping;
+
+  if( color_samples > coverage_samples )
+  {
+    SG_LOG
+    (
+      SG_GL,
+      SG_WARN,
+      "FGODGauge::setSampling: color_samples > coverage_samples not allowed!"
+    );
+    color_samples = coverage_samples;
+  }
+
+  _coverage_samples = coverage_samples;
+  _color_samples = color_samples;
+
+  updateSampling();
 }
 
-void FGODGauge::Clear(void) {
-    if(rtAvailable) {
-        glClear(GL_COLOR_BUFFER_BIT);
-    }
-    else {
-        glDisable(GL_BLEND);
-        glDisable(GL_ALPHA_TEST);
-          glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
-        glRectf(-256.0, -256.0, 256.0, 256.0);
-        glEnable(GL_BLEND);
-        glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
-        glEnable(GL_ALPHA_TEST);
-    }
+//------------------------------------------------------------------------------
+bool FGODGauge::serviceable(void) 
+{
+  return rtAvailable;
+}
+
+//------------------------------------------------------------------------------
+void FGODGauge::allocRT(osg::NodeCallback* camera_cull_callback)
+{
+  camera = new osg::Camera;
+  camera->setDataVariance(osg::Object::DYNAMIC);
+  // Only the far camera should trigger this texture to be rendered.
+  camera->setNodeMask(simgear::BACKGROUND_BIT);
+  camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
+  camera->setRenderOrder(osg::Camera::PRE_RENDER);
+  camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f , 0.0f));
+  camera->setClearStencil(0);
+  camera->setRenderTargetImplementation( osg::Camera::FRAME_BUFFER_OBJECT,
+                                             osg::Camera::FRAME_BUFFER );
+
+  if( camera_cull_callback )
+    camera->setCullCallback(camera_cull_callback);
+
+  updateCoordinateFrame();
+  updateStencil();
+
+  osg::StateSet* stateSet = camera->getOrCreateStateSet();
+  stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
+  stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
+  stateSet->setAttributeAndModes(new osg::PolygonMode(osg::PolygonMode::FRONT_AND_BACK,
+          osg::PolygonMode::FILL),
+          osg::StateAttribute::ON);
+  stateSet->setAttributeAndModes(new osg::AlphaFunc(osg::AlphaFunc::GREATER,
+          0.0f),
+          osg::StateAttribute::ON);
+  stateSet->setAttribute(new osg::ShadeModel(osg::ShadeModel::FLAT));
+  stateSet->setAttributeAndModes(new osg::BlendFunc(osg::BlendFunc::SRC_ALPHA,
+          osg::BlendFunc::ONE_MINUS_SRC_ALPHA),
+          osg::StateAttribute::ON);
+  if( !texture )
+  {
+    texture = new osg::Texture2D;
+    texture->setTextureSize(_size_x, _size_y);
+    texture->setInternalFormat(GL_RGBA);
+  }
+
+  updateSampling();
+
+  globals->get_renderer()->addCamera(camera.get(), false);
+  rtAvailable = true;
 }
 
-void FGODGauge::endCapture(GLuint texID) {
-    glBindTexture(GL_TEXTURE_2D, texID);
-    glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, textureWH, textureWH);
-    if(rtAvailable)
-        rt->EndCapture();
-    else
-        set3D();
+//------------------------------------------------------------------------------
+void FGODGauge::updateCoordinateFrame()
+{
+  assert( camera );
+
+  if( _view_width < 0 )
+    _view_width = _size_x;
+  if( _view_height < 0 )
+    _view_height = _size_y;
+
+  camera->setViewport(0, 0, _size_x, _size_y);
+
+  if( _use_image_coords )
+    camera->setProjectionMatrix(
+      osg::Matrix::ortho2D(0, _view_width, _view_height, 0)
+    );
+  else
+    camera->setProjectionMatrix(
+      osg::Matrix::ortho2D( -_view_width/2.,  _view_width/2.,
+                            -_view_height/2., _view_height/2. )
+    );
 }
 
-void FGODGauge::setSize(int viewSize) {
-    textureWH = viewSize;
-    glViewport(0, 0, textureWH, textureWH);
+//------------------------------------------------------------------------------
+void FGODGauge::updateStencil()
+{
+  assert( camera );
+
+  GLbitfield mask = GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT;
+
+  if( _use_stencil)
+  {
+    camera->attach( osg::Camera::PACKED_DEPTH_STENCIL_BUFFER,
+                     GL_DEPTH_STENCIL_EXT );
+    mask |= GL_STENCIL_BUFFER_BIT;
+  }
+  else
+  {
+    camera->detach(osg::Camera::PACKED_DEPTH_STENCIL_BUFFER);
+  }
+
+  camera->setClearMask(mask);
 }
 
-bool FGODGauge::serviceable(void) {
-    return rtAvailable;
+//------------------------------------------------------------------------------
+void FGODGauge::updateSampling()
+{
+  assert( camera );
+  assert( texture );
+
+  texture->setFilter(
+    osg::Texture2D::MIN_FILTER,
+    _use_mipmapping ? osg::Texture2D::LINEAR_MIPMAP_NEAREST
+                    : osg::Texture2D::LINEAR
+  );
+  camera->attach(
+    osg::Camera::COLOR_BUFFER,
+    texture.get(),
+    0, 0,
+    _use_mipmapping,
+    _coverage_samples,
+    _color_samples
+  );
 }
 
 /**
- * Locate a texture SSG node in a branch.
+ * Replace a texture in the airplane model with the gauge texture.
  */
-static ssgTexture *
-find_texture_node (ssgEntity * node, const char * name)
+class ReplaceStaticTextureVisitor:
+  public osg::NodeVisitor
 {
-  if( node->isA( ssgTypeTexture() ) ) {
-    ssgTexture *tex = (ssgTexture *) node;
-    char * texture_name = tex->getFilename();
-    if (texture_name != 0 && !strcmp(name, texture_name))
-      return tex;
-  }
-  else if (node->isAKindOf(ssgTypeBranch())) {
-    int nKids = node->getNumKids();
-    for (int i = 0; i < nKids; i++) {
-      ssgTexture * result =
-        find_texture_node(((ssgBranch*)node)->getKid(i), name);
-      if (result != 0)
-        return result;
+  public:
+
+    ReplaceStaticTextureVisitor( const char* name,
+                                 osg::Texture2D* new_texture ):
+        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+        _tex_name( osgDB::getSimpleFileName(name) ),
+        _new_texture(new_texture)
+    {}
+
+    ReplaceStaticTextureVisitor( const SGPropertyNode* placement,
+                                 osg::Texture2D* new_texture,
+                                 osg::NodeCallback* cull_callback = 0 ):
+        osg::NodeVisitor(osg::NodeVisitor::TRAVERSE_ALL_CHILDREN),
+        _tex_name( osgDB::getSimpleFileName(
+          placement->getStringValue("texture"))
+        ),
+        _node_name( placement->getStringValue("node") ),
+        _parent_name( placement->getStringValue("parent") ),
+        _new_texture(new_texture),
+        _cull_callback(cull_callback)
+    {
+      if(    _tex_name.empty()
+          && _node_name.empty()
+          && _parent_name.empty() )
+        SG_LOG
+        (
+          SG_GL,
+          SG_WARN,
+          "No filter criterion for replacing texture. "
+          " Every texture will be replaced!"
+        );
     }
-  } 
-  return 0;
-}
 
-void FGODGauge::set_texture(const char * name, GLuint new_texture) {
-    ssgEntity * root = globals->get_scenery()->get_aircraft_branch();
-    ssgTexture * node = find_texture_node( root, name );
-    if( node )
-        node->setHandle( new_texture );
-}
+    /**
+     * Get a list of groups which have been inserted into the scene graph to
+     * replace the given texture
+     */
+    Placements& getPlacements()
+    {
+      return _placements;
+    }
+
+    virtual void apply(osg::Geode& node)
+    {
+      simgear::EffectGeode* eg = dynamic_cast<simgear::EffectGeode*>(&node);
+      if( !eg )
+        return;
+
+      osg::StateSet* ss = eg->getEffect()->getDefaultStateSet();
+      if( !ss )
+        return;
+
+      osg::Group *parent = node.getParent(0);
+      if( !_node_name.empty() && parent->getName() != _node_name )
+        return;
+
+      if( !_parent_name.empty() )
+      {
+        // Traverse nodes upwards starting at the parent node (skip current
+        // node)
+        const osg::NodePath& np = getNodePath();
+        bool found = false;
+        for( int i = static_cast<int>(np.size()) - 2; i >= 0; --i )
+        {
+          const osg::Node* path_segment = np[i];
+          const osg::Node* path_parent = path_segment->getParent(0);
+
+          // A node without a name is always the parent of the root node of
+          // the model just containing the file name
+          if( path_parent && path_parent->getName().empty() )
+            return;
+
+          if( path_segment->getName() == _parent_name )
+          {
+            found = true;
+            break;
+          }
+        }
+
+        if( !found )
+          return;
+      }
+
+      for( size_t unit = 0; unit < ss->getNumTextureAttributeLists(); ++unit )
+      {
+        osg::Texture2D* tex = dynamic_cast<osg::Texture2D*>
+        (
+          ss->getTextureAttribute(unit, osg::StateAttribute::TEXTURE)
+        );
+
+        if( !tex || !tex->getImage() || tex == _new_texture )
+          continue;
+
+        if( !_tex_name.empty() )
+        {
+          std::string tex_name = tex->getImage()->getFileName();
+          std::string tex_name_simple = osgDB::getSimpleFileName(tex_name);
+          if( !osgDB::equalCaseInsensitive(_tex_name, tex_name_simple) )
+            continue;
+        }
+
+        // insert a new group between the geode an it's parent which overrides
+        // the texture
+        osg::ref_ptr<osg::Group> group = new osg::Group;
+        group->setName("canvas texture group");
+        group->addChild(eg);
+        parent->removeChild(eg);
+        parent->addChild(group);
+
+        if( _cull_callback )
+          group->setCullCallback(_cull_callback);
 
-void FGODGauge::set2D() {
-    glPushAttrib ( GL_ENABLE_BIT | GL_VIEWPORT_BIT  | GL_TRANSFORM_BIT | GL_LIGHTING_BIT ) ;
-
-    glDisable(GL_LIGHTING);
-    glEnable(GL_COLOR_MATERIAL);
-    glDisable(GL_CULL_FACE);
-    glDisable(GL_FOG);
-    glDisable(GL_DEPTH_TEST);
-    glClearColor(0.0, 0.0, 0.0, 0.0);
-    glEnable(GL_TEXTURE_2D);
-    glDisable(GL_SMOOTH);
-    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
-    glBindTexture(GL_TEXTURE_2D, 0);
-
-    glViewport ( 0, 0, textureWH, textureWH ) ;
-    glMatrixMode   ( GL_PROJECTION ) ;
-    glPushMatrix   () ;
-    glLoadIdentity () ;
-    gluOrtho2D( -256.0, 256.0, -256.0, 256.0 );
-    glMatrixMode   ( GL_MODELVIEW ) ;
-    glPushMatrix   () ;
-    glLoadIdentity () ;
-
-    glAlphaFunc(GL_GREATER, 0.0f);
+        _placements.push_back(group);
 
+        osg::StateSet* stateSet = group->getOrCreateStateSet();
+        stateSet->setTextureAttribute( unit, _new_texture,
+                                             osg::StateAttribute::OVERRIDE );
+        stateSet->setTextureMode( unit, GL_TEXTURE_2D,
+                                        osg::StateAttribute::ON );
+
+        SG_LOG
+        (
+          SG_GL,
+          SG_INFO,
+             "Replaced texture '" << _tex_name << "'"
+          << " for object '" << parent->getName() << "'"
+          << (!_parent_name.empty() ? " with parent '" + _parent_name + "'"
+                                    : "")
+        );
+        return;
+      }
+    }
+
+
+
+  protected:
+
+    std::string _tex_name,      ///<! Name of texture to be replaced
+                _node_name,     ///<! Only replace if node name matches
+                _parent_name;   ///<! Only replace if any parent node matches
+                                ///   given name (all the tree upwards)
+    osg::Texture2D     *_new_texture;
+    osg::NodeCallback  *_cull_callback;
+
+    Placements _placements;
+};
+
+//------------------------------------------------------------------------------
+  Placements FGODGauge::set_texture(const char* name, osg::Texture2D* new_texture)
+{
+  osg::Group* root = globals->get_scenery()->get_aircraft_branch();
+  ReplaceStaticTextureVisitor visitor(name, new_texture);
+  root->accept(visitor);
+  return visitor.getPlacements();
 }
 
-void FGODGauge::set3D() {
-    glMatrixMode   ( GL_PROJECTION ) ;
-    glPopMatrix    () ;
-    glMatrixMode   ( GL_MODELVIEW ) ;
-    glPopMatrix    () ;
-    glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
-    glPopAttrib    () ;
+//------------------------------------------------------------------------------
+Placements FGODGauge::set_texture( const SGPropertyNode* placement,
+                             osg::Texture2D* new_texture,
+                             osg::NodeCallback* cull_callback )
+{
+  osg::Group* root = globals->get_scenery()->get_aircraft_branch();
+  ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
+  root->accept(visitor);
+  return visitor.getPlacements();
 }