]> git.mxchange.org Git - simgear.git/commitdiff
support osgText in models. See docs/README.osgtext for details
authortorsten <torsten>
Mon, 24 Aug 2009 17:29:38 +0000 (17:29 +0000)
committerTim Moore <timoore@redhat.com>
Tue, 25 Aug 2009 07:11:27 +0000 (09:11 +0200)
simgear/scene/model/Makefile.am
simgear/scene/model/SGReaderWriterXML.cxx
simgear/scene/model/SGText.cxx [new file with mode: 0644]
simgear/scene/model/SGText.hxx [new file with mode: 0644]

index 77845fe449ee2e7f7a4b6f06864c6402342587c7..e5fac33f3f2cdebce359e485e99c54c959fc2250 100644 (file)
@@ -22,7 +22,8 @@ include_HEADERS = \
        SGReaderWriterXMLOptions.hxx \
        SGRotateTransform.hxx \
        SGScaleTransform.hxx \
-       SGTranslateTransform.hxx
+       SGTranslateTransform.hxx \
+       SGText.hxx
 
 libsgmodel_a_SOURCES = \
        animation.cxx \
@@ -42,6 +43,7 @@ libsgmodel_a_SOURCES = \
        SGReaderWriterXML.cxx \
        SGRotateTransform.cxx \
        SGScaleTransform.cxx \
-       SGTranslateTransform.cxx
+       SGTranslateTransform.cxx \
+       SGText.cxx
 
 INCLUDES = -I$(top_srcdir)
index e9020407644134af8bf9e0ade487edc93da33e08..31c0c56dddaf42ad27e0589a78851e9a30cc2539 100644 (file)
@@ -41,6 +41,7 @@
 #include "animation.hxx"
 #include "particles.hxx"
 #include "model.hxx"
+#include "SGText.hxx"
 
 using namespace simgear;
 
@@ -305,6 +306,14 @@ sgLoad3DModel_internal(const string &path,
                         options.get()));
     }
 
+    std::vector<SGPropertyNode_ptr> text_nodes;
+    text_nodes = props->getChildren("text");
+    for (unsigned i = 0; i < text_nodes.size(); ++i) {
+        group->addChild(SGText::appendText(text_nodes[i],
+                        prop_root,
+                        options.get()));
+    }
+
     std::vector<SGPropertyNode_ptr> animation_nodes;
     animation_nodes = props->getChildren("animation");
     for (unsigned i = 0; i < animation_nodes.size(); ++i)
diff --git a/simgear/scene/model/SGText.cxx b/simgear/scene/model/SGText.cxx
new file mode 100644 (file)
index 0000000..3599a1d
--- /dev/null
@@ -0,0 +1,252 @@
+// SGText.cxx - Manage text in the scene graph
+// Copyright (C) 2009 Torsten Dreyer Torsten (_at_) t3r *dot* de
+//
+// 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.
+//
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include "SGText.hxx"
+
+#include <simgear/math/SGMath.hxx>
+#include <simgear/misc/sg_path.hxx>
+
+#include <osg/Geode>
+#include <osg/MatrixTransform>
+#include <osgText/Text>
+#include <osgText/Font>
+
+class SGText::UpdateCallback : public osg::NodeCallback {
+public:
+  UpdateCallback( osgText::Text * aText, SGConstPropertyNode_ptr aProperty, double aScale, double aOffset, double aTruncate, double aNumeric, const char * aFormat ) :
+    text( aText ),
+    property( aProperty ),
+    scale( aScale ),
+    offset( aOffset ),
+    truncate( aTruncate ),
+    numeric( aNumeric ),
+    format( aFormat )
+  {
+    if( format.size() == 0 ) {
+      if( numeric ) format = "%f";
+      else format = "%s";
+    }
+  }
+
+private:
+  virtual void operator()(osg::Node * node, osg::NodeVisitor *nv );
+  osgText::Text * text;
+  SGConstPropertyNode_ptr property;
+  double scale;
+  double offset;
+  bool truncate;
+  bool numeric;
+  string format;
+};
+
+void SGText::UpdateCallback::operator()(osg::Node * node, osg::NodeVisitor *nv ) 
+{
+  // FIXME:
+  // hopefully the users never specifies bad formats here
+  // this should better be something more robust
+  char buf[256];
+  if( numeric ) {
+    double d = property->getDoubleValue() * scale + offset;
+    if (truncate)  d = (d < 0) ? -floor(-d) : floor(d);
+    snprintf( buf, sizeof(buf)-1, format.c_str(), d );
+  } else {
+    snprintf( buf, sizeof(buf)-1, format.c_str(), property->getStringValue() );
+  }
+  if( text->getText().createUTF8EncodedString().compare( buf )  ) {
+    // be lazy and set the text only if the property has changed.
+    // update() computes the glyph representation which looks 
+    // more expensive than a the above string compare.
+    text->setText( buf );
+    text->update();
+  }
+  traverse( node, nv );
+}
+
+osg::Group * SGText::appendText(const SGPropertyNode* configNode, 
+  SGPropertyNode* modelRoot, const osgDB::ReaderWriter::Options* options)
+{
+  SGConstPropertyNode_ptr p;
+
+  SG_LOG(SG_GENERAL, SG_DEBUG, "Creating a text object");
+
+  // Set up the alignment node ("stolen" from animation.cxx)
+  // XXX Order of rotations is probably not correct.
+  osg::MatrixTransform *align = new osg::MatrixTransform;
+  osg::Matrix res_matrix;
+  res_matrix.makeRotate(
+      configNode->getFloatValue("offsets/pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+      osg::Vec3(0, 1, 0),
+      configNode->getFloatValue("offsets/roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+      osg::Vec3(1, 0, 0),
+      configNode->getFloatValue("offsets/heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
+      osg::Vec3(0, 0, 1));
+
+  osg::Matrix tmat;
+  tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0),
+                     configNode->getFloatValue("offsets/y-m", 0.0),
+                     configNode->getFloatValue("offsets/z-m", 0.0));
+
+  align->setMatrix(res_matrix * tmat);
+
+  if( (p = configNode->getNode( "name" )) != NULL )
+    align->setName(p->getStringValue());
+  else
+    align->setName("text align");
+/*
+  Create a fragment of the graph:
+  MatrixTransform
+   +-Geode
+    +-Text
+*/
+  osgText::Text * text = new osgText::Text();
+  osg::Geode * g = new osg::Geode;
+  g->addDrawable( text );
+  align->addChild( g );
+
+  SGPath path("Fonts" );
+  path.append( configNode->getStringValue( "font", "Helvetica" ));
+  text->setFont( path.str() );
+
+  text->setCharacterSize(configNode->getDoubleValue("character-size", 1.0 ), 
+                         configNode->getDoubleValue("character-aspect-ratio", 1.0 ));
+  
+  if( (p = configNode->getNode( "font-resolution" )) != NULL )
+    text->setFontResolution( p->getIntValue( "width", 32 ), p->getIntValue( "height", 32 ) );
+
+  if( (p = configNode->getNode( "kerning" )) != NULL ) {
+    string kerning = p->getStringValue();
+    if( kerning.compare( "default" ) == 0 ) {
+      text->setKerningType( osgText::KERNING_DEFAULT );
+    } else if( kerning.compare( "unfitted" ) == 0 ) {
+      text->setKerningType( osgText::KERNING_UNFITTED );
+    } else if( kerning.compare( "none" ) == 0 ) {
+      text->setKerningType( osgText::KERNING_NONE );
+    } else {
+      SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown kerning'" << kerning << "'." );
+    }
+  }
+
+  if( ( p = configNode->getNode( "axis-alignment" )) != NULL ) {
+    string axisAlignment = p->getStringValue();
+    if( axisAlignment.compare( "xy-plane" ) == 0 ) {
+      text->setAxisAlignment( osgText::Text::XY_PLANE );
+    } else if( axisAlignment.compare( "reversed-xy-plane" ) == 0 ) {
+      text->setAxisAlignment( osgText::Text::REVERSED_XY_PLANE );
+    } else if( axisAlignment.compare( "xz-plane" ) == 0 ) {
+      text->setAxisAlignment( osgText::Text::XZ_PLANE );
+    } else if( axisAlignment.compare( "reversed-xz-plane" ) == 0 ) {
+      text->setAxisAlignment( osgText::Text::REVERSED_XZ_PLANE );
+    } else if( axisAlignment.compare( "yz-plane" ) == 0 ) {
+      text->setAxisAlignment( osgText::Text::YZ_PLANE );
+    } else if( axisAlignment.compare( "reversed-yz-plane" ) == 0 ) {
+      text->setAxisAlignment( osgText::Text::REVERSED_YZ_PLANE );
+    } else if( axisAlignment.compare( "screen" ) == 0 ) {
+      text->setAxisAlignment( osgText::Text::SCREEN );
+    } else {
+      SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown axis-alignment'" << axisAlignment << "'." );
+    }
+  }
+
+  unsigned drawMode = osgText::Text::TEXT;
+  if( (p = configNode->getNode( "draw-text" )) != NULL && p->getBoolValue() == false )
+    drawMode &= ~ osgText::Text::TEXT;
+
+  if( (p = configNode->getNode( "draw-alignment" )) != NULL && p->getBoolValue() == true )
+    drawMode |= osgText::Text::ALIGNMENT;
+
+  if( (p = configNode->getNode( "draw-boundingbox" )) != NULL && p->getBoolValue() == true )
+    drawMode |= osgText::Text::BOUNDINGBOX;
+
+  text->setDrawMode( drawMode );
+
+  if( (p = configNode->getNode( "alignment" )) != NULL ) {
+    string alignment = p->getStringValue();
+    if( alignment.compare( "left-top" ) == 0 ) {
+      text->setAlignment( osgText::Text::LEFT_TOP );
+    } else if( alignment.compare( "left-center" ) == 0 ) {
+      text->setAlignment( osgText::Text::LEFT_CENTER );
+    } else if( alignment.compare( "left-bottom" ) == 0 ) {
+      text->setAlignment( osgText::Text::LEFT_BOTTOM );
+    } else if( alignment.compare( "center-top" ) == 0 ) {
+      text->setAlignment( osgText::Text::CENTER_TOP );
+    } else if( alignment.compare( "center-center" ) == 0 ) {
+      text->setAlignment( osgText::Text::CENTER_CENTER );
+    } else if( alignment.compare( "center-bottom" ) == 0 ) {
+      text->setAlignment( osgText::Text::CENTER_BOTTOM );
+    } else if( alignment.compare( "right-top" ) == 0 ) {
+      text->setAlignment( osgText::Text::RIGHT_TOP );
+    } else if( alignment.compare( "right-center" ) == 0 ) {
+      text->setAlignment( osgText::Text::RIGHT_CENTER );
+    } else if( alignment.compare( "right-bottom" ) == 0 ) {
+      text->setAlignment( osgText::Text::RIGHT_BOTTOM );
+    } else if( alignment.compare( "left-baseline" ) == 0 ) {
+      text->setAlignment( osgText::Text::LEFT_BASE_LINE );
+    } else if( alignment.compare( "center-baseline" ) == 0 ) {
+      text->setAlignment( osgText::Text::CENTER_BASE_LINE );
+    } else if( alignment.compare( "right-baseline" ) == 0 ) {
+      text->setAlignment( osgText::Text::RIGHT_BASE_LINE );
+    } else if( alignment.compare( "baseline" ) == 0 ) {
+      text->setAlignment( osgText::Text::BASE_LINE );
+    } else {
+      SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown text-alignment '" << alignment <<"'." );
+    }
+  }
+
+  if( (p = configNode->getNode( "layout" )) != NULL ) {
+    string layout = p->getStringValue();
+    if( layout.compare( "left-to-right" ) == 0 ) {
+      text->setLayout( osgText::Text::LEFT_TO_RIGHT );
+    } else if( layout.compare( "right-to-left" ) == 0 ) {
+      text->setLayout( osgText::Text::RIGHT_TO_LEFT );
+    } else if( layout.compare( "vertical" ) == 0 ) {
+      text->setLayout( osgText::Text::VERTICAL );
+    } else {
+      SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown layout '" << layout <<"'." );
+    }
+  }
+  
+  if( (p = configNode->getNode( "max-width" )) != NULL )
+    text->setMaximumWidth( p->getDoubleValue() );
+
+  if( (p = configNode->getNode( "max-height" )) != NULL )
+    text->setMaximumHeight( p->getDoubleValue() );
+
+  // FIXME: Should we support <chunks><chunk/><chunk/></chunks> here?
+  string type = configNode->getStringValue( "type", "literal" );
+  if( type == "literal" ) {
+    text->setText( configNode->getStringValue( "text", "" ) );
+  } else {
+    SGConstPropertyNode_ptr property = modelRoot->getNode( configNode->getStringValue( "property", "foo" ), true );
+    const char * format = configNode->getStringValue( "format", "" );
+    double scale = configNode->getDoubleValue( "scale", 1.0 );
+    double offset = configNode->getDoubleValue( "offset", 0.0 );
+    bool   truncate = configNode->getBoolValue( "truncate", false );
+
+    if( (p = configNode->getNode( "property")) != NULL ) {
+      p = modelRoot->getNode( p->getStringValue(), true );
+      UpdateCallback * uc = new UpdateCallback( text, property, scale, offset, truncate, type == "number-value", format );
+      g->setUpdateCallback( uc );
+    }
+  }
+
+  return align;
+}
diff --git a/simgear/scene/model/SGText.hxx b/simgear/scene/model/SGText.hxx
new file mode 100644 (file)
index 0000000..6677c29
--- /dev/null
@@ -0,0 +1,36 @@
+// SGText.hxx - Manage text in the scene graph
+// Copyright (C) 2009 Torsten Dreyer Torsten (_at_) t3r *dot* de
+//
+// 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 _SGTEXT_HXX
+#define _SGTEXT_HXX 1
+
+#include <osgDB/ReaderWriter>
+#include <osg/Group>
+
+#include <simgear/props/props.hxx>
+
+class SGText : public osg::NodeCallback 
+{
+public:
+  static osg::Group * appendText(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::ReaderWriter::Options* options);
+private:
+  class UpdateCallback;
+};
+
+#endif
+