]> git.mxchange.org Git - simgear.git/blob - simgear/scene/model/SGText.cxx
Performance optimization: empty() instead of size()>0
[simgear.git] / simgear / scene / model / SGText.cxx
1 // SGText.cxx - Manage text in the scene graph
2 // Copyright (C) 2009 Torsten Dreyer Torsten (_at_) t3r *dot* de
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License as
6 // published by the Free Software Foundation; either version 2 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful, but
10 // WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 //
18
19 #ifdef HAVE_CONFIG_H
20 #  include <simgear_config.h>
21 #endif
22
23 #include <cstdio>
24
25 #include "SGText.hxx"
26
27 #include <simgear/math/SGMath.hxx>
28 #include <simgear/misc/sg_path.hxx>
29
30 #include <osg/Geode>
31 #include <osg/MatrixTransform>
32 #include <osgText/Text>
33 #include <osgText/Font>
34
35 using std::string;
36
37 class SGText::UpdateCallback : public osg::NodeCallback {
38 public:
39   UpdateCallback( osgText::Text * aText, SGConstPropertyNode_ptr aProperty, double aScale, double aOffset, bool aTruncate, bool aNumeric, const char * aFormat ) :
40     text( aText ),
41     property( aProperty ),
42     scale( aScale ),
43     offset( aOffset ),
44     truncate( aTruncate ),
45     numeric( aNumeric ),
46     format( aFormat )
47   {
48     if( format.empty() ) {
49       if( numeric ) format = "%f";
50       else format = "%s";
51     }
52   }
53
54 private:
55   virtual void operator()(osg::Node * node, osg::NodeVisitor *nv );
56   osgText::Text * text;
57   SGConstPropertyNode_ptr property;
58   double scale;
59   double offset;
60   bool truncate;
61   bool numeric;
62   string format;
63 };
64
65 void SGText::UpdateCallback::operator()(osg::Node * node, osg::NodeVisitor *nv ) 
66 {
67   // FIXME:
68   // hopefully the users never specifies bad formats here
69   // this should better be something more robust
70   char buf[256];
71   if( numeric ) {
72     double d = property->getDoubleValue() * scale + offset;
73     if (truncate)  d = (d < 0) ? -floor(-d) : floor(d);
74     snprintf( buf, sizeof(buf)-1, format.c_str(), d );
75   } else {
76     snprintf( buf, sizeof(buf)-1, format.c_str(), property->getStringValue() );
77   }
78   if( text->getText().createUTF8EncodedString().compare( buf )  ) {
79     // be lazy and set the text only if the property has changed.
80     // update() computes the glyph representation which looks 
81     // more expensive than a the above string compare.
82     text->setText( buf );
83     text->update();
84   }
85   traverse( node, nv );
86 }
87
88 osg::Node * SGText::appendText(const SGPropertyNode* configNode, 
89   SGPropertyNode* modelRoot, const osgDB::Options* options)
90 {
91   SGConstPropertyNode_ptr p;
92
93   SG_LOG(SG_GENERAL, SG_DEBUG, "Creating a text object");
94
95   osgText::Text * text = new osgText::Text();
96   osg::Geode * g = new osg::Geode;
97   g->addDrawable( text );
98
99   SGPath path("Fonts" );
100   path.append( configNode->getStringValue( "font", "Helvetica" ));
101   text->setFont( path.str() );
102
103   text->setCharacterSize(configNode->getDoubleValue("character-size", 1.0 ), 
104                          configNode->getDoubleValue("character-aspect-ratio", 1.0 ));
105   
106   if( (p = configNode->getNode( "font-resolution" )) != NULL )
107     text->setFontResolution( p->getIntValue( "width", 32 ), p->getIntValue( "height", 32 ) );
108
109   if( (p = configNode->getNode( "kerning" )) != NULL ) {
110     string kerning = p->getStringValue();
111     if( kerning.compare( "default" ) == 0 ) {
112       text->setKerningType( osgText::KERNING_DEFAULT );
113     } else if( kerning.compare( "unfitted" ) == 0 ) {
114       text->setKerningType( osgText::KERNING_UNFITTED );
115     } else if( kerning.compare( "none" ) == 0 ) {
116       text->setKerningType( osgText::KERNING_NONE );
117     } else {
118       SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown kerning'" << kerning << "'." );
119     }
120   }
121
122   if( ( p = configNode->getNode( "axis-alignment" )) != NULL ) {
123     string axisAlignment = p->getStringValue();
124     if( axisAlignment.compare( "xy-plane" ) == 0 ) {
125       text->setAxisAlignment( osgText::Text::XY_PLANE );
126     } else if( axisAlignment.compare( "reversed-xy-plane" ) == 0 ) {
127       text->setAxisAlignment( osgText::Text::REVERSED_XY_PLANE );
128     } else if( axisAlignment.compare( "xz-plane" ) == 0 ) {
129       text->setAxisAlignment( osgText::Text::XZ_PLANE );
130     } else if( axisAlignment.compare( "reversed-xz-plane" ) == 0 ) {
131       text->setAxisAlignment( osgText::Text::REVERSED_XZ_PLANE );
132     } else if( axisAlignment.compare( "yz-plane" ) == 0 ) {
133       text->setAxisAlignment( osgText::Text::YZ_PLANE );
134     } else if( axisAlignment.compare( "reversed-yz-plane" ) == 0 ) {
135       text->setAxisAlignment( osgText::Text::REVERSED_YZ_PLANE );
136     } else if( axisAlignment.compare( "screen" ) == 0 ) {
137       text->setAxisAlignment( osgText::Text::SCREEN );
138     } else {
139       SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown axis-alignment'" << axisAlignment << "'." );
140     }
141   }
142
143   unsigned drawMode = osgText::Text::TEXT;
144   if( (p = configNode->getNode( "draw-text" )) != NULL && p->getBoolValue() == false )
145     drawMode &= ~ osgText::Text::TEXT;
146
147   if( (p = configNode->getNode( "draw-alignment" )) != NULL && p->getBoolValue() == true )
148     drawMode |= osgText::Text::ALIGNMENT;
149
150   if( (p = configNode->getNode( "draw-boundingbox" )) != NULL && p->getBoolValue() == true )
151     drawMode |= osgText::Text::BOUNDINGBOX;
152
153   text->setDrawMode( drawMode );
154
155   if( (p = configNode->getNode( "alignment" )) != NULL ) {
156     string alignment = p->getStringValue();
157     if( alignment.compare( "left-top" ) == 0 ) {
158       text->setAlignment( osgText::Text::LEFT_TOP );
159     } else if( alignment.compare( "left-center" ) == 0 ) {
160       text->setAlignment( osgText::Text::LEFT_CENTER );
161     } else if( alignment.compare( "left-bottom" ) == 0 ) {
162       text->setAlignment( osgText::Text::LEFT_BOTTOM );
163     } else if( alignment.compare( "center-top" ) == 0 ) {
164       text->setAlignment( osgText::Text::CENTER_TOP );
165     } else if( alignment.compare( "center-center" ) == 0 ) {
166       text->setAlignment( osgText::Text::CENTER_CENTER );
167     } else if( alignment.compare( "center-bottom" ) == 0 ) {
168       text->setAlignment( osgText::Text::CENTER_BOTTOM );
169     } else if( alignment.compare( "right-top" ) == 0 ) {
170       text->setAlignment( osgText::Text::RIGHT_TOP );
171     } else if( alignment.compare( "right-center" ) == 0 ) {
172       text->setAlignment( osgText::Text::RIGHT_CENTER );
173     } else if( alignment.compare( "right-bottom" ) == 0 ) {
174       text->setAlignment( osgText::Text::RIGHT_BOTTOM );
175     } else if( alignment.compare( "left-baseline" ) == 0 ) {
176       text->setAlignment( osgText::Text::LEFT_BASE_LINE );
177     } else if( alignment.compare( "center-baseline" ) == 0 ) {
178       text->setAlignment( osgText::Text::CENTER_BASE_LINE );
179     } else if( alignment.compare( "right-baseline" ) == 0 ) {
180       text->setAlignment( osgText::Text::RIGHT_BASE_LINE );
181     } else if( alignment.compare( "baseline" ) == 0 ) {
182       text->setAlignment( osgText::Text::BASE_LINE );
183     } else {
184       SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown text-alignment '" << alignment <<"'." );
185     }
186   }
187
188   if( (p = configNode->getNode( "layout" )) != NULL ) {
189     string layout = p->getStringValue();
190     if( layout.compare( "left-to-right" ) == 0 ) {
191       text->setLayout( osgText::Text::LEFT_TO_RIGHT );
192     } else if( layout.compare( "right-to-left" ) == 0 ) {
193       text->setLayout( osgText::Text::RIGHT_TO_LEFT );
194     } else if( layout.compare( "vertical" ) == 0 ) {
195       text->setLayout( osgText::Text::VERTICAL );
196     } else {
197       SG_LOG(SG_GENERAL, SG_ALERT, "ignoring unknown layout '" << layout <<"'." );
198     }
199   }
200   
201   if( (p = configNode->getNode( "max-width" )) != NULL )
202     text->setMaximumWidth( p->getDoubleValue() );
203
204   if( (p = configNode->getNode( "max-height" )) != NULL )
205     text->setMaximumHeight( p->getDoubleValue() );
206
207   // FIXME: Should we support <chunks><chunk/><chunk/></chunks> here?
208   string type = configNode->getStringValue( "type", "literal" );
209   if( type == "literal" ) {
210     text->setText( configNode->getStringValue( "text", "" ) );
211   } else {
212     SGConstPropertyNode_ptr property = modelRoot->getNode( configNode->getStringValue( "property", "foo" ), true );
213     const char * format = configNode->getStringValue( "format", "" );
214     double scale = configNode->getDoubleValue( "scale", 1.0 );
215     double offset = configNode->getDoubleValue( "offset", 0.0 );
216     bool   truncate = configNode->getBoolValue( "truncate", false );
217
218     if( (p = configNode->getNode( "property")) != NULL ) {
219       p = modelRoot->getNode( p->getStringValue(), true );
220       UpdateCallback * uc = new UpdateCallback( text, property, scale, offset, truncate, type == "number-value", format );
221       g->setUpdateCallback( uc );
222     }
223   }
224
225   osg::Node * reply = NULL;
226   if( (p = configNode->getNode( "offsets")) == NULL ) {
227     reply = g;
228   } else {
229     // Set up the alignment node ("stolen" from animation.cxx)
230     // XXX Order of rotations is probably not correct.
231     osg::MatrixTransform *align = new osg::MatrixTransform;
232     osg::Matrix res_matrix;
233     res_matrix.makeRotate(
234         p->getFloatValue("pitch-deg", 0.0)*SG_DEGREES_TO_RADIANS,
235         osg::Vec3(0, 1, 0),
236         p->getFloatValue("roll-deg", 0.0)*SG_DEGREES_TO_RADIANS,
237         osg::Vec3(1, 0, 0),
238         p->getFloatValue("heading-deg", 0.0)*SG_DEGREES_TO_RADIANS,
239         osg::Vec3(0, 0, 1));
240
241     osg::Matrix tmat;
242     tmat.makeTranslate(configNode->getFloatValue("offsets/x-m", 0.0),
243                        configNode->getFloatValue("offsets/y-m", 0.0),
244                        configNode->getFloatValue("offsets/z-m", 0.0));
245
246     align->setMatrix(res_matrix * tmat);
247     align->addChild( g );
248     reply = align;
249   }
250
251   if( (p = configNode->getNode( "name" )) != NULL )
252     reply->setName(p->getStringValue());
253   else
254     reply->setName("text");
255   return reply;
256 }