From c4116da564db28b56e3d3872c5cf2233cf4d5d14 Mon Sep 17 00:00:00 2001 From: torsten Date: Mon, 24 Aug 2009 17:29:38 +0000 Subject: [PATCH] support osgText in models. See docs/README.osgtext for details --- simgear/scene/model/Makefile.am | 6 +- simgear/scene/model/SGReaderWriterXML.cxx | 9 + simgear/scene/model/SGText.cxx | 252 ++++++++++++++++++++++ simgear/scene/model/SGText.hxx | 36 ++++ 4 files changed, 301 insertions(+), 2 deletions(-) create mode 100644 simgear/scene/model/SGText.cxx create mode 100644 simgear/scene/model/SGText.hxx diff --git a/simgear/scene/model/Makefile.am b/simgear/scene/model/Makefile.am index 77845fe4..e5fac33f 100644 --- a/simgear/scene/model/Makefile.am +++ b/simgear/scene/model/Makefile.am @@ -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) diff --git a/simgear/scene/model/SGReaderWriterXML.cxx b/simgear/scene/model/SGReaderWriterXML.cxx index e9020407..31c0c56d 100644 --- a/simgear/scene/model/SGReaderWriterXML.cxx +++ b/simgear/scene/model/SGReaderWriterXML.cxx @@ -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 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 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 index 00000000..3599a1dc --- /dev/null +++ b/simgear/scene/model/SGText.cxx @@ -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 +#endif + +#include "SGText.hxx" + +#include +#include + +#include +#include +#include +#include + +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 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 index 00000000..6677c29e --- /dev/null +++ b/simgear/scene/model/SGText.hxx @@ -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 +#include + +#include + +class SGText : public osg::NodeCallback +{ +public: + static osg::Group * appendText(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::ReaderWriter::Options* options); +private: + class UpdateCallback; +}; + +#endif + -- 2.39.5