Platformer in OpenGL
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

678 lines
19 KiB

5 years ago
  1. /*****************************************************************************
  2. * Title: GLBoing
  3. * Desc: Tribute to Amiga Boing.
  4. * Author: Jim Brooks <gfx@jimbrooks.org>
  5. * Original Amiga authors were R.J. Mical and Dale Luck.
  6. * GLFW conversion by Marcus Geelnard
  7. * Notes: - 360' = 2*PI [radian]
  8. *
  9. * - Distances between objects are created by doing a relative
  10. * Z translations.
  11. *
  12. * - Although OpenGL enticingly supports alpha-blending,
  13. * the shadow of the original Boing didn't affect the color
  14. * of the grid.
  15. *
  16. * - [Marcus] Changed timing scheme from interval driven to frame-
  17. * time based animation steps (which results in much smoother
  18. * movement)
  19. *
  20. * History of Amiga Boing:
  21. *
  22. * Boing was demonstrated on the prototype Amiga (codenamed "Lorraine") in
  23. * 1985. According to legend, it was written ad-hoc in one night by
  24. * R. J. Mical and Dale Luck. Because the bouncing ball animation was so fast
  25. * and smooth, attendees did not believe the Amiga prototype was really doing
  26. * the rendering. Suspecting a trick, they began looking around the booth for
  27. * a hidden computer or VCR.
  28. *****************************************************************************/
  29. #if defined(_MSC_VER)
  30. // Make MS math.h define M_PI
  31. #define _USE_MATH_DEFINES
  32. #endif
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <math.h>
  36. #include <glad/glad.h>
  37. #include <GLFW/glfw3.h>
  38. #include <linmath.h>
  39. /*****************************************************************************
  40. * Various declarations and macros
  41. *****************************************************************************/
  42. /* Prototypes */
  43. void init( void );
  44. void display( void );
  45. void reshape( GLFWwindow* window, int w, int h );
  46. void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods );
  47. void mouse_button_callback( GLFWwindow* window, int button, int action, int mods );
  48. void cursor_position_callback( GLFWwindow* window, double x, double y );
  49. void DrawBoingBall( void );
  50. void BounceBall( double dt );
  51. void DrawBoingBallBand( GLfloat long_lo, GLfloat long_hi );
  52. void DrawGrid( void );
  53. #define RADIUS 70.f
  54. #define STEP_LONGITUDE 22.5f /* 22.5 makes 8 bands like original Boing */
  55. #define STEP_LATITUDE 22.5f
  56. #define DIST_BALL (RADIUS * 2.f + RADIUS * 0.1f)
  57. #define VIEW_SCENE_DIST (DIST_BALL * 3.f + 200.f)/* distance from viewer to middle of boing area */
  58. #define GRID_SIZE (RADIUS * 4.5f) /* length (width) of grid */
  59. #define BOUNCE_HEIGHT (RADIUS * 2.1f)
  60. #define BOUNCE_WIDTH (RADIUS * 2.1f)
  61. #define SHADOW_OFFSET_X -20.f
  62. #define SHADOW_OFFSET_Y 10.f
  63. #define SHADOW_OFFSET_Z 0.f
  64. #define WALL_L_OFFSET 0.f
  65. #define WALL_R_OFFSET 5.f
  66. /* Animation speed (50.0 mimics the original GLUT demo speed) */
  67. #define ANIMATION_SPEED 50.f
  68. /* Maximum allowed delta time per physics iteration */
  69. #define MAX_DELTA_T 0.02f
  70. /* Draw ball, or its shadow */
  71. typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM;
  72. /* Vertex type */
  73. typedef struct {float x; float y; float z;} vertex_t;
  74. /* Global vars */
  75. int windowed_xpos, windowed_ypos, windowed_width, windowed_height;
  76. int width, height;
  77. GLfloat deg_rot_y = 0.f;
  78. GLfloat deg_rot_y_inc = 2.f;
  79. int override_pos = GLFW_FALSE;
  80. GLfloat cursor_x = 0.f;
  81. GLfloat cursor_y = 0.f;
  82. GLfloat ball_x = -RADIUS;
  83. GLfloat ball_y = -RADIUS;
  84. GLfloat ball_x_inc = 1.f;
  85. GLfloat ball_y_inc = 2.f;
  86. DRAW_BALL_ENUM drawBallHow;
  87. double t;
  88. double t_old = 0.f;
  89. double dt;
  90. /* Random number generator */
  91. #ifndef RAND_MAX
  92. #define RAND_MAX 4095
  93. #endif
  94. /*****************************************************************************
  95. * Truncate a degree.
  96. *****************************************************************************/
  97. GLfloat TruncateDeg( GLfloat deg )
  98. {
  99. if ( deg >= 360.f )
  100. return (deg - 360.f);
  101. else
  102. return deg;
  103. }
  104. /*****************************************************************************
  105. * Convert a degree (360-based) into a radian.
  106. * 360' = 2 * PI
  107. *****************************************************************************/
  108. double deg2rad( double deg )
  109. {
  110. return deg / 360 * (2 * M_PI);
  111. }
  112. /*****************************************************************************
  113. * 360' sin().
  114. *****************************************************************************/
  115. double sin_deg( double deg )
  116. {
  117. return sin( deg2rad( deg ) );
  118. }
  119. /*****************************************************************************
  120. * 360' cos().
  121. *****************************************************************************/
  122. double cos_deg( double deg )
  123. {
  124. return cos( deg2rad( deg ) );
  125. }
  126. /*****************************************************************************
  127. * Compute a cross product (for a normal vector).
  128. *
  129. * c = a x b
  130. *****************************************************************************/
  131. void CrossProduct( vertex_t a, vertex_t b, vertex_t c, vertex_t *n )
  132. {
  133. GLfloat u1, u2, u3;
  134. GLfloat v1, v2, v3;
  135. u1 = b.x - a.x;
  136. u2 = b.y - a.y;
  137. u3 = b.y - a.z;
  138. v1 = c.x - a.x;
  139. v2 = c.y - a.y;
  140. v3 = c.z - a.z;
  141. n->x = u2 * v3 - v2 * v3;
  142. n->y = u3 * v1 - v3 * u1;
  143. n->z = u1 * v2 - v1 * u2;
  144. }
  145. #define BOING_DEBUG 0
  146. /*****************************************************************************
  147. * init()
  148. *****************************************************************************/
  149. void init( void )
  150. {
  151. /*
  152. * Clear background.
  153. */
  154. glClearColor( 0.55f, 0.55f, 0.55f, 0.f );
  155. glShadeModel( GL_FLAT );
  156. }
  157. /*****************************************************************************
  158. * display()
  159. *****************************************************************************/
  160. void display(void)
  161. {
  162. glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
  163. glPushMatrix();
  164. drawBallHow = DRAW_BALL_SHADOW;
  165. DrawBoingBall();
  166. DrawGrid();
  167. drawBallHow = DRAW_BALL;
  168. DrawBoingBall();
  169. glPopMatrix();
  170. glFlush();
  171. }
  172. /*****************************************************************************
  173. * reshape()
  174. *****************************************************************************/
  175. void reshape( GLFWwindow* window, int w, int h )
  176. {
  177. mat4x4 projection, view;
  178. glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
  179. glMatrixMode( GL_PROJECTION );
  180. mat4x4_perspective( projection,
  181. 2.f * (float) atan2( RADIUS, 200.f ),
  182. (float)w / (float)h,
  183. 1.f, VIEW_SCENE_DIST );
  184. glLoadMatrixf((const GLfloat*) projection);
  185. glMatrixMode( GL_MODELVIEW );
  186. {
  187. vec3 eye = { 0.f, 0.f, VIEW_SCENE_DIST };
  188. vec3 center = { 0.f, 0.f, 0.f };
  189. vec3 up = { 0.f, -1.f, 0.f };
  190. mat4x4_look_at( view, eye, center, up );
  191. }
  192. glLoadMatrixf((const GLfloat*) view);
  193. }
  194. void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods )
  195. {
  196. if (action != GLFW_PRESS)
  197. return;
  198. if (key == GLFW_KEY_ESCAPE && mods == 0)
  199. glfwSetWindowShouldClose(window, GLFW_TRUE);
  200. if ((key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT) ||
  201. (key == GLFW_KEY_F11 && mods == GLFW_MOD_ALT))
  202. {
  203. if (glfwGetWindowMonitor(window))
  204. {
  205. glfwSetWindowMonitor(window, NULL,
  206. windowed_xpos, windowed_ypos,
  207. windowed_width, windowed_height, 0);
  208. }
  209. else
  210. {
  211. GLFWmonitor* monitor = glfwGetPrimaryMonitor();
  212. if (monitor)
  213. {
  214. const GLFWvidmode* mode = glfwGetVideoMode(monitor);
  215. glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos);
  216. glfwGetWindowSize(window, &windowed_width, &windowed_height);
  217. glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
  218. }
  219. }
  220. }
  221. }
  222. static void set_ball_pos ( GLfloat x, GLfloat y )
  223. {
  224. ball_x = (width / 2) - x;
  225. ball_y = y - (height / 2);
  226. }
  227. void mouse_button_callback( GLFWwindow* window, int button, int action, int mods )
  228. {
  229. if (button != GLFW_MOUSE_BUTTON_LEFT)
  230. return;
  231. if (action == GLFW_PRESS)
  232. {
  233. override_pos = GLFW_TRUE;
  234. set_ball_pos(cursor_x, cursor_y);
  235. }
  236. else
  237. {
  238. override_pos = GLFW_FALSE;
  239. }
  240. }
  241. void cursor_position_callback( GLFWwindow* window, double x, double y )
  242. {
  243. cursor_x = (float) x;
  244. cursor_y = (float) y;
  245. if ( override_pos )
  246. set_ball_pos(cursor_x, cursor_y);
  247. }
  248. /*****************************************************************************
  249. * Draw the Boing ball.
  250. *
  251. * The Boing ball is sphere in which each facet is a rectangle.
  252. * Facet colors alternate between red and white.
  253. * The ball is built by stacking latitudinal circles. Each circle is composed
  254. * of a widely-separated set of points, so that each facet is noticably large.
  255. *****************************************************************************/
  256. void DrawBoingBall( void )
  257. {
  258. GLfloat lon_deg; /* degree of longitude */
  259. double dt_total, dt2;
  260. glPushMatrix();
  261. glMatrixMode( GL_MODELVIEW );
  262. /*
  263. * Another relative Z translation to separate objects.
  264. */
  265. glTranslatef( 0.0, 0.0, DIST_BALL );
  266. /* Update ball position and rotation (iterate if necessary) */
  267. dt_total = dt;
  268. while( dt_total > 0.0 )
  269. {
  270. dt2 = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
  271. dt_total -= dt2;
  272. BounceBall( dt2 );
  273. deg_rot_y = TruncateDeg( deg_rot_y + deg_rot_y_inc*((float)dt2*ANIMATION_SPEED) );
  274. }
  275. /* Set ball position */
  276. glTranslatef( ball_x, ball_y, 0.0 );
  277. /*
  278. * Offset the shadow.
  279. */
  280. if ( drawBallHow == DRAW_BALL_SHADOW )
  281. {
  282. glTranslatef( SHADOW_OFFSET_X,
  283. SHADOW_OFFSET_Y,
  284. SHADOW_OFFSET_Z );
  285. }
  286. /*
  287. * Tilt the ball.
  288. */
  289. glRotatef( -20.0, 0.0, 0.0, 1.0 );
  290. /*
  291. * Continually rotate ball around Y axis.
  292. */
  293. glRotatef( deg_rot_y, 0.0, 1.0, 0.0 );
  294. /*
  295. * Set OpenGL state for Boing ball.
  296. */
  297. glCullFace( GL_FRONT );
  298. glEnable( GL_CULL_FACE );
  299. glEnable( GL_NORMALIZE );
  300. /*
  301. * Build a faceted latitude slice of the Boing ball,
  302. * stepping same-sized vertical bands of the sphere.
  303. */
  304. for ( lon_deg = 0;
  305. lon_deg < 180;
  306. lon_deg += STEP_LONGITUDE )
  307. {
  308. /*
  309. * Draw a latitude circle at this longitude.
  310. */
  311. DrawBoingBallBand( lon_deg,
  312. lon_deg + STEP_LONGITUDE );
  313. }
  314. glPopMatrix();
  315. return;
  316. }
  317. /*****************************************************************************
  318. * Bounce the ball.
  319. *****************************************************************************/
  320. void BounceBall( double delta_t )
  321. {
  322. GLfloat sign;
  323. GLfloat deg;
  324. if ( override_pos )
  325. return;
  326. /* Bounce on walls */
  327. if ( ball_x > (BOUNCE_WIDTH/2 + WALL_R_OFFSET ) )
  328. {
  329. ball_x_inc = -0.5f - 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
  330. deg_rot_y_inc = -deg_rot_y_inc;
  331. }
  332. if ( ball_x < -(BOUNCE_HEIGHT/2 + WALL_L_OFFSET) )
  333. {
  334. ball_x_inc = 0.5f + 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
  335. deg_rot_y_inc = -deg_rot_y_inc;
  336. }
  337. /* Bounce on floor / roof */
  338. if ( ball_y > BOUNCE_HEIGHT/2 )
  339. {
  340. ball_y_inc = -0.75f - 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
  341. }
  342. if ( ball_y < -BOUNCE_HEIGHT/2*0.85 )
  343. {
  344. ball_y_inc = 0.75f + 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
  345. }
  346. /* Update ball position */
  347. ball_x += ball_x_inc * ((float)delta_t*ANIMATION_SPEED);
  348. ball_y += ball_y_inc * ((float)delta_t*ANIMATION_SPEED);
  349. /*
  350. * Simulate the effects of gravity on Y movement.
  351. */
  352. if ( ball_y_inc < 0 ) sign = -1.0; else sign = 1.0;
  353. deg = (ball_y + BOUNCE_HEIGHT/2) * 90 / BOUNCE_HEIGHT;
  354. if ( deg > 80 ) deg = 80;
  355. if ( deg < 10 ) deg = 10;
  356. ball_y_inc = sign * 4.f * (float) sin_deg( deg );
  357. }
  358. /*****************************************************************************
  359. * Draw a faceted latitude band of the Boing ball.
  360. *
  361. * Parms: long_lo, long_hi
  362. * Low and high longitudes of slice, resp.
  363. *****************************************************************************/
  364. void DrawBoingBallBand( GLfloat long_lo,
  365. GLfloat long_hi )
  366. {
  367. vertex_t vert_ne; /* "ne" means south-east, so on */
  368. vertex_t vert_nw;
  369. vertex_t vert_sw;
  370. vertex_t vert_se;
  371. vertex_t vert_norm;
  372. GLfloat lat_deg;
  373. static int colorToggle = 0;
  374. /*
  375. * Iterate thru the points of a latitude circle.
  376. * A latitude circle is a 2D set of X,Z points.
  377. */
  378. for ( lat_deg = 0;
  379. lat_deg <= (360 - STEP_LATITUDE);
  380. lat_deg += STEP_LATITUDE )
  381. {
  382. /*
  383. * Color this polygon with red or white.
  384. */
  385. if ( colorToggle )
  386. glColor3f( 0.8f, 0.1f, 0.1f );
  387. else
  388. glColor3f( 0.95f, 0.95f, 0.95f );
  389. #if 0
  390. if ( lat_deg >= 180 )
  391. if ( colorToggle )
  392. glColor3f( 0.1f, 0.8f, 0.1f );
  393. else
  394. glColor3f( 0.5f, 0.5f, 0.95f );
  395. #endif
  396. colorToggle = ! colorToggle;
  397. /*
  398. * Change color if drawing shadow.
  399. */
  400. if ( drawBallHow == DRAW_BALL_SHADOW )
  401. glColor3f( 0.35f, 0.35f, 0.35f );
  402. /*
  403. * Assign each Y.
  404. */
  405. vert_ne.y = vert_nw.y = (float) cos_deg(long_hi) * RADIUS;
  406. vert_sw.y = vert_se.y = (float) cos_deg(long_lo) * RADIUS;
  407. /*
  408. * Assign each X,Z with sin,cos values scaled by latitude radius indexed by longitude.
  409. * Eg, long=0 and long=180 are at the poles, so zero scale is sin(longitude),
  410. * while long=90 (sin(90)=1) is at equator.
  411. */
  412. vert_ne.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
  413. vert_se.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
  414. vert_nw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
  415. vert_sw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
  416. vert_ne.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
  417. vert_se.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
  418. vert_nw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
  419. vert_sw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
  420. /*
  421. * Draw the facet.
  422. */
  423. glBegin( GL_POLYGON );
  424. CrossProduct( vert_ne, vert_nw, vert_sw, &vert_norm );
  425. glNormal3f( vert_norm.x, vert_norm.y, vert_norm.z );
  426. glVertex3f( vert_ne.x, vert_ne.y, vert_ne.z );
  427. glVertex3f( vert_nw.x, vert_nw.y, vert_nw.z );
  428. glVertex3f( vert_sw.x, vert_sw.y, vert_sw.z );
  429. glVertex3f( vert_se.x, vert_se.y, vert_se.z );
  430. glEnd();
  431. #if BOING_DEBUG
  432. printf( "----------------------------------------------------------- \n" );
  433. printf( "lat = %f long_lo = %f long_hi = %f \n", lat_deg, long_lo, long_hi );
  434. printf( "vert_ne x = %.8f y = %.8f z = %.8f \n", vert_ne.x, vert_ne.y, vert_ne.z );
  435. printf( "vert_nw x = %.8f y = %.8f z = %.8f \n", vert_nw.x, vert_nw.y, vert_nw.z );
  436. printf( "vert_se x = %.8f y = %.8f z = %.8f \n", vert_se.x, vert_se.y, vert_se.z );
  437. printf( "vert_sw x = %.8f y = %.8f z = %.8f \n", vert_sw.x, vert_sw.y, vert_sw.z );
  438. #endif
  439. }
  440. /*
  441. * Toggle color so that next band will opposite red/white colors than this one.
  442. */
  443. colorToggle = ! colorToggle;
  444. /*
  445. * This circular band is done.
  446. */
  447. return;
  448. }
  449. /*****************************************************************************
  450. * Draw the purple grid of lines, behind the Boing ball.
  451. * When the Workbench is dropped to the bottom, Boing shows 12 rows.
  452. *****************************************************************************/
  453. void DrawGrid( void )
  454. {
  455. int row, col;
  456. const int rowTotal = 12; /* must be divisible by 2 */
  457. const int colTotal = rowTotal; /* must be same as rowTotal */
  458. const GLfloat widthLine = 2.0; /* should be divisible by 2 */
  459. const GLfloat sizeCell = GRID_SIZE / rowTotal;
  460. const GLfloat z_offset = -40.0;
  461. GLfloat xl, xr;
  462. GLfloat yt, yb;
  463. glPushMatrix();
  464. glDisable( GL_CULL_FACE );
  465. /*
  466. * Another relative Z translation to separate objects.
  467. */
  468. glTranslatef( 0.0, 0.0, DIST_BALL );
  469. /*
  470. * Draw vertical lines (as skinny 3D rectangles).
  471. */
  472. for ( col = 0; col <= colTotal; col++ )
  473. {
  474. /*
  475. * Compute co-ords of line.
  476. */
  477. xl = -GRID_SIZE / 2 + col * sizeCell;
  478. xr = xl + widthLine;
  479. yt = GRID_SIZE / 2;
  480. yb = -GRID_SIZE / 2 - widthLine;
  481. glBegin( GL_POLYGON );
  482. glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
  483. glVertex3f( xr, yt, z_offset ); /* NE */
  484. glVertex3f( xl, yt, z_offset ); /* NW */
  485. glVertex3f( xl, yb, z_offset ); /* SW */
  486. glVertex3f( xr, yb, z_offset ); /* SE */
  487. glEnd();
  488. }
  489. /*
  490. * Draw horizontal lines (as skinny 3D rectangles).
  491. */
  492. for ( row = 0; row <= rowTotal; row++ )
  493. {
  494. /*
  495. * Compute co-ords of line.
  496. */
  497. yt = GRID_SIZE / 2 - row * sizeCell;
  498. yb = yt - widthLine;
  499. xl = -GRID_SIZE / 2;
  500. xr = GRID_SIZE / 2 + widthLine;
  501. glBegin( GL_POLYGON );
  502. glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
  503. glVertex3f( xr, yt, z_offset ); /* NE */
  504. glVertex3f( xl, yt, z_offset ); /* NW */
  505. glVertex3f( xl, yb, z_offset ); /* SW */
  506. glVertex3f( xr, yb, z_offset ); /* SE */
  507. glEnd();
  508. }
  509. glPopMatrix();
  510. return;
  511. }
  512. /*======================================================================*
  513. * main()
  514. *======================================================================*/
  515. int main( void )
  516. {
  517. GLFWwindow* window;
  518. /* Init GLFW */
  519. if( !glfwInit() )
  520. exit( EXIT_FAILURE );
  521. window = glfwCreateWindow( 400, 400, "Boing (classic Amiga demo)", NULL, NULL );
  522. if (!window)
  523. {
  524. glfwTerminate();
  525. exit( EXIT_FAILURE );
  526. }
  527. glfwSetWindowAspectRatio(window, 1, 1);
  528. glfwSetFramebufferSizeCallback(window, reshape);
  529. glfwSetKeyCallback(window, key_callback);
  530. glfwSetMouseButtonCallback(window, mouse_button_callback);
  531. glfwSetCursorPosCallback(window, cursor_position_callback);
  532. glfwMakeContextCurrent(window);
  533. gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);
  534. glfwSwapInterval( 1 );
  535. glfwGetFramebufferSize(window, &width, &height);
  536. reshape(window, width, height);
  537. glfwSetTime( 0.0 );
  538. init();
  539. /* Main loop */
  540. for (;;)
  541. {
  542. /* Timing */
  543. t = glfwGetTime();
  544. dt = t - t_old;
  545. t_old = t;
  546. /* Draw one frame */
  547. display();
  548. /* Swap buffers */
  549. glfwSwapBuffers(window);
  550. glfwPollEvents();
  551. /* Check if we are still running */
  552. if (glfwWindowShouldClose(window))
  553. break;
  554. }
  555. glfwTerminate();
  556. exit( EXIT_SUCCESS );
  557. }