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