1 /***************************************************************************
2 util.cpp - General utility functions
3 -------------------
4 begin : Sun Jun 22 2003
5 copyright : (C) 2003 by Gabor Torok
6 email : cctorok@yahoo.com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "common/constants.h"
19 #include "util.h"
20 #include "render/renderlib.h"
21 #include "debug.h"
22
23 using namespace std;
24
25 /// Rotate the 2D point(x,y) by angle(in degrees). Return the result in px,py.
26
rotate(Sint16 x,Sint16 y,Sint16 * px,Sint16 * py,float angle)27 void Util::rotate( Sint16 x, Sint16 y, Sint16 *px, Sint16 *py, float angle ) {
28 // convert to radians
29 // angle = degreesToRadians(angle);
30 // rotate
31 float oldx = static_cast<float>( x );
32 float oldy = static_cast<float>( y );
33 *px = ( Sint16 )rint( ( oldx * Constants::cosFromAngle( angle ) ) - ( oldy * Constants::sinFromAngle( angle ) ) );
34 *py = ( Sint16 )rint( ( oldx * Constants::sinFromAngle( angle ) ) + ( oldy * Constants::cosFromAngle( angle ) ) );
35 }
36
dot_product(float v1[3],float v2[3])37 float Util::dot_product( float v1[3], float v2[3] ) {
38 return ( v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2] );
39 }
40
normalize(float v[3])41 void Util::normalize( float v[3] ) {
42 float f = 1.0f / sqrt( dot_product( v, v ) );
43
44 v[0] *= f;
45 v[1] *= f;
46 v[2] *= f;
47 }
48
cross_product(const float * v1,const float * v2,float * out)49 void Util::cross_product( const float *v1, const float *v2, float *out ) {
50 out[0] = v1[1] * v2[2] - v1[2] * v2[1];
51 out[1] = v1[2] * v2[0] - v1[0] * v2[2];
52 out[2] = v1[0] * v2[1] - v1[1] * v2[0];
53 }
54
multiply_vector_by_matrix(const float m[9],float v[3])55 void Util::multiply_vector_by_matrix( const float m[9], float v[3] ) {
56 float tmp[3];
57
58 tmp[0] = v[0] * m[0] + v[1] * m[3] + v[2] * m[6];
59 tmp[1] = v[0] * m[1] + v[1] * m[4] + v[2] * m[7];
60 tmp[2] = v[0] * m[2] + v[1] * m[5] + v[2] * m[8];
61
62 v[0] = tmp[0];
63 v[1] = tmp[1];
64 v[2] = tmp[2];
65 }
66
multiply_vector_by_matrix2(const float m[16],float v[4])67 void Util::multiply_vector_by_matrix2( const float m[16], float v[4] ) {
68 float tmp[4];
69
70 tmp[0] = v[0] * m[0] + v[1] * m[4] + v[2] * m[8] + v[3] * m[12];
71 tmp[1] = v[0] * m[1] + v[1] * m[5] + v[2] * m[9] + v[3] * m[13];
72 tmp[2] = v[0] * m[2] + v[1] * m[6] + v[2] * m[10] + v[3] * m[14];
73 tmp[3] = v[0] * m[3] + v[1] * m[7] + v[2] * m[11] + v[3] * m[15];
74
75 v[0] = tmp[0];
76 v[1] = tmp[1];
77 v[2] = tmp[2];
78 v[3] = tmp[3];
79 }
80
81
82 /// Returns the last OpenGL error ( simple non-GLU version).
83
84 /// Return a string containing the last OpenGL error.
85 /// Useful to debug strange OpenGL behaviors
86
getOpenGLError()87 char * Util :: getOpenGLError() {
88 int error;
89 error = glGetError();
90
91 // All openGl errors possible
92 switch ( error ) {
93 case GL_NO_ERROR : return "GL_NO_ERROR";
94 case GL_INVALID_ENUM : return "GL_INVALID_ENUM";
95 case GL_INVALID_VALUE : return "GL_INVALID_VALUE";
96 case GL_INVALID_OPERATION : return "GL_INVALID_OPERATION";
97 case GL_STACK_OVERFLOW : return "GL_STACK_OVERFLOW";
98 case GL_OUT_OF_MEMORY : return "GL_OUT_OF_MEMORY";
99 default :
100 cerr << "GL Error=" << error << endl;
101 return "Unknown error";
102 }
103 }
104
105 /// Returns next word from the given position.
106
107 /// If there is not a space at the given
108 /// position, the function suppose it is the first letter of the word wanted.
109
getNextWord(const string & theInput,int fromPos,int & endWord)110 string Util::getNextWord( const string& theInput, int fromPos, int &endWord ) {
111 int firstChar, lastStringChar;
112 string sub;
113
114 if ( theInput.empty() || fromPos == -1 )
115 return sub;
116
117 lastStringChar = theInput.find_last_not_of( ' ' );
118 if ( theInput[fromPos] == ' ' )
119 firstChar = theInput.find_first_not_of( ' ', fromPos );
120 else
121 firstChar = fromPos;
122
123 endWord = theInput.find_first_of( ' ', firstChar );
124
125 if ( endWord == -1 && lastStringChar >= firstChar && firstChar != -1 )
126 sub = theInput.substr( firstChar, lastStringChar - firstChar + 1 );
127 else
128 sub = theInput.substr( firstChar, endWord - firstChar );
129
130 return sub;
131 }
132
133 /// get the angle between two shapes (x,y,width,depth)
134
getAngle(float fx,float fy,float fw,float fd,float tx,float ty,float tw,float td)135 float Util::getAngle( float fx, float fy, float fw, float fd, float tx, float ty, float tw, float td ) {
136 // figure out targetCreatureAngle
137 float sx = fx + ( fw / 2 );
138 float sy = fy - ( fd / 2 );
139 float ex = tx + ( tw / 2 );
140 float ey = ty - ( td / 2 );
141
142 float x = ex - sx;
143 float y = ey - sy;
144 if ( x == 0.0f )
145 x = 0.001f;
146 // FIXME: Use lookup table instead of expensive atan().
147 float angle = Constants::toAngle( atan( y / x ) );
148
149 // read about the arctan problem:
150 // http://hyperphysics.phy-astr.gsu.edu/hbase/ttrig.html#c3
151 // q = 1;
152 if ( x < 0 ) { // Quadrant 2 & 3
153 // q = ( y >= 0 ? 2 : 3);
154 angle += 180;
155 } else if ( y < 0 ) { // Quadrant 4
156 // q = 4;
157 angle += 360;
158 }
159
160 // normalize
161 if ( angle < 0.0f )
162 angle = 360.0f + angle;
163 if ( angle >= 360.0f )
164 angle -= 360.0f;
165
166 return angle;
167 }
168
169 #define FOV_ANGLE 60
170
171 /// Is px,py in the field of vision defined by x,y,angle?
172
isInFOV(float x,float y,float angle,float px,float py)173 bool Util::isInFOV( float x, float y, float angle, float px, float py ) {
174 float angleToP = getAngle( x, y, 1, 1, px, py, 1, 1 );
175
176 float diff = fabs( diffAngle( angle, angleToP ) );
177 bool b = ( diff < FOV_ANGLE );
178
179 return b;
180 }
181
182 /// Returns the difference between two angles (degrees version).
183
diffAngle(float a,float b)184 float Util::diffAngle( float a, float b ) {
185 // a -= (static_cast<int>(a) / 360) * 360;
186 // b -= (static_cast<int>(b) / 360) * 360;
187 float diff = a - b;
188 if ( diff > 180.0f ) {
189 diff = -( 360.0f - diff );
190 } else if ( diff < -180.0f ) {
191 diff = 360 + diff;
192 }
193 return diff;
194 }
195
196 /// Draws a little bar that displays a value graphically.
197
drawBar(int x,int y,float barLength,float value,float maxValue,float red,float green,float blue,float gradient,GuiTheme * theme,int layout)198 void Util::drawBar( int x, int y, float barLength, float value, float maxValue,
199 float red, float green, float blue, float gradient, GuiTheme *theme,
200 int layout ) {
201 float percent = ( maxValue == 0 ? 0 : ( value >= maxValue ? 100.0f : value / ( maxValue / 100.0f ) ) );
202 float length = barLength * ( percent / 100.0f );
203 if ( length < 0 ) {
204 length = percent = 0;
205 }
206
207 glPushMatrix();
208 glTranslatef( x, y, 0 );
209
210 glLineWidth( 6.0f );
211
212 // glColor3f( 0.2f, 0.2f, 0.2f );
213 if ( theme && theme->getWindowBorder() ) {
214 glColor4f( theme->getWindowBorder()->color.r,
215 theme->getWindowBorder()->color.g,
216 theme->getWindowBorder()->color.b,
217 theme->getWindowBorder()->color.a );
218 } else {
219 glColor3f( 0, 0, 0 );
220 }
221 glBegin( GL_LINES );
222 if ( layout == HORIZONTAL_LAYOUT ) {
223 glVertex3f( 0, 0, 0 );
224 glVertex3f( barLength, 0, 0 );
225 } else {
226 glVertex3f( 0, 0, 0 );
227 glVertex3f( 0, barLength, 0 );
228 }
229 glEnd();
230
231 // default args so I don't have to recompile .h file
232 if ( red == -1 ) {
233 red = 0.5f;
234 green = 1.0f;
235 blue = 0.5f;
236 }
237 if ( !gradient || percent > 40.0f ) {
238 glColor3f( red, green, blue );
239 } else if ( percent > 25.0f ) {
240 glColor3f( 1.0f, 1.0f, 0.5f );
241 } else {
242 glColor3f( 1.0f, 0.5f, 0.5f );
243 }
244 glBegin( GL_LINES );
245 if ( layout == HORIZONTAL_LAYOUT ) {
246 glVertex3f( 0, 0, 0 );
247 glVertex3f( length, 0, 0 );
248 } else {
249 glVertex3f( 0, barLength - length, 0 );
250 glVertex3f( 0, barLength, 0 );
251 }
252 glEnd();
253
254 glLineWidth( 1.0f );
255
256 glPopMatrix();
257 }
258
getRandomSum(float base,int count,float div)259 float Util::getRandomSum( float base, int count, float div ) {
260 float sum = 0;
261 float third = base / div;
262 for ( int i = 0; i < ( count < 1 ? 1 : count ); i++ ) {
263 sum += Util::roll( 0.0f, third ) + base - third;
264 }
265 return sum;
266 }
267
toLowerCase(char * s)268 char *Util::toLowerCase( char *s ) {
269 char *p = s;
270 while ( *p ) {
271 if ( *p >= 'A' && *p <= 'Z' ) *p = *p - 'A' + 'a';
272 p++;
273 }
274 return s;
275 }
276
toLowerCase(string & s)277 string& Util::toLowerCase( string& s ) {
278 for ( unsigned int i = 0; i < s.size(); i++ ) {
279 if ( 'A' <= s[i] && s[i] <= 'Z' ) s[i] -= 'A' - 'a';
280 }
281 return s;
282 }
283
284 // FIXME: take into account, existing |-s in text
addLineBreaks(const char * in,char * out,int lineLength)285 char *Util::addLineBreaks( const char *in, char *out, int lineLength ) {
286 strcpy( out, "" );
287 char tmp[3000];
288 strcpy( tmp, in );
289 char *token = strtok( tmp, " \r\n\t" );
290 int count = 0;
291 while ( token ) {
292 int len = static_cast<int>( strlen( token ) );
293 for ( int i = len - 1; i >= 0; i-- ) {
294 if ( token[i] == '|' ) {
295 count = len - i;
296 len = count;
297 break;
298 }
299 }
300 if ( count + len >= lineLength ) {
301 strcat( out, "|" );
302 count = 0;
303 }
304 strcat( out, token );
305 strcat( out, " " );
306 count += len + 1;
307 token = strtok( NULL, " \r\n\t" );
308 }
309 return out;
310 }
311
312 /// Break a string into paragraphs using a special delimiter.
313
getLines(const char * in,vector<string> * out)314 void Util::getLines( const char *in, vector<string> *out ) {
315 char tmp[3000];
316 char *q = tmp;
317 strncpy( tmp, in, 2999 );
318 tmp[2999] = '\0';
319
320 char *p = strchr( q, '|' );
321 while ( p ) {
322 *p = 0;
323 string s = q;
324 out->push_back( s );
325 q = p + 1;
326 p = strchr( q, '|' );
327 }
328 string s = q;
329 out->push_back( s );
330 }
331
getLight(float * normal,float lightAngle)332 float Util::getLight( float *normal, float lightAngle ) {
333 // Simple light rendering:
334 // need the normal as mapped on the xy plane
335 // it's degree is the intensity of light it gets
336 float x = ( normal[0] == 0 ? 0.01f : normal[0] );
337 float y = ( normal[1] == 0 ? 0.01f : normal[1] );
338 float z = ( normal[2] == 0 ? 0.01f : normal[2] );
339
340 return ( getLightComp( x, y, lightAngle ) +
341 getLightComp( z, y, lightAngle ) +
342 getLightComp( z, z, lightAngle ) ) / 3.0f;
343 }
344
getLightComp(float x,float y,float lightAngle)345 float Util::getLightComp( float x, float y, float lightAngle ) {
346 float rad = atan( y / x );
347 float angle = ( 180.0f * rad ) / 3.14159;
348
349 // read about the arctan problem:
350 // http://hyperphysics.phy-astr.gsu.edu/hbase/ttrig.html#c3
351 int q = 1;
352 if ( x < 0 ) { // Quadrant 2 & 3
353 q = ( y >= 0 ? 2 : 3 );
354 angle += 180;
355 } else if ( y < 0 ) { // Quadrant 4
356 q = 4;
357 angle += 360;
358 }
359
360 // assertion
361 #ifdef DEBUG_3DS
362 if ( angle < 0 || angle > 360 ) {
363 cerr << "Warning: object: " << getName() << " angle=" << angle << " quadrant=" << q << endl;
364 }
365 #endif
366
367 // calculate the angle distance from the light
368 float delta = 0;
369 if ( angle > lightAngle && angle < lightAngle + 180.0f ) {
370 delta = angle - lightAngle;
371 } else {
372 if ( angle < lightAngle ) angle += 360.0f;
373 delta = ( 360 + lightAngle ) - angle;
374 }
375
376 // assertion
377 #ifdef DEBUG_3DS
378 if ( delta < 0 || delta > 180.0f ) {
379 cerr << "WARNING: object: " << getName() << " angle=" << angle << " delta=" << delta << endl;
380 }
381 #endif
382
383 // reverse and convert to value between 0 and 1
384 delta = 1 - ( 0.4f * ( delta / 180.0f ) );
385
386 // store the value
387 return delta;
388 }
389
StringCaseCompare(const std::string sStr1,const std::string sStr2)390 bool Util::StringCaseCompare( const std::string sStr1, const std::string sStr2 ) {
391 if ( sStr1.length() == sStr2.length() )
392 return std::equal( sStr1.begin(), sStr1.end(), sStr2.begin(), equal_ignore_case<std::string::value_type>() );
393 else
394 return false;
395 }
396
397 // *** algorithms based on the Mersenne Twister random number generator ***
398
399 #define MT_N 624L
400 #define MT_M 397L
401
402 static unsigned long mt_sequence[MT_N];
403 static long mt_index = MT_N + 1;
404
405 /// Starts the Mersenne Twister random number generator with a specified seed.
406
mt_srand(unsigned long s)407 void Util::mt_srand( unsigned long s ) {
408 mt_sequence[0] = s & 0xffffffffUL;
409
410 for ( mt_index = 1; mt_index < MT_N; mt_index++ ) {
411 mt_sequence[mt_index] = ( 1812433253UL * ( mt_sequence[mt_index - 1] ^ ( mt_sequence[mt_index - 1] >> 30 ) ) + mt_index );
412 mt_sequence[mt_index] &= 0xffffffffUL;
413 }
414 }
415
416 #define MT_HI 0x80000000UL
417 #define MT_LO 0x7fffffffUL
418
419 static unsigned long mag[2] = { 0x0UL, 0x9908b0dfUL };
420 static const float multiplier = 1.0 / 4294967296.0;
421
422 /// Mersenne Twister random number generator.
423
424 /// This is the Mersenne Twister core algorithm. Returns a float between 0 and 1.
425 /// Multiple times faster than rand() and has a period of (2^19937 - 1).
426
mt_rand()427 float Util::mt_rand() {
428 register unsigned long y;
429
430 if ( mt_index >= MT_N ) {
431 register long k;
432
433 // Seed the generator when not yet done.
434 if ( mt_index == MT_N + 1 ) mt_srand( ( unsigned long )time( ( time_t* )NULL ) );
435
436 for ( k = 0; k < MT_N - MT_M; ++k ) {
437 y = ( mt_sequence[k] & MT_HI ) | ( mt_sequence[k + 1] & MT_LO );
438 mt_sequence[k] = mt_sequence[k + MT_M] ^ ( y >> 1 ) ^ mag[y & 0x1UL];
439 }
440
441 for ( ; k < MT_N - 1; ++k ) {
442 y = ( mt_sequence[k] & MT_HI ) | ( mt_sequence[k + 1] & MT_LO );
443 mt_sequence[k] = mt_sequence[k + ( MT_M - MT_N )] ^ ( y >> 1 ) ^ mag[y & 0x1UL];
444 }
445
446 y = ( mt_sequence[MT_N - 1] & MT_HI ) | ( mt_sequence[0] & MT_LO );
447 mt_sequence[MT_N - 1] = mt_sequence[MT_M - 1] ^ ( y >> 1 ) ^ mag[y & 0x1UL];
448 mt_index = 0;
449 }
450
451 y = mt_sequence[mt_index++];
452
453 /* Tempering */
454 y ^= ( y >> 11 );
455 y ^= ( y << 7 ) & 0x9d2c5680UL;
456 y ^= ( y << 15 ) & 0xefc60000UL;
457 y ^= ( y >> 18 );
458
459 float ret = ( float )y * multiplier;
460 if ( ret < 0 || ret >= 1 ) {
461 cerr << "*** error: rand=" << ret << " mt_index=" << mt_index << " y=" << y << ". Correcting..." << endl;
462 // this happens occasianlly... fall back to rand()
463 ret = rand() / RAND_MAX;
464 }
465 return ret;
466 }
467
468 /// Returns a random integer from 0 to size - 1.
469
470 /// size must be bigger than 0 and not bigger than RAND_MAX + 1
471 /// makes noise otherwise ;-)
472 /// size is exclusive
473
dice(int size)474 int Util::dice( int size ) {
475 return static_cast<int>( roll( 0, size ) );
476 }
477
478 /// Returns a random integer between min and max.
479
480 /// min must be <= max
481 /// this method is inclusive on both min and max
482
pickOne(int min,int max)483 int Util::pickOne( int min, int max ) {
484 return dice( max + 1 - min ) + min;
485 }
486
487 /// random float from min to max (both inclusive)
488
roll(float min,float max)489 float Util::roll( float min, float max ) {
490 return ( max - min ) * mt_rand() + min;
491 }
492