Last active
January 26, 2025 18:39
-
-
Save faustinoaq/2d4330ba04b5ffda48feef2d46af5d98 to your computer and use it in GitHub Desktop.
OpenGL animation of a bouncing ball inside a transparent rotating cube that changes direction on click
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
/** | |
* @file bounce.cpp | |
* @brief OpenGL animation of a bouncing ball inside a transparent cube | |
* @author @faustinoaq | |
* | |
* This program creates a 3D animation using OpenGL and GLFW showing a red sphere | |
* bouncing inside a transparent cube with colored faces. The animation includes: | |
* - A rotating view of the scene | |
* - Sphere-cube collision detection and response | |
* - Transparent cube faces with colored edges | |
* - Fullscreen display options | |
* | |
* Usage: | |
* ./bounce 1 # Display 1 | |
* ./bounce 2 # Display 2 | |
* | |
* Controls: | |
* - ESC: Exit the program | |
* | |
* Dependencies: | |
* - GLFW3 | |
* - OpenGL | |
* - GLU | |
* Ubuntu 22.04: sudo apt-get install libglfw3-dev libglu1-mesa-dev | |
* | |
* Classes: | |
* - Ball: Represents the bouncing sphere with position, velocity and collision logic | |
* | |
* Key functions: | |
* - drawSphere(): Renders the bouncing ball | |
* - drawCube(): Renders the transparent cube with colored faces | |
* - main(): Program entry point and main rendering loop | |
* | |
*/ | |
#include <GLFW/glfw3.h> | |
#include <GL/glu.h> | |
#include <vector> | |
#include <cmath> | |
#include <iostream> | |
#include <cstring> | |
class Ball { | |
public: | |
float pos[3] = {0.0f, 0.0f, 0.0f}; | |
float vel[3] = {0.05f, 0.07f, 0.03f}; | |
float radius = 0.2f; | |
void update() { | |
for (int i = 0; i < 3; i++) { | |
pos[i] += vel[i]; | |
if (std::abs(pos[i]) + radius > 1.0f) { | |
pos[i] = (1.0f - radius) * (pos[i] > 0 ? 1 : -1); | |
vel[i] *= -1; | |
} | |
} | |
} | |
}; | |
void drawSphere(const Ball& ball) { | |
glPushMatrix(); | |
glTranslatef(ball.pos[0], ball.pos[1], ball.pos[2]); | |
glColor4f(1.0f, 0.0f, 0.0f, 0.7f); | |
GLUquadric* quad = gluNewQuadric(); | |
gluQuadricDrawStyle(quad, GLU_FILL); | |
gluSphere(quad, ball.radius, 32, 32); | |
gluDeleteQuadric(quad); | |
glPopMatrix(); | |
} | |
void drawCube() { | |
const float vertices[][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} | |
}; | |
const int surfaces[][4] = { | |
{0,1,2,3}, {3,2,7,6}, {6,7,5,4}, {4,5,1,0}, {1,5,7,2}, {4,0,3,6} | |
}; | |
const float colors[][4] = { | |
{0,1,1,0.1f}, {1,0,1,0.1f}, {1,0,0,0.1f}, | |
{0,1,0,0.1f}, {0,0,1,0.1f}, {1,1,0,0.1f} | |
}; | |
const int edges[][2] = { | |
{0,1}, {1,2}, {2,3}, {3,0}, {4,5}, {5,7}, | |
{7,6}, {6,4}, {0,4}, {1,5}, {2,7}, {3,6} | |
}; | |
glDepthMask(GL_FALSE); | |
glBegin(GL_QUADS); | |
for (int i = 0; i < 6; i++) { | |
glColor4fv(colors[i]); | |
for (int j = 0; j < 4; j++) { | |
glVertex3fv(vertices[surfaces[i][j]]); | |
} | |
} | |
glEnd(); | |
glDepthMask(GL_TRUE); | |
glColor4f(0, 0, 0, 0.5f); | |
glBegin(GL_LINES); | |
for (const auto& edge : edges) { | |
glVertex3fv(vertices[edge[0]]); | |
glVertex3fv(vertices[edge[1]]); | |
} | |
glEnd(); | |
} | |
// Global variable to track rotation direction | |
float rotationDirection = 1.0f; | |
// Mouse button callback function | |
void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { | |
if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) { | |
rotationDirection *= -1.0f; | |
} | |
} | |
int main(int argc, char* argv[]) { | |
if (!glfwInit()) return -1; | |
// Get primary monitor resolution and position | |
GLFWmonitor* primaryMonitor = glfwGetPrimaryMonitor(); | |
const GLFWvidmode* mode = glfwGetVideoMode(primaryMonitor); | |
int totalWidth = mode->width; //* 2; // For dual monitors | |
int height = mode->height; | |
// Set window hints for borderless fullscreen | |
glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Remove window decorations | |
glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); // Keep window on top | |
glfwWindowHint(GLFW_AUTO_ICONIFY, GLFW_FALSE); // Prevent minimizing | |
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Prevent resizing | |
// Create borderless window | |
// glfwCreateWindow(width, height, title, monitor, share) | |
GLFWwindow* window; | |
if (argc < 2) { | |
std::cout << "Please provide an argument '1' or '2'." << std::endl; | |
return -1; | |
} | |
if (strcmp(argv[1], "2") == 0) { | |
window = glfwCreateWindow(totalWidth, height, "Cube Bounce", | |
primaryMonitor, nullptr); | |
} else if (strcmp(argv[1], "1") == 0) { | |
window = glfwCreateWindow(totalWidth, height, "Cube Bounce", | |
nullptr, nullptr); | |
} else { | |
std::cout << "Invalid argument. Please use '1' or '2'." << std::endl; | |
return -1; | |
} | |
if (!window) { | |
glfwTerminate(); | |
return -1; | |
} | |
// Position window at (0,0) to span monitors | |
glfwSetWindowPos(window, 0, 0); | |
// Make this window the current OpenGL context | |
glfwMakeContextCurrent(window); | |
// Register mouse button callback | |
glfwSetMouseButtonCallback(window, mouseButtonCallback); | |
// Rest of the code remains the same... | |
// Setup perspective | |
float aspectRatio = (float)totalWidth / height; | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
gluPerspective(45, aspectRatio, 0.1, 50.0); | |
glMatrixMode(GL_MODELVIEW); | |
glLoadIdentity(); | |
glTranslatef(0.0f, 0.0f, -8.0f); | |
// Enable needed GL features | |
glEnable(GL_DEPTH_TEST); | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
Ball ball; | |
float rotationAngle = 0.0f; | |
while (!glfwWindowShouldClose(window)) { | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
glPushMatrix(); | |
glRotatef(rotationAngle, 1, 1, 1); | |
ball.update(); | |
drawSphere(ball); | |
drawCube(); | |
glPopMatrix(); | |
rotationAngle += rotationDirection; | |
if (rotationAngle > 360.0f) rotationAngle -= 360.0f; | |
if (rotationAngle < 0.0f) rotationAngle += 360.0f; | |
glfwSwapBuffers(window); | |
glfwPollEvents(); | |
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) | |
glfwSetWindowShouldClose(window, GLFW_TRUE); | |
glfwWaitEventsTimeout(0.01); | |
} | |
glfwTerminate(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
bounce.mp4