Last active
September 18, 2022 13:57
-
-
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).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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