/* Tutor intended to teach the TW API for materials and lighting, which is a simplification of the OpenGL API, in which we commit to gray light and opaque material where the ambient and diffuse values are the same color and the specular is gray. Thus, a material can be specified with just five values (instead of 13), and a light can be specified with three values (instead of 9). The code is based on the lightmaterial tutorial by Nate Robins. Written by Caroline Geiersbach and Scott D. Anderson scott.anderson@acm.org Summer 2003 Fall 2005, revised to use twGrayLight */ #include #include #include #include #include #include #define GAP 25 //gap between subwindows int cWinWidth = 537; //command window width int cWinHeight = 537; //command window height int mWinWidth = GAP*4+256*3; //main window width int mWinHeight = GAP*3+256*2; //main window height //colors used in program twTriple plainText = {1,1,1}; // white twTriple headerText = {0,1,1}; // cyan twTriple infoText = {1,0,0}; // red twTriple gray = {0.8,0.8,0.8}; twTriple green = {0.8,1,0.6}; //cells are used in the command window; each cell has an id; a //raster location at x,y; min and max values; a current value; the //step for adjusting values; info on what the cell specifies; and //finally the format of the printed values. typedef struct _cell { int id; int x, y; GLfloat min, max; GLfloat value; GLfloat step; char* info; char* format; } cell; // The Y coordinate is the 3rd one. A spacing of about 40 works pretty well for "one line" cell lightPosition[4] = { { 1, 225, 120, -5.0, 5.0, 3.0, 0.05, "Specifies X coordinate of light vector.", "%.2f" }, { 2, 285, 120, -5.0, 5.0, 0.0, 0.05, "Specifies Y coordinate of light vector.", "%.2f" }, { 3, 345, 120, -5.0, 5.0, 2.0, 0.05, "Specifies Z coordinate of light vector.", "%.2f" }, { 4, 405, 120, 0.0, 1.0, 1.0, 1.0, "Specifies directional (0) or positional (1) light.", "%.2f" }, }; cell lightIntensity[4] = { { 9, 250, 160, 0.0, 1.0, 0.5, 0.01, "Specifies ambient intensity of the gray light.", "%.2f" }, { 10, 310, 160, 0.0, 1.0, 0.5, 0.01, "Specifies diffuse intensity of the gray light.", "%.2f" }, { 11, 370, 160, 0.0, 1.0, 0.5, 0.01, "Specifies specular intensity of the gray light.", "%.2f" }, }; cell gAmbient[1] = { { 17, 115, 200, 0.0, 1.0, 0.3, 0.01, "Specifies global ambient light value.", "%.2f" }, }; cell material[5] = { { 21, 200, 300, 0.0, 1.0, 0.5, 0.01, "Specifies red component of the material.", "%.2f" }, { 22, 280, 300, 0.0, 1.0, 0.7, 0.01, "Specifies green component of the material.", "%.2f" }, { 23, 360, 300, 0.0, 1.0, 0.5, 0.01, "Specifies blue component of the material.", "%.2f" }, { 24, 180, 340, 0.0, 1.0, 0.7, 0.01, "Specifies specularity of the material.", "%.2f" }, { 25, 260, 340, 0.0, 128, 30.0, 1.0, "Specifies shininess of the material.", "%.2f" }, }; GLboolean savedDraw = GL_TRUE; GLint selection = 0; // the index (id) of the selected cell int old_y; //for command mouse //temp values for lighting and material GLfloat posTemp[4], lATemp[4], lDTemp[4], lSTemp[4], lIntensityTemp[3], twCTemp[5]; GLfloat lGATemp = gAmbient[0].value; void redisplayAll(void); GLdouble projection[16], modelview[16], inverse[16]; GLuint window, adjustable, saved, command; void cellDraw(cell* cell) { if (selection == cell->id) { glColor3fv(infoText); twDrawString(10, 20, cell->info); twDrawString(cell->x, cell->y, cell->format, cell->value); } else { glColor3fv(plainText); twDrawString(cell->x, cell->y, cell->format, cell->value); } } //returns cell id if cell has been clicked on; 0 otherwise int cellHit(cell* cell, int x, int y) { if (x > cell->x && x < cell->x+55 && y > cell->y-15 && y < cell->y+15){ return cell->id; } return 0; } //updates the cell's value void cellUpdate(cell* cell, int update) { if (selection != cell->id) return; cell->value += update * cell->step; //tests for min and max values of the points if (cell->value < cell->min) cell->value = cell->min; else if (cell->value > cell->max) cell->value = cell->max; } //converts values from cell to a destination vector void cellVector(GLfloat* dst, cell* cell, int num) { while (--num >= 0) dst[num] = cell[num].value; } void mainDisplay() { //set up camera to allow for window labeling glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0,mWinWidth,mWinHeight,0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.7, 0.7, 1.0, 0.0); //light blue background glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); twColorName(TW_BLACK); twSetFont("helvetica", 12); twDrawString(GAP,20, "Saved"); twDrawString(GAP,GAP+256+20, "Adjustable"); glutSwapBuffers(); } void adjustableLighting() { GLfloat pos[4], lA[4], lI[3], lS[4]; GLfloat twC[3], lGA[4]; //convert vals from cells to vectors cellVector(pos, lightPosition, 4); // cellVector(lA, lightAmbient, 4); cellVector(lI, lightIntensity, 3); // cellVector(lS, lightSpecular, 4); //lighting glShadeModel(GL_SMOOTH); twAmbient(gAmbient[0].value); twGrayLight(GL_LIGHT0, pos, lI[0], lI[1], lI[2]); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); } //display in the lower left (adjustable-space view) void adjustableDisplay() { twCamera(); glClearColor(0,0,0,1); //clear sub-window to black glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); adjustableLighting(); twTriple colorVal = {material[0].value, material[1].value, material[2].value}; twColor(colorVal, material[3].value,material[4].value); glutSolidTorus(0.25, 0.75, 28, 28); glDisable(GL_LIGHTING); glDisable(GL_LIGHT0); //draw line from light source to center of object glPushMatrix(); twColor(gray,0,0); glBegin(GL_LINE_STRIP); glVertex3f(0, 0, 0); glVertex3f(lightPosition[0].value, lightPosition[1].value, lightPosition[2].value); glEnd(); glPopMatrix(); glFlush(); glutSwapBuffers(); } void savedLighting() { //convert vals from cells to vectors if(savedDraw) { //get new values cellVector(posTemp, lightPosition, 4); cellVector(lIntensityTemp, lightIntensity, 3); cellVector(twCTemp, material,5); lGATemp = gAmbient[0].value; } savedDraw = GL_FALSE; //lighting glShadeModel(GL_SMOOTH); twAmbient(lGATemp); twGrayLight(GL_LIGHT1, posTemp, lIntensityTemp[0], lIntensityTemp[1], lIntensityTemp[2] ); glEnable(GL_LIGHT1); glEnable(GL_LIGHTING); } //display in the upper left (saved-space view) void savedDisplay() { twCamera(); glClearColor(0,0,0,1); //clear sub-window to black glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); savedLighting(); twTriple colorVal2 = {twCTemp[0],twCTemp[1],twCTemp[2]}; twColor(colorVal2, twCTemp[3],twCTemp[4]); glutSolidTorus(0.25, 0.75, 28, 28); glDisable(GL_LIGHT0); glDisable(GL_LIGHTING); //draw line from light source to center of object glPushMatrix(); twColor(gray,0,0); glBegin(GL_LINE_STRIP); glVertex3f(0, 0, 0); glVertex3f(posTemp[0],posTemp[1],posTemp[2]); glEnd(); glPopMatrix(); glFlush(); glutSwapBuffers(); } //display on right side; where user can adjust values void commandDisplay() { //set up camera glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0, cWinWidth, cWinHeight, 0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glClearColor(0.0, 0.0, 0.0, 1.0); //clear to black glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); twSetFont("helvetica", 18); glColor3fv(plainText); twDrawString(10, lightPosition[0].y, "GLfloat lightPosition[ ] = {"); twDrawString(10, lightIntensity[0].y, "twGrayLight( lightPosition, "); twDrawString(10, gAmbient[0].y, "twAmbient ("); twDrawString(10, material[0].y, "twTriple colorVals = ("); twDrawString(10, material[3].y, "twColor(colorVals,"); twDrawString(lightPosition[0].x+50, lightPosition[0].y, ","); twDrawString(lightPosition[1].x+50, lightPosition[1].y, ","); twDrawString(lightPosition[2].x+50, lightPosition[2].y, ","); twDrawString(lightPosition[3].x+50, lightPosition[3].y, "};"); twDrawString(lightIntensity[0].x+50, lightIntensity[0].y, ","); twDrawString(lightIntensity[1].x+50, lightIntensity[1].y, ","); twDrawString(lightIntensity[2].x+50, lightIntensity[2].y, ");"); twDrawString(gAmbient[0].x+50, gAmbient[0].y, ");"); twDrawString(material[0].x+50, material[0].y, ","); twDrawString(material[1].x+50, material[1].y, ","); twDrawString(material[2].x+50, material[2].y, ");"); twDrawString(material[3].x+50, material[3].y, ","); twDrawString(material[4].x+60, material[4].y, ");"); //draw values of each cell cellDraw(&lightPosition[0]); cellDraw(&lightPosition[1]); cellDraw(&lightPosition[2]); cellDraw(&lightPosition[3]); cellDraw(&lightIntensity[0]); cellDraw(&lightIntensity[1]); cellDraw(&lightIntensity[2]); cellDraw(&gAmbient[0]); cellDraw(&material[0]); cellDraw(&material[1]); cellDraw(&material[2]); cellDraw(&material[3]); cellDraw(&material[4]); glColor3fv(headerText); if (!selection) { twDrawString(10, 20, "Click on the arguments and move the mouse to modify values."); } twDrawString(10,80,"Light Properties"); twDrawString(10,260,"Material Properties"); //directions at bottom of command window twDrawString(10,420, "Hit to save values,

to print saved values."); twDrawString(10,460, "Hit to reset adjustable to original values."); twDrawString(10,500, "Hit to reset adjustable to original view."); glutSwapBuffers(); } // Because the selection is only set when the button goes down and isn't // reset to zero when then button goes up, we can then use keyboard // callbacks to adjust the numbers. void commandMouse(int button, int state, int x, int y) { if(state == GLUT_DOWN) { selection = 0; /* mouse should only hit _one_ of the cells, so adding up all the hits just propagates a single hit. */ //lighting selection += cellHit(&lightPosition[0], x, y); selection += cellHit(&lightPosition[1], x, y); selection += cellHit(&lightPosition[2], x, y); selection += cellHit(&lightPosition[3], x, y); selection += cellHit(&lightIntensity[0], x, y); selection += cellHit(&lightIntensity[1], x, y); selection += cellHit(&lightIntensity[2], x, y); selection += cellHit(&gAmbient[0], x, y); selection += cellHit(&material[0], x, y); selection += cellHit(&material[1], x, y); selection += cellHit(&material[2], x, y); selection += cellHit(&material[3], x, y); selection += cellHit(&material[4], x, y); } old_y = y; redisplayAll(); } // The "update" argument is the number of steps of the cell's value. // Anytime we create a new cell (or delete one), this function must be // modified. void cellUpdateAll(int update) { cellUpdate(&lightPosition[0], update); cellUpdate(&lightPosition[1], update); cellUpdate(&lightPosition[2], update); cellUpdate(&lightPosition[3], update); cellUpdate(&lightIntensity[0], update); cellUpdate(&lightIntensity[1], update); cellUpdate(&lightIntensity[2], update); cellUpdate(&gAmbient[0], update); cellUpdate(&material[0], update); cellUpdate(&material[1], update); cellUpdate(&material[2], update); cellUpdate(&material[3], update); cellUpdate(&material[4], update); } //allows for click and drag void commandMotion(int x, int y) { cellUpdateAll(old_y - y); old_y = y; redisplayAll(); } void reset(unsigned char key, int x, int y) { switch(key) { case'R': lightPosition[0].value = 3; lightPosition[1].value = 0; lightPosition[2].value = 2; lightPosition[3].value = 1; lightIntensity[0].value = 0.5; lightIntensity[1].value = 0.5; lightIntensity[2].value = 0.5; gAmbient[0].value = 0.3; material[0].value = 0.5; material[1].value = 0.7; material[2].value = 0.5; material[3].value = 0.7; material[4].value = 30.0; break; case'r': glutSetWindow(adjustable); twZview(); glutSetWindow(saved); twZview(); break; } redisplayAll(); } void myKeyboard(unsigned char key, int x, int y) { switch(key) { case 'P': printf("your saved light and material values: \n"); printf("lightPosition[] = {%.2f,%.2f,%.2f,%.2f}\n", posTemp[0],posTemp[1],posTemp[2],posTemp[3]); printf("lightIntensity[] = {%.2f,%.2f,%.2f}\n", lIntensityTemp[0],lIntensityTemp[1],lIntensityTemp[2]); printf("twAmbient(%.2f)\n",lGATemp); printf("colorVals = {%.2f,%.2f,%.2f}\n",twCTemp[0], twCTemp[1],twCTemp[2]); printf("twColor=(colorVals,%.2f,%.2f)\n", twCTemp[3],twCTemp[4]); break; case 'S': savedDraw = GL_TRUE; break; case '+': cellUpdateAll(1); break; case '-': cellUpdateAll(-1); break; } redisplayAll(); } void keyInit() { twKeyCallback('R',reset,"resets back to original settings"); twKeyCallback('r',reset,"resets back to original view"); twKeyCallback('P',myKeyboard,"prints saved values"); twKeyCallback('S',myKeyboard,"saves current values"); twKeyCallback('+',myKeyboard,"increment selected value"); twKeyCallback('-',myKeyboard,"decrement selected value"); } void redisplayAll(void) { glutSetWindow(saved); glutPostRedisplay(); glutSetWindow(command); glutPostRedisplay(); glutSetWindow(adjustable); glutPostRedisplay(); } void myInit() { twMainInit(); keyInit(); } int main(int argc, char** argv) { glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutInitWindowSize(mWinWidth, mWinHeight); glutInitWindowPosition(0, 0); glutInit(&argc, argv); //parent window window = glutCreateWindow("Light & Material Tutor"); glutDisplayFunc(mainDisplay); myInit(); //upper left sub-window saved = glutCreateSubWindow(window, GAP, GAP, 256, 256); twBoundingBox(-1,1,-1,1,-1,1); glutDisplayFunc(savedDisplay); myInit(); //right sub-window command = glutCreateSubWindow(window, GAP*2+256, GAP, cWinWidth,cWinHeight); glutDisplayFunc(commandDisplay); myInit(); glutMotionFunc(commandMotion); glutMouseFunc(commandMouse); //lower left sub-window adjustable = glutCreateSubWindow(window, GAP, GAP*2+256, 256, 256); twBoundingBox(-1,1,-1,1,-1,1); glutDisplayFunc(adjustableDisplay); myInit(); redisplayAll(); glutMainLoop(); return 0; }