blob: 451445b01a8d3b298152bc29aaa0e5d4e4f9bade [file] [log] [blame]
Camilla Berglund3249f812010-09-07 17:34:51 +02001/*****************************************************************************
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
30#include <stdio.h>
31#include <stdlib.h>
32#include <math.h>
33#include <GL/glfw.h>
34
35
36/*****************************************************************************
37 * Various declarations and macros
38 *****************************************************************************/
39
40/* Prototypes */
41void init( void );
42void display( void );
43void reshape( int w, int h );
44void DrawBoingBall( void );
45void BounceBall( double dt );
46void DrawBoingBallBand( GLfloat long_lo, GLfloat long_hi );
47void DrawGrid( void );
48
49#define RADIUS 70.f
50#define STEP_LONGITUDE 22.5f /* 22.5 makes 8 bands like original Boing */
51#define STEP_LATITUDE 22.5f
52
53#define DIST_BALL (RADIUS * 2.f + RADIUS * 0.1f)
54
55#define VIEW_SCENE_DIST (DIST_BALL * 3.f + 200.f)/* distance from viewer to middle of boing area */
56#define GRID_SIZE (RADIUS * 4.5f) /* length (width) of grid */
57#define BOUNCE_HEIGHT (RADIUS * 2.1f)
58#define BOUNCE_WIDTH (RADIUS * 2.1f)
59
60#define SHADOW_OFFSET_X -20.f
61#define SHADOW_OFFSET_Y 10.f
62#define SHADOW_OFFSET_Z 0.f
63
64#define WALL_L_OFFSET 0.f
65#define WALL_R_OFFSET 5.f
66
67/* Animation speed (50.0 mimics the original GLUT demo speed) */
68#define ANIMATION_SPEED 50.f
69
70/* Maximum allowed delta time per physics iteration */
71#define MAX_DELTA_T 0.02f
72
73/* Draw ball, or its shadow */
74typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM;
75
76/* Vertex type */
77typedef struct {float x; float y; float z;} vertex_t;
78
79/* Global vars */
80GLfloat deg_rot_y = 0.f;
81GLfloat deg_rot_y_inc = 2.f;
82GLfloat ball_x = -RADIUS;
83GLfloat ball_y = -RADIUS;
84GLfloat ball_x_inc = 1.f;
85GLfloat ball_y_inc = 2.f;
86DRAW_BALL_ENUM drawBallHow;
87double t;
88double t_old = 0.f;
89double dt;
90
91/* Random number generator */
92#ifndef RAND_MAX
93 #define RAND_MAX 4095
94#endif
95
96/* PI */
97#ifndef M_PI
98 #define M_PI 3.1415926535897932384626433832795
99#endif
100
101
102/*****************************************************************************
103 * Truncate a degree.
104 *****************************************************************************/
105GLfloat TruncateDeg( GLfloat deg )
106{
107 if ( deg >= 360.f )
108 return (deg - 360.f);
109 else
110 return deg;
111}
112
113/*****************************************************************************
114 * Convert a degree (360-based) into a radian.
115 * 360' = 2 * PI
116 *****************************************************************************/
117double deg2rad( double deg )
118{
119 return deg / 360 * (2 * M_PI);
120}
121
122/*****************************************************************************
123 * 360' sin().
124 *****************************************************************************/
125double sin_deg( double deg )
126{
127 return sin( deg2rad( deg ) );
128}
129
130/*****************************************************************************
131 * 360' cos().
132 *****************************************************************************/
133double cos_deg( double deg )
134{
135 return cos( deg2rad( deg ) );
136}
137
138/*****************************************************************************
139 * Compute a cross product (for a normal vector).
140 *
141 * c = a x b
142 *****************************************************************************/
143void CrossProduct( vertex_t a, vertex_t b, vertex_t c, vertex_t *n )
144{
145 GLfloat u1, u2, u3;
146 GLfloat v1, v2, v3;
147
148 u1 = b.x - a.x;
149 u2 = b.y - a.y;
150 u3 = b.y - a.z;
151
152 v1 = c.x - a.x;
153 v2 = c.y - a.y;
154 v3 = c.z - a.z;
155
156 n->x = u2 * v3 - v2 * v3;
157 n->y = u3 * v1 - v3 * u1;
158 n->z = u1 * v2 - v1 * u2;
159}
160
161/*****************************************************************************
162 * Calculate the angle to be passed to gluPerspective() so that a scene
163 * is visible. This function originates from the OpenGL Red Book.
164 *
165 * Parms : size
166 * The size of the segment when the angle is intersected at "dist"
167 * (ie at the outermost edge of the angle of vision).
168 *
169 * dist
170 * Distance from viewpoint to scene.
171 *****************************************************************************/
172GLfloat PerspectiveAngle( GLfloat size,
173 GLfloat dist )
174{
175 GLfloat radTheta, degTheta;
176
177 radTheta = 2.f * (GLfloat) atan2( size / 2.f, dist );
178 degTheta = (180.f * radTheta) / (GLfloat) M_PI;
179 return degTheta;
180}
181
182
183
184#define BOING_DEBUG 0
185
186
187/*****************************************************************************
188 * init()
189 *****************************************************************************/
190void init( void )
191{
192 /*
193 * Clear background.
194 */
195 glClearColor( 0.55f, 0.55f, 0.55f, 0.f );
196
197 glShadeModel( GL_FLAT );
198}
199
200
201/*****************************************************************************
202 * display()
203 *****************************************************************************/
204void display(void)
205{
206 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
207 glPushMatrix();
208
209 drawBallHow = DRAW_BALL_SHADOW;
210 DrawBoingBall();
211
212 DrawGrid();
213
214 drawBallHow = DRAW_BALL;
215 DrawBoingBall();
216
217 glPopMatrix();
218 glFlush();
219}
220
221
222/*****************************************************************************
223 * reshape()
224 *****************************************************************************/
225void reshape( int w, int h )
226{
227 glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
228
229 glMatrixMode( GL_PROJECTION );
230 glLoadIdentity();
231
232 gluPerspective( PerspectiveAngle( RADIUS * 2, 200 ),
233 (GLfloat)w / (GLfloat)h,
234 1.0,
235 VIEW_SCENE_DIST );
236
237 glMatrixMode( GL_MODELVIEW );
238 glLoadIdentity();
239
240 gluLookAt( 0.0, 0.0, VIEW_SCENE_DIST,/* eye */
241 0.0, 0.0, 0.0, /* center of vision */
242 0.0, -1.0, 0.0 ); /* up vector */
243}
244
245
246/*****************************************************************************
247 * Draw the Boing ball.
248 *
249 * The Boing ball is sphere in which each facet is a rectangle.
250 * Facet colors alternate between red and white.
251 * The ball is built by stacking latitudinal circles. Each circle is composed
252 * of a widely-separated set of points, so that each facet is noticably large.
253 *****************************************************************************/
254void DrawBoingBall( void )
255{
256 GLfloat lon_deg; /* degree of longitude */
257 double dt_total, dt2;
258
259 glPushMatrix();
260 glMatrixMode( GL_MODELVIEW );
261
262 /*
263 * Another relative Z translation to separate objects.
264 */
265 glTranslatef( 0.0, 0.0, DIST_BALL );
266
267 /* Update ball position and rotation (iterate if necessary) */
268 dt_total = dt;
269 while( dt_total > 0.0 )
270 {
271 dt2 = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
272 dt_total -= dt2;
273 BounceBall( dt2 );
274 deg_rot_y = TruncateDeg( deg_rot_y + deg_rot_y_inc*((float)dt2*ANIMATION_SPEED) );
275 }
276
277 /* Set ball position */
278 glTranslatef( ball_x, ball_y, 0.0 );
279
280 /*
281 * Offset the shadow.
282 */
283 if ( drawBallHow == DRAW_BALL_SHADOW )
284 {
285 glTranslatef( SHADOW_OFFSET_X,
286 SHADOW_OFFSET_Y,
287 SHADOW_OFFSET_Z );
288 }
289
290 /*
291 * Tilt the ball.
292 */
293 glRotatef( -20.0, 0.0, 0.0, 1.0 );
294
295 /*
296 * Continually rotate ball around Y axis.
297 */
298 glRotatef( deg_rot_y, 0.0, 1.0, 0.0 );
299
300 /*
301 * Set OpenGL state for Boing ball.
302 */
303 glCullFace( GL_FRONT );
304 glEnable( GL_CULL_FACE );
305 glEnable( GL_NORMALIZE );
306
307 /*
308 * Build a faceted latitude slice of the Boing ball,
309 * stepping same-sized vertical bands of the sphere.
310 */
311 for ( lon_deg = 0;
312 lon_deg < 180;
313 lon_deg += STEP_LONGITUDE )
314 {
315 /*
316 * Draw a latitude circle at this longitude.
317 */
318 DrawBoingBallBand( lon_deg,
319 lon_deg + STEP_LONGITUDE );
320 }
321
322 glPopMatrix();
323
324 return;
325}
326
327
328/*****************************************************************************
329 * Bounce the ball.
330 *****************************************************************************/
331void BounceBall( double dt )
332{
333 GLfloat sign;
334 GLfloat deg;
335
336 /* Bounce on walls */
337 if ( ball_x > (BOUNCE_WIDTH/2 + WALL_R_OFFSET ) )
338 {
339 ball_x_inc = -0.5f - 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
340 deg_rot_y_inc = -deg_rot_y_inc;
341 }
342 if ( ball_x < -(BOUNCE_HEIGHT/2 + WALL_L_OFFSET) )
343 {
344 ball_x_inc = 0.5f + 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
345 deg_rot_y_inc = -deg_rot_y_inc;
346 }
347
348 /* Bounce on floor / roof */
349 if ( ball_y > BOUNCE_HEIGHT/2 )
350 {
351 ball_y_inc = -0.75f - 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
352 }
353 if ( ball_y < -BOUNCE_HEIGHT/2*0.85 )
354 {
355 ball_y_inc = 0.75f + 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
356 }
357
358 /* Update ball position */
359 ball_x += ball_x_inc * ((float)dt*ANIMATION_SPEED);
360 ball_y += ball_y_inc * ((float)dt*ANIMATION_SPEED);
361
362 /*
363 * Simulate the effects of gravity on Y movement.
364 */
365 if ( ball_y_inc < 0 ) sign = -1.0; else sign = 1.0;
366
367 deg = (ball_y + BOUNCE_HEIGHT/2) * 90 / BOUNCE_HEIGHT;
368 if ( deg > 80 ) deg = 80;
369 if ( deg < 10 ) deg = 10;
370
371 ball_y_inc = sign * 4.f * (float) sin_deg( deg );
372}
373
374
375/*****************************************************************************
376 * Draw a faceted latitude band of the Boing ball.
377 *
378 * Parms: long_lo, long_hi
379 * Low and high longitudes of slice, resp.
380 *****************************************************************************/
381void DrawBoingBallBand( GLfloat long_lo,
382 GLfloat long_hi )
383{
384 vertex_t vert_ne; /* "ne" means south-east, so on */
385 vertex_t vert_nw;
386 vertex_t vert_sw;
387 vertex_t vert_se;
388 vertex_t vert_norm;
389 GLfloat lat_deg;
390 static int colorToggle = 0;
391
392 /*
393 * Iterate thru the points of a latitude circle.
394 * A latitude circle is a 2D set of X,Z points.
395 */
396 for ( lat_deg = 0;
397 lat_deg <= (360 - STEP_LATITUDE);
398 lat_deg += STEP_LATITUDE )
399 {
400 /*
401 * Color this polygon with red or white.
402 */
403 if ( colorToggle )
404 glColor3f( 0.8f, 0.1f, 0.1f );
405 else
406 glColor3f( 0.95f, 0.95f, 0.95f );
407#if 0
408 if ( lat_deg >= 180 )
409 if ( colorToggle )
410 glColor3f( 0.1f, 0.8f, 0.1f );
411 else
412 glColor3f( 0.5f, 0.5f, 0.95f );
413#endif
414 colorToggle = ! colorToggle;
415
416 /*
417 * Change color if drawing shadow.
418 */
419 if ( drawBallHow == DRAW_BALL_SHADOW )
420 glColor3f( 0.35f, 0.35f, 0.35f );
421
422 /*
423 * Assign each Y.
424 */
425 vert_ne.y = vert_nw.y = (float) cos_deg(long_hi) * RADIUS;
426 vert_sw.y = vert_se.y = (float) cos_deg(long_lo) * RADIUS;
427
428 /*
429 * Assign each X,Z with sin,cos values scaled by latitude radius indexed by longitude.
430 * Eg, long=0 and long=180 are at the poles, so zero scale is sin(longitude),
431 * while long=90 (sin(90)=1) is at equator.
432 */
433 vert_ne.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
434 vert_se.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
435 vert_nw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
436 vert_sw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
437
438 vert_ne.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
439 vert_se.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
440 vert_nw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
441 vert_sw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
442
443 /*
444 * Draw the facet.
445 */
446 glBegin( GL_POLYGON );
447
448 CrossProduct( vert_ne, vert_nw, vert_sw, &vert_norm );
449 glNormal3f( vert_norm.x, vert_norm.y, vert_norm.z );
450
451 glVertex3f( vert_ne.x, vert_ne.y, vert_ne.z );
452 glVertex3f( vert_nw.x, vert_nw.y, vert_nw.z );
453 glVertex3f( vert_sw.x, vert_sw.y, vert_sw.z );
454 glVertex3f( vert_se.x, vert_se.y, vert_se.z );
455
456 glEnd();
457
458#if BOING_DEBUG
459 printf( "----------------------------------------------------------- \n" );
460 printf( "lat = %f long_lo = %f long_hi = %f \n", lat_deg, long_lo, long_hi );
461 printf( "vert_ne x = %.8f y = %.8f z = %.8f \n", vert_ne.x, vert_ne.y, vert_ne.z );
462 printf( "vert_nw x = %.8f y = %.8f z = %.8f \n", vert_nw.x, vert_nw.y, vert_nw.z );
463 printf( "vert_se x = %.8f y = %.8f z = %.8f \n", vert_se.x, vert_se.y, vert_se.z );
464 printf( "vert_sw x = %.8f y = %.8f z = %.8f \n", vert_sw.x, vert_sw.y, vert_sw.z );
465#endif
466
467 }
468
469 /*
470 * Toggle color so that next band will opposite red/white colors than this one.
471 */
472 colorToggle = ! colorToggle;
473
474 /*
475 * This circular band is done.
476 */
477 return;
478}
479
480
481/*****************************************************************************
482 * Draw the purple grid of lines, behind the Boing ball.
483 * When the Workbench is dropped to the bottom, Boing shows 12 rows.
484 *****************************************************************************/
485void DrawGrid( void )
486{
487 int row, col;
488 const int rowTotal = 12; /* must be divisible by 2 */
489 const int colTotal = rowTotal; /* must be same as rowTotal */
490 const GLfloat widthLine = 2.0; /* should be divisible by 2 */
491 const GLfloat sizeCell = GRID_SIZE / rowTotal;
492 const GLfloat z_offset = -40.0;
493 GLfloat xl, xr;
494 GLfloat yt, yb;
495
496 glPushMatrix();
497 glDisable( GL_CULL_FACE );
498
499 /*
500 * Another relative Z translation to separate objects.
501 */
502 glTranslatef( 0.0, 0.0, DIST_BALL );
503
504 /*
505 * Draw vertical lines (as skinny 3D rectangles).
506 */
507 for ( col = 0; col <= colTotal; col++ )
508 {
509 /*
510 * Compute co-ords of line.
511 */
512 xl = -GRID_SIZE / 2 + col * sizeCell;
513 xr = xl + widthLine;
514
515 yt = GRID_SIZE / 2;
516 yb = -GRID_SIZE / 2 - widthLine;
517
518 glBegin( GL_POLYGON );
519
520 glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
521
522 glVertex3f( xr, yt, z_offset ); /* NE */
523 glVertex3f( xl, yt, z_offset ); /* NW */
524 glVertex3f( xl, yb, z_offset ); /* SW */
525 glVertex3f( xr, yb, z_offset ); /* SE */
526
527 glEnd();
528 }
529
530 /*
531 * Draw horizontal lines (as skinny 3D rectangles).
532 */
533 for ( row = 0; row <= rowTotal; row++ )
534 {
535 /*
536 * Compute co-ords of line.
537 */
538 yt = GRID_SIZE / 2 - row * sizeCell;
539 yb = yt - widthLine;
540
541 xl = -GRID_SIZE / 2;
542 xr = GRID_SIZE / 2 + widthLine;
543
544 glBegin( GL_POLYGON );
545
546 glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
547
548 glVertex3f( xr, yt, z_offset ); /* NE */
549 glVertex3f( xl, yt, z_offset ); /* NW */
550 glVertex3f( xl, yb, z_offset ); /* SW */
551 glVertex3f( xr, yb, z_offset ); /* SE */
552
553 glEnd();
554 }
555
556 glPopMatrix();
557
558 return;
559}
560
561
562/*======================================================================*
563 * main()
564 *======================================================================*/
565
566int main( void )
567{
568 int running;
569
570 /* Init GLFW */
571 if( !glfwInit() )
572 {
573 fprintf( stderr, "Failed to initialize GLFW\n" );
574 exit( EXIT_FAILURE );
575 }
576
577 if( !glfwOpenWindow( 400,400, 0,0,0,0, 16,0, GLFW_WINDOW ) )
578 {
579 fprintf( stderr, "Failed to open GLFW window\n" );
580 glfwTerminate();
581 exit( EXIT_FAILURE );
582 }
583
584 glfwSetWindowTitle( "Boing (classic Amiga demo)" );
585 glfwSetWindowSizeCallback( reshape );
586 glfwEnable( GLFW_STICKY_KEYS );
587 glfwSwapInterval( 1 );
588 glfwSetTime( 0.0 );
589
590 init();
591
592 /* Main loop */
593 do
594 {
595 /* Timing */
596 t = glfwGetTime();
597 dt = t - t_old;
598 t_old = t;
599
600 /* Draw one frame */
601 display();
602
603 /* Swap buffers */
604 glfwSwapBuffers();
605
606 /* Check if we are still running */
607 running = !glfwGetKey( GLFW_KEY_ESC ) &&
608 glfwGetWindowParam( GLFW_OPENED );
609 }
610 while( running );
611
612 glfwTerminate();
613 exit( EXIT_SUCCESS );
614}
615