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