When Ridit was seven, his father gave him a blank notebook and wrote two words on the first page: Design Pattern...
Every day, they sat by the window. One day, they talked about if
statements. Another day, about loops
. Then, about pixels. Slowly, the boy who once played around Scratch started coding his own in Java.
Years passed. Ridit, now in class IX, walked into his father’s study with a sketch.
“Dad, I’m making a 3D editor in OpenGL. However, I’m unsure — should I use the Command Pattern for undo/redo or opt for an Event Queue? How about making a Strategy pattern for rendering? I think we must make world a unique pointer.”
His father looked up from a book and smiled.
“I taught you how to write a function, three languages - Java, Python and C++, UML and Design Pattern. And now you’re debating architectural design?”
Ridit grinned. “Well, you said good code should age like good books.”
They opened a fresh diagram. The son led the whiteboard this time. But the father’s eyes gleamed — not with pride, but with recognition.
Two ends of the zero had met.
The boy who once asked what a for
loop was now explained memory layouts. The father who once debugged with gdb
now listened to his son debug framebuffers.
And here I am. My son Ridit asked me to explore Bullet Physics engine and for the visuals - openGL.
Here we go...
Watch the transformation of a young boy of Bharat...
The father-son team is exploring software together - we are looking in the same direction - in complete harmony.
My exploration continues...
Here's the code for a Pendulum motion using Bullet physics engine and OpenGL combination.
// OpenGL + Bullet integration for a pendulum simulation using GLFW and GLAD
// Requires: Bullet (static libs in bullet_dev_install), GLFW, GLAD, GLM
#include "./../libs/include/glad/glad.h"
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <btBulletDynamicsCommon.h>
#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
// Global Bullet variables
btDiscreteDynamicsWorld* dynamicsWorld;
btRigidBody* pendulumBob;
btHingeConstraint* hinge;
// OpenGL sphere
GLuint sphereVAO = 0, sphereVBO, sphereEBO;
int indexCount = 0;
GLuint shaderProgram;
// === Shader Loading ===
std::string readFile(const char* path) {
std::ifstream file(path);
std::stringstream ss;
ss << file.rdbuf();
return ss.str();
}
GLuint loadShaders(const char* vPath, const char* fPath) {
std::string vertCode = readFile(vPath);
std::string fragCode = readFile(fPath);
const char* vShaderCode = vertCode.c_str();
const char* fShaderCode = fragCode.c_str();
GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vShader, 1, &vShaderCode, NULL);
glCompileShader(vShader);
GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fShader, 1, &fShaderCode, NULL);
glCompileShader(fShader);
GLuint prog = glCreateProgram();
glAttachShader(prog, vShader);
glAttachShader(prog, fShader);
glLinkProgram(prog);
glDeleteShader(vShader);
glDeleteShader(fShader);
return prog;
}
// === Sphere Mesh ===
void createSphereMesh(int stacks = 18, int sectors = 36) {
std::vector<float> vertices;
std::vector<unsigned int> indices;
for (int i = 0; i <= stacks; ++i) {
float stackAngle = glm::pi<float>() / 2 - i * glm::pi<float>() / stacks;
float xy = cos(stackAngle);
float z = sin(stackAngle);
for (int j = 0; j <= sectors; ++j) {
float sectorAngle = j * 2 * glm::pi<float>() / sectors;
float x = xy * cos(sectorAngle);
float y = xy * sin(sectorAngle);
vertices.insert(vertices.end(), { x, y, z });
}
}
for (int i = 0; i < stacks; ++i) {
int k1 = i * (sectors + 1);
int k2 = k1 + sectors + 1;
for (int j = 0; j < sectors; ++j, ++k1, ++k2) {
if (i != 0)
indices.insert(indices.end(), { k1, k2, k1 + 1 }); // ✅ fixed
if (i != (stacks - 1))
indices.insert(indices.end(), { k1 + 1, k2, k2 + 1 }); // ✅ fixed
}
}
indexCount = indices.size();
glGenVertexArrays(1, &sphereVAO);
glGenBuffers(1, &sphereVBO);
glGenBuffers(1, &sphereEBO);
glBindVertexArray(sphereVAO);
glBindBuffer(GL_ARRAY_BUFFER, sphereVBO);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphereEBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), indices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
}
// === Render Function ===
void renderSphere(glm::vec3 position,float scale) {
glm::mat4 model = glm::translate(glm::mat4(1.0f), position);
model = glm::scale(model, glm::vec3(scale));
glUseProgram(shaderProgram);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "model"), 1, GL_FALSE, &model[0][0]);
glBindVertexArray(sphereVAO);
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
}
// === Bullet Initialization ===
void initBullet() {
auto* broadphase = new btDbvtBroadphase();
auto* config = new btDefaultCollisionConfiguration();
auto* dispatcher = new btCollisionDispatcher(config);
auto* solver = new btSequentialImpulseConstraintSolver();
dynamicsWorld = new btDiscreteDynamicsWorld(dispatcher, broadphase, solver, config);
dynamicsWorld->setGravity(btVector3(0, -10, 0));
auto* shape = new btSphereShape(1.0);
btTransform start;
start.setIdentity();
start.setOrigin(btVector3(5, -5, 0)); // give offset from hinge point
btVector3 inertia(0, 0, 0);
shape->calculateLocalInertia(1.0, inertia);
auto* motionState = new btDefaultMotionState(start);
btRigidBody::btRigidBodyConstructionInfo info(1.0, motionState, shape, inertia);
pendulumBob = new btRigidBody(info);
dynamicsWorld->addRigidBody(pendulumBob);
btVector3 pivot(0, 0, 0);
btVector3 pivotInA = pendulumBob->getCenterOfMassTransform().inverse() * pivot;
btVector3 axisInA(0, 0, 1);
hinge = new btHingeConstraint(*pendulumBob, pivotInA, axisInA);
dynamicsWorld->addConstraint(hinge);
}
void drawLine(glm::vec3 a, glm::vec3 b) {
GLfloat vertices[] = {
b.x, b.y, b.z,
a.x, a.y, a.z
};
GLuint VBO, VAO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);
glUseProgram(shaderProgram);
glBindVertexArray(VAO);
glDrawArrays(GL_LINES, 0, 2);
glDeleteBuffers(1, &VBO);
glDeleteVertexArrays(1, &VAO);
}
// === Main ===
int main() {
// GLFW Init
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow* window = glfwCreateWindow(800, 600, "Bullet Pendulum", NULL, NULL);
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // dark background
glEnable(GL_DEPTH_TEST);
shaderProgram = loadShaders("shaders/vertex.glsl", "shaders/fragment.glsl");
createSphereMesh();
initBullet();
//glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.0f/600.0f, 0.1f, 100.0f);
//glm::mat4 view = glm::lookAt(glm::vec3(0, 0, 20), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
glm::mat4 view = glm::lookAt(glm::vec3(0, 5, 20), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0));
glm::mat4 projection = glm::perspective(glm::radians(50.0f), 800.0f/600.0f, 0.1f, 100.0f);
while (!glfwWindowShouldClose(window)) {
dynamicsWorld->stepSimulation(1.f/60.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUseProgram(shaderProgram);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "view"), 1, GL_FALSE, &view[0][0]);
glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, &projection[0][0]);
btTransform trans;
pendulumBob->getMotionState()->getWorldTransform(trans);
btVector3 pos = trans.getOrigin();
glm::vec3 bobPos(pos.getX(), pos.getY(), pos.getZ());
// draw the pendulum bob
renderSphere(bobPos,1.f);
// draw rope from hinge (0,0,0) to bob
drawLine(glm::vec3(0.0f, 0.0f, 0.0f), -bobPos); // hinge to bob
renderSphere(glm::vec3(0, 0, 0),0.2f); // draw a tiny sphere at the hinge
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}
The Vertex Shader code (Vertex.glsl)
#version 330 core
layout(location = 0) in vec3 aPos;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
void main() {
gl_Position = projection * view * model * vec4(aPos, 1.0);
}
The Fragment Shader code (fragment.glsl)
#version 330 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.0, 0.8, 0.2, 1.0); // Yellowish sphere
}
And this is how it looks like...
The Guru had seeded. The Disciple had bloomed. And now, they code together, not teacher and student, but equals bound by curiosity.
When learning is shared, not taught, the journey loops back to its source — and the circle completes itself.
No comments:
Post a Comment