#include "Renderer.h" #include "defs.h" #include "structs.h" #include #include #include #include //For FPS #ifndef CLK_TCK #define CLK_TCK CLOCKS_PER_SEC #endif #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define VPD_MIN 200 #define VPD_DEFAULT 800 #define VPD_MAX 1024 #ifndef max #define max(a,b) ( (a) > (b) ? (a) : (b) ) #endif #ifndef min #define min(a,b) ( (a) < (b) ? (a) : (b) ) #endif inline GLvoid reshape(GLint vpw, GLint vph) { Renderer::getInstance()->window_resize(vpw,vph); } inline GLvoid draw() { if (!Renderer::getInstance()->drawShadows) { Renderer::getInstance()->render(); } else { Renderer::getInstance()->renderWithShadows(); } } Renderer Renderer::instance; Renderer * Renderer::getInstance() { return & instance; } Renderer::Renderer() { spheres = new vector; triangles = new vector; lights = new vector; this->vpd = VPD_DEFAULT; this->viewAngle = -1.; lightCenter = gluNewQuadric(); this->primitivesOnly = true; this->drawShadows = true; this->backFaceCull = true; } Renderer::~Renderer() { delete spheres; delete triangles; delete lights; } void Renderer::apply_attributes(MaterialAttributes attrib, float alpha) { GLfloat mat_specular[4] = { attrib.k_s, attrib.k_s, attrib.k_s, alpha }; //GLfloat mat_ambient_and_diffuse[4] = { 0.5, 0.5, 0.5, 1.0 }; GLfloat mat_ambient[4] = { attrib.k_ar, attrib.k_ag, attrib.k_ab, alpha }; GLfloat mat_diffuse[4] = { attrib.k_dr, attrib.k_dg, attrib.k_db, alpha }; #ifdef _NV_OPENGL GLfloat mat_shininess[1] = { (attrib.n_spec * 5. ) }; #else //_ATI_OPENGL GLfloat mat_shininess[1] = { (attrib.n_spec) }; #endif /*mat_specular[0] = mat_ambient_and_diffuse[0] = r; mat_specular[1] = mat_ambient_and_diffuse[1] = g; mat_specular[2] = mat_ambient_and_diffuse[2] = b;*/ glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shininess); glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_ambient); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diffuse); } GLvoid Renderer::init_lightsource ( GLvoid ) { Light l = *(Renderer::getInstance()->lights->front()); //Light 0 parameters GLfloat light_diffuse[] = { l.intensity, l.intensity, l.intensity, 1.0 }; #ifdef _NV_OPENGL GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 }; #else //_ATI_OPENGL GLfloat light_specular[] = { l.intensity, l.intensity, l.intensity, 1.0 }; #endif GLfloat light_position[] = { l.center.x, l.center.y, l.center.z, 0 }; //Set Light 0 properties glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glLightf(GL_LIGHT0,GL_CONSTANT_ATTENUATION,1.0); glLightf(GL_LIGHT0,GL_LINEAR_ATTENUATION,0.0); glLightf(GL_LIGHT0,GL_QUADRATIC_ATTENUATION,0.0); //Enable light0 glEnable(GL_LIGHT0); } void Renderer::fixVertexDirection(Triangle* t) { if (checkTriangle(t,viewport)) { Point temp = t->a2; t->a2 = t->a3; t->a3 = temp; } } /** * Initializes the Triangle sceneID * Initializes all the Sphere quadrics * Sets the view angle */ void Renderer::init_scene() { triangleScene = glGenLists(1); glNewList(triangleScene,GL_COMPILE); glBegin(GL_TRIANGLES); vector::iterator t_iterator = triangles->begin(); Triangle *t; Point normal; Point lightCenter; for(;t_iterator != triangles->end(); t_iterator++) { t = *t_iterator; fixVertexDirection(t); apply_attributes(t->m_attr); this->calculateNormal(&normal,t); lightCenter = (lights->front())->center; if (this->checkTriNormal(normal,lightCenter)) { glNormal3f(-normal.x,-normal.y,-normal.z); } else { glNormal3f(normal.x,normal.y,normal.z); } glVertex3f(t->a1.x,t->a1.y,t->a1.z); glVertex3f(t->a2.x,t->a2.y,t->a2.z); glVertex3f(t->a3.x,t->a3.y,t->a3.z); } glEnd(); //glEndList();*/ vector::iterator s_iter = spheres->begin(); for (; s_iter != spheres->end(); s_iter++) { apply_attributes((*s_iter)->m_attr); (*s_iter)->glSphere = gluNewQuadric(); gluQuadricOrientation((*s_iter)->glSphere,GLU_OUTSIDE); glPushMatrix(); glTranslatef((*s_iter)->center.x, (*s_iter)->center.y, (*s_iter)->center.z); gluSphere((*s_iter)->glSphere, (*s_iter)->radius, 64, 64); glPopMatrix(); } glEndList(); //Initialize the view this->viewAngle = 2.*(180./M_PI)* atan(fabs(viewport.y - l.y) / fabs(viewport.z - l.z)); //printf("viewAngle: %f\n",viewAngle); } /* * returns true if the light and the normal are on opposite sides */ bool Renderer::checkTriNormal(Point normal, Point lightCenter) { return ( (lightCenter.x*normal.x) + (lightCenter.y*normal.y) + (lightCenter.z*normal.z) < 0); } void Renderer::calculateNormal(Point *dest, Triangle* triangle) { Point a1a2, a1a3; Point a1 = triangle->a1, a2 = triangle->a2, a3 = triangle->a3; a1a2.x = a2.x - a1.x; a1a2.y = a2.y - a1.y; a1a2.z = a2.z - a1.z; a1a3.x = a3.x - a1.x; a1a3.y = a3.y - a1.y; a1a3.z = a3.z - a1.z; Point::cross(*dest,a1a2,a1a3); } bool Renderer::checkTriangle(Triangle* triangle, Point lightCenter) { Point normal; this->calculateNormal(&normal,triangle); return checkTriNormal(normal,lightCenter); } void Renderer::getVector(Point *dest, Point *to, Point *from) { dest->x = to->x - from->x; dest->y = to->y - from->y; dest->z = to->z - from->z; } void Renderer::generateTriangleShadows() { vector::iterator t_iter = triangles->begin(); Triangle* t; for (int i =0; t_iter != triangles->end(); t_iter++, i++) { t = *t_iter; Light light = *(lights->front()); //TODO: MAKE A QUAD STRIP! Point la1, la2, la3; getVector(&la1,&t->a1,&light.center); getVector(&la2,&t->a2,&light.center); getVector(&la3,&t->a3,&light.center); glBegin(GL_QUADS); if (!checkTriangle(t,light.center)) { /* a1' a1 a3 a3' a3' a3 a2 a2' a2' a2 a1 a1' */ glVertex4f(la1.x, la1.y, la1.z, 0); glVertex3f(t->a1.x,t->a1.y,t->a1.z); glVertex3f(t->a3.x,t->a3.y,t->a3.z); glVertex4f(la3.x, la3.y, la3.z, 0); glVertex4f(la3.x, la3.y, la3.z, 0); glVertex3f(t->a3.x,t->a3.y,t->a3.z); glVertex3f(t->a2.x,t->a2.y,t->a2.z); glVertex4f(la2.x, la2.y, la2.z, 0); glVertex4f(la2.x, la2.y, la2.z, 0); glVertex3f(t->a2.x,t->a2.y,t->a2.z); glVertex3f(t->a1.x,t->a1.y,t->a1.z); glVertex4f(la1.x, la1.y, la1.z, 0); } else { /* a1 a1' a3' a3 a3 a3' a2' a2 a2 a2' a1' a1 */ glColor3d(1, 0, 0); glVertex3f(t->a1.x,t->a1.y,t->a1.z); glVertex4f(la1.x, la1.y, la1.z, 0); glVertex4f(la3.x, la3.y, la3.z, 0); glVertex3f(t->a3.x,t->a3.y,t->a3.z); glVertex3f(t->a3.x,t->a3.y,t->a3.z); glVertex4f(la3.x, la3.y, la3.z, 0); glVertex4f(la2.x, la2.y, la2.z, 0); glVertex3f(t->a2.x,t->a2.y,t->a2.z); glVertex3f(t->a2.x,t->a2.y,t->a2.z); glVertex4f(la2.x, la2.y, la2.z, 0); glVertex4f(la1.x, la1.y, la1.z, 0); glVertex3f(t->a1.x,t->a1.y,t->a1.z); } glEnd(); } } static inline Point wB(m_float B, Point& u, Point& v) { Point temp1,temp2; Point::mul(temp1,v,cos(B)); Point::mul(temp2,u,sin(B)); Point::add(temp1,temp1,temp2); return temp1; } void Renderer::generateSphereShadows() { Light* light = this->lights->front(); Point PL, LP, LC, temp, P, u, v; vector::iterator s_iter; Sphere* s; m_float radius; glBegin(GL_QUADS); for (s_iter = spheres->begin(); s_iter != spheres->end(); s_iter++) { s = *s_iter; //LC Point::sub(LC,s->center,light->center); m_float LClen = LC.magnitude(); m_float PLlen = (pow(LClen,2) - pow(s->radius,2))/LClen; radius = sqrt(pow(LC.magnitude(),2) - pow(s->radius,2) - pow(PLlen,2));//PL.magnitude(),2)); //printf("Radius %f\n",radius); temp = Point::normalize(LC); Point::mul(temp, temp, PLlen); Point::add(P,light->center,temp); //P.printMe(); if (fabs(LC.z) > fabs(LC.x)) { if (fabs(LC.x) > fabs(LC.y)) { u.x = -1. * LC.z; u.y = 0; u.z = LC.x; } else { //LC.x < LC.y u.x = 0; u.y = LC.z * -1.; u.z = LC.y; } } else { //|LC.z| < |LC.x| if (fabs(LC.z) > fabs(LC.y)) { u.x = -1. * LC.z; u.y = 0; u.z = LC.x; } else { //LC.z < LC.y u.x = -1. * LC.y; u.y = LC.x; u.z = 0; } } Point::mul(u,Point::normalize(u),radius); //u.printMe(); //printf("length %f\n",u.magnitude()); Point::cross(v,u,LC); Point::mul(v,Point::normalize(v),radius); Point Q, Q2; Point Qb, Q2b; for (int k = 0; k < 64; k++) { Point::add(Q, P,wB((k*2*M_PI) / 64,u,v)); Point::add(Q2,P,wB(((k+1)*2*M_PI) / 64,u,v)); //Q.printMe(); getVector(&Qb, &Q, &light->center); getVector(&Q2b, &Q2, &light->center); //Q.printMe(); glVertex4f(Qb.x, Qb.y, Qb.z, 0); glVertex3f(Q.x,Q.y,Q.z); glVertex3f(Q2.x,Q2.y,Q2.z); glVertex4f(Q2b.x, Q2b.y, Q2b.z, 0); } } glEnd(); } m_uint Renderer::calculateShadowVolumes() { apply_attributes(this->m_shadow_attrib,.2f); generateTriangleShadows(); generateSphereShadows(); //glEndList(); return 0;//shadows; } void Renderer::drawPrimitives(bool withLight) { /* Do project fux0ring */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); //Set the perspective transformation gluPerspective(viewAngle,h.x/v.y,1,150); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //Look at the right point in the world gluLookAt(viewport.x,viewport.y,viewport.z, viewport.x,viewport.y,l.z, v.x,v.y,v.z); if (withLight) { init_lightsource(); } glCallList(triangleScene); //GLuint shadows = this->calculateShadowVolumes(); //glDeleteLists(shadows,1); /*vector::iterator s_iter = spheres->begin(); Sphere *s; for(int i = 0 ;s_iter != spheres->end();i++, s_iter++) { s = *s_iter; glPushMatrix(); apply_attributes(s->m_attr); glTranslatef(s->center.x,s->center.y,s->center.z); gluSphere(s->glSphere,s->radius,64,64); glPopMatrix(); }*/ } void Renderer::renderWithShadows(void) { /* ensure we're drawing to the correct GLUT window */ glutSetWindow(wid); /* * Clear all buffers */ /* clear the color buffers */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); /********************************************************************/ /* Step 2 - Draw the scene, no lighting only primitives and abience */ //Disable the LightSource glDisable(GL_LIGHT0); glDisable(GL_STENCIL_TEST); //Enable Backface culling glEnable(GL_CULL_FACE); if (this->backFaceCull) { glCullFace(GL_BACK); } else { glCullFace(GL_FRONT); } //glCullFace(GL_BACK); //Depth test to Less than or Equal glDepthFunc(GL_LEQUAL); //Draw the Primitives drawPrimitives(false); glFlush(); /*****************************************************/ /* Step 3 - Draw the shadow Polys with BackFace cull */ //Read only Depth and Color glDepthMask(0); glColorMask(0,0,0,0); //Stencil Buffer writeable //glStencilMask(1); //Stencil paramaters glEnable(GL_STENCIL_TEST); glStencilOp(GL_KEEP,GL_KEEP,GL_INCR); glStencilFunc(GL_ALWAYS,0,~0); /*glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(viewAngle,h.x/v.y,1,150); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(viewport.x,viewport.y,viewport.z, viewport.x,viewport.y,l.z, v.x,v.y,v.z);*/ //Draw Shadow Volumes this->calculateShadowVolumes(); glFlush(); /*****************************************************/ /* Step 4 - Draw the shadow Polys with FrontFace cull */ //Front face culling glCullFace(GL_FRONT); //Stencil Decrements glStencilOp(GL_KEEP,GL_KEEP,GL_DECR); glStencilFunc(GL_ALWAYS,0,~0); /*glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(viewAngle,h.x/v.y,1,150); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(viewport.x,viewport.y,viewport.z, viewport.x,viewport.y,l.z, v.x,v.y,v.z);*/ this->calculateShadowVolumes(); glFlush(); /*************************************/ /* Step 5 - Draw the fully lit scene */ glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, 0, ~0); glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP); glDepthFunc(GL_LEQUAL); if (this->backFaceCull) { glCullFace(GL_BACK); } else { glCullFace(GL_FRONT); } glDepthMask(1); glColorMask(1,1,1,1); glEnable(GL_LIGHT0); this->drawPrimitives(true); glDisable(GL_STENCIL_TEST); //glDeleteLists(shadows,1); glFlush(); /* look at our handiwork */ glutSwapBuffers(); #ifdef SHOW_FPS fps++; //printf("%d\n",fps); clock_t time = clock(); if (time-lastClock >= CLK_TCK) { printf("Rendered %d frames in %f seconds\n", fps,(time-lastClock)/(float)CLK_TCK); lastClock = clock(); fps = 0; } glutPostRedisplay(); #endif } void Renderer::render(void) { /* ensure we're drawing to the correct GLUT window */ glutSetWindow(wid); /* clear the color buffers */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); drawPrimitives(true); if (!this->primitivesOnly) { this->calculateShadowVolumes(); } /* flush the pipeline */ glFlush(); /* look at our handiwork */ glutSwapBuffers(); #ifdef SHOW_FPS fps++; //printf("%d\n",fps); clock_t time = clock(); if (time-lastClock >= CLK_TCK) { printf("Rendered %d frames in %f seconds\n", fps,(time-lastClock)/(float)CLK_TCK); lastClock = clock(); fps = 0; } glutPostRedisplay(); #endif //glutPostRedisplay(); } void Renderer::init_opengl() { /* back-face culling on */ glEnable(GL_CULL_FACE); glCullFace(GL_BACK); /* We want to cull the back */ glFrontFace(GL_CCW); /*if (viewport.z - l.z < 0) { glFrontFace(GL_CW); } else { glFrontFace(GL_CCW); }*/ /* automatically scale normals to unit length after transformation */ glEnable(GL_NORMALIZE); //Enable Lighting glEnable(GL_LIGHTING); /* clear to BLACK */ glClearColor(0, 0, 0, 1.0); glClearStencil(0); /* Enable depth test */ glEnable(GL_DEPTH_TEST); //Global parameter GLfloat light_ambient[] = { ambient, ambient, ambient, 1.0 }; //Set global ambience glLightModelfv(GL_LIGHT_MODEL_AMBIENT, light_ambient); //glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); //glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); } GLvoid Renderer::window_resize(GLint vpw, GLint vph) { glutSetWindow(wid); /* maintain a square viewport, not too small, not too big */ if( vpw < vph ) vpd = vph; else vpd = vpw; if( vpd < VPD_MIN ) vpd = VPD_MIN; if( vpd > VPD_MAX ) vpd = VPD_MAX; glViewport(0, 0, vpd, vpd); glutReshapeWindow(vpd, vpd); glutPostRedisplay(); } m_int Renderer::init_glut(m_int *argc, char **argv) { m_int id; glutInit(argc,argv); /* size and placement hints to the window system */ glutInitWindowSize(vpd, vpd); glutInitWindowPosition(10,10); /* double buffered, RGB color mode */ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH | GLUT_STENCIL); /* create a GLUT window (not drawn until glutMainLoop() is entered) */ id = glutCreateWindow("cs4451 project 4: Super Cool Shadows!"); /* register callbacks */ /* window size changes */ //glutReshapeFunc(NULL); glutReshapeFunc(reshape); /* keypress handling when the current window has input focus */ glutKeyboardFunc(keyboard_event); /* mouse event handling */ glutMouseFunc(mouse_button); /* button press/release */ glutMotionFunc(button_motion); /* mouse motion w/ button down */ glutPassiveMotionFunc(passive_motion); /* mouse motion with button up */ /* window obscured/revealed event handler */ glutVisibilityFunc(NULL); /* handling of keyboard SHIFT, ALT, CTRL keys */ glutSpecialFunc(keyboard_special_event); /* what to do when mouse cursor enters/exits the current window */ glutEntryFunc(NULL); /* what to do on each display loop iteration */ glutDisplayFunc(draw); /* Create the menu */ //Create the Zoom Menu GLint menuID = glutCreateMenu(menu); glutAddMenuEntry("Show Shadows (a)",MENU_SHADOWS); glutAddMenuEntry("Show Primitives Only (p)",MENU_PRIMITIVES_ONLY); glutAddMenuEntry("Show Shadow Volumes (v)",MENU_SHADOW_VOLUMES); glutAddMenuEntry("Clear to White (x)",MENU_CLEAR_WHITE); glutAddMenuEntry("Clear to Black (c)",MENU_CLEAR_BLACK); glutSetMenu(menuID); glutAttachMenu(GLUT_RIGHT_BUTTON); return id; } void Renderer::calculateDistance() { Point distances; m_float total = 0; vector::iterator s_iter; vector::iterator t_iter; Point centerOfMass; for (s_iter = spheres->begin(); s_iter != spheres->end(); s_iter++) { distances.x += this->viewport.x - (*s_iter)->center.x; distances.y += this->viewport.y - (*s_iter)->center.y; distances.z += this->viewport.z - (*s_iter)->center.z; total++; } for (t_iter = triangles->begin(); t_iter != triangles->end(); t_iter++) { centerOfMass = this->centerOfTriangle(*t_iter); distances.x += this->viewport.x - centerOfMass.x; distances.y += this->viewport.y - centerOfMass.y; distances.z += this->viewport.z - centerOfMass.z; total++; } distances.x /= total; distances.y /= total; distances.z /= total; this->distanceToCenter = distances; //distances.printMe(); }