Files
GTSchoolShit/CS4451/proj4/Renderer.cpp
2025-06-07 01:59:34 -04:00

800 lines
19 KiB
C++

#include "Renderer.h"
#include "defs.h"
#include "structs.h"
#include <GL/glut.h>
#include <math.h>
#include <assert.h>
#include <time.h> //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<Sphere*>;
triangles = new vector<Triangle*>;
lights = new vector<Light*>;
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<Triangle*>::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<Sphere*>::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<Triangle*>::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<Sphere*>::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<Sphere*>::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<Sphere*>::iterator s_iter;
vector<Triangle*>::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();
}