Skip to content

Instantly share code, notes, and snippets.

@mieki256
Last active September 18, 2022 13:57
Show Gist options
  • Save mieki256/42d37ea61b72802b430e447946e17e27 to your computer and use it in GitHub Desktop.
Save mieki256/42d37ea61b72802b430e447946e17e27 to your computer and use it in GitHub Desktop.
Bound ball animation by using C + OpenGL + freeglut 3.0.0. use MinGW (gcc 9.2.0).
/* Last updated: <2022/09/18 11:40:29 +0900> */
/*
OpenGL Bound Ball by mieki256
ESC, q, key : Exit
f key : Display FPS ON/OFF
Use Windows10 x64 21H2 + MinGW(gcc 9.2.0) + freeglut 3.0.0 (freeglut-MinGW-3.0.0-1.mp.zip)
Build:
gcc 01_gl_bound_ball.c -o 01_gl_bound_ball.exe -D FREEGLUT_STATIC -lfreeglut_static -lopengl32 -lglu32 -lwinmm -lgdi32 -mwindows
*/
#define _USE_MATH_DEFINES
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <math.h>
#include <mmsystem.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#if 1
#define SCRW 1600
#define SCRH 900
#else
#define SCRW 512
#define SCRH 288
#endif
#define FPS 60
#define BOX_W 20
#define SPHERE_R 0.5
#define DEG2RAD(x) (double)((x)*M_PI / 180.0)
// globals for size of screen
static int Width, Height;
// global work
static double ang = 0.0;
static double ang_d = 0.0;
static GLfloat bx = 0.0;
static GLfloat by = 0.0;
static GLfloat bz = 0.0;
static GLfloat dx = 0.0;
static GLfloat dy = 0.0;
static GLfloat dz = 0.0;
static GLfloat cx = 0.0;
static GLfloat cy = 0.0;
static GLfloat cz = 0.0;
static float hit = 0.0;
static int hit_dir = 0;
static GLUquadricObj *qobj;
static int fps_bar_enable;
static DWORD rec_time;
static int count_frame;
static int count_fps;
static GLfloat light0pos[] = {BOX_W * 0.25, BOX_W * 1.0, BOX_W * 0.5, 1.0};
static GLfloat light0dif[] = {1.0, 1.0, 1.0, 1.0};
static GLfloat light0spe[] = {1.0, 1.0, 1.0, 1.0};
static GLfloat light0amb[] = {0.5, 0.5, 1.0, 1.0};
static GLfloat green[] = {0.0, 1.0, 0.0, 1.0};
static void InitCountFps(void)
{
timeBeginPeriod(1);
rec_time = timeGetTime();
count_fps = 0;
count_frame = 0;
fps_bar_enable = 0;
}
static void CloseCountFps(void)
{
timeEndPeriod(1);
}
static void CalcFps(void)
{
count_frame++;
DWORD t = timeGetTime() - rec_time;
if (t >= 1000)
{
rec_time += 1000;
count_fps = count_frame;
count_frame = 0;
}
else if (t < 0)
{
rec_time = timeGetTime();
count_fps = 0;
count_frame = 0;
}
}
static void InitWork(int box_w, int fps)
{
ang = 0.0;
ang_d = 360.0 / (FPS * 30.0);
bx = 0.0;
by = 0.0;
bz = 0.0;
cx = 0.0;
cy = 0.0;
cz = 0.0;
hit = 0.0;
hit_dir = 0;
dx = ((float)box_w / (float)fps) * 0.5;
dy = dx * 0.3;
dz = dx * 0.7;
}
void DrawString(GLfloat x, GLfloat y, GLfloat z, char *str, int length)
{
// light disable
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
glColor3f(0.5, 0.5, 0.5);
glRasterPos3f(x, y, z);
for (int i = 0; i < length; i++)
{
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, str[i]);
}
}
static GLfloat cube_pos[8][3] =
{
{-1, -1, -1},
{-1, 1, -1},
{1, 1, -1},
{1, -1, -1},
{-1, -1, 1},
{-1, 1, 1},
{1, 1, 1},
{1, -1, 1}
};
static int cube_indexs[12][2] =
{
{0, 1},
{1, 2},
{2, 3},
{3, 0},
{4, 5},
{5, 6},
{6, 7},
{7, 4},
{0, 4},
{1, 5},
{2, 6},
{3, 7}
};
static void DrawWireCube(GLfloat width)
{
GLfloat w = width / 2.0;
for (int i = 0; i < 12; i++)
{
int i0 = cube_indexs[i][0];
int i1 = cube_indexs[i][1];
GLfloat x0 = cube_pos[i0][0];
GLfloat y0 = cube_pos[i0][1];
GLfloat z0 = cube_pos[i0][2];
GLfloat x1 = cube_pos[i1][0];
GLfloat y1 = cube_pos[i1][1];
GLfloat z1 = cube_pos[i1][2];
glBegin(GL_LINES);
glVertex3f(w * x0, w * y0, w * z0);
glVertex3f(w * x1, w * y1, w * z1);
glEnd();
}
if (fps_bar_enable)
{
// draw FPS bar
GLfloat ww = width * (float)count_fps / (float)FPS;
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_LINES);
glVertex3f(-w, -w, w * 0.98);
glVertex3f(ww - w, -w, w * 0.98);
glEnd();
}
}
static void DrawGrid(GLfloat width)
{
GLfloat w, x, y, d;
w = width / 2.0;
x = -w;
y = -w;
d = width / 10.0;
while (x <= (width / 2))
{
glBegin(GL_LINES);
glVertex3f(x, y, -w);
glVertex3f(x, y, w);
glEnd();
glBegin(GL_LINES);
glVertex3f(-w, y, x);
glVertex3f(w, y, x);
glEnd();
x += d;
}
}
static void DrawCircle(GLfloat bx, GLfloat by, GLfloat bz, GLfloat r, int hit_dir)
{
GLfloat x, y, z, d;
glPushMatrix();
glTranslatef(bx, by, bz);
if (hit_dir == 0)
glRotatef(90.0, 0.0, 1.0, 0.0);
else if (hit_dir == 1)
glRotatef(90.0, 1.0, 0.0, 0.0);
y = -r;
d = r / 5.0;
while (y <= r)
{
x = sqrt((double)(r * r - (y * y)));
glBegin(GL_LINES);
glVertex3f(-x, y, 0.0);
glVertex3f(+x, y, 0.0);
glEnd();
glBegin(GL_LINES);
glVertex3f(y, -x, 0.0);
glVertex3f(y, +x, 0.0);
glEnd();
y += d;
}
glPopMatrix();
}
/* Display callback function */
void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (fps_bar_enable)
{
char dstr[256];
sprintf(dstr, "FPS: %d", count_fps);
DrawString(0, 9.5, 0, dstr, strlen(dstr));
}
// set light
glDisable(GL_LIGHTING);
glEnable(GL_LIGHTING);
glEnable(GL_NORMALIZE);
glLightfv(GL_LIGHT0, GL_POSITION, light0pos);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light0dif);
glLightfv(GL_LIGHT0, GL_SPECULAR, light0spe);
glLightfv(GL_LIGHT0, GL_AMBIENT, light0amb);
glLoadIdentity();
// rotate camera position
{
GLdouble r = (BOX_W / 2.0) + 15.0;
GLdouble x = r * cos(DEG2RAD(ang));
GLdouble y = r * 0.2 + 10 * sin(DEG2RAD(ang * 2));
GLdouble z = r * sin(DEG2RAD(ang));
// camera xyz, the xyz to look at, and the up vector (+y is up)
gluLookAt(x, y, z, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
}
glScalef(1.0, 1.0, 1.0);
// light disable
glDisable(GL_LIGHT0);
glDisable(GL_LIGHTING);
// set color
if (hit <= 0.0)
glColor3f(0.0, 0.0, 0.8);
else
glColor3f(hit, hit, 0.8 + 0.2 * hit);
// draw grid
DrawGrid(BOX_W);
// draw cube
// glutWireCube(BOX_W)
DrawWireCube(BOX_W);
// draw hit effect
if (hit > 0.0)
{
glColor3f(0.0, hit, hit);
DrawCircle(cx, cy, cz, 0.1 + 2.0 - 2.0 * hit, hit_dir);
}
// light enable
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glPushMatrix();
glTranslatef(bx, by, bz);
// set color
if (hit <= 0.0)
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, green);
else
{
GLfloat col[] = {0.0, 0.0, 0.0, 1.0};
col[0] = hit;
col[1] = 1.0 - hit;
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, col);
}
// draw sphere
// gluSphere(qobj, radius, slices, stacks);
gluSphere(qobj, SPHERE_R, 32, 16);
glPopMatrix();
// glFlush();
// glFinish();
// SwapBuffers(hDC);
glutSwapBuffers();
CalcFps();
}
void InitViewAndPers(int w, int h)
{
// window resizing stuff
Width = w;
Height = h;
glViewport(0, 0, (GLsizei)w, (GLsizei)h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, (float)Width / (float)Height, 0.1, 100.0);
glMatrixMode(GL_MODELVIEW);
}
void InitGL(int w, int h)
{
InitViewAndPers(w, h);
glLoadIdentity();
// background
glClearColor(0.0, 0.0, 0.0, 0.0); // 0.0s is black
glClearDepth(1.0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
// glShadeModel(GL_FLAT);
glShadeModel(GL_SMOOTH);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// glEnable(GL_CULL_FACE);
// glCullFace(GL_BACK);
glDisable(GL_LIGHTING);
glEnable(GL_LIGHTING);
glEnable(GL_NORMALIZE);
InitWork(BOX_W, FPS);
qobj = gluNewQuadric();
InitCountFps();
}
void resize(int w, int h)
{
InitViewAndPers(w, h);
}
void OnTimer(int value)
{
// update angle and position
ang += ang_d;
if (ang >= 360.0)
ang -= 360.0;
bx += dx;
by += dy;
bz += dz;
if (hit > 0.0)
{
hit -= 0.03;
if (hit <= 0.0)
hit = 0.0;
}
// collision check
{
float w, v0, v1;
w = BOX_W / 2.0;
v0 = bx - SPHERE_R;
v1 = bx + SPHERE_R;
if (v0 <= -w || v1 >= w)
{
dx *= -1;
hit = 1.0;
hit_dir = 0;
cx = (v0 <= -w) ? v0 : v1;
cy = by;
cz = bz;
}
v0 = by - SPHERE_R;
v1 = by + SPHERE_R;
if (v0 <= -w || v1 >= w)
{
dy *= -1;
hit = 1.0;
hit_dir = 1;
cx = bx;
cy = (v0 <= -w) ? v0 : v1;
cz = bz;
}
v0 = bz - SPHERE_R;
v1 = bz + SPHERE_R;
if (v0 <= -w || v1 >= w)
{
dz *= -1;
hit = 1.0;
hit_dir = 2;
cx = bx;
cy = by;
cz = (v0 <= -w) ? v0 : v1;
}
}
glutPostRedisplay();
glutTimerFunc((int)(1000 / FPS), OnTimer, 0);
}
void CloseGL(void)
{
if (qobj)
{
gluDeleteQuadric(qobj);
}
CloseCountFps();
}
/* Keyboard callback function */
void keyboard(unsigned char key, int x, int y)
{
switch (key)
{
case '\x1B':
case 'q':
case 'Q':
/* Exit on escape key press */
CloseGL();
exit(EXIT_SUCCESS);
break;
case 'f':
case 'F':
fps_bar_enable = !fps_bar_enable;
break;
default:
break;
}
}
/* Main method */
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitWindowPosition(100, 50);
glutInitWindowSize(SCRW, SCRH);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
/* Create a single window with a keyboard and display callback */
glutCreateWindow("GL Bound Ball");
glutReshapeFunc(resize);
glutKeyboardFunc(&keyboard);
glutDisplayFunc(&display);
glutTimerFunc((int)(1000 / FPS), OnTimer, 0);
InitGL(SCRW, SCRH);
/* Run the GLUT event loop */
glutMainLoop();
CloseGL();
return EXIT_SUCCESS;
}
01_gl_bound_ball.exe: 01_gl_bound_ball.c
gcc 01_gl_bound_ball.c -o 01_gl_bound_ball.exe -static -D FREEGLUT_STATIC -lfreeglut_static -lopengl32 -lglu32 -lwinmm -lgdi32 -mwindows
.PHONY: clean
clean:
rm -f *.exe
rm -f *.o
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment