1 /*****************************************************************************
2 * $LastChangedDate: 2011-08-20 12:07:38 -0400 (Sat, 20 Aug 2011) $
3 * @file
4 * @author Jim E. Brooks http://www.palomino3d.org
5 * @brief HUD text objects.
6 * @remarks
7 * - In OSG, multiple lines of text is drawn by attaching nodes of
8 * osg::Text objects, contained in Geodes, that define different
9 * 2D positions and strings. These are abstracted by the HudText class.
10 * - HudText is auto-drawn by OSG since the HUD is comprised of a camera with
11 * a 2D projection matrix and osg::Text objects which define a line of text.
12 *//*
13 * LEGAL: COPYRIGHT (C) 2007 JIM E. BROOKS
14 * THIS SOURCE CODE IS RELEASED UNDER THE TERMS
15 * OF THE GNU GENERAL PUBLIC LICENSE VERSION 2 (GPL 2).
16 *****************************************************************************/
17
18 #define HUD_HUD_TEXT_CC 1
19 #include "base/module.hh"
20 #include "base/conf.hh"
21 #include "base/timer.hh"
22 using namespace base;
23 #include "math/module.hh"
24 #include "math/types_trig.hh"
25 #include "math/defs_trig.hh"
26 #include "math/funcs.hh"
27 #include "math/quaternion.hh"
28 using namespace math;
29 #include "gfx/module.hh"
30 #include "gfx/osg.hh"
31 using namespace gfx;
32 #include "graph/module.hh"
33 using namespace graph;
34 #include "view/module.hh"
35 #include "view/view.hh"
36 using namespace view;
37 #include "hud/module.hh"
38 #include "hud/hud_text.hh"
39 #include "hud/font.hh"
40
41 #include <osg/Camera>
42
43 namespace hud {
44
45 INTERN const fp HUD_TEXT_COLOR_ALPHA = 1.0f;
46 INTERN const osg::Vec4f HUD_TEXT_COLOR( 0.8f, 1.0f, 0.8f, HUD_TEXT_COLOR_ALPHA );
47 INTERN const Milliseconds HUD_TEXT_ANIMATION_FLIP_MILLISECONDS( 2000 );
48
49 ////////////////////////////////////////////////////////////////////////////////
50 ////////////////////////////// HudText::Args /////////////////////////////////
51 ////////////////////////////////////////////////////////////////////////////////
52
53 /*****************************************************************************
54 * ctor.
55 *****************************************************************************/
Args(void)56 HudText::Args::Args( void )
57 : mText(),
58 mPos(0,0),
59 mFont(GET_FONT_MAP().GetDefaultFont()),
60 mOrientation(eOrientation_BOTTOM_LEFT),
61 mColor(HUD_TEXT_COLOR),
62 mOffset(0,0),
63 mAnimation(eAnimation_NONE)
64 {
65 }
66
67 ////////////////////////////////////////////////////////////////////////////////
68 /////////////////////// HudText::Animation struct ////////////////////////////
69 ////////////////////////////////////////////////////////////////////////////////
70
71 /*****************************************************************************
72 * ctor.
73 *****************************************************************************/
Animation(const HudText::Args & args)74 HudText::Animation::Animation( const HudText::Args& args )
75 : mAnimation(args.mAnimation),
76 mStartMilliseconds(GET_TIMER().GetElapsedTime()),
77 mFlipRotation(0.0f)
78 {
79 }
80
81 ////////////////////////////////////////////////////////////////////////////////
82 ///////////////////////////////// HudText ////////////////////////////////////
83 ////////////////////////////////////////////////////////////////////////////////
84
85 /*****************************************************************************
86 * ctor/dtor.
87 *****************************************************************************/
HudText(const Args & args)88 HudText::HudText( const Args& args )
89 : Parent(),
90 mGeode(new osg::Geode),
91 mOsgText(new osgText::Text),
92 m3D(args.mOffset),
93 mAnimation(args)
94 {
95 // Empty mText is ok.
96 ASSERT( args.mPos[XX] >= 0 and args.mPos[XX] < 50000 );
97 ASSERT( args.mPos[YY] >= 0 and args.mPos[YY] < 50000 );
98
99 SET_TYPESIG(this,TYPESIG_HUD_TEXT);
100
101 // Compose geode.
102 mGeode->addDrawable( mOsgText.get() );
103
104 // Set the position according to orientation/alignment.
105 const Vector2 pos = OrientPosition( args );
106 mOsgText->setPosition( osg::Vec3( pos[XX], pos[YY], 0.0f ) );
107
108 // Set other text attributes.
109 mOsgText->setText( args.mText );
110 mOsgText->setFont( args.mFont.GetPathName() );
111 mOsgText->setCharacterSize( args.mFont.GetSize(), 1.0f );
112 mOsgText->setColor( RGBA2VEC4F(args.mColor) );
113
114 // Initial animation.
115 UpdateAnimation( mAnimation.mStartMilliseconds );
116 }
117
~HudText()118 HudText::~HudText()
119 {
120 INVALIDATE_TYPESIG(this,TYPESIG_HUD_TEXT);
121 }
122
123 /*****************************************************************************
124 * (private ctor subroutine)
125 * Re-orient the position of a HudText.
126 * The input position must be oriented at BOTTOM_LEFT.
127 *****************************************************************************/
128 Vector2
OrientPosition(const Args & args)129 HudText::OrientPosition( const Args& args )
130 {
131 CHECK_TYPESIG(this,TYPESIG_HUD_TEXT);
132
133 Vector2 pos = args.mPos;
134 const fp fontWidth = args.mFont.GetWidth();
135 const fp fontHeight = args.mFont.GetHeight();
136
137 // Computing Y is straightforward.
138 // Computing X for the right-alignment cases,
139 // requires accounting for how many chars the text string has.
140 switch ( args.mOrientation )
141 {
142 case eOrientation_BOTTOM_LEFT: default:
143 {
144 // NOP
145 //pos[XX] unchanged
146 //pos[YY] unchanged
147 }
148 break;
149
150 case eOrientation_BOTTOM_RIGHT:
151 {
152 pos[XX] = HUD_ORTHO_X2 - pos[XX] - fontWidth * args.mText.size();
153 //pos[YY] unchanged
154 }
155 break;
156
157 case eOrientation_TOP_LEFT:
158 {
159 //pos[XX] unchanged
160 pos[YY] = HUD_ORTHO_Y2 - pos[YY] - fontHeight;
161 }
162 break;
163
164 case eOrientation_TOP_RIGHT:
165 {
166 pos[XX] = HUD_ORTHO_X2 - pos[XX] - fontWidth * args.mText.size();
167 pos[YY] = HUD_ORTHO_Y2 - pos[YY] - fontHeight;
168 }
169 break;
170
171 case eOrientation_CENTER:
172 {
173 pos[XX] = HUD_ORTHO_X2 * 0.5f - (fontWidth * args.mText.size() * 0.5f);
174 pos[YY] = HUD_ORTHO_Y2 * 0.5f + pos[YY]; // client may add Y offset
175 }
176 break;
177 }
178
179 return pos;
180 }
181
182 /*****************************************************************************
183 * Change the value of HudText.
184 *****************************************************************************/
185 void
SetText(const string & text)186 HudText::SetText( const string& text )
187 {
188 CHECK_TYPESIG(this,TYPESIG_HUD_TEXT);
189
190 mOsgText->setText( text.c_str() );
191 }
192
193 void
SetText(const char text[])194 HudText::SetText( const char text[] )
195 {
196 CHECK_TYPESIG(this,TYPESIG_HUD_TEXT);
197
198 mOsgText->setText( text );
199 }
200
201 /*****************************************************************************
202 * Set position by transforming 3D vertex into 2D.
203 *****************************************************************************/
204 void
SetPosition3D(const WorldVertex & pos3)205 HudText::SetPosition3D( const WorldVertex& pos3 )
206 {
207 CHECK_TYPESIG(this,TYPESIG_HUD_TEXT);
208
209 // Deferred to Update().
210 m3D.mValid = true;
211 m3D.mPos3 = pos3;
212 m3D.mLastTime = Milliseconds(0);
213 }
214
215 /*****************************************************************************
216 * Set font size.
217 *****************************************************************************/
218 void
SetFontSize(const fp fontSize)219 HudText::SetFontSize( const fp fontSize )
220 {
221 CHECK_TYPESIG(this,TYPESIG_HUD_TEXT);
222
223 mOsgText->setCharacterSize( fontSize, 1.0f );
224 }
225
226 /*****************************************************************************
227 * Set color of text.
228 *****************************************************************************/
229 void
SetColor(const RGBA rgba)230 HudText::SetColor( const RGBA rgba )
231 {
232 CHECK_TYPESIG(this,TYPESIG_HUD_TEXT);
233
234 mOsgText->setColor( RGBA2VEC4F(rgba) );
235 }
236
237 /*****************************************************************************
238 * Set drawing text with 4 gradient colors.
239 *****************************************************************************/
240 void
SetGradientColors4(const TinyArray<4,RGBA> & colors4)241 HudText::SetGradientColors4( const TinyArray<4,RGBA>& colors4 )
242 {
243 CHECK_TYPESIG(this,TYPESIG_HUD_TEXT);
244 ASSERT( colors4.size() == 4 );
245
246 mOsgText->setColorGradientCorners( RGBA2VEC4F(colors4[0]),
247 RGBA2VEC4F(colors4[1]),
248 RGBA2VEC4F(colors4[2]),
249 RGBA2VEC4F(colors4[3]) );
250 mOsgText->setColorGradientMode( osgText::Text::OVERALL );
251 }
252
253 /*****************************************************************************
254 * Rotate text.
255 *****************************************************************************/
256 void
Rotate(const uint axis,const Radian rad)257 HudText::Rotate( const uint axis, const Radian rad )
258 {
259 ASSERT_AXIS3(axis);
260
261 // osg::Text has a quaternion.
262 Quaternion quat = mOsgText->getRotation();
263 quat.Rotate( axis, rad );
264 mOsgText->setRotation( quat );
265 }
266
267 /*****************************************************************************
268 * Update callback.
269 *****************************************************************************/
270 void
Update(View & view)271 HudText::Update( View& view )
272 {
273 const Milliseconds elapsedMilliseconds = GET_TIMER().GetElapsedTime();
274
275 // Update 3D position.
276 UpdatePosition3D( view, elapsedMilliseconds );
277
278 // Do text animation.
279 UpdateAnimation( elapsedMilliseconds );
280 }
281
282 void
UpdatePosition3D(View & view,const Milliseconds elapsedMilliseconds)283 HudText::UpdatePosition3D( View& view, const Milliseconds elapsedMilliseconds )
284 {
285 // 3D position:
286 // Deferred action of SetPosition3D().
287 // Note that osgText::setPosition() is a bit slow,
288 // so only call it a few times a second.
289 if ( m3D.mValid and (elapsedMilliseconds - m3D.mLastTime > Milliseconds(defs::HUD_FREQ)) )
290 {
291 // Remember time.
292 m3D.mLastTime = elapsedMilliseconds;
293
294 // Don't draw if 3D vertex is behind viewpoint.
295 const bool ifZFacing = view.IF_Z_FACING( m3D.mPos3 );
296 mGeode->setNodeMask( ifZFacing ? ~0 : 0 );
297 if ( ifZFacing )
298 {
299 // Transform 3D --> 2D.
300 Vector2 pos2 = view.Project( m3D.mPos3 );
301
302 // Remap 2D from viewport's dimensions to HUD's dimensions.
303 const Rect<int> viewport = view.GetRect();
304 pos2[XX] = Remap( pos2[XX], viewport.x2, HUD_ORTHO_X2 );
305 pos2[YY] = Remap( pos2[YY], viewport.y2, HUD_ORTHO_Y2 );
306
307 // Adjust so that text isn't over 3D object.
308 pos2[XX] += m3D.mOffset2[XX];
309 pos2[YY] += m3D.mOffset2[YY];
310
311 // Directly change position of osg::Text.
312 mOsgText->setPosition( osg::Vec3( pos2[XX], pos2[YY], 0.0f ) );
313 }
314 }
315 }
316
317 void
UpdateAnimation(const Milliseconds elapsedMilliseconds)318 HudText::UpdateAnimation( const Milliseconds elapsedMilliseconds )
319 {
320 const Milliseconds delta = elapsedMilliseconds - mAnimation.mStartMilliseconds;
321 ASSERT( delta >= Milliseconds(0) );
322
323 switch ( mAnimation.mAnimation )
324 {
325 case eAnimation_FLIP:
326 {
327 if ( delta < HUD_TEXT_ANIMATION_FLIP_MILLISECONDS )
328 {
329 // Compute 90 degrees of rotation according to elapsed milliseconds.
330 const Radian rad( (1.0 - (delta.FPX()) / HUD_TEXT_ANIMATION_FLIP_MILLISECONDS.FPX()) * math::RADIAN_90 );
331
332 // First undo rotation then do new rotation.
333 Rotate( AXIS_X, -mAnimation.mFlipRotation );
334 Rotate( AXIS_X, rad );
335
336 // Remember previous rotation to later undo it.
337 mAnimation.mFlipRotation = rad;
338 }
339 }
340 break;
341
342 case eAnimation_NONE: default:
343 break;
344 }
345 }
346
347 } // namespace hud
348