#include #include #include #include #include #include #include #include #include "ui.h" #include "grass_texture_color.h" #include "help.h" #include "cow.h" // close enough #define PI 3.14 // user controlled light thing vec3 lightPos = { 0.0, 10, 25.0 }; // Camera rotation and stuff float camRadius = 40.0; vec3 camCenter = { 0.0, 0.0, 0.0 }; vec3 camRot = { -PI * 0.25, PI, 0.0 }; struct { unsigned char lockYMovement: 1; unsigned char showHelp: 1; unsigned char cowsPOV: 1; unsigned char controlLight: 1; GLenum textureFilter; } settings = { 0, 0, 0, 0, GL_NEAREST, }; struct { int main; int cowControl; int filters; } menus; // to simply hold the menues in a labed manner struct { GLuint grass; } lists; // Ui stuff int lastMouseButton = GLUT_LEFT_BUTTON; int lastMouseX = 0, lastMouseY = 0; ui_slider* draggedSlider = NULL; ui_slider sliders[] = { { // ambient red /* pos */ { { 0.9, 0.9, 0.0 }, { -50.0, -10.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.1 }, { // ambient green /* pos */ { { 0.9, 0.9, 0.0 }, { -50.0, -40.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.1 }, { // ambient blue /* pos */ { { 0.9, 0.9, 0.0 }, { -50.0, -70.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.1 }, { // drag speed /* pos */ { { 0.9, 0.9, 0.0 }, { -50.0, -120.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.5 }, { // rotation speed /* pos */ { { 0.9, 0.9, 0.0 }, { -50.0, -170.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.3 }, { // user light - red /* pos */ { { 0., 0.9, 0.0 }, { 20.0, -10.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.0 }, { // user light - green /* pos */ { { 0.0, 0.9, 0.0 }, { 20.0, -40.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.0 }, { // user light - blue /* pos */ { { 0.0, 0.9, 0.0 }, { 20.0, -70.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 1.0 }, { // user light - movement step /* pos */ { { 0.0, 0.9, 0.0 }, { 20.0, -100.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.2 }, { // user light - constant attenuation /* pos */ { { 0.0, 0.9, 0.0 }, { 20.0, -130.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.0 }, { // user light - linear attenuation /* pos */ { { 0.0, 0.9, 0.0 }, { 20.0, -160.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.0 }, { // user light - cubic attenuation /* pos */ { { 0.0, 0.9, 0.0 }, { 20.0, -190.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, 0.5 }, }; // sliders named, struct to give sliders a name, they are stored in an array to ease handling them struct { ui_slider* ambientRed; ui_slider* ambientGreen; ui_slider* ambientBlue; ui_slider* dragSpeed; ui_slider* rotSpeed; ui_slider* lightRed; ui_slider* lightGreen; ui_slider* lightBlue; ui_slider* lightMove; ui_slider* lightConAtten; ui_slider* lightLinAtten; ui_slider* lightQuadAtten; } slidersN = { sliders, sliders + 1, sliders + 2, sliders + 3, sliders + 4, sliders + 5, sliders + 6, sliders + 7, sliders + 8, sliders + 9, sliders + 10, sliders + 11 }; void generateGrassVertexList() { lists.grass = glGenLists(1); glNewList(lists.grass, GL_COMPILE); glDisable(GL_CULL_FACE); for(float x = -50.0; x < 50.0; x += 1.0) { for(float z = -50.0; z < 50.0; z += 1.0) { float tx = -0.03 * (rand() % 21), tz = -0.03 * (rand() % 21); glPushMatrix(); glTranslatef(x, 0.0, z); glRotatef(rand() % 360, 0.0, 1.0, 0.0); glBegin(GL_TRIANGLES); glColor3f(0.0, 0.3, 0.0); glVertex3f(0.0, 0.0, 0.0); glColor3f(0.0, 0.5, 0.0); glVertex3f(0.5, 0.0, 0.0); glVertex3f(tx, 1.5, tz); glColor3f(0.0, 0.2, 0.0); glVertex3f(0.0, 0.0, 0.0); glVertex3f(0.0, 0.0, 0.5); glColor3f(0.0, 0.5, 0.0); glVertex3f(tx, 1.5, tz); glEnd(); glPopMatrix(); } } glEnable(GL_CULL_FACE); glEndList(); } void drawTree(float rad, float height) { // Bark glEnable(GL_COLOR_MATERIAL); glColor3f(0.3, 0.1, 0.3); glPushMatrix(); glTranslatef(0.0, height, 0.0); glRotatef(90.0, 1.0, 0.0, 0.0); glutSolidCylinder(rad, height, 16, 3); glPopMatrix(); glColor3f(0.0, 1.0, 0.0); glPushMatrix(); glTranslatef(0.0, height, 0.0); vec3 ts[] = { vec3_new(1.0, 0.0, 0.0), vec3_new(-1.0, 0.0, 0.0), vec3_new(0.0, 0.0, -1.0), vec3_new(0.0, 0.0, 1.0), vec3_new(0.0, 1.0, 0.0), }; for(int i = 0; i < 5; i += 1) { vec3 t = ts[i]; glPushMatrix(); glTranslatef(t.x * rad, t.y * rad, t.z * rad); glutSolidSphere(rad, 8, 8); glPopMatrix(); } glPopMatrix(); glColor3f(1.0, 1.0, 1.0); glDisable(GL_COLOR_MATERIAL); } vec3 rotateByCameraRotation(vec3 v) { vec3 r; // rotate along the x axis(yz plane) r.z = cosf(camRot.x) * v.z + sinf(camRot.x) * v.y; r.y = -sinf(camRot.x) * v.z + cosf(camRot.x) * v.y; v = r; // rotate along the y axis(xy plane) r.z = cosf(camRot.y) * v.z + sinf(camRot.y) * v.x; r.x = -sinf(camRot.y) * v.z + cosf(camRot.y) * v.x; return r; } void drawUi(void) { glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // set ui camera int wWidth = glutGet(GLUT_WINDOW_WIDTH); int wHeight = glutGet(GLUT_WINDOW_HEIGHT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, wWidth, 0.0, wHeight); for(ui_slider* s = sliders; s < sliders + sizeof sliders / sizeof(ui_slider); s += 1) { ui_slider_draw(s); } // sliders labels glColor3f(1.0, 1.0, 1.0); unsigned char t[] = "Ambient light"; glRasterPos2f(wWidth * 0.9 - glutBitmapLength(GLUT_BITMAP_TIMES_ROMAN_24, t) * 0.5, wHeight * 0.90 + 20.0); glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, t); glRasterPos2f(wWidth * 0.9 - 75.0, wHeight * 0.9 - 10.0); glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, (unsigned char*)"r\ng\nb"); unsigned char td[] = "Drag speed"; unsigned char tr[] = "Rotate speed"; glRasterPos2f(wWidth * 0.9 - glutBitmapLength(GLUT_BITMAP_TIMES_ROMAN_24, td) * 0.5, wHeight * 0.9 - 100.0); glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, td); glRasterPos2f(wWidth * 0.9 - glutBitmapLength(GLUT_BITMAP_TIMES_ROMAN_24, tr) * 0.5, wHeight * 0.9 - 150.0); glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, tr); // light sliders unsigned char t2[] = "Light orb"; unsigned char side[] = "r\ng\nb\nSpeed\nConstant Atten\nLinear Atten\nQuadraic Atten"; glRasterPos2f(10.0, wHeight * 0.9 + 20.0); glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, t2); glRasterPos2f(140.0, wHeight * 0.9 - 10.0); glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, side); if(settings.showHelp) { glColor3f(0.3, 0.3, 0.3); glBegin(GL_QUADS); glVertex3f(wWidth * 0.1, wHeight * 0.1, 0.0); glVertex3f(wWidth * 0.9, wHeight * 0.1, 0.0); glVertex3f(wWidth * 0.9, wHeight * 0.9, 0.0); glVertex3f(wWidth * 0.1, wHeight * 0.9, 0.0); glEnd(); glColor3f(1.0,1.0,1.0); glRasterPos2f(wWidth * 0.12, wHeight * 0.8); glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, helpString); } int err = glGetError(); if(err != 0) { printf("opengl error %d: %s\n", err, gluErrorString(err)); } } void drawWorld(void) { glEnable(GL_DEPTH_TEST); // set perspective camera GLdouble wWidth = (GLdouble) glutGet(GLUT_WINDOW_WIDTH); GLdouble wHeight = (GLdouble) glutGet(GLUT_WINDOW_HEIGHT); // Calculate current camera position from rotation glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if(settings.cowsPOV) { setCameraToCowPOV(); } else { vec3 cp = vec3_new(0.0, 0.0, camRadius); cp = rotateByCameraRotation(cp); cp = vec3_add(cp, camCenter); vec3 up = rotateByCameraRotation(vec3_new(0.0, 1.0, 0.0)); gluLookAt(cp.x, cp.y, cp.z, camCenter.x, camCenter.y, camCenter.z, up.x, up.y, up.z); } glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(90, wWidth / wHeight , 0.5, 500.0); // Lights glEnable(GL_LIGHTING); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); // Ambient light GLfloat ambient[] = { slidersN.ambientRed->value, slidersN.ambientGreen->value, slidersN.ambientBlue->value, 1.0 }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient); // GL_LIGHT0 is reserved for the cow's flashlight // user light glEnable(GL_LIGHT1); GLfloat ulPos[] = { lightPos.x, lightPos.y, lightPos.z, 1.0 }; GLfloat ulColor[] = { slidersN.lightRed->value, slidersN.lightGreen->value, slidersN.lightBlue->value, 1.0 }; glLightfv(GL_LIGHT1, GL_POSITION, ulPos); glLightfv(GL_LIGHT1, GL_AMBIENT, ulColor); glLightfv(GL_LIGHT1, GL_DIFFUSE, ulColor); glLightfv(GL_LIGHT1, GL_SPECULAR, ulColor); glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, slidersN.lightConAtten->value * 5.0 + 0.01); glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, slidersN.lightLinAtten->value * 0.3); glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, slidersN.lightQuadAtten->value * 0.01); // draw a little glowing orb where it stands glMatrixMode(GL_MODELVIEW); glPushMatrix(); glTranslatef(lightPos.x, lightPos.y, lightPos.z); glEnable(GL_COLOR_MATERIAL); glColor3f(1.0, 1.0, 1.0); glMaterialfv(GL_FRONT, GL_EMISSION, ulColor); glutSolidSphere(1.0, 8, 8); GLfloat reset[] = { 0.0, 0.0, 0.0, 1.0 }; glMaterialfv(GL_FRONT, GL_EMISSION, reset); glDisable(GL_COLOR_MATERIAL); glPopMatrix(); // World drawCow(settings.cowsPOV); // Ground glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, settings.textureFilter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, settings.textureFilter); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, grass_color_128); glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glColor3f(1.0, 1.0, 0.0); glNormal3f(0.0, 5.0, 0.0); glTexCoord2f(0.0, 0.0); glVertex3f(-50.0, 0.0, -50.0); glTexCoord2f(0.0, 1.0); glVertex3f(-50.0, 0.0, 50.0); glTexCoord2f(1.0, 1.0); glVertex3f(50.0, 0.0, 50.0); glTexCoord2f(1.0, 0.0); glVertex3f(50.0, 0.0, -50.0); glEnd(); glDisable(GL_TEXTURE_2D); glPushMatrix(); glTranslatef(25.0, 0.0, 25.0); drawTree(3.0, 20.0); glTranslatef(-50.0, 0.0, 0.0); drawTree(2.0, 19.0); glPopMatrix(); // Scary shady cube glPushMatrix(); glTranslatef(0.0, 10.0, 50.0); glEnable(GL_COLOR_MATERIAL); glColor3f(0.05, 0.05, 0.1); GLfloat mat[] = { 0.25, 0.25, 0.25, 0.4, 0.4, 0.4, 0.774597, 0.774597, 0.774597, 0.6 }; glMaterialf(GL_FRONT, GL_SHININESS, mat[9] * 128.0); glMaterialfv(GL_FRONT, GL_AMBIENT, mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat + 3); glMaterialfv(GL_FRONT, GL_SPECULAR, mat + 6); glutSolidCube(20.0); GLfloat matReset[] = {0.0, 0.0, 0.0}; glMaterialf(GL_FRONT, GL_SHININESS, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, matReset); glMaterialfv(GL_FRONT, GL_DIFFUSE, matReset); glMaterialfv(GL_FRONT, GL_SPECULAR, matReset); glPopMatrix(); glColor3f(0.1, 0.3, 0.0); glCallList(lists.grass); glColor3f(1.0, 1.0, 1.0); glDisable(GL_COLOR_MATERIAL); } void display(void) { // Clear screen glClearColor(0.0, 0.0, 0.0, 1.0); // Dark theme :) glClear(GL_COLOR_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT); drawWorld(); drawUi(); glutSwapBuffers(); glFlush(); } void mouseEvent(int button, int state, int x, int y) { lastMouseX = x; lastMouseY = y; if(state == 0) lastMouseButton = button; if(state == 0 && button == GLUT_LEFT_BUTTON) { for(ui_slider* s = sliders; s < sliders + sizeof sliders / sizeof(ui_slider); s += 1) { if(ui_slider_mouse_over(s, x, y)) { ui_slider_onclick(s, x, y); draggedSlider = s; } } } else if(state == 1 && button == GLUT_LEFT_BUTTON) { draggedSlider = NULL; } glutPostRedisplay(); } void mouseMotionEvent(int x, int y) { if(lastMouseButton == GLUT_LEFT_BUTTON) { if(draggedSlider != NULL) { if(ui_slider_mouse_over(draggedSlider, x, y)) { ui_slider_onclick(draggedSlider, x, y); } } else { // rotate the camera float speed = slidersN.rotSpeed->value * 0.095 + 0.005; camRot.y -= (x - lastMouseX) * speed; if(camRot.y > 2 * PI) camRot.y -= 2 * PI; else if(camRot.y < 0.0) camRot.y += 2 * PI; camRot.x = fmin(PI * 0.5, fmax(-PI * 0.5, camRot.x - (y - lastMouseY) * speed)); } } else if(lastMouseButton == GLUT_MIDDLE_BUTTON) { vec3 moveX = rotateByCameraRotation(vec3_new(1.0, 0.0, 0.0)); vec3 moveY = rotateByCameraRotation(vec3_new(0.0, 1.0, 0.0)); if(settings.lockYMovement) { moveX.y = 0; moveY.y = 0; } float speed = slidersN.dragSpeed->value * 0.9 + 0.1; camCenter = vec3_add(camCenter, vec3_mult(moveX, (lastMouseX - x) * speed)); camCenter = vec3_add(camCenter, vec3_mult(moveY, (y - lastMouseY) * speed)); } lastMouseX = x; lastMouseY = y; glutPostRedisplay(); } void mouseWheelEvent(int wheel, int dir, int x, int y) { char slided = 0; for(ui_slider* s = sliders; s < sliders + sizeof sliders / sizeof(ui_slider); s += 1) { if(ui_slider_mouse_over(s, x, y)) { s->value += dir * 0.05; if(s->value < 0.0) s->value = 0.0; if(s->value > 1.0) s->value = 1.0; slided = 1; } } if(!slided) { camRadius *= 1.0 - (dir * 0.1); } glutPostRedisplay(); } void keyboardEvent(unsigned char c, int x, int y) { if(c == '\e') { if(settings.showHelp) { settings.showHelp = 0; glutPostRedisplay(); } else { glutLeaveMainLoop(); exit(0); } } onCowKeyboardInput(c); // Move light source if applicable if(settings.controlLight) { // Controls: wasd - movement over xz plane, qe movement over y axle // color and such shall be controlled using the sliders float step = slidersN.lightMove->value * 9.0 + 1.0; // go from 1 to 10; lightPos.x += ((c == 'd') - (c == 'a')) * step; lightPos.y += ((c == 'q') - (c == 'e')) * step; lightPos.z += ((c == 'w') - (c == 's')) * step; glutPostRedisplay(); } } void onMenuItem(int item) { lastMouseButton = -1; // to prevent jumps in view position/rotation switch(item) { case 0: // Lock y settings.lockYMovement ^= 1; glutChangeToMenuEntry(3, settings.lockYMovement ? "Unlock Y position" : "Lock Y position", 0); break; case 1: // cow pov settings.cowsPOV ^= 1; glutPostRedisplay(); break; case 2: // Show help settings.showHelp ^= 1; glutPostRedisplay(); break; case 3: // reste view camRadius = 40.0; camCenter = vec3_new(0.0, 0.0, 0.0); camRot = vec3_new(-PI * 0.125, 0.0, 0.0); glutPostRedisplay(); break; case 4: // quit glutLeaveMainLoop(); exit(0); case 5: // control light source updateCowControl(COW_CONTROL_NONE); settings.controlLight = 1; } } void onControlCowMenu(int item) { lastMouseButton = GLUT_RIGHT_BUTTON; settings.controlLight = 0; updateCowControl(item); } void onFilterMenu(int item) { lastMouseButton = GLUT_RIGHT_BUTTON; GLenum filters[] = { GL_LINEAR, GL_NEAREST }; settings.textureFilter = filters[item]; } void menuSetup() { // control cow sub-menu menus.cowControl = glutCreateMenu(onControlCowMenu); glutAddMenuEntry("Movement", COW_CONTROL_MOVE); glutAddMenuEntry("Head", COW_CONTROL_HEAD); glutAddMenuEntry("Tail", COW_CONTROL_TAIL); // filters menus.filters = glutCreateMenu(onFilterMenu); glutAddMenuEntry("Linear", 0); glutAddMenuEntry("Nearest", 1); menus.main = glutCreateMenu(onMenuItem); glutAddSubMenu("Control Cow", menus.cowControl); glutAddSubMenu("Texture filters", menus.filters); glutAddMenuEntry("Lock Y position", 0); glutAddMenuEntry("Change to Cow POV", 1); glutAddMenuEntry("Reset view", 3); glutAddMenuEntry("Control point light", 5); glutAddMenuEntry("Help", 2); glutAddMenuEntry("Quit", 4); glutAttachMenu(GLUT_RIGHT_BUTTON); } int main(int argc, char** argv) { /* initialize glut and window */ glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(1280, 720); glutInitWindowPosition(450, 450); /* create window and set callbacks */ glutCreateWindow("World of Cow - Aviv Romem"); glutDisplayFunc(display); glutMouseFunc(mouseEvent); glutKeyboardFunc(keyboardEvent); glutMouseWheelFunc(mouseWheelEvent); glutMotionFunc(mouseMotionEvent); menuSetup(); // set up some rendering related stuff glEnable(GL_CULL_FACE); glCullFace(GL_BACK); generateGrassVertexList(); glutMainLoop(); return 0; }