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