#include <GL/glut.
h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stdio.h>
#define MAX_CHILDREN 10
#define M_PI 3.14159265358979323846
/*
Controls:
- Arrow keys: Move camera around robot (Left/Right: orbit, Up/Down: adjust
height).
- Mouse (left button): Rotate view orientation.
- Right-click: Open menu to toggle walking animation, save/load configurations
(logs can be seen in the console).
- 1-9 keys: Select a joint (shoulders, elbows, hips, knees, neck).
1 -- left ShoulderJoint
2 -- right ShoulderJoint
3 -- left ElbowJoint
4 -- right ElbowJoint
5 -- left HipJoint
6 -- right HipJoint
7 -- left KneeJoint
8 -- right KneeJoint
9 -- neckJoint
- a/d: Rotate selected joint on X-axis.
- w/s: Rotate selected joint on Y-axis.
PS: I recommend toggling walking animation off before loading configurations so it
is more observable.
*/
typedef struct SceneNode {
char name[50];
int isJoint;
float angleX, angleY, angleZ;
float posX, posY, posZ;
float sizeX, sizeY, sizeZ;
struct SceneNode* parent;
struct SceneNode* children[MAX_CHILDREN];
int childCount;
} SceneNode;
typedef struct {
SceneNode* root;
} Robot;
Robot robot;
int windowWidth = 800, windowHeight = 600;
int frameCount = 0;
float viewAngleX = 0.0f, viewAngleY = 0.0f;
int lastX = 0, lastY = 0;
int mouseDown = 0;
float cameraRadius = 10.0f;
float cameraTheta = M_PI / 2.0f;
float cameraPhi = M_PI / 3.0f;
float cameraX, cameraY, cameraZ;
SceneNode* selectedJoint = NULL;
int walkingEnabled = 1;
float bgColorR = 0.0f, bgColorG = 0.0f, bgColorB = 0.0f;
float feedbackTimer = 0.0f;
int feedbackState = 0;
void updateCameraPosition() {
cameraX = cameraRadius * sin(cameraPhi) * cos(cameraTheta);
cameraY = cameraRadius * cos(cameraPhi);
cameraZ = cameraRadius * sin(cameraPhi) * sin(cameraTheta);
}
SceneNode* createNode(const char* name, int isJoint, SceneNode* parent) {
SceneNode* node = (SceneNode*)malloc(sizeof(SceneNode));
strcpy(node->name, name);
node->isJoint = isJoint;
node->angleX = node->angleY = node->angleZ = 0.0f;
node->posX = node->posY = node->posZ = 0.0f;
node->sizeX = node->sizeY = node->sizeZ = 1.0f;
node->parent = parent;
node->childCount = 0;
if (parent != NULL) {
parent->children[parent->childCount++] = node;
}
return node;
}
void drawComponent(SceneNode* node) {
if (strstr(node->name, "Arm") != NULL || strstr(node->name, "Leg") != NULL) {
GLUquadric* quad = gluNewQuadric();
glPushMatrix();
glRotatef(90, 1, 0, 0);
gluCylinder(quad, 0.15, 0.15, node->sizeY, 20, 20);
glPopMatrix();
gluDeleteQuadric(quad);
} else if (strstr(node->name, "Head") != NULL) {
glutSolidSphere(0.25, 20, 20);
glPushMatrix();
glTranslatef(0.0f, 0.3f, 0.0f);
glutSolidSphere(0.05, 10, 10);
glPopMatrix();
} else if (strcmp(node->name, "floor") == 0) {
glBegin(GL_QUADS);
glNormal3f(0.0f, 1.0f, 0.0f);
glVertex3f(-10.0f, 0.0f, -10.0f);
glVertex3f(-10.0f, 0.0f, 10.0f);
glVertex3f(10.0f, 0.0f, 10.0f);
glVertex3f(10.0f, 0.0f, -10.0f);
glEnd();
} else {
glScalef(node->sizeX, node->sizeY, node->sizeZ);
glutSolidCube(1.0);
}
}
void updateNode(SceneNode* node) {
glPushMatrix();
glTranslatef(node->posX, node->posY, node->posZ);
if (node->isJoint) {
glRotatef(node->angleX, 1, 0, 0);
glRotatef(node->angleY, 0, 1, 0);
glRotatef(node->angleZ, 0, 0, 1);
GLfloat mat_ambient[] = {0.3f, 0.3f, 0.3f, 1.0f};
GLfloat mat_diffuse[] = {0.7f, 0.7f, 0.7f, 1.0f};
GLfloat mat_specular[] = {1.0f, 1.0f, 1.0f, 1.0f};
GLfloat mat_shininess[] = {50.0f};
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glutSolidSphere(0.1, 20, 20);
} else {
if (strcmp(node->name, "lowerBody") == 0) {
GLfloat mat_diffuse[] = {0.2f, 0.2f, 0.8f, 1.0f};
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
} else if (strcmp(node->name, "torso") == 0) {
GLfloat mat_diffuse[] = {0.8f, 0.2f, 0.2f, 1.0f};
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
} else if (strstr(node->name, "Arm") || strstr(node->name, "Leg")) {
GLfloat mat_diffuse[] = {0.2f, 0.8f, 0.2f, 1.0f};
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
} else if (strstr(node->name, "Hand") || strstr(node->name, "Foot")) {
GLfloat mat_diffuse[] = {0.8f, 0.8f, 0.2f, 1.0f};
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
} else if (strcmp(node->name, "head") == 0) {
GLfloat mat_diffuse[] = {0.5f, 0.5f, 0.5f, 1.0f};
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
} else if (strcmp(node->name, "floor") == 0) {
GLfloat mat_diffuse[] = {0.4f, 0.4f, 0.4f, 1.0f};
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
}
drawComponent(node);
}
for (int i = 0; i < node->childCount; i++) {
updateNode(node->children[i]);
}
glPopMatrix();
}
SceneNode* findNode(SceneNode* node, const char* name) {
if (strcmp(node->name, name) == 0) return node;
for (int i = 0; i < node->childCount; i++) {
SceneNode* found = findNode(node->children[i], name);
if (found) return found;
}
return NULL;
}
void saveRobotConfig(const char* filename) {
FILE* file = fopen(filename, "w");
if (!file) {
printf("Error: Could not open file %s for saving. Check permissions or
path.\n", filename);
return;
}
fprintf(file, "Camera %f %f %f\n", cameraTheta, cameraPhi, cameraRadius);
void saveNode(SceneNode* node, FILE* file) {
if (node->isJoint) {
fprintf(file, "%s %f %f %f\n", node->name, node->angleX, node->angleY,
node->angleZ);
}
for (int i = 0; i < node->childCount; i++) {
saveNode(node->children[i], file);
}
}
saveNode([Link], file);
fclose(file);
char fullpath[1024];
if (_fullpath(fullpath, filename, sizeof(fullpath))) {
printf("Configuration saved to %s\n", fullpath);
} else {
printf("Configuration saved to %s (full path unavailable)\n", filename);
}
bgColorR = 0.0f; bgColorG = 1.0f; bgColorB = 0.0f;
feedbackTimer = 30.0f;
feedbackState = 1;
}
void loadRobotConfig(const char* filename) {
FILE* file = fopen(filename, "r");
if (!file) {
printf("Error: Could not open file %s for loading. Check if the file
exists.\n", filename);
return;
}
char name[50];
float val1, val2, val3;
while (fscanf(file, "%s %f %f %f\n", name, &val1, &val2, &val3) == 4) {
if (strcmp(name, "Camera") == 0) {
cameraTheta = val1;
cameraPhi = val2;
cameraRadius = val3;
updateCameraPosition();
} else {
SceneNode* node = findNode([Link], name);
if (node) {
node->angleX = val1;
node->angleY = val2;
node->angleZ = val3;
}
}
}
fclose(file);
char fullpath[1024];
if (_fullpath(fullpath, filename, sizeof(fullpath))) {
printf("Configuration loaded from %s\n", fullpath);
} else {
printf("Configuration loaded from %s (full path unavailable)\n", filename);
}
bgColorR = 0.0f; bgColorG = 0.0f; bgColorB = 1.0f;
feedbackTimer = 30.0f;
feedbackState = 2;
}
void animateRobot() {
if (!walkingEnabled) return;
float t = (frameCount % 100) / 100.0f;
float walkAngle = sin(t * 2 * M_PI) * 30.0f;
float kneeAngle = fabs(walkAngle) / 2.0f;
float headAngle = sin(t * 4 * M_PI) * 5.0f;
SceneNode* lHip = findNode([Link], "lHipJoint");
if (lHip) lHip->angleX = walkAngle;
SceneNode* lKnee = findNode([Link], "lKneeJoint");
if (lKnee) lKnee->angleX = kneeAngle;
SceneNode* rHip = findNode([Link], "rHipJoint");
if (rHip) rHip->angleX = -walkAngle;
SceneNode* rKnee = findNode([Link], "rKneeJoint");
if (rKnee) rKnee->angleX = kneeAngle;
SceneNode* lShoulder = findNode([Link], "lShoulderJoint");
if (lShoulder) lShoulder->angleX = -walkAngle / 2.0f;
SceneNode* lElbow = findNode([Link], "lElbowJoint");
if (lElbow) lElbow->angleX = kneeAngle / 2.0f;
SceneNode* rShoulder = findNode([Link], "rShoulderJoint");
if (rShoulder) rShoulder->angleX = walkAngle / 2.0f;
SceneNode* rElbow = findNode([Link], "rElbowJoint");
if (rElbow) rElbow->angleX = kneeAngle / 2.0f;
SceneNode* neck = findNode([Link], "neckJoint");
if (neck) neck->angleY = headAngle;
frameCount++;
}
void buildRobot() {
[Link] = createNode("lowerBody", 0, NULL);
[Link]->posX = 0; [Link]->posY = 0; [Link]->posZ = 0;
[Link]->sizeX = 1.2f; [Link]->sizeY = 0.6f; [Link]->sizeZ = 0.8f;
SceneNode* torso = createNode("torso", 0, [Link]);
torso->posX = 0; torso->posY = 1.15f; torso->posZ = 0;
torso->sizeX = 1.0f; torso->sizeY = 1.5f; torso->sizeZ = 0.6f;
SceneNode* neckJoint = createNode("neckJoint", 1, torso);
neckJoint->posX = 0; neckJoint->posY = 0.75f; neckJoint->posZ = 0;
SceneNode* head = createNode("head", 0, neckJoint);
head->posX = 0; head->posY = 0.3f; head->posZ = 0;
SceneNode* lShoulderJoint = createNode("lShoulderJoint", 1, torso);
lShoulderJoint->posX = -0.5f; lShoulderJoint->posY = 0.6f; lShoulderJoint->posZ
= 0;
SceneNode* lUpperArm = createNode("lUpperArm", 0, lShoulderJoint);
lUpperArm->posX = 0; lUpperArm->posY = 0; lUpperArm->posZ = 0;
lUpperArm->sizeY = 1.0f;
SceneNode* lElbowJoint = createNode("lElbowJoint", 1, lUpperArm);
lElbowJoint->posY = -lUpperArm->sizeY;
SceneNode* lLowerArm = createNode("lLowerArm", 0, lElbowJoint);
lLowerArm->posX = 0; lLowerArm->posY = 0; lLowerArm->posZ = 0;
lLowerArm->sizeY = 0.8f;
SceneNode* lWristJoint = createNode("lWristJoint", 1, lLowerArm);
lWristJoint->posY = -lLowerArm->sizeY;
SceneNode* lHand = createNode("lHand", 0, lWristJoint);
lHand->posX = 0; lHand->posY = -0.1f; lHand->posZ = 0;
lHand->sizeX = 0.25f; lHand->sizeY = 0.1f; lHand->sizeZ = 0.15f;
SceneNode* rShoulderJoint = createNode("rShoulderJoint", 1, torso);
rShoulderJoint->posX = 0.5f; rShoulderJoint->posY = 0.6f; rShoulderJoint->posZ
= 0;
SceneNode* rUpperArm = createNode("rUpperArm", 0, rShoulderJoint);
rUpperArm->posX = 0; rUpperArm->posY = 0; rUpperArm->posZ = 0;
rUpperArm->sizeY = 1.0f;
SceneNode* rElbowJoint = createNode("rElbowJoint", 1, rUpperArm);
rElbowJoint->posY = -rUpperArm->sizeY;
SceneNode* rLowerArm = createNode("rLowerArm", 0, rElbowJoint);
rLowerArm->posX = 0; rLowerArm->posY = 0; rLowerArm->posZ = 0;
rLowerArm->sizeY = 0.8f;
SceneNode* rWristJoint = createNode("rWristJoint", 1, rLowerArm);
rWristJoint->posY = -rLowerArm->sizeY;
SceneNode* rHand = createNode("rHand", 0, rWristJoint);
rHand->posX = 0; rHand->posY = -0.1f; rHand->posZ = 0;
rHand->sizeX = 0.25f; rHand->sizeY = 0.1f; rHand->sizeZ = 0.15f;
SceneNode* lHipJoint = createNode("lHipJoint", 1, [Link]);
lHipJoint->posX = -0.3f; lHipJoint->posY = -0.3f; lHipJoint->posZ = 0;
SceneNode* lUpperLeg = createNode("lUpperLeg", 0, lHipJoint);
lUpperLeg->posX = 0; lUpperLeg->posY = 0; lUpperLeg->posZ = 0;
lUpperLeg->sizeY = 1.2f;
SceneNode* lKneeJoint = createNode("lKneeJoint", 1, lUpperLeg);
lKneeJoint->posY = -lUpperLeg->sizeY;
SceneNode* lLowerLeg = createNode("lLowerLeg", 0, lKneeJoint);
lLowerLeg->posX = 0; lLowerLeg->posY = 0; lLowerLeg->posZ = 0;
lLowerLeg->sizeY = 1.0f;
SceneNode* lAnkleJoint = createNode("lAnkleJoint", 1, lLowerLeg);
lAnkleJoint->posY = -lLowerLeg->sizeY;
SceneNode* lFoot = createNode("lFoot", 0, lAnkleJoint);
lFoot->posX = 0; lFoot->posY = -0.1f; lFoot->posZ = 0.2f;
lFoot->sizeX = 0.3f; lFoot->sizeY = 0.1f; lFoot->sizeZ = 0.5f;
SceneNode* rHipJoint = createNode("rHipJoint", 1, [Link]);
rHipJoint->posX = 0.3f; rHipJoint->posY = -0.3f; rHipJoint->posZ = 0;
SceneNode* rUpperLeg = createNode("rUpperLeg", 0, rHipJoint);
rUpperLeg->posX = 0; rUpperLeg->posY = 0; rUpperLeg->posZ = 0;
rUpperLeg->sizeY = 1.2f;
SceneNode* rKneeJoint = createNode("rKneeJoint", 1, rUpperLeg);
rKneeJoint->posY = -rUpperLeg->sizeY;
SceneNode* rLowerLeg = createNode("rLowerLeg", 0, rKneeJoint);
rLowerLeg->posX = 0; rLowerLeg->posY = 0; rLowerLeg->posZ = 0;
rLowerLeg->sizeY = 1.0f;
SceneNode* rAnkleJoint = createNode("rAnkleJoint", 1, rLowerLeg);
rAnkleJoint->posY = -rLowerLeg->sizeY;
SceneNode* rFoot = createNode("rFoot", 0, rAnkleJoint);
rFoot->posX = 0; rFoot->posY = -0.1f; rFoot->posZ = 0.2f;
rFoot->sizeX = 0.3f; rFoot->sizeY = 0.1f; rFoot->sizeZ = 0.5f;
SceneNode* floor = createNode("floor", 0, NULL);
floor->posY = -2.4f;
}
void keyboard(unsigned char key, int x, int y) {
if (key == '1') selectedJoint = findNode([Link], "lShoulderJoint");
if (key == '2') selectedJoint = findNode([Link], "rShoulderJoint");
if (key == '3') selectedJoint = findNode([Link], "lElbowJoint");
if (key == '4') selectedJoint = findNode([Link], "rElbowJoint");
if (key == '5') selectedJoint = findNode([Link], "lHipJoint");
if (key == '6') selectedJoint = findNode([Link], "rHipJoint");
if (key == '7') selectedJoint = findNode([Link], "lKneeJoint");
if (key == '8') selectedJoint = findNode([Link], "rKneeJoint");
if (key == '9') selectedJoint = findNode([Link], "neckJoint");
if (selectedJoint) {
if (key == 'a') selectedJoint->angleX += 5.0f;
if (key == 'd') selectedJoint->angleX -= 5.0f;
if (key == 'w') selectedJoint->angleY += 5.0f;
if (key == 's') selectedJoint->angleY -= 5.0f;
}
glutPostRedisplay();
}
void specialKeys(int key, int x, int y) {
if (key == GLUT_KEY_LEFT) {
cameraTheta -= 0.1f;
}
if (key == GLUT_KEY_RIGHT) {
cameraTheta += 0.1f;
}
if (key == GLUT_KEY_UP) {
cameraPhi -= 0.1f;
if (cameraPhi < 0.1f) cameraPhi = 0.1f;
}
if (key == GLUT_KEY_DOWN) {
cameraPhi += 0.1f;
if (cameraPhi > M_PI - 0.1f) cameraPhi = M_PI - 0.1f;
}
updateCameraPosition();
glutPostRedisplay();
}
void mouse(int button, int state, int x, int y) {
if (button == GLUT_LEFT_BUTTON) {
if (state == GLUT_DOWN) {
mouseDown = 1;
lastX = x;
lastY = y;
} else {
mouseDown = 0;
}
}
}
void motion(int x, int y) {
if (mouseDown) {
viewAngleX += (x - lastX) * 0.5f;
viewAngleY += (y - lastY) * 0.5f;
lastX = x;
lastY = y;
glutPostRedisplay();
}
}
void menu(int value) {
if (value == 1) {
walkingEnabled = !walkingEnabled;
} else if (value >= 2 && value <= 4) {
char filename[20];
sprintf(filename, "robot_config%[Link]", value - 1);
saveRobotConfig(filename);
} else if (value >= 5 && value <= 7) {
char filename[20];
sprintf(filename, "robot_config%[Link]", value - 4);
loadRobotConfig(filename);
}
glutPostRedisplay();
}
void createMenu() {
int saveMenu = glutCreateMenu(menu);
glutAddMenuEntry("Config 1", 2);
glutAddMenuEntry("Config 2", 3);
glutAddMenuEntry("Config 3", 4);
int loadMenu = glutCreateMenu(menu);
glutAddMenuEntry("Config 1", 5);
glutAddMenuEntry("Config 2", 6);
glutAddMenuEntry("Config 3", 7);
glutCreateMenu(menu);
glutAddMenuEntry("Toggle Walking Animation", 1);
glutAddSubMenu("Save Configuration", saveMenu);
glutAddSubMenu("Load Configuration", loadMenu);
glutAttachMenu(GLUT_RIGHT_BUTTON);
}
void display() {
if (feedbackTimer > 0) {
feedbackTimer -= 1.0f;
if (feedbackTimer <= 0) {
bgColorR = 0.0f; bgColorG = 0.0f; bgColorB = 0.0f;
feedbackState = 0;
}
}
glClearColor(bgColorR, bgColorG, bgColorB, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glRotatef(viewAngleY, 1, 0, 0);
glRotatef(viewAngleX, 0, 1, 0);
gluLookAt(cameraX, cameraY + 2.2f, cameraZ, 0.0, 2.2, 0.0, 0.0, 1.0, 0.0);
animateRobot();
updateNode([Link]);
SceneNode* floor = findNode([Link], "floor");
if (floor) updateNode(floor);
glutSwapBuffers();
}
void reshape(int w, int h) {
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (double)w / (double)h, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
}
void timer(int value) {
glutPostRedisplay();
glutTimerFunc(16, timer, 0);
}
void init() {
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
float lightPos[] = {1.0, 1.0, 1.0, 0.0};
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
glClearColor(0.0, 0.0, 0.0, 1.0);
updateCameraPosition();
buildRobot();
}
int main(int argc, char** argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(windowWidth, windowHeight);
glutCreateWindow("Robotboy");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutTimerFunc(0, timer, 0);
glutKeyboardFunc(keyboard);
glutSpecialFunc(specialKeys);
glutMouseFunc(mouse);
glutMotionFunc(motion);
createMenu();
glutMainLoop();
return 0;
}