123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680 |
- /*****************************************************************************
- * Title: GLBoing
- * Desc: Tribute to Amiga Boing.
- * Author: Jim Brooks <gfx@jimbrooks.org>
- * Original Amiga authors were R.J. Mical and Dale Luck.
- * GLFW conversion by Marcus Geelnard
- * Notes: - 360' = 2*PI [radian]
- *
- * - Distances between objects are created by doing a relative
- * Z translations.
- *
- * - Although OpenGL enticingly supports alpha-blending,
- * the shadow of the original Boing didn't affect the color
- * of the grid.
- *
- * - [Marcus] Changed timing scheme from interval driven to frame-
- * time based animation steps (which results in much smoother
- * movement)
- *
- * History of Amiga Boing:
- *
- * Boing was demonstrated on the prototype Amiga (codenamed "Lorraine") in
- * 1985. According to legend, it was written ad-hoc in one night by
- * R. J. Mical and Dale Luck. Because the bouncing ball animation was so fast
- * and smooth, attendees did not believe the Amiga prototype was really doing
- * the rendering. Suspecting a trick, they began looking around the booth for
- * a hidden computer or VCR.
- *****************************************************************************/
- #if defined(_MSC_VER)
- // Make MS math.h define M_PI
- #define _USE_MATH_DEFINES
- #endif
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <glad/gl.h>
- #define GLFW_INCLUDE_NONE
- #include <GLFW/glfw3.h>
- #include <linmath.h>
- /*****************************************************************************
- * Various declarations and macros
- *****************************************************************************/
- /* Prototypes */
- void init( void );
- void display( void );
- void reshape( GLFWwindow* window, int w, int h );
- void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods );
- void mouse_button_callback( GLFWwindow* window, int button, int action, int mods );
- void cursor_position_callback( GLFWwindow* window, double x, double y );
- void DrawBoingBall( void );
- void BounceBall( double dt );
- void DrawBoingBallBand( GLfloat long_lo, GLfloat long_hi );
- void DrawGrid( void );
- #define RADIUS 70.f
- #define STEP_LONGITUDE 22.5f /* 22.5 makes 8 bands like original Boing */
- #define STEP_LATITUDE 22.5f
- #define DIST_BALL (RADIUS * 2.f + RADIUS * 0.1f)
- #define VIEW_SCENE_DIST (DIST_BALL * 3.f + 200.f)/* distance from viewer to middle of boing area */
- #define GRID_SIZE (RADIUS * 4.5f) /* length (width) of grid */
- #define BOUNCE_HEIGHT (RADIUS * 2.1f)
- #define BOUNCE_WIDTH (RADIUS * 2.1f)
- #define SHADOW_OFFSET_X -20.f
- #define SHADOW_OFFSET_Y 10.f
- #define SHADOW_OFFSET_Z 0.f
- #define WALL_L_OFFSET 0.f
- #define WALL_R_OFFSET 5.f
- /* Animation speed (50.0 mimics the original GLUT demo speed) */
- #define ANIMATION_SPEED 50.f
- /* Maximum allowed delta time per physics iteration */
- #define MAX_DELTA_T 0.02f
- /* Draw ball, or its shadow */
- typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM;
- /* Vertex type */
- typedef struct {float x; float y; float z;} vertex_t;
- /* Global vars */
- int windowed_xpos, windowed_ypos, windowed_width, windowed_height;
- int width, height;
- GLfloat deg_rot_y = 0.f;
- GLfloat deg_rot_y_inc = 2.f;
- int override_pos = GLFW_FALSE;
- GLfloat cursor_x = 0.f;
- GLfloat cursor_y = 0.f;
- GLfloat ball_x = -RADIUS;
- GLfloat ball_y = -RADIUS;
- GLfloat ball_x_inc = 1.f;
- GLfloat ball_y_inc = 2.f;
- DRAW_BALL_ENUM drawBallHow;
- double t;
- double t_old = 0.f;
- double dt;
- /* Random number generator */
- #ifndef RAND_MAX
- #define RAND_MAX 4095
- #endif
- /*****************************************************************************
- * Truncate a degree.
- *****************************************************************************/
- GLfloat TruncateDeg( GLfloat deg )
- {
- if ( deg >= 360.f )
- return (deg - 360.f);
- else
- return deg;
- }
- /*****************************************************************************
- * Convert a degree (360-based) into a radian.
- * 360' = 2 * PI
- *****************************************************************************/
- double deg2rad( double deg )
- {
- return deg / 360 * (2 * M_PI);
- }
- /*****************************************************************************
- * 360' sin().
- *****************************************************************************/
- double sin_deg( double deg )
- {
- return sin( deg2rad( deg ) );
- }
- /*****************************************************************************
- * 360' cos().
- *****************************************************************************/
- double cos_deg( double deg )
- {
- return cos( deg2rad( deg ) );
- }
- /*****************************************************************************
- * Compute a cross product (for a normal vector).
- *
- * c = a x b
- *****************************************************************************/
- void CrossProduct( vertex_t a, vertex_t b, vertex_t c, vertex_t *n )
- {
- GLfloat u1, u2, u3;
- GLfloat v1, v2, v3;
- u1 = b.x - a.x;
- u2 = b.y - a.y;
- u3 = b.y - a.z;
- v1 = c.x - a.x;
- v2 = c.y - a.y;
- v3 = c.z - a.z;
- n->x = u2 * v3 - v2 * u3;
- n->y = u3 * v1 - v3 * u1;
- n->z = u1 * v2 - v1 * u2;
- }
- #define BOING_DEBUG 0
- /*****************************************************************************
- * init()
- *****************************************************************************/
- void init( void )
- {
- /*
- * Clear background.
- */
- glClearColor( 0.55f, 0.55f, 0.55f, 0.f );
- glShadeModel( GL_FLAT );
- }
- /*****************************************************************************
- * display()
- *****************************************************************************/
- void display(void)
- {
- glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
- glPushMatrix();
- drawBallHow = DRAW_BALL_SHADOW;
- DrawBoingBall();
- DrawGrid();
- drawBallHow = DRAW_BALL;
- DrawBoingBall();
- glPopMatrix();
- glFlush();
- }
- /*****************************************************************************
- * reshape()
- *****************************************************************************/
- void reshape( GLFWwindow* window, int w, int h )
- {
- mat4x4 projection, view;
- glViewport( 0, 0, (GLsizei)w, (GLsizei)h );
- glMatrixMode( GL_PROJECTION );
- mat4x4_perspective( projection,
- 2.f * (float) atan2( RADIUS, 200.f ),
- (float)w / (float)h,
- 1.f, VIEW_SCENE_DIST );
- glLoadMatrixf((const GLfloat*) projection);
- glMatrixMode( GL_MODELVIEW );
- {
- vec3 eye = { 0.f, 0.f, VIEW_SCENE_DIST };
- vec3 center = { 0.f, 0.f, 0.f };
- vec3 up = { 0.f, -1.f, 0.f };
- mat4x4_look_at( view, eye, center, up );
- }
- glLoadMatrixf((const GLfloat*) view);
- }
- void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods )
- {
- if (action != GLFW_PRESS)
- return;
- if (key == GLFW_KEY_ESCAPE && mods == 0)
- glfwSetWindowShouldClose(window, GLFW_TRUE);
- if ((key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT) ||
- (key == GLFW_KEY_F11 && mods == GLFW_MOD_ALT))
- {
- if (glfwGetWindowMonitor(window))
- {
- glfwSetWindowMonitor(window, NULL,
- windowed_xpos, windowed_ypos,
- windowed_width, windowed_height, 0);
- }
- else
- {
- GLFWmonitor* monitor = glfwGetPrimaryMonitor();
- if (monitor)
- {
- const GLFWvidmode* mode = glfwGetVideoMode(monitor);
- glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos);
- glfwGetWindowSize(window, &windowed_width, &windowed_height);
- glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate);
- }
- }
- }
- }
- static void set_ball_pos ( GLfloat x, GLfloat y )
- {
- ball_x = (width / 2) - x;
- ball_y = y - (height / 2);
- }
- void mouse_button_callback( GLFWwindow* window, int button, int action, int mods )
- {
- if (button != GLFW_MOUSE_BUTTON_LEFT)
- return;
- if (action == GLFW_PRESS)
- {
- override_pos = GLFW_TRUE;
- set_ball_pos(cursor_x, cursor_y);
- }
- else
- {
- override_pos = GLFW_FALSE;
- }
- }
- void cursor_position_callback( GLFWwindow* window, double x, double y )
- {
- cursor_x = (float) x;
- cursor_y = (float) y;
- if ( override_pos )
- set_ball_pos(cursor_x, cursor_y);
- }
- /*****************************************************************************
- * Draw the Boing ball.
- *
- * The Boing ball is sphere in which each facet is a rectangle.
- * Facet colors alternate between red and white.
- * The ball is built by stacking latitudinal circles. Each circle is composed
- * of a widely-separated set of points, so that each facet is noticeably large.
- *****************************************************************************/
- void DrawBoingBall( void )
- {
- GLfloat lon_deg; /* degree of longitude */
- double dt_total, dt2;
- glPushMatrix();
- glMatrixMode( GL_MODELVIEW );
- /*
- * Another relative Z translation to separate objects.
- */
- glTranslatef( 0.0, 0.0, DIST_BALL );
- /* Update ball position and rotation (iterate if necessary) */
- dt_total = dt;
- while( dt_total > 0.0 )
- {
- dt2 = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total;
- dt_total -= dt2;
- BounceBall( dt2 );
- deg_rot_y = TruncateDeg( deg_rot_y + deg_rot_y_inc*((float)dt2*ANIMATION_SPEED) );
- }
- /* Set ball position */
- glTranslatef( ball_x, ball_y, 0.0 );
- /*
- * Offset the shadow.
- */
- if ( drawBallHow == DRAW_BALL_SHADOW )
- {
- glTranslatef( SHADOW_OFFSET_X,
- SHADOW_OFFSET_Y,
- SHADOW_OFFSET_Z );
- }
- /*
- * Tilt the ball.
- */
- glRotatef( -20.0, 0.0, 0.0, 1.0 );
- /*
- * Continually rotate ball around Y axis.
- */
- glRotatef( deg_rot_y, 0.0, 1.0, 0.0 );
- /*
- * Set OpenGL state for Boing ball.
- */
- glCullFace( GL_FRONT );
- glEnable( GL_CULL_FACE );
- glEnable( GL_NORMALIZE );
- /*
- * Build a faceted latitude slice of the Boing ball,
- * stepping same-sized vertical bands of the sphere.
- */
- for ( lon_deg = 0;
- lon_deg < 180;
- lon_deg += STEP_LONGITUDE )
- {
- /*
- * Draw a latitude circle at this longitude.
- */
- DrawBoingBallBand( lon_deg,
- lon_deg + STEP_LONGITUDE );
- }
- glPopMatrix();
- return;
- }
- /*****************************************************************************
- * Bounce the ball.
- *****************************************************************************/
- void BounceBall( double delta_t )
- {
- GLfloat sign;
- GLfloat deg;
- if ( override_pos )
- return;
- /* Bounce on walls */
- if ( ball_x > (BOUNCE_WIDTH/2 + WALL_R_OFFSET ) )
- {
- ball_x_inc = -0.5f - 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
- deg_rot_y_inc = -deg_rot_y_inc;
- }
- if ( ball_x < -(BOUNCE_HEIGHT/2 + WALL_L_OFFSET) )
- {
- ball_x_inc = 0.5f + 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX;
- deg_rot_y_inc = -deg_rot_y_inc;
- }
- /* Bounce on floor / roof */
- if ( ball_y > BOUNCE_HEIGHT/2 )
- {
- ball_y_inc = -0.75f - 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
- }
- if ( ball_y < -BOUNCE_HEIGHT/2*0.85 )
- {
- ball_y_inc = 0.75f + 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX;
- }
- /* Update ball position */
- ball_x += ball_x_inc * ((float)delta_t*ANIMATION_SPEED);
- ball_y += ball_y_inc * ((float)delta_t*ANIMATION_SPEED);
- /*
- * Simulate the effects of gravity on Y movement.
- */
- if ( ball_y_inc < 0 ) sign = -1.0; else sign = 1.0;
- deg = (ball_y + BOUNCE_HEIGHT/2) * 90 / BOUNCE_HEIGHT;
- if ( deg > 80 ) deg = 80;
- if ( deg < 10 ) deg = 10;
- ball_y_inc = sign * 4.f * (float) sin_deg( deg );
- }
- /*****************************************************************************
- * Draw a faceted latitude band of the Boing ball.
- *
- * Parms: long_lo, long_hi
- * Low and high longitudes of slice, resp.
- *****************************************************************************/
- void DrawBoingBallBand( GLfloat long_lo,
- GLfloat long_hi )
- {
- vertex_t vert_ne; /* "ne" means south-east, so on */
- vertex_t vert_nw;
- vertex_t vert_sw;
- vertex_t vert_se;
- vertex_t vert_norm;
- GLfloat lat_deg;
- static int colorToggle = 0;
- /*
- * Iterate through the points of a latitude circle.
- * A latitude circle is a 2D set of X,Z points.
- */
- for ( lat_deg = 0;
- lat_deg <= (360 - STEP_LATITUDE);
- lat_deg += STEP_LATITUDE )
- {
- /*
- * Color this polygon with red or white.
- */
- if ( colorToggle )
- glColor3f( 0.8f, 0.1f, 0.1f );
- else
- glColor3f( 0.95f, 0.95f, 0.95f );
- #if 0
- if ( lat_deg >= 180 )
- if ( colorToggle )
- glColor3f( 0.1f, 0.8f, 0.1f );
- else
- glColor3f( 0.5f, 0.5f, 0.95f );
- #endif
- colorToggle = ! colorToggle;
- /*
- * Change color if drawing shadow.
- */
- if ( drawBallHow == DRAW_BALL_SHADOW )
- glColor3f( 0.35f, 0.35f, 0.35f );
- /*
- * Assign each Y.
- */
- vert_ne.y = vert_nw.y = (float) cos_deg(long_hi) * RADIUS;
- vert_sw.y = vert_se.y = (float) cos_deg(long_lo) * RADIUS;
- /*
- * Assign each X,Z with sin,cos values scaled by latitude radius indexed by longitude.
- * Eg, long=0 and long=180 are at the poles, so zero scale is sin(longitude),
- * while long=90 (sin(90)=1) is at equator.
- */
- vert_ne.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
- vert_se.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
- vert_nw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
- vert_sw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
- vert_ne.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
- vert_se.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo ));
- vert_nw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE ));
- vert_sw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo ));
- /*
- * Draw the facet.
- */
- glBegin( GL_POLYGON );
- CrossProduct( vert_ne, vert_nw, vert_sw, &vert_norm );
- glNormal3f( vert_norm.x, vert_norm.y, vert_norm.z );
- glVertex3f( vert_ne.x, vert_ne.y, vert_ne.z );
- glVertex3f( vert_nw.x, vert_nw.y, vert_nw.z );
- glVertex3f( vert_sw.x, vert_sw.y, vert_sw.z );
- glVertex3f( vert_se.x, vert_se.y, vert_se.z );
- glEnd();
- #if BOING_DEBUG
- printf( "----------------------------------------------------------- \n" );
- printf( "lat = %f long_lo = %f long_hi = %f \n", lat_deg, long_lo, long_hi );
- printf( "vert_ne x = %.8f y = %.8f z = %.8f \n", vert_ne.x, vert_ne.y, vert_ne.z );
- printf( "vert_nw x = %.8f y = %.8f z = %.8f \n", vert_nw.x, vert_nw.y, vert_nw.z );
- printf( "vert_se x = %.8f y = %.8f z = %.8f \n", vert_se.x, vert_se.y, vert_se.z );
- printf( "vert_sw x = %.8f y = %.8f z = %.8f \n", vert_sw.x, vert_sw.y, vert_sw.z );
- #endif
- }
- /*
- * Toggle color so that next band will opposite red/white colors than this one.
- */
- colorToggle = ! colorToggle;
- /*
- * This circular band is done.
- */
- return;
- }
- /*****************************************************************************
- * Draw the purple grid of lines, behind the Boing ball.
- * When the Workbench is dropped to the bottom, Boing shows 12 rows.
- *****************************************************************************/
- void DrawGrid( void )
- {
- int row, col;
- const int rowTotal = 12; /* must be divisible by 2 */
- const int colTotal = rowTotal; /* must be same as rowTotal */
- const GLfloat widthLine = 2.0; /* should be divisible by 2 */
- const GLfloat sizeCell = GRID_SIZE / rowTotal;
- const GLfloat z_offset = -40.0;
- GLfloat xl, xr;
- GLfloat yt, yb;
- glPushMatrix();
- glDisable( GL_CULL_FACE );
- /*
- * Another relative Z translation to separate objects.
- */
- glTranslatef( 0.0, 0.0, DIST_BALL );
- /*
- * Draw vertical lines (as skinny 3D rectangles).
- */
- for ( col = 0; col <= colTotal; col++ )
- {
- /*
- * Compute co-ords of line.
- */
- xl = -GRID_SIZE / 2 + col * sizeCell;
- xr = xl + widthLine;
- yt = GRID_SIZE / 2;
- yb = -GRID_SIZE / 2 - widthLine;
- glBegin( GL_POLYGON );
- glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
- glVertex3f( xr, yt, z_offset ); /* NE */
- glVertex3f( xl, yt, z_offset ); /* NW */
- glVertex3f( xl, yb, z_offset ); /* SW */
- glVertex3f( xr, yb, z_offset ); /* SE */
- glEnd();
- }
- /*
- * Draw horizontal lines (as skinny 3D rectangles).
- */
- for ( row = 0; row <= rowTotal; row++ )
- {
- /*
- * Compute co-ords of line.
- */
- yt = GRID_SIZE / 2 - row * sizeCell;
- yb = yt - widthLine;
- xl = -GRID_SIZE / 2;
- xr = GRID_SIZE / 2 + widthLine;
- glBegin( GL_POLYGON );
- glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */
- glVertex3f( xr, yt, z_offset ); /* NE */
- glVertex3f( xl, yt, z_offset ); /* NW */
- glVertex3f( xl, yb, z_offset ); /* SW */
- glVertex3f( xr, yb, z_offset ); /* SE */
- glEnd();
- }
- glPopMatrix();
- return;
- }
- /*======================================================================*
- * main()
- *======================================================================*/
- int main( void )
- {
- GLFWwindow* window;
- /* Init GLFW */
- if( !glfwInit() )
- exit( EXIT_FAILURE );
- window = glfwCreateWindow( 400, 400, "Boing (classic Amiga demo)", NULL, NULL );
- if (!window)
- {
- glfwTerminate();
- exit( EXIT_FAILURE );
- }
- glfwSetWindowAspectRatio(window, 1, 1);
- glfwSetFramebufferSizeCallback(window, reshape);
- glfwSetKeyCallback(window, key_callback);
- glfwSetMouseButtonCallback(window, mouse_button_callback);
- glfwSetCursorPosCallback(window, cursor_position_callback);
- glfwMakeContextCurrent(window);
- gladLoadGL(glfwGetProcAddress);
- glfwSwapInterval( 1 );
- glfwGetFramebufferSize(window, &width, &height);
- reshape(window, width, height);
- glfwSetTime( 0.0 );
- init();
- /* Main loop */
- for (;;)
- {
- /* Timing */
- t = glfwGetTime();
- dt = t - t_old;
- t_old = t;
- /* Draw one frame */
- display();
- /* Swap buffers */
- glfwSwapBuffers(window);
- glfwPollEvents();
- /* Check if we are still running */
- if (glfwWindowShouldClose(window))
- break;
- }
- glfwTerminate();
- exit( EXIT_SUCCESS );
- }
|