/* Author : Josh Grant
Date : November 7th, 2000
Description: OpenGL program used to apply a texture onto a sphere. The
file must be specified at the command line and be in binary
PPM format.
Other parameters can be adjusted from the command line or
interactively by use of the keyboard at runtime. The
parameters are...
-xpos: shape's x center position, default = 0.0
-ypos: shape's y center position, default = 0.0
-zpos: shape's z center position, default = 0.0
-uDim: number of points between that will be
computed for the u parameter, default = 20
-vDim: number of points between that will be
computed for the v parameter, default = 20
-r: radius of sphere, default = 0.5
-file: file name to be applied to sphere.
-keyboard: lists a message describing the functionality
of various keys
*/
#include <GL/glut.h>
#include <stdio.h>
#include <iostream.h>
#include <math.h>
#include <stdlib.h>
#include <vector.h>
#include <userInterface.h>
#include <strings.h>
// the global parameters used for generating the shape
double xpos = 0.0,
ypos = 0.0,
zpos = 0.0,
uMax = 20.0,
vMax = 20.0,
r = 1.5;
#define height 128
#define width 256
static GLubyte image[height][width][4];
static GLuint texName;
char filename[80];
int filenameset = 0;
GLfloat mat_diffuse[] = { 1.0, 0.5, 0.4, 1.0 };
void readImage(char *filename) {
int index, x, y, w, h, maxint, total;
int nread;
unsigned char *ppmimage;
char line[80];
FILE *fptr;
fptr = fopen(filename, "r");
if (fptr) {
fscanf(fptr, "%s\n", line);
if (!strcmp(line, "P6")) {
fscanf(fptr, "%d %d\n", &w, &h);
fscanf(fptr, "%d\n", &maxint);
fflush(stdout);
total = w*h*3;
ppmimage = new unsigned char[total];
nread = fread(ppmimage, sizeof(unsigned char), total, fptr);
if (nread == total) {
for (index = 0; index < total; index += 3) {
y = abs((index / (w*3)) - h + 1);
x = (index % (w*3)) / 3;
image[y][x][0] = (GLubyte) ppmimage[index + 0];
image[y][x][1] = (GLubyte) ppmimage[index + 1];
image[y][x][2] = (GLubyte) ppmimage[index + 2];
image[y][x][3] = (GLubyte) 255;
}
}
else
printf("Invalid PPM Format\n");
}
else
printf("Invalid PPM Format\n");
}
fclose(fptr);
}
// Given a u and v, a point on a sphere is computed along with its normal. A
// check is made to determine if v equals 0 or pi. If the check is not made
// then the sphere is rendered with a hole at each point, because the normals
// at those points are zero.
void spherePoint(double u, double v) {
double x, y, z;
Vector T1, T2, N;
x = xpos + r*cos(u)*sin(v);
y = ypos + r*sin(u)*sin(v);
z = zpos + r*cos(v);
T1 = Vector(r*cos(u)*cos(v), r*sin(u)*cos(v), -r*sin(v));
T2 = Vector(-r*sin(u)*sin(v), r*cos(u)*sin(v), 0.0);
if (v == 0.0)
N = Vector(0.0, 0.0, 1.0);
else if (v == M_PI)
N = Vector(0.0, 0.0, -1.0);
else
N = cross(T1, T2);
N.normalize();
glTexCoord2f(u/(2*M_PI), v/M_PI);
glNormal3f(N.x(), N.y(), N.z());
glVertex3f(x, y, z);
}
// This is where each surface is generated. A check for the shape type is
// made and then the appropriate shape is created. The parameters uDim and
// vDim are used to determine how many points between their respective
// intervals will be computed.
void surface(int uDim, int vDim) {
double u, v, uc, vc, udelta, vdelta;
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glBegin(GL_TRIANGLE_STRIP);
udelta = (2*M_PI)/(uDim - 1.0);
vdelta = M_PI/(vDim - 1.0);
for (v = 0.0, vc = 0.0; vc < vDim; v += vdelta, vc++) {
for (u = 0.0, uc = 0.0; uc < uDim; u += udelta, uc++) {
spherePoint(u, v);
if (vc < vDim - 1.0)
spherePoint(u, v + vdelta);
else
spherePoint(u, M_PI);
}
}
glEnd();
}
/* initialize state */
void init(void) {
GLfloat light_position[] = { -5.0, 5.0, 5.0, 0.0 };
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel (GL_SMOOTH);
glDepthFunc (GL_LESS);
glEnable(GL_DEPTH_TEST);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &texName);
glBindTexture(GL_TEXTURE_2D, texName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width,
height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
image);
glLightfv (GL_LIGHT0, GL_POSITION, light_position);
glEnable(GL_NORMALIZE); /* Make my normals unit length for me. */
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
}
/* Clear window and draw surface. */
void display(void) {
GLfloat matrix[16];
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glGetFloatv(GL_MODELVIEW_MATRIX, matrix);
glPushMatrix();
glLoadIdentity();
gluLookAt(0, 0, 8, 0, 0, 0, 0, 1, 0);
glMultMatrixf(matrix);
glEnable(GL_TEXTURE_2D);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
glBindTexture(GL_TEXTURE_2D, texName);
surface(uMax, vMax);
glFlush();
glDisable(GL_TEXTURE_2D);
glPopMatrix();
glutSwapBuffers();
}
/* Handle window resize. */
void reshape(int w, int h) {
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(30, (GLfloat) w/(GLfloat) h, 1.0, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, 0, 0, 0, 0, -1, 0, 1, 0);
}
// Function written to handle the keyboard events by the user. Every
// parameter involved in generating the surface can be modified with a
// keyboard command. A mapping can be viewed when -keyboard is given as an
// argument from the command line or whenever the program is run.
void mykeyboard (unsigned char key, int x, int y) {
switch (key)
{
case '0':
exit(0);
break;
case 's':
glShadeModel(GL_SMOOTH);
break;
case 'f':
glShadeModel(GL_FLAT);
break;
case '2':
r += 0.01;
break;
case '@':
r -= 0.01;
break;
case 'i':
ypos += 0.05;
break;
case 'k':
ypos -= 0.05;
break;
case 'l':
xpos += 0.05;
break;
case 'j':
xpos -= 0.05;
break;
case 'm':
zpos += 0.05;
break;
case 'n':
zpos -= 0.05;
break;
case 'u':
uMax += 1;
break;
case 'U':
uMax -= 1;
break;
case 'v':
vMax += 1;
break;
case 'V':
vMax -= 1;
break;
default:
break;
}
}
// prints out the keyboard mapping
void keys() {
cout << "Usage - shape (keys)" << endl;
cout << " Keyboard: Various keys on the keyboard are used to modify\n"
<< " various parameters of within the program. Below\n"
<< " is a description of each.\n\n"
<< " 0 : exit program\n"
<< " 2 : increases r by 0.01\n"
<< " <shift> 2 : decreases r by 0.01\n"
<< " u : increases uDim by 1\n"
<< " U : decreases uDim by 1\n"
<< " v : increases vDim by 1\n"
<< " V : decreases vDim by 1\n"
<< " s : render surface with smooth shading\n"
<< " f : render surface with flat shading\n"
<< "\n"
<< " +ypos\n"
<< " <i>\n"
<< " -xpos <j> <l> +xpos\n"
<< " <k>\n"
<< " -ypos\n"
<< "\n"
<< " -zpos <n> <m> +zpos\n"
<< endl;
}
// checkArgs() checks the command line arguments and assigns all variables
// not listed on command to zero.
int checkArgs(int myargc, char **myargv) {
int i;
for (i = 1; i < myargc; i += 2) {
if (!strcmp(myargv[i], "-keyboard")) {
keys();
exit(0);
}
if (i + 1 == myargc) return 0;
if (!strcmp(myargv[i], "-uDim"))
uMax = atof(myargv[i + 1]);
else if (!strcmp(myargv[i], "-vDim"))
vMax = atof(myargv[i + 1]);
else if (!strcmp(myargv[i], "-xpos"))
xpos = atof(myargv[i + 1]);
else if (!strcmp(myargv[i], "-ypos"))
ypos = atof(myargv[i + 1]);
else if (!strcmp(myargv[i], "-zpos"))
zpos = atof(myargv[i + 1]);
else if (!strcmp(myargv[i], "-r"))
r = atof(myargv[i + 1]);
else if (!strcmp(myargv[i], "-file")) {
strcpy(filename, myargv[i + 1]);
filenameset = 1;
}
else if (!strcmp(myargv[i], "-h"))
return 0;
else if (!strcmp(myargv[i], "-help"))
return 0;
else
return 0;
}
return 1;
}
void printUsage() {
cout << "Usage - texture" << endl;
cout << " Description: Creates an interactive 3D viewer with a \n"
<< " texture applied to a sphere. The texture used\n"
<< " must be a ppm file in binary format. The file \n"
<< " to be used must also be specified at the command\n"
<< " line.\n"
<< endl;
cout
<< " -xpos: shape's x center position, default = 0.0\n"
<< " -ypos: shape's y center position, default = 0.0\n"
<< " -zpos: shape's z center position, default = 0.0\n"
<< " -uDim: number of points between that will be\n"
<< " computed for the u parameter, default = 20\n"
<< " -vDim: number of points between that will be\n"
<< " computed for the v parameter, default = 20\n"
<< " -r: minor radius of the sphere, default = 1.5\n"
<< " -file: file name of texture to be applied.\n"
<< " -keyboard: lists a message describing the functionality\n"
<< " of various keys\n"
<< endl;
}
int main(int argc, char **argv) {
if (!checkArgs(argc, argv)) {
printUsage();
exit(-1);
}
if (filenameset) {
keys();
readImage(filename);
glutInitWindowSize(300, 300);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutCreateWindow(argv[0]);
init();
glutReshapeFunc(reshape);
userInterfaceInit();
glutKeyboardFunc(mykeyboard);
glutDisplayFunc(display);
glutMainLoop();
} else {
cout << endl << "***********Must specifiy a file*************"
<< endl << endl;
printUsage();
}
return 0;
}