world_of_cow/cow.c

260 lines
8.5 KiB
C

#include <GL/freeglut.h>
#include <GL/freeglut_std.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include "cow.h"
#include "vec.h"
#include <math.h>
#include <stdio.h>
// positional variables
vec3 headRot = { 0.0, 0.0, 0.0 };
const vec3 headLimit = { 30.0, 30.0, 45.0 };
vec3 tailRot = { 0.0, 0.0, 0.0 };
const vec3 tailLimit = { 90.0, 90.0, 0.0 };
vec3 cowPos = { 0.0, 4.9, 0.0 };
float cowRot = 0.0; // rotation along the Y axis
const float COW_ROT_SPEED = 5.0;
const float COW_MOVE_SPEED = 1.0;
enum COW_CONTROL control;
float deg2rad(float deg) {
return deg * 3.14 / 180.0;
}
/// Adds a rotation and wraps around(wraps around the +- 180 mark, so -190 turns to 170)
float addAndWrapRot(float rot, float add) {
rot += add;
if(rot > 180.0) { rot -= 360.0; }
else if(rot < -180.0) { rot += 360.0; }
return rot;
}
/// Adds a rotation and makes sure the result is within the limit
float addAndLimitRot(float rot, float add, float limit) {
rot += add;
if(rot > limit) { rot = limit; }
else if(rot < -limit) { rot = -limit; }
return rot;
}
// controls the cow movement based on keyboard input
void cowMove(char c) {
// check for rotations
cowRot = addAndWrapRot(cowRot, ((c == 'e') - (c == 'q')) * COW_ROT_SPEED);
// movement
vec3 dir = vec3_new((c == 'a') - (c == 'd'), 0.0, (c == 'w') - (c == 's'));
vec3 r = vec3_splat(0.0);
float rot = -deg2rad(cowRot);
r.z = cosf(rot) * dir.z + sinf(rot) * dir.x;
r.x = -sinf(rot) * dir.z + cosf(rot) * dir.x;
cowPos = vec3_add(cowPos, vec3_mult(r, COW_MOVE_SPEED));
glutPostRedisplay();
}
// conrols cow head rotation(movement is a result of the offset of rotation) on keyboard input
void cowHead(char c) {
headRot.z = addAndLimitRot(headRot.z, ((c == 'e') - (c == 'q')) * COW_ROT_SPEED, headLimit.z);
headRot.y = addAndLimitRot(headRot.y, ((c == 'a') - (c == 'd')) * COW_ROT_SPEED, headLimit.y);
headRot.x = addAndLimitRot(headRot.x, ((c == 's') - (c == 'w')) * COW_ROT_SPEED, headLimit.x);
glutPostRedisplay();
}
// controls cow tail rotaion on keyboard input
void cowTail(char c) {
tailRot.y = addAndLimitRot(tailRot.y, ((c == 'a') - (c == 'd')) * COW_ROT_SPEED, tailLimit.y);
tailRot.x = addAndLimitRot(tailRot.x, ((c == 's') - (c == 'w')) * COW_ROT_SPEED, tailLimit.x);
glutPostRedisplay();
}
// Draws the cow... will skip drawing the head parts when in cow point of view
void drawCow(char cowsPOV) {
glMatrixMode(GL_MODELVIEW);
glEnable(GL_COLOR_MATERIAL);
glPushMatrix(); // cow pos
// Main cow position - as center of cow body
glTranslatef(cowPos.x, cowPos.y, cowPos.z);
glRotatef(cowRot, 0.0, 1.0, 0.0);
glScalef(2.0, 2.0, 2.0); // general cow scale, as i blocked it in blender and it was mostly normalized
// Draw cow body
glPushMatrix(); // cow body scale
glColor3f(0.8,0.4,0.11);
glScalef(1.2, 1.2, 2.3);
glutSolidSphere(1.0, 16, 16);
glPopMatrix(); // cow body scale
if(!cowsPOV) { // only draw head if we are not looking from within it
// head
glPushMatrix(); // head center - offset for the head rotation, allows for head movement relative to rotation easily
glTranslatef(0.0, 1.0, 1.8);
glRotatef(headRot.y, 0.0, 1.0, 0.0);
glRotatef(headRot.x, 1.0, 0.0, 0.0);
glRotatef(headRot.z, 0.0, 0.0 ,1.0);
glPushMatrix(); // head sphere 1
glScalef(0.6, 0.4, 0.7);
glutSolidSphere(1.0, 8, 8);
glPopMatrix(); // head sphere 1
glPushMatrix(); // head sphere 2
glTranslatef(0.0, 0.3, -0.3);
glScalef(0.6, 0.45, 0.75);
glutSolidSphere(1.0, 8, 8);
glPopMatrix();
// eyes :)
glColor3f(0.0,0.0,0.0); // Eyes black as night
glPushMatrix(); // eye 1
glTranslatef(-0.3, 0.5, 0.0);
glScalef(0.17, 0.17, 0.17);
glutSolidSphere(1.0, 8, 8);
glPopMatrix(); // eye 1
glPushMatrix(); // eye 2
glTranslatef(0.3, 0.5, 0.0);
glScalef(0.17, 0.17, 0.17);
glutSolidSphere(1.0, 8, 8);
glPopMatrix(); // eye 2
glColor3f(0.8,0.4,0.11); // go back to the normal cow color
// EARS! they look like little pancakes to the side of the head
glPushMatrix(); // ear 1
glTranslatef(-0.7, 0.5, -0.4);
glScalef(0.3, 0.18, 0.07);
glutSolidSphere(1.0, 8, 8);
glPopMatrix(); // ear 1
glPushMatrix(); // ear 2
glTranslatef(0.7, 0.5, -0.4);
glScalef(0.3, 0.18, 0.07);
glutSolidSphere(1.0, 8, 8);
glPopMatrix(); // ear 2
// Add a little teapon earing :D
glPushMatrix();
glColor3f(1.0, 1.0, 1.0);
glTranslatef(-0.8, 0.19, -0.4);
glRotatef(146.0, 0.0, 1.0, 0.0);
glRotatef(-90.0, 1.0, 0.0, 0.0);
glRotatef(90.0, 0.0, 1.0, 0.0);
glCullFace(GL_FRONT); // for some reason, teapot normals are backwards compared to other shapes, maybe a bug in freeglut 3.4.0-1(artix world repo)
glutSolidTeapot(0.17);
glCullFace(GL_BACK); // reset face culling
glPopMatrix(); // teapot!
glPushMatrix(); // little nose
glColor3f(0.9, 0.3, 0.4);
glTranslatef(0.0, 0.1, 0.6);
glScalef(0.3, 0.15, 0.14);
glutSolidSphere(1.0, 8, 8);
glPopMatrix(); // little nose
glPopMatrix(); // head center
}
// tins
glColor3f(0.9, 0.3, 0.4);
glPushMatrix(); // TINS!
glTranslatef(0.0, -1.0, -0.58);
glScalef(0.7, 0.4, 0.7);
glutSolidSphere(1.0, 8, 8);
glPopMatrix(); // Thins!
glColor3f(0.8,0.4,0.11); // go back to the normal cow color
// looping over this array makes changes much easier as the legs should be mirrored in both x and z directions
vec3 legs[] = {
vec3_new(1.0, 0.0, 1.0), vec3_new(-1.0, 0.0, 1.0),
vec3_new(1.0, 0.0, -1.0), vec3_new(-1.0, 0.0, -1.0)
};
for(int i = 0; i < 4; i += 1) {
glPushMatrix();
glTranslatef(0.5 * legs[i].x, -1.2, 1.4 * legs[i].z);
glScalef(0.4, 1.2, 0.4);
glutSolidSphere(1.0, 8, 8);
glPopMatrix();
}
// now a tail
glPushMatrix(); // tail
glTranslatef(0.0, 0.0, -2.2); // connection point to the cow
glRotatef(tailRot.y, 0.0 ,1.0, 0.0);
glRotatef(tailRot.x, 1.0, 0.0, 0.0);
glTranslatef(0.0, -0.83, 0.0);
glScalef(0.2, 1.0, 0.2);
glutSolidSphere(1.0, 8, 8);
glPopMatrix(); // Tails(from sonic the hedgehog)
// Give the cow a little flashlight because its dark outside
glPushMatrix(); // fl
glTranslatef(0.5, -1.2, 1.4);
glColor3f(0.3, 0.3 ,0.3);
glutSolidCylinder(0.3, 2.0, 8, 1);
glTranslatef(0.0, 0.0, 2.0);
glRotatef(180.0, 0.0, 1.0, 0.0);
glutSolidCone(0.6, 0.6, 8, 2);
glEnable(GL_LIGHT0);
// Directional light - aka light
GLfloat light1[] = { 0.0, 0.0, 0.0, 1.0 };
GLfloat lightColor[] = {0.9, 0.9, 0.1, 1.0};
GLfloat dir[] = { 0.0, 0.0, -1.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light1);
glLightfv(GL_LIGHT0, GL_AMBIENT, lightColor);
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
glLightfv(GL_LIGHT0, GL_SPECULAR, lightColor);
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir);
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 30.0);
glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 2.0);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.005);
glPopMatrix(); // fl
glPopMatrix(); // cow pos
// reset color material or else it will garbage all the colors next frame(for non color material enabled objects)
glColor3f(1.0, 1.0, 1.0);
glDisable(GL_COLOR_MATERIAL);
}
// controls which body part to move based on the `control` variable, called from the keyboard event callback of the main file
void onCowKeyboardInput(char key) {
switch(control) {
case COW_CONTROL_MOVE:
cowMove(key);
break;
case COW_CONTROL_HEAD:
cowHead(key);
break;
case COW_CONTROL_TAIL:
cowTail(key);
break;
default:
break;
}
}
// updates the control mode, based on the right click submenu(the first one)
void updateCowControl(enum COW_CONTROL c) {
control = c;
}
// rotates the camera to the cow's perspective
void setCameraToCowPOV() {
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
// rotate to look at Z+
glRotatef(180.0, 0.0, 1.0, 0.0);
// head
glRotatef(-headRot.z, 0.0, 0.0 ,1.0);
glTranslatef(0.0, 0.0, -1.0);
glRotatef(-headRot.x, 1.0, 0.0, 0.0);
glRotatef(-headRot.y, 0.0, 1.0, 0.0);
glTranslatef(0.0, 0.0, -5.0);
// body
glRotatef(-cowRot, 0.0, 1.0, 0.0);
glTranslatef(-cowPos.x, -cowPos.y, -cowPos.z);
}