//Base code for the Catmull-Clark subdivion assignment for cpe 572 //Use a 3d array of Vector3s, 2 dimensions are the surface the third is the level of subdivision //Only supports subdivision up to 4 levels (starts out at level 0) //only draws wireframe and uses aswd camera movement but no rotations //ASSIGNMENT: compute the correct re-positioning of geometric values per level given the Catmull-Clark rules //ZJW 4/29/10 #include <stdio.h> #include <math.h> #include <stdlib.h> #include <vector> #include <iostream> #include <fstream> #include <GLUT/glut.h> #include <OPENGL/gl.h> #include <OPENGL/glext.h> #include <assert.h> using namespace std; /* structure to store a 3d point, note that for this assignment, the vector also stores a level which is used to denote which level of subdivion has been computed on that point (to not displace a point twice), given the data structure we are using */ typedef struct Vector3 { float x; float y; float z; int level; Vector3(float in_x, float in_y, float in_z) : x(in_x), y(in_y), z(in_z), level(0) {} Vector3() : x(0), y(0), z(0), level(0) {} Vector3(const Vector3& in_v) : x(in_v.x), y(in_v.y), z(in_v.z), level(0) {} Vector3(float in) : x(in), y(in), z(in), level(0) {} inline Vector3& operator =(const Vector3& v2) { x = v2.x; y=v2.y; z=v2.z; level =v2.level; return *this;} inline void set(float in_x, float in_y, float in_z) { x = in_x; y = in_y; z = in_z; level = 0;} } Vector3; #define FLT_MIN 1.1754E-38F #define FLT_MAX 1.1754E+38F int GW; int GH; //for camera changes instead Vector3 eye, la; char strPop[256]; //helper functions for working with vectors Vector3 cross(const Vector3& a, const Vector3& b) { Vector3 c; c.x = a.y*b.z - a.z*b.y; c.y = a.z*b.x - a.x*b.z; c.z = a.x*b.y - a.y*b.x; return(c); } float dot(const Vector3& a, const Vector3& b) { return(a.x*b.x + a.y*b.y + a.z*b.z); } float length(const Vector3& a) { return(sqrt(dot(a, a))); } Vector3 normalize(const Vector3& a) { Vector3 u; float mag_a = length(a); u.x = a.x/mag_a; u.y = a.y/mag_a; u.z = a.z/mag_a; return(u); } void PrintVec(const Vector3 v1) { printf("vec: %f %f %f\n", v1.x, v1.y, v1.z); } void renderBitmapCharacher(float x, float y, float z, void *font,char *string) { char *c; glRasterPos3f(x, y,z); for (c=string; *c != '\0'; c++) { glutBitmapCharacter(font, *c); } } //storage for the data points for our Catmull-Clark Subdivision surface (up to 4 levels of subdivision) Vector3 CCSurf[17][17][4]; //Globals for the global extents of the patch int L, R, T, B; int DrawLevel; /*assumes ordering as 3--------2 | | | | 0--------1 */ void InitCCSurf() { int level=0; L=B=0; R=T=16; CCSurf[L][B][level] = Vector3(-1, 0, 1); CCSurf[R][B][level] = Vector3(1, 0, 1); CCSurf[R][T][level] = Vector3(1, 0, -1); CCSurf[L][T][level] = Vector3(-1, 0, -1); //setting center for interesting geometry and edge midpoints - kind of weird CCSurf[(R-L)/2][(T-B)/2][level] = Vector3(0, 1, 0); CCSurf[(R-L)/2][B][level] = Vector3(0, 0, 1); CCSurf[R][(T-B)/2][level] = Vector3(1, 0, 0); CCSurf[(R-L)/2][T][level] = Vector3(0, 0, -1); CCSurf[L][(T-B)/2][level] = Vector3(-1, 0, 0); } /*Routine to draw a given portion of a patch at a given level*/ void DrawPatch(int Lev, int Lft, int Rght, int Bot, int Top) { int i, j, skip; Vector3 v1, v2,norm; skip = (Rght-Lft)/powf(2,(Lev+1)); for (i=Lft; i <Rght; i += skip) { for (j=Bot; j<Top; j+=skip) { printf("draw called for i %d j %d for level %d with a skip of %d\n", i, j, Lev, skip); glBegin(GL_LINE_LOOP); glColor3f(0, 0, 0); glVertex3f(CCSurf[i][j][Lev].x, CCSurf[i][j][Lev].y, CCSurf[i][j][Lev].z); glVertex3f(CCSurf[i+skip][j][Lev].x, CCSurf[i+skip][j][Lev].y, CCSurf[i+skip][j][Lev].z); glVertex3f(CCSurf[i+skip][j+skip][Lev].x, CCSurf[i+skip][j+skip][Lev].y, CCSurf[i+skip][j+skip][Lev].z); glVertex3f(CCSurf[i][j+skip][Lev].x, CCSurf[i][j+skip][Lev].y, CCSurf[i][j+skip][Lev].z); glEnd(); /* debugging statements can be pulled */ PrintVec(CCSurf[i][j][Lev]); PrintVec(CCSurf[i+skip][j][Lev]); PrintVec(CCSurf[i+skip][j+skip][Lev]); PrintVec(CCSurf[i][j+skip][Lev]); } } } /* Set the face points and compute the edge midpoints */ void SubdivFace(int L1, int R1, int B1, int T1, int Lev) { //compute the sum of edges using previous level positions! float div =2; //bottom edge if (CCSurf[L1+(R1-L1)/2][B1][Lev].level < Lev) { CCSurf[L1+(R1-L1)/2][B1][Lev] = Vector3(CCSurf[L1][B1][Lev-1].x+CCSurf[R1][B1][Lev-1].x, CCSurf[L1][B1][Lev-1].y+CCSurf[R1][B1][Lev-1].y, CCSurf[L1][B1][Lev-1].z+CCSurf[R1][B1][Lev-1].z); CCSurf[L1+(R1-L1)/2][B1][Lev].x /= div; CCSurf[L1+(R1-L1)/2][B1][Lev].y /= div; CCSurf[L1+(R1-L1)/2][B1][Lev].z /= div; CCSurf[L1+(R1-L1)/2][B1][Lev].level = Lev; /* debugging statements can be pulled */ printf("bot edge point for i %d j %d after\n", L1+(R1-L1)/2, B1); PrintVec(CCSurf[L1+(R1-L1)/2][B1][Lev]); } //top edge if (CCSurf[L1+(R1-L1)/2][T1][Lev].level < Lev) { CCSurf[L1+(R1-L1)/2][T1][Lev] = Vector3(CCSurf[L1][T1][Lev-1].x+CCSurf[R1][T1][Lev-1].x, CCSurf[L1][T1][Lev-1].y+CCSurf[R1][T1][Lev-1].y, CCSurf[L1][T1][Lev-1].z+CCSurf[R1][T1][Lev-1].z); CCSurf[L1+(R1-L1)/2][T1][Lev].x /= div; CCSurf[L1+(R1-L1)/2][T1][Lev].y /= div; CCSurf[L1+(R1-L1)/2][T1][Lev].z /= div; CCSurf[L1+(R1-L1)/2][T1][Lev].level = Lev; /* debugging statements can be pulled */ printf("top edge point for i %d j %d after\n", L1+(R1-L1)/2, T1); PrintVec(CCSurf[L1+(R1-L1)/2][T1][Lev]); } //leftedge if (CCSurf[L1][B1+(T1-B1)/2][Lev].level < Lev) { CCSurf[L1][B1+(T1-B1)/2][Lev] = Vector3(CCSurf[L1][B1][Lev-1].x+CCSurf[L1][T1][Lev-1].x, CCSurf[L1][B1][Lev-1].y+CCSurf[L1][T1][Lev-1].y, CCSurf[L1][B1][Lev-1].z+CCSurf[L1][T1][Lev-1].z); CCSurf[L1][B1+(T1-B1)/2][Lev].x /= div; CCSurf[L1][B1+(T1-B1)/2][Lev].y /=div; CCSurf[L1][B1+(T1-B1)/2][Lev].z /= div; CCSurf[L1][B1+(T1-B1)/2][Lev].level = Lev; /* debugging statements can be pulled */ printf("left edge point for i %d j %d after\n", L1, T1, B1+(T1-B1)/2); PrintVec(CCSurf[L1][B1+(T1-B1)/2][Lev]); } //right edge if (CCSurf[R1][B1+(T1-B1)/2][Lev].level < Lev) { CCSurf[R1][B1+(T1-B1)/2][Lev] = Vector3((CCSurf[R1][B1][Lev-1].x+CCSurf[R1][T1][Lev-1].x), (CCSurf[R1][B1][Lev-1].y+CCSurf[R1][T1][Lev-1].y), (CCSurf[R1][B1][Lev-1].z+CCSurf[R1][T1][Lev-1].z)); CCSurf[R1][B1+(T1-B1)/2][Lev].x /= div; CCSurf[R1][B1+(T1-B1)/2][Lev].y /= div; CCSurf[R1][B1+(T1-B1)/2][Lev].z /= div; CCSurf[R1][B1+(T1-B1)/2][Lev].level = Lev; /* debugging statements can be pulled */ printf("rght edge point for i %d j %d after\n", L1, T1, B1+(T1-B1)/2); PrintVec(CCSurf[R1][B1+(T1-B1)/2][Lev]); } //set the face point to centroid (note same as bi-linear on 2 edges, using left and right) CCSurf[L1+(R1-L1)/2][B1+(T1-B1)/2][Lev] = Vector3((CCSurf[L1][B1+(T1-B1)/2][Lev].x+CCSurf[R1][B1+(T1-B1)/2][Lev].x)/2.0, (CCSurf[L1][B1+(T1-B1)/2][Lev].y+CCSurf[R1][B1+(T1-B1)/2][Lev].y)/2.0, (CCSurf[L1][B1+(T1-B1)/2][Lev].z+CCSurf[R1][B1+(T1-B1)/2][Lev].z)/2.0); /* debugging statements can be pulled */ printf("new face point for i %d j%d\n", L1+(R1-L1)/2, B1+(T1-B1)/2); PrintVec(CCSurf[L1+(R1-L1)/2][B1+(T1-B1)/2][Lev]); /*set current level 'original' to same as previous - replace in assignment*/ CCSurf[R1][B1][Lev] = CCSurf[R1][B1][Lev-1]; CCSurf[R1][T1][Lev] = CCSurf[R1][T1][Lev-1]; CCSurf[L1][B1][Lev] = CCSurf[L1][B1][Lev-1]; CCSurf[L1][T1][Lev] = CCSurf[L1][T1][Lev-1]; } /*Routine to subdivide a given portion of a patch at a given level*/ void SubDivPatch(int Lev, int Lft, int Rght, int Bot, int Top) { int i, j, skip; Vector3 v1, v2,norm; skip = (Rght-Lft)/powf(2,Lev); //subdivide the faces - including the edges right now for (i=Lft; i <Rght; i +=skip) { for (j=Bot; j<Top; j+=skip) { printf("subdiv face called for i %d j %d for level %d with skip of %d\n", i, j, Lev, skip); SubdivFace(i, i+skip, j, j+skip, Lev); } } } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glPushMatrix(); //set up the camera gluLookAt(0, 0, 2.5, 0, 0, 0, 0, 1, 0); renderBitmapCharacher(-1.3,1.3,0,(void *)GLUT_BITMAP_HELVETICA_12,strPop); glPopMatrix(); glPushMatrix(); //set up the camera, aswd keys work but no rotation gluLookAt(eye.x, eye.y, eye.z, la.x, la.y, la.z, 0, 1,0); //rotate patch just for nice angle of viewing glRotatef(45, 1, 0, 0); DrawPatch(DrawLevel, L, R, B, T); glPopMatrix(); glutSwapBuffers(); /*not sure why I am needing this on mac??*/ glFlush(); } void keyboard(unsigned char key, int x, int y) { float delta = 0.1; Vector3 gaze,left; switch(key) { case 'a': gaze.x = la.x - eye.x; gaze.y = la.y - eye.y; gaze.z = la.z - eye.z; left = cross(Vector3(0, 1, 0), gaze); eye.x += delta*left.x; eye.y += delta*left.y; eye.z += delta*left.z; la.x += delta*left.x; la.y += delta*left.y; la.z += delta*left.z; break; case 'd': gaze.x = la.x - eye.x; gaze.y = la.y - eye.y; gaze.z = la.z - eye.z; left = cross(Vector3(0, 1, 0), gaze); eye.x -= delta*left.x; eye.y -= delta*left.y; eye.z -= delta*left.z; la.x -= delta*left.x; la.y -= delta*left.y; la.z -= delta*left.z; glutPostRedisplay(); break; case 's': gaze.x = la.x - eye.x; gaze.y = la.y - eye.y; gaze.z = la.z - eye.z; eye.x -= delta*gaze.x; eye.y -= delta*gaze.y; eye.z -= delta*gaze.z; la.x -= delta*gaze.x; la.y -= delta*gaze.y; la.z -= delta*gaze.z; glutPostRedisplay(); break; case 'w': gaze.x = la.x - eye.x; gaze.y = la.y - eye.y; gaze.z = la.z - eye.z; eye.x += delta*gaze.x; eye.y += delta*gaze.y; eye.z += delta*gaze.z; la.x += delta*gaze.x; la.y += delta*gaze.y; la.z += delta*gaze.z; glutPostRedisplay(); break; case 'q': exit( EXIT_SUCCESS ); break; case 'p': DrawLevel++; if (DrawLevel < 4) { SubDivPatch(DrawLevel,L,R, B, T); sprintf(strPop, "Catmull-Clark Subdivision Level %d", DrawLevel); } else { DrawLevel--; sprintf(strPop, "Max Level reached!"); } glutPostRedisplay(); break; default: break; } glutPostRedisplay(); } void reshape(int w, int h) { GW = w; GH = h; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, (float)w/h, 0.1, 50); glMatrixMode(GL_MODELVIEW); glViewport(0, 0, w, h); glutPostRedisplay(); } int main( int argc, char** argv ) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(500, 500); glutInitWindowPosition(100, 100); glutCreateWindow("Subdivision"); glClearColor(1.0, 1.0, 1.0, 1.0); glutDisplayFunc( display ); glutReshapeFunc( reshape ); glutKeyboardFunc(keyboard); glEnable(GL_DEPTH_TEST); GW=GH = 500; InitCCSurf(); DrawLevel = 0; eye.x = 0.0; eye.y = 0.0; eye.z = 4.0; la.x = 0; la.y = 0.0; la.z = 0; sprintf(strPop, "Catmull-Clark Subdivision Level %d", DrawLevel); glutMainLoop(); }