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