liblashgame

Pathfinder and path decision making library for 2D tile game
git clone git://holbrook.no/liblashgame.git
Log | Files | Refs

fallbounceline.cpp (16222B)


      1 // ball fall and bounce against line
      2 
      3 #include "SDL.h"
      4 #include "SDL_gfxPrimitives.h"
      5 #include "../lash_game_sprite.h"
      6 #include "liblash/lash_tree3.h"
      7 #include "../../../liblash/lash_tree3_dump.h"
      8 #include "../../lash_game_standard.h"
      9 #include <stdio.h>
     10 #include <math.h>
     11 #include <stdlib.h>
     12 #include <time.h>
     13 #include <vector>
     14 
     15 #define SCREEN_WIDTH 1600
     16 #define SCREEN_HEIGHT 1000
     17 #define BPP 32
     18 #define BALL_LIMIT 12
     19 #define PIXELS_METER 100
     20 #define FPS 60
     21 #define LINE_WEIGHT 1
     22 #define BOUNCE 0.4
     23 #define BALL_MASS 2.0
     24 
     25 
     26 namespace lash_fallbounceline {
     27 	struct vertices {
     28 		int16_t x1;
     29 		int16_t y1;
     30 		int16_t x2;
     31 		int16_t y2;
     32 	};
     33 
     34 	struct ball {
     35 		//unsigned int index;
     36 		Lash_Sprite_2D_Simple *sprite;
     37 		unsigned int color;
     38 	};
     39 	
     40 	class e_display : public std::exception {
     41 		virtual const char* what() const throw() {
     42 			return "Could not generate display!";
     43 		}
     44 	} displayException;	
     45 	
     46 	class e_collision : public std::exception {
     47 		virtual const char* what() const throw() {
     48 			return "Invalid collision data!";
     49 		}
     50 	} collisionException;	
     51 };
     52 
     53 void cursordata (uint8_t *data, uint8_t *mask) {
     54 	int i, j;
     55 	for (i=0; i<8; i++) {
     56 		for (j=0; j<8; j++) {
     57 			if (j >= 2 || j <= 6) {
     58 				*(data+j+(8*i)) = 0x01;
     59 			}
     60 			*(mask+j+(8*i)) = 0x01;
     61 		}
     62 	}
     63 }
     64 
     65 int main(int argc, char *argv[]) {
     66 	
     67 	int i;
     68 	
     69 	//int test_warmup_count = 100;
     70 	
     71 	//struct timespec test_approx_time_begin, test_approx_time_end, test_isec_time_begin, test_isec_time_end;
     72 	//long test_approx_time_total, test_isec_time_total;
     73 	//int test_approx_count = 10000;
     74 	//int test_isec_count = 10000;
     75 	
     76 	int run = 1;
     77 	int pause = 0;
     78 	int step = 0;
     79 	//unsigned long frame_current_time = 0;
     80 	unsigned int frames_total = 0;
     81 	//unsigned int frame_lastmouse = 0;
     82 	float bounce;
     83 	lash_fallbounceline::ball *spawn;
     84 	unsigned short spawnx;
     85 	//float alignedxtoball, alignedytoball;
     86 	lash_game_coords_float_t ballcoll1, ballcoll2;
     87 	
     88 	float ball_radius;
     89 	float line1_rads, line2_rads;
     90 	lash_game_line_t line1, line2;
     91 	lash_game_coords_float_t line1_start, line2_start, line1_end, line2_end;
     92 	float line1_length, line2_length;
     93 	//lash_fallbounceline::vertices line1_v, line2_v;
     94 	
     95 	SDL_Surface *screen;
     96 	SDL_Event event;
     97 	SDL_Cursor *cursor;
     98 	
     99 	SDL_Rect screen_r;
    100 	//SDL_Rect ball_r;
    101 	
    102 	int16_t *line1_collision, *line2_collision;
    103 	
    104 	uint32_t screen_c;
    105 	uint32_t line1_c, line2_c;
    106 	uint32_t cursor_c;
    107 	//uint32_t line1border_c;
    108 	uint32_t line2border_c; 
    109 	uint32_t borderactive_c, borderinactive_c;
    110 	uint8_t cursor_data[64], cursor_mask[64];
    111 	
    112 	int mousex, mousey;
    113 	char debugstring[512];
    114 	int coll;
    115 	
    116 	// vectors cannot be created with references
    117 	std::vector<struct lash_fallbounceline::ball> balls;
    118 	balls.resize(BALL_LIMIT);
    119 	std::vector<struct lash_fallbounceline::ball*> balls_free;
    120 	std::vector<struct lash_fallbounceline::ball*> balls_active;
    121 	
    122 	if (argc < 10) {
    123 		printf("Usage: %s ball_radius line1_m line1_c line1_x line1_l line2_m line2_c line2_x line2_l [bounce]\n", argv[0]);
    124 		return 1;
    125 	}
    126 	
    127 	ball_radius = atof(argv[1]);
    128 	
    129 	line1.m = atof(argv[2]);
    130 	line1.c = atof(argv[3]);
    131 	line1_start.x = atof(argv[4]);
    132 	line1_length = atof(argv[5]);
    133 	
    134 	line2.m = atof(argv[6]);
    135 	line2.c = atof(argv[7]);
    136 	line2_start.x = atof(argv[8]);
    137 	line2_length = atof(argv[9]);
    138 	
    139 	if (argv[10] != NULL)
    140 		bounce = atof(argv[10]);
    141 	else
    142 		bounce = BOUNCE;
    143 	
    144 	line1_rads = atan(line1.m / 1);
    145 	line2_rads = atan(line2.m / 1);
    146 	
    147 	line1_start.y = (line1.m * line1_start.x) + line1.c;
    148 	line2_start.y = (line2.m * line2_start.x) + line2.c;
    149 	
    150 	//line1_v.y1 = line1_start.y;
    151 	//line1_v.x1 = line1_start.x;
    152 	line1_end.y = line1_start.y + sin(line1_rads) * line1_length;
    153 	line1_end.x = line1_start.x + cos(line1_rads) * line1_length;
    154 	//line1_v.y2 = round(line1_end.y);
    155 	//line1_v.x2 = round(line1_end.x);
    156 
    157 	//line2_v.y1 = line2_start.y;
    158 	//line2_v.x1 = line2_start.x;
    159 	line2_end.y = line2_start.y + sin(line2_rads) * line2_length;
    160 	line2_end.x = line2_start.x + cos(line2_rads) * line2_length;
    161 	//line2_v.y2 = round(line2_end.y);
    162 	//line2_v.x2 = round(line2_end.x);
    163 	
    164 	line1_collision = (int16_t*)malloc(sizeof(int16_t)*8);
    165 	line2_collision = (int16_t*)malloc(sizeof(int16_t)*8);
    166 	
    167 	if (line1_collision == NULL || line2_collision == NULL) {
    168 		return 1;
    169 	} else {
    170 		uint16_t modx, mody, startx, starty, endx, endy;
    171 		modx = cos(line1_rads) * ball_radius;
    172 		mody = sin(line1_rads) * ball_radius;
    173 		
    174 		startx = line1_start.x;
    175 		starty = line1_start.y;
    176 		endx = line1_end.x;
    177 		endy = line1_end.y;
    178 		
    179 		// top left
    180 		*line1_collision = startx - (modx + mody);
    181 		*(line1_collision+4) = starty - (mody - modx);
    182 		
    183 		// top right
    184 		*(line1_collision+1) = endx + (modx - mody);
    185 		*(line1_collision+5) = endy + (mody + modx);
    186 		
    187 		// bottom right
    188 		*(line1_collision+2) = endx + (modx + mody);
    189 		*(line1_collision+6) = endy + (mody - modx);
    190 		
    191 		// bottom left
    192 		*(line1_collision+3) = startx - (modx - mody);
    193 		*(line1_collision+7) = starty - (mody + modx);
    194 		
    195 		
    196 		modx = cos(line2_rads) * ball_radius;
    197 		mody = sin(line2_rads) * ball_radius;
    198 		startx = line2_start.x;
    199 		starty = line2_start.y;
    200 		endx = line2_end.x;
    201 		endy = line2_end.y;
    202 		
    203 		// top left
    204 		*line2_collision = startx - (modx + mody);
    205 		*(line2_collision+4) = starty - (mody - modx);
    206 		
    207 		// top right
    208 		*(line2_collision+1) = endx + (modx - mody);
    209 		*(line2_collision+5) = endy + (mody + modx);
    210 		
    211 		// bottom right
    212 		*(line2_collision+2) = endx + (modx + mody);
    213 		*(line2_collision+6) = endy + (mody - modx);
    214 		
    215 		// bottom left
    216 		*(line2_collision+3) = startx - (modx - mody);
    217 		*(line2_collision+7) = starty - (mody + modx);
    218 		
    219 	}
    220 	
    221 	srand(clock());
    222 	
    223 	for (i = 0; i < BALL_LIMIT; i++) {
    224 		balls[i].sprite = new Lash_Sprite_2D_Simple((uint8_t)ball_radius * 2, (uint8_t)ball_radius * 2, 0.0, 0.0);
    225 		balls_free.push_back(&balls[i]);
    226 	}
    227 	
    228 	screen_c = 0xFF707070;
    229 	line1_c = 0xFF00AAFF;
    230 	line2_c = 0xFFAA00FF;
    231 	cursor_c = 0x00FF00FF;
    232 	borderinactive_c = 0xFFFFFF99;
    233 	borderactive_c = 0xFFFFFFFF;
    234 	//line1border_c = borderinactive_c;
    235 	line2border_c = borderinactive_c;
    236 	
    237 	screen_r.x = 0;
    238 	screen_r.y = 0;
    239 	screen_r.w = SCREEN_WIDTH;
    240 	screen_r.h = SCREEN_HEIGHT;
    241 	
    242 	SDL_Init(SDL_INIT_VIDEO);
    243 	screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, BPP, SDL_HWSURFACE | SDL_DOUBLEBUF);
    244 	
    245 	if (screen == NULL)
    246 		throw lash_fallbounceline::displayException;
    247 	
    248 	cursordata(cursor_data, cursor_mask);
    249 	
    250 	cursor = SDL_CreateCursor(cursor_data, cursor_mask, 8, 8, 4, 0);
    251 	//SDL_SetCursor(cursor);
    252 	//SDL_ShowCursor(1);
    253 	
    254 	while (run) {
    255 		
    256 		//frame_current_time = SDL_GetTicks();
    257 		
    258 		SDL_GetMouseState(&mousex, &mousey);
    259 		spawn = NULL;
    260 		
    261 		while (SDL_PollEvent(&event)) {
    262 			switch(event.type) {
    263 				case SDL_KEYDOWN:
    264 					if (event.key.keysym.sym == SDLK_ESCAPE)
    265 						run = 0;
    266 					else if (event.key.keysym.sym == SDLK_p)
    267 						if (pause == 1)
    268 							pause = 0;
    269 						else
    270 							pause = 1;
    271 					else if (event.key.keysym.sym == SDLK_s)
    272 						step = 1;
    273 					break;
    274 				case SDL_MOUSEMOTION:
    275 					break;
    276 					
    277 				/// \todo after exhausting BALL_LIMIT it always picks the same ball object
    278 				case SDL_MOUSEBUTTONDOWN:
    279 					if (event.button.button == SDL_BUTTON_LEFT && pause == 0) {
    280 						if (balls_free.size() != 0 && spawn == NULL) {
    281 							spawnx = event.button.x;
    282 							balls_active.push_back(balls_free.back());
    283 							balls_free.pop_back();
    284 							//frame_lastmouse = frame_current_time;
    285 							spawn = balls_active.back();
    286 						}
    287 					}
    288 					break;
    289 			}
    290 		}
    291 		
    292 		if (pause == 0 || step == 1) {
    293 			
    294 		if (spawn != NULL) {
    295 			spawn->sprite->clear();
    296 			spawn->sprite->setX(spawnx);
    297 			spawn->sprite->setY(spawn->sprite->getH());
    298 			spawn->sprite->setMass(BALL_MASS);
    299 			spawn->sprite->setAcceleration(((LASH_GAME_GRAVITY_EARTH* PIXELS_METER) / FPS )/ FPS, (3 * M_PI_2));
    300 			spawn->color = rand() % (int)pow(2,24);
    301 			spawn->color <<= 8;
    302 			spawn->color |= 255;
    303 		}
    304 		
    305 		if (balls_active.size() > 0) {
    306 		// 
    307 		// MOVEMENT
    308 		//
    309 	
    310 		
    311 		// erasing an element inside an iteration; new iterator must be returned instead of incrementing ... http://stackoverflow.com/questions/4645705/vector-erase-iterator
    312 		for (std::vector<struct lash_fallbounceline::ball*>::iterator it = balls_active.begin(); it != balls_active.end(); ) {
    313 			(*it)->sprite->move();
    314 			(*it)->sprite->accelerate();
    315 			if ((*it)->sprite->getY() > SCREEN_HEIGHT) {
    316 				balls_free.push_back((*it));
    317 				it = balls_active.erase(it);
    318 			} else {
    319 				++it;
    320 			}
    321 		}
    322 	
    323 		} // if balls_active.size() > 0
    324 			
    325 		} // if pause == 0
    326 		
    327 		
    328 		if (balls_active.size() > 0) {
    329 			
    330 		//
    331 		// COLLISION
    332 		//
    333 		
    334 		/// \todo move commented collision approximation code to separate file, maybe can be used later
    335 		// APPROXIMATION
    336 		/*
    337 		float ytoball, xtoball, distancetoball, alignedangletoball;
    338 		
    339 		for (i = 0; i < test_approx_count + test_warmup_count; i++) {
    340 		
    341 		clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &test_approx_time_begin);
    342 		
    343 		coll = 0;
    344 		ytoball = balls_active[0]->sprite->getY() - line2_end.y;
    345 		xtoball = balls_active[0]->sprite->getX() - line2_end.x;
    346 		distancetoball = sqrt(pow(ytoball, 2) + pow(xtoball,2));
    347 		//alignedangletoball = M_PI - line2_rads - fabs(atan2(ytoball, xtoball));
    348 		alignedangletoball = atan(ytoball / xtoball) - line2_rads;
    349 		alignedxtoball = cos(alignedangletoball) * distancetoball;
    350 		alignedytoball = sin(alignedangletoball) * distancetoball;
    351 		if (alignedxtoball > -ball_radius && alignedxtoball - line2_length < ball_radius && abs(alignedytoball) < ball_radius)
    352 			coll = 1;
    353 		
    354 		clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &test_approx_time_end);
    355 		
    356 		if (i >= test_warmup_count)
    357 			test_approx_time_total = test_approx_time_end.tv_nsec - test_approx_time_begin.tv_nsec;
    358 		
    359 		} // test approx timing
    360 		*/
    361 
    362 		// INTERSECTION TEST
    363 		
    364 		for (i = 0; i < (int)balls_active.size(); i++) {
    365 			
    366 		char iseccollcount;
    367 		float linerads, line_x, line_y;
    368 		lash_game_coords_float_t ballcoords;
    369 		ballcoords.x = balls_active[i]->sprite->getX();
    370 		ballcoords.y = balls_active[i]->sprite->getY();
    371 	
    372 		coll = 0;
    373 		iseccollcount = lashCollisionCheckCircleLine(ballcoords, ball_radius, line2, &ballcoll1, &ballcoll2);
    374 		if (iseccollcount == 2) {
    375 			// check both sides!
    376 			//if ((ballcoll2.x > line2_start.x && ballcoll1.x < line2_start.x) || (ballcoll1.x < line2_end.x && ballcoll2.x > line2_end.x) || (ballcoll1.x > line2_start.x && ballcoll2.x < line2_end.x)) {
    377 			if (ballcoll2.x > line2_start.x && ballcoll1.x < line2_start.x) {
    378 				//linerads = line2_rads + M_PI_2;
    379 				linerads = atan((balls_active[i]->sprite->getY() - line2_start.y) / (balls_active[i]->sprite->getX() - line2_start.x)) + M_PI_2;
    380 				line_x = line2_start.x;
    381 				line_y = line2_start.y;
    382 				coll = -1;
    383 			}
    384 			else if (ballcoll1.x < line2_end.x && ballcoll2.x > line2_end.x) {
    385 				linerads = atan((balls_active[i]->sprite->getY() - line2_end.y) / (balls_active[i]->sprite->getX() - line2_end.x)) + M_PI_2;
    386 				line_x = line2_end.x;
    387 				line_y = line2_end.y;
    388 				coll = 1;
    389 			}
    390 			else if (ballcoll1.x > line2_start.x && ballcoll2.x < line2_end.x) {
    391 				linerads = line2_rads;
    392 				coll = 2;
    393 			}
    394 		} else if (iseccollcount == 1) {
    395 			linerads = line2_rads;
    396 			coll = 1;
    397 		}
    398 		
    399 		if (coll == 0) {
    400 			iseccollcount = lashCollisionCheckCircleLine(ballcoords, ball_radius, line1, &ballcoll1, &ballcoll2);
    401 			if (iseccollcount == 2) {
    402 				// check both sides!
    403 				if (ballcoll2.x > line1_start.x && ballcoll1.x < line1_start.x) {
    404 					//linerads = line1_rads + M_PI_2;
    405 					linerads = atan((balls_active[i]->sprite->getY() - line1_start.y) / (balls_active[i]->sprite->getX() - line1_start.x)) + M_PI_2;
    406 					line_x = line1_start.x;
    407 					line_y = line1_start.y;
    408 					coll = -1;
    409 				} else if (ballcoll1.x < line1_end.x && ballcoll2.x > line1_end.x) {
    410 					linerads = atan((balls_active[i]->sprite->getY() - line1_end.y) / (balls_active[i]->sprite->getX() - line1_end.x)) + M_PI_2;
    411 					line_x = line1_end.x;
    412 					line_y = line1_end.y;
    413 					coll = 1;
    414 				} else if (ballcoll1.x > line1_start.x && ballcoll2.x < line1_end.x) {
    415 					linerads = line1_rads;
    416 					coll = 2;
    417 				}
    418 			} else if (iseccollcount == 1) {
    419 				linerads = line1_rads;
    420 				coll = 1;
    421 			}	
    422 		}
    423 		
    424 		if (coll && (pause == 0 || step == 1)) {
    425 			float ballvel, ballrad, oldrad, balloverlap, balladjust;
    426 			//lash_game_coords_float_t ballbouncecorrection;
    427 			balls_active[i]->sprite->getVel(&ballvel, &ballrad);
    428 			oldrad = ballrad;
    429 			
    430 			// check for later implementations that we are on the right side of the horizontal, useless use of resources to convert back and forth
    431 			// oldrad = ballrad;
    432 			//ballrad = -ballrad;
    433 			/*
    434 			
    435 			ballrad = -ballrad;
    436 			*/
    437 			// correct for the movement past the surface
    438 			// (finds the difference (distance) from the circle centre to the perpendicular intersection of the surface line, and subtracts that from radius)
    439 			
    440 			if (coll == 2)
    441 				balloverlap = ball_radius - sqrt(pow(balls_active[i]->sprite->getX() - (ballcoll1.x + ((ballcoll2.x - ballcoll1.x) / 2)), 2) + pow(balls_active[i]->sprite->getY() - (ballcoll1.y + ((ballcoll2.y - ballcoll1.y) / 2)), 2));
    442 			else if (coll == 1)
    443 				balloverlap = ball_radius - sqrt(pow(balls_active[i]->sprite->getX() - line_x, 2) + pow(balls_active[i]->sprite->getY() - line_y, 2));
    444 			else if (coll == -1)
    445 				balloverlap = ball_radius - sqrt(pow(balls_active[i]->sprite->getX() - line_x, 2) + pow(balls_active[i]->sprite->getY() - line_y, 2));
    446 			else // shouldnt be here
    447 				throw lash_fallbounceline::collisionException;
    448 		
    449 			
    450 			if ((lashGetQuadrant(oldrad) - lashGetQuadrant(-linerads)) % 2 != 0)
    451 				balladjust = balloverlap / fabs(cos(M_PI_2 - lashNormalizeQuadrantRadians(ballrad) - lashNormalizeQuadrantRadians(linerads)));
    452 			else 
    453 				balladjust = balloverlap / fabs(cos(lashNormalizeQuadrantRadians(ballrad) + lashNormalizeQuadrantRadians(linerads)));
    454 			
    455 			balls_active[i]->sprite->moveBy(balladjust, ballrad + M_PI);
    456 			
    457 			lashCollisionSurfaceDeflectSimple(&ballvel, &ballrad, -linerads, bounce);
    458 
    459 			// these are attempts of manually simulating the velocity of the ball along the surface of the force preserved from the collision WITHOUT bounce.
    460 			// None of the attempts have been successful. The routine seem to work without, but is less accurate
    461 			//balls_active[i]->sprite->moveBy(balloverlap / tan(linerads), -linerads);
    462 			//balls_active[i]->sprite->moveBy(balloverlap / cos(linerads), -linerads);
    463 			
    464 			// move the bounce ratio part of the split velocity perp away from the slope
    465 			balls_active[i]->sprite->moveBy(balloverlap * bounce, ballrad);
    466 			balls_active[i]->sprite->setVel(ballvel, ballrad);
    467 		}
    468 		
    469 		} // for balls_active
    470 		
    471 		} // if balls_active[0].size() > 0
    472 
    473 		
    474 		//
    475 		// RENDERING
    476 		// 
    477 		
    478 		if (balls_active.size() > 0)
    479 			sprintf(debugstring, "Mouse X %d | Ball 1 X %f Y %f V %f R %f", mousex, balls_active[0]->sprite->getX(), balls_active[0]->sprite->getY(), balls_active[0]->sprite->getVel(), balls_active[0]->sprite->getVelRadians());
    480 		else
    481 			sprintf(debugstring, "Mouse X %d", mousex);
    482 			
    483 		SDL_FillRect(screen, &screen_r, screen_c);
    484 		
    485 		// cursor
    486 		thickLineColor(screen, mousex, 0, mousex, 16, 4, cursor_c);
    487 		
    488 		// lines
    489 		thickLineColor(screen, line1_start.x, line1_start.y, line1_end.x, line1_end.y, LINE_WEIGHT, line1_c);
    490 		thickLineColor(screen, line2_start.x, line2_start.y, line2_end.x, line2_end.y, LINE_WEIGHT, line2_c);
    491 		
    492 		// balls
    493 		// note the (*it) dereference, struggled  with this, but found solution here: http://stackoverflow.com/questions/10325774/iterator-with-vector-pointer
    494 		for (std::vector<struct lash_fallbounceline::ball*>::iterator it = balls_active.begin(); it != balls_active.end(); ++it) {
    495 			filledCircleColor(screen, (*it)->sprite->getX(), (*it)->sprite->getY(), ball_radius, (*it)->color);
    496 		}
    497 		
    498 		// debug
    499 		stringColor(screen, 10, SCREEN_HEIGHT - 15, debugstring, 0x00000099);
    500 		if (coll == 1 && line2border_c == borderinactive_c)
    501 			line2border_c = borderactive_c;
    502 		else if (coll == 0 && line2border_c == borderactive_c) 
    503 			line2border_c = borderinactive_c;
    504 			
    505 		//aapolygonColor(screen, line2_collision, (line2_collision+4), 4, line2border_c);
    506 		//aapolygonColor(screen, line1_collision, (line1_collision+4), 4, borderinactive_c);
    507 		
    508 		SDL_Flip(screen);
    509 		SDL_Delay(1000 / FPS);
    510 		
    511 		frames_total++;
    512 		step = 0;
    513 	}
    514 	
    515 	SDL_FreeSurface(screen);
    516 	SDL_FreeCursor(cursor);
    517 	SDL_Quit();
    518 	free(line1_collision);
    519 	free(line2_collision);
    520 		
    521 	return 0;
    522 }
    523