diff --git a/README.md b/README.md index 91fe090..74d469a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ Code here is published under the [AGPL-v3](https://www.gnu.org/licenses/agpl-3.0 # TODO +- User controlled light source + - Add light source + - move the light + - change intensity - Add popping-floating text of funny cow thoughts when in cow view -- Construct the required pdf with explanations and screenshots -- Properly comment the code :/ \ No newline at end of file +- Add Textures and stuff :)(optional) \ No newline at end of file diff --git a/cow.c b/cow.c index 8de0898..f4a21f1 100644 --- a/cow.c +++ b/cow.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -164,32 +163,10 @@ void drawCow(char cowsPOV) { 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); - glPopMatrix(); // fl glPopMatrix(); // cow pos glColor3f(1.0, 1.0, 1.0); glDisable(GL_COLOR_MATERIAL); } - void onCowKeyboardInput(char key) { switch(control) { case COW_CONTROL_MOVE: @@ -222,6 +199,6 @@ void setCameraToCowPOV() { 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); + glRotatef(-cowRot, 0.0, 1.0, 0.0); } \ No newline at end of file diff --git a/mmn_17.c b/mmn_17.c index bb642a6..6b29ace 100644 --- a/mmn_17.c +++ b/mmn_17.c @@ -13,9 +13,6 @@ // 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 }; @@ -24,10 +21,10 @@ 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, + 0, 0, 0, GL_NEAREST, }; struct { int main; @@ -43,21 +40,22 @@ struct { int lastMouseButton = GLUT_LEFT_BUTTON; int lastMouseX = 0, lastMouseY = 0; ui_slider* draggedSlider = NULL; +ui_button buttons[] = {}; // TODO: maybe delete? the menu thing is easier to work with 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 + 0.0 }, { // ambient green /* pos */ { { 0.9, 0.9, 0.0 }, { -50.0, -40.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, - 0.1 + 0.0 }, { // ambient blue /* pos */ { { 0.9, 0.9, 0.0 }, { -50.0, -70.0, 0.0 } }, /* size */ { 100.0, 10.0, 10.0 }, - 0.1 + 0.3 }, { // drag speed /* pos */ { { 0.9, 0.9, 0.0 }, { -50.0, -120.0, 0.0 } }, @@ -68,60 +66,7 @@ ui_slider sliders[] = { /* 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() { @@ -208,6 +153,10 @@ void drawUi(void) { glLoadIdentity(); gluOrtho2D(0.0, wWidth, 0.0, wHeight); + // Draw buttons + for(ui_button* b = buttons; b < buttons + sizeof buttons / sizeof(ui_button); b += 1) { + ui_button_draw(b); + } for(ui_slider* s = sliders; s < sliders + sizeof sliders / sizeof(ui_slider); s += 1) { ui_slider_draw(s); } @@ -224,14 +173,7 @@ void drawUi(void) { 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); @@ -277,32 +219,16 @@ void drawWorld(void) { 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 }; + GLfloat ambient[] = { sliders[0].value, sliders[1].value, sliders[2].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(); + glEnable(GL_LIGHT0); + // Directional light - aka light + GLfloat light1[] = { 0.0, 1.0, 0.0, 0.0 }; + glLightfv(GL_LIGHT0, GL_POSITION, light1); + GLfloat lightColor[] = {0.5, 0.4, 0.1, 1.0}; + glLightfv(GL_LIGHT0, GL_AMBIENT, lightColor); + glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor); + glLightfv(GL_LIGHT0, GL_SPECULAR, lightColor); // World drawCow(settings.cowsPOV); // Ground @@ -365,7 +291,11 @@ 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_button* b = buttons; b < buttons + sizeof buttons / sizeof(ui_button); b += 1) { + if(ui_button_mouse_over(b, x, y) && b->onClick != NULL) { + b->clicked = 1; + } + } 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); @@ -375,6 +305,12 @@ void mouseEvent(int button, int state, int x, int y) { } else if(state == 1 && button == GLUT_LEFT_BUTTON) { draggedSlider = NULL; + for(ui_button* b = buttons; b < buttons + sizeof buttons / sizeof(ui_button); b += 1) { + b->clicked = 0; + if(ui_button_mouse_over(b, x, y) && b->onClick != NULL) { + b->onClick(); + } + } } glutPostRedisplay(); } @@ -388,7 +324,7 @@ void mouseMotionEvent(int x, int y) { } else { // rotate the camera - float speed = slidersN.rotSpeed->value * 0.095 + 0.005; + float speed = sliders[4].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; @@ -405,7 +341,7 @@ void mouseMotionEvent(int x, int y) { moveX.y = 0; moveY.y = 0; } - float speed = slidersN.dragSpeed->value * 0.9 + 0.1; + float speed = sliders[3].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)); } @@ -441,16 +377,6 @@ void keyboardEvent(unsigned char c, int x, int y) { } } 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) { @@ -477,16 +403,11 @@ void onMenuItem(int item) { 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); } @@ -513,7 +434,6 @@ void menuSetup() { 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); @@ -523,7 +443,7 @@ int main(int argc, char** argv) { /* initialize glut and window */ glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); - glutInitWindowSize(1280, 720); + glutInitWindowSize(800, 500); glutInitWindowPosition(450, 450); /* create window and set callbacks */ glutCreateWindow("World of Cow - Aviv Romem"); diff --git a/ui.c b/ui.c index 140e6b0..5204707 100644 --- a/ui.c +++ b/ui.c @@ -7,6 +7,53 @@ #include #define BACK_COLOR 0.3, 0.35, 0.35 +#define BACK_CLICKED_COLOR 0.25, 0.3, 0.3 + +void ui_button_draw(ui_button* b) { + int wWidth = glutGet(GLUT_WINDOW_WIDTH); + int wHeight = glutGet(GLUT_WINDOW_HEIGHT); + + vec3 pos = vec3_add(b->position.absolute, vec3_new(b->position.relative.x * wWidth, b->position.relative.y * wHeight, 0.0)); + vec3 ext = b->size; + glColorVec(b->clicked ? vec3_new(BACK_CLICKED_COLOR) : vec3_new(BACK_COLOR)); + glBegin(GL_QUADS); + glVertexVec(pos); + glVertex3f(pos.x + ext.x, pos.y, pos.z); + glVertexVec(vec3_add(pos, ext)); + glVertex3f(pos.x, pos.y + ext.y, pos.z); + glEnd(); + glColor3f(0.7, 0.7, 0.8); + glLineWidth(2.0); + glBegin(GL_LINE_LOOP); + glVertexVec(pos); + glVertex3f(pos.x + ext.x, pos.y, pos.z); + glVertexVec(vec3_add(pos, ext)); + glVertex3f(pos.x, pos.y + ext.y, pos.z); + glEnd(); + // Draw text if needed + if(b->text != NULL) { + glColor3f(1.0, 1.0, 1.0); + // Get text size + int textWidth = glutBitmapLength(GLUT_BITMAP_TIMES_ROMAN_24, (unsigned char*)b->text); + int textHeight = glutBitmapHeight(GLUT_BITMAP_TIMES_ROMAN_24); + // set raster position - not gonna take into account multiple line text buttons + vec3 rPos = vec3_add(pos, vec3_mult(ext, 0.5)); + rPos.x -= textWidth * 0.5; + rPos.y -= textHeight * 0.25; + glRasterPos3f(rPos.x, rPos.y, rPos.z); + glutBitmapString(GLUT_BITMAP_TIMES_ROMAN_24, (unsigned char*)b->text); + } +} + +char ui_button_mouse_over(ui_button* b, int mouseX, int mouseY) { + int wWidth = glutGet(GLUT_WINDOW_WIDTH); + int wHeight = glutGet(GLUT_WINDOW_HEIGHT); + + vec3 pos = vec3_add(b->position.absolute, vec3_new(b->position.relative.x * wWidth, b->position.relative.y * wHeight, 0.0)); + vec3 ext = b->size; + vec3 m = vec3_sub(vec3_new(mouseX, wHeight - mouseY, 0.0), pos); + return m.x > 0.0 && m.x < ext.x && m.y > 0.0 && m.y < ext.y; +} void ui_slider_draw(ui_slider* s) { int wWidth = glutGet(GLUT_WINDOW_WIDTH); diff --git a/ui.h b/ui.h index 703dc2b..5e2a26b 100644 --- a/ui.h +++ b/ui.h @@ -7,6 +7,15 @@ typedef struct _ui_position { vec3 absolute; } ui_pos; +typedef struct _ui_button { + ui_pos position; + /* x,y - width,height, z - unused */ + vec3 size; // size in pixels - if screen is too small i am not sure if it is a smart idea to make everything smaller + void (*onClick)(); // instead of passing stuff in, im just gonna put every state variable in global scope to ensure everything lives... + char* text; + char clicked; +} ui_button; + typedef struct _ui_slider { ui_pos position; /* x - width, y - height, z - middle circle radius */ @@ -14,6 +23,9 @@ typedef struct _ui_slider { float value; // Value will always range between 0 and 1 } ui_slider; +void ui_button_draw(ui_button* b); +char ui_button_mouse_over(ui_button* b, int mouseX, int mouseY); + void ui_slider_draw(ui_slider* s); char ui_slider_mouse_over(ui_slider* s, int mouseX, int mouseY); void ui_slider_onclick(ui_slider* s, int mouseX, int mouseY);