2 * CITS2231 Graphics Scene Editor
\r
3 * @author Ashley Tyndall (20915779)
\r
12 #include <GL/glut.h>
\r
16 // Type definitions for vertex-coordinates, normals, texture-coordinates,
\r
17 // and triangles (via the indices of 3 vertices).
\r
18 typedef GLfloat vertex[3];
\r
19 typedef GLfloat normal[3];
\r
20 typedef GLfloat texCoord[2];
\r
21 typedef GLint vertexIndex;
\r
22 typedef vertexIndex triangle[3];
\r
24 // A type for a mesh
\r
26 int nVertices; // The number of vertices in the mesh
\r
27 vertex* vertices; // Array with coordinates of vertices
\r
28 normal* normals; // Array with normals of vertices
\r
29 texCoord* texCoords; // Array with texture-coordinates of vertices
\r
30 int nTriangles; // The number of triangles in the mesh
\r
31 triangle* triangles; // Array of trangles via 3 indices into "vertices"
\r
34 #define NMESH 54 // The number of meshes (in the models-textures dir)
\r
35 mesh* meshes[NMESH]; // An array of pointers to the meshes - see getMesh
\r
37 // A type for a 2D texture, with height and width in pixels
\r
41 GLubyte *rgbData; // Array of bytes with the colour data for the texture
\r
44 #define NTEXTURE 30 // The number of textures (in the models-textures dir)
\r
45 texture* textures[NTEXTURE]; // An array of texture pointers - see getTexture
\r
48 // You'll need to add scale, rotation, material, mesh number, etc.,
\r
49 // to this structure
\r
58 ROTATION_TEXTURE_SCALE,
\r
63 MATERIAL_AMBIENT_RGB,
\r
64 MATERIAL_DIFFUSE_RGB,
\r
65 MATERIAL_SPECULAR_RGB,
\r
68 MATERIAL_GREEN_ADSS,
\r
73 LIGHT_RGBALL_LIGHT_1,
\r
75 LIGHT_RGBALL_LIGHT_2
\r
79 const char *textureMenuEntries[NTEXTURE] = {
\r
80 "1 Plain", "2 Rust", "3 Concrete", "4 Carpet", "5 Beach Sand",
\r
81 "6 Rocky", "7 Brick", "8 Water", "9 Paper", "10 Marble",
\r
82 "11 Wood", "12 Scales", "13 Fur", "14 Denim", "15 Hessian",
\r
83 "16 Orange Peel", "17 Ice Crystals", "18 Grass", "19 Corrugated Iron", "20 Styrofoam",
\r
84 "21 Bubble Wrap", "22 Leather", "23 Camouflage", "24 Asphalt", "25 Scratched Ice",
\r
85 "26 Rattan", "27 Snow", "28 Dry Mud", "29 Old Concrete", "30 Leopard Skin"
\r
88 const char *objectMenuEntries[NMESH] = {
\r
89 "1 Thin Dinosaur","2 Big Dog","3 Saddle Dinosaur", "4 Dragon", "5 Cleopatra",
\r
90 "6 Bone I", "7 Bone II", "8 Rabbit", "9 Long Dragon", "10 Buddha",
\r
91 "11 Sitting Rabbit", "12 Frog", "13 Cow", "14 Monster", "15 Sea Horse",
\r
92 "16 Head", "17 Pelican", "18 Horse", "19 Kneeling Angel", "20 Porsche I",
\r
93 "21 Truck", "22 Statue of Liberty", "23 Sitting Angel", "24 Metal Part", "25 Car",
\r
94 "26 Apatosaurus", "27 Airliner", "28 Motorbike", "29 Dolphin", "30 Spaceman",
\r
95 "31 Winnie the Pooh", "32 Shark", "33 Crocodile", "34 Toddler", "35 Fat Dinosaur",
\r
96 "36 Chihuahua", "37 Sabre-toothed Tiger", "38 Lioness", "39 Fish", "40 Horse (head down)",
\r
97 "41 Horse (head up)", "42 Skull", "43 Fighter Jet I", "44 Toad", "45 Convertible",
\r
98 "46 Porsche II", "47 Hare", "48 Vintage Car", "49 Fighter Jet II", "50 Winged Monkey",
\r
99 "51 Chef", "52 Parasaurolophus", "53 Rooster", "54 T-rex"
\r
102 #define MAXOBJECTS 256
\r
103 SceneObject sceneObjs[MAXOBJECTS]; // An array with details of the objects in a scene
\r
104 int nObjects=0; // How many objects there are in the scene currently.
\r
106 // Directories containing models
\r
107 char *dirDefault1 = "models-textures";
\r
108 char *dirDefault2 = "/cslinux/examples/CITS2231/project-files/models-textures";
\r
110 char dataDir[200]; // Stores the directory name for the meshes and textures.
\r
112 static GLfloat floorVertices[4][3] = {
\r
113 { -20.0, 0.0, 20.0 },
\r
114 { 20.0, 0.0, 20.0 },
\r
115 { 20.0, 0.0, -20.0 },
\r
116 { -20.0, 0.0, -20.0 },
\r
119 static GLfloat lightColor[] = {1.0, 1.0, 1.0, 1.0}; // White light
\r
120 static GLfloat lightPosition[4];
\r
121 static float lightAngle = 10.0, lightHeight = 20;
\r
124 * Prints out error message when file cannot be read
\r
125 * @param fileName Name of file that could not be read
\r
127 void fileErr(char* fileName) {
\r
128 printf("Error reading file: %s\n", fileName);
\r
129 printf("If not in the CSSE labs, you will need to include the directory containing\n");
\r
130 printf("the models on the command line, or put it in the same folder as the exectutable.");
\r
131 exit(EXIT_FAILURE);
\r
135 * Reads .bmp texture files and converts them to a texture object
\r
136 * @param fileName .bmp texture file
\r
137 * @return texture object
\r
139 texture* loadTexture(char *fileName) {
\r
140 texture* t = malloc(sizeof (texture));
\r
143 t->rgbData = LoadDIBitmap(fileName, &info);
\r
144 t->height=info->bmiHeader.biHeight;
\r
145 t->width=info->bmiHeader.biWidth;
\r
151 * Reads .x files and converts them to a mesh object
\r
152 * @param fileName .x mesh file
\r
153 * @return mesh object
\r
155 mesh* loadMesh(char* fileName) {
\r
156 mesh* m = malloc(sizeof (mesh));
\r
157 FILE* fp = fopen(fileName, "r");
\r
158 char line[256] = "";
\r
159 int lineBuffSize = 256;
\r
161 if(fp == NULL) fileErr(fileName);
\r
163 while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 )
\r
164 fgets(line, lineBuffSize, fp);
\r
166 fscanf(fp, "%d;\n", &(m->nVertices));
\r
167 m->vertices = malloc(m->nVertices * sizeof(vertex));
\r
168 for(int i=0; i < m->nVertices; i++)
\r
169 fscanf(fp, "%f; %f; %f;%*[,;]\n", &(m->vertices[i][0]), &(m->vertices[i][1]), &(m->vertices[i][2]) );
\r
171 fscanf(fp, "%d;\n", &(m->nTriangles));
\r
172 m->triangles = malloc(m->nTriangles * sizeof(triangle));
\r
173 for(int i=0; i < m->nTriangles; i++)
\r
174 fscanf(fp, "%*d; %d, %d, %d;%*[;,]", m->triangles[i], m->triangles[i]+1, m->triangles[i]+2);
\r
176 while(strcmp(line," MeshNormals {\r\n") != 0 && strcmp(line," MeshNormals {\n") != 0)
\r
177 fgets(line, lineBuffSize, fp);
\r
179 fgets(line, lineBuffSize, fp);
\r
180 m->normals = malloc(m->nVertices * sizeof(normal));
\r
181 for(int i=0; i < m->nVertices; i++)
\r
182 fscanf(fp, "%f; %f; %f;%*[;,]\n",
\r
183 &(m->normals[i][0]), &(m->normals[i][1]), &(m->normals[i][2]));
\r
185 while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0)
\r
186 fgets(line, lineBuffSize, fp);
\r
188 fgets(line, lineBuffSize, fp);
\r
189 m->texCoords = malloc(m->nVertices * sizeof(texCoord));
\r
190 for(int i=0; i < m->nVertices; i++)
\r
191 fscanf(fp, "%f;%f;%*[,;]\n", &(m->texCoords[i][0]), &(m->texCoords[i][1]) );
\r
197 // [You may want to add to this function.]
\r
199 * Loads mesh[i] if it isn't already loaded.
\r
200 * You must call getMesh(i) at least once before using mesh[i].
\r
204 void getMesh(int i) { // getMesh(i) loads mesh[i] if it isn't already loaded.
\r
205 char fileName[220];
\r
206 if(i>=NMESH || i<0) {
\r
207 printf("Error in getMesh - wrong model number");
\r
210 if(meshes[i] != NULL)
\r
212 sprintf(fileName, "%s/model%d.x", dataDir, i+1);
\r
213 meshes[i] = loadMesh(fileName);
\r
217 * Loads texture i if it isn't already loaded
\r
219 * After calling getTexture(i), you can make texture i the current texture using
\r
220 * glBindTexture(GL_TEXTURE_2D, i);
\r
221 * Use i=0 to return to the default plain texture.
\r
223 * You can then scale the texture via:
\r
224 * glMatrixMode(GL_TEXTURE);
\r
225 * See the textbook, section 8.8.3.
\r
227 * You must call getTexture(i) at least once before using texture i.
\r
228 * @param i Texture ID
\r
230 void getTexture(int i) {
\r
231 char fileName[220];
\r
232 if(i<1 || i>NTEXTURE) {
\r
233 printf("Error in getTexture - wrong texture number");
\r
236 if(textures[i-1] != NULL)
\r
238 sprintf(fileName, "%s/texture%d.bmp", dataDir, i);
\r
240 textures[i-1] = loadTexture(fileName);
\r
242 glBindTexture(GL_TEXTURE_2D, i);
\r
244 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textures[i-1]->width, textures[i-1]->height,
\r
245 0, GL_RGB, GL_UNSIGNED_BYTE, textures[i-1]->rgbData);
\r
246 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, textures[i-1]->width, textures[i-1]->height, GL_RGB,
\r
247 GL_UNSIGNED_BYTE, textures[i-1]->rgbData);
\r
248 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
\r
249 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
\r
250 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
\r
251 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
\r
253 glBindTexture(GL_TEXTURE_2D, 0); // Back to default texture
\r
257 * Event hander for main menu events
\r
258 * @param id ID of menu item selected
\r
260 void processMainEvents(int id) {
\r
262 case ROTATE_MOVE_CAMERA:
\r
266 case POSITION_SCALE:
\r
270 case ROTATION_TEXTURE_SCALE:
\r
275 exit(EXIT_SUCCESS);
\r
281 * Event hander for materials menu events
\r
282 * @param id ID of menu item selected
\r
284 void processMaterialEvents(int id) {
\r
286 case MATERIAL_ALL_RGB:
\r
290 case MATERIAL_AMBIENT_RGB:
\r
294 case MATERIAL_DIFFUSE_RGB:
\r
298 case MATERIAL_SPECULAR_RGB:
\r
302 case MATERIAL_ALL_ADSS:
\r
306 case MATERIAL_RED_ADSS:
\r
310 case MATERIAL_GREEN_ADSS:
\r
314 case MATERIAL_BLUE_ADSS:
\r
322 * Event hander for light menu events
\r
323 * @param id ID of menu item selected
\r
325 void processLightEvents(int id) {
\r
327 case LIGHT_MOVE_LIGHT_1:
\r
331 case LIGHT_RGBALL_LIGHT_1:
\r
335 case LIGHT_MOVE_LIGHT_2:
\r
339 case LIGHT_RGBALL_LIGHT_2:
\r
347 * Event hander for object menu events
\r
348 * @param id ID of object selected
\r
350 void processObjectEvents(int id) {
\r
355 * Event hander for texture menu events
\r
356 * @param id ID of texutre selected
\r
358 void processTextureEvents(int id) {
\r
363 * Event hander for ground texture menu events
\r
364 * @param id ID of ground texture selected
\r
366 void processGTextureEvents(int id) {
\r
371 * Rounds up numbers, from http://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
\r
372 * @param numToRound Number to round
\r
373 * @param multiple Multiple to round up to
\r
374 * @return Rounded number
\r
376 int roundUp(int numToRound, int multiple) {
\r
377 if(multiple == 0) {
\r
381 int remainder = numToRound % multiple;
\r
382 if (remainder == 0)
\r
384 return numToRound + multiple - remainder;
\r
388 * Makes a submenu from an array of items, splitting the list into subsubmenus
\r
389 * of only 10 items.
\r
390 * @param menuEntries Array of menu items
\r
391 * @param menuEntriesSize Size of menuEntries
\r
392 * @param callback Callback function for this array of menu items
\r
393 * @return Reference to menu created
\r
395 int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) {
\r
396 if ( menuEntriesSize == 0 ) return -1;
\r
398 int menuNumber = roundUp(menuEntriesSize, 10) / 10;
\r
399 int submenuObjects[menuNumber-1];
\r
401 for( int i = 0; i < menuNumber; i++ ) {
\r
402 submenuObjects[i] = glutCreateMenu(callback);
\r
403 int startNum = i*11 - (i-1);
\r
404 for ( int j = startNum - 1; j < (startNum+9); j++ ) {
\r
405 if ( j == menuEntriesSize ) break; // Detect if we've reached the end of the array
\r
406 glutAddMenuEntry( menuEntries[j], j + 1 );
\r
410 int mainMenu = glutCreateMenu(callback);
\r
411 for ( int i = 0; i < menuNumber; i++ ) {
\r
412 char name[10]; // buffer to hold name
\r
413 int startNum = i*11 - (i-1);
\r
414 int endNum = startNum + 9;
\r
415 if ( i == menuNumber - 1 ) { // We're on the last one
\r
416 endNum = startNum + (menuEntriesSize - startNum); // Work out final number
\r
418 sprintf(name, "%d-%d", startNum, endNum);
\r
419 glutAddSubMenu( name, submenuObjects[i] );
\r
426 * Creates menu for program
\r
429 // Construct material menu
\r
430 int materialMenu = glutCreateMenu(processMaterialEvents);
\r
431 glutAddMenuEntry("All R/G/B", MATERIAL_ALL_RGB);
\r
432 glutAddMenuEntry("Ambient R/G/B", MATERIAL_AMBIENT_RGB);
\r
433 glutAddMenuEntry("Diffuse R/G/B", MATERIAL_DIFFUSE_RGB);
\r
434 glutAddMenuEntry("Specular R/G/B", MATERIAL_SPECULAR_RGB);
\r
435 glutAddMenuEntry("All Amb/Diff/Spec/Shine", MATERIAL_ALL_ADSS);
\r
436 glutAddMenuEntry("Red Amb/Diff/Spec/Shine", MATERIAL_RED_ADSS);
\r
437 glutAddMenuEntry("Green Amb/Diff/Spec/Shine", MATERIAL_GREEN_ADSS);
\r
438 glutAddMenuEntry("Blue Amb/Diff/Spec/Shine", MATERIAL_BLUE_ADSS);
\r
440 // Construct light menu
\r
441 int lightMenu = glutCreateMenu(processLightEvents);
\r
442 glutAddMenuEntry("Move Light 1", LIGHT_MOVE_LIGHT_1);
\r
443 glutAddMenuEntry("R/G/B/All Light 1", LIGHT_RGBALL_LIGHT_1);
\r
444 glutAddMenuEntry("Move Light 2", LIGHT_MOVE_LIGHT_2);
\r
445 glutAddMenuEntry("R/G/B/All Light 2", LIGHT_RGBALL_LIGHT_2);
\r
447 // Construct object menu
\r
448 int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]);
\r
449 int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents );
\r
451 // Construct texture / ground texture menus
\r
452 int textureMenuEntriesSize = sizeof(textureMenuEntries) / sizeof(textureMenuEntries[0]);
\r
453 int textureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processTextureEvents );
\r
454 int gTextureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processGTextureEvents );
\r
456 // Construct main menu
\r
457 glutCreateMenu(processMainEvents);
\r
458 //glutAddMenuEntry("Rotate/Move Camera", ROTATE_MOVE_CAMERA);
\r
459 //glutAddSubMenu("Add object", objectMenu);
\r
460 //glutAddMenuEntry("Position/Scale", POSITION_SCALE);
\r
461 //glutAddMenuEntry("Rotation/Texture Scale", ROTATION_TEXTURE_SCALE);
\r
462 //glutAddSubMenu("Material", materialMenu);
\r
463 //glutAddSubMenu("Texture", textureMenu);
\r
464 //glutAddSubMenu("Ground texture", gTextureMenu);
\r
465 //glutAddSubMenu("Lights", lightMenu);
\r
466 glutAddMenuEntry("Exit", EXIT);
\r
468 // Bind to right mouse button
\r
469 glutAttachMenu(GLUT_RIGHT_BUTTON);
\r
473 * Called when window is resized
\r
474 * @param w New width
\r
475 * @param h New height
\r
477 void windowReshape(int w, int h) {
\r
478 GLdouble near = -10.0;
\r
479 GLdouble far = 10.0;
\r
481 glViewport(0, 0, (GLsizei) w, (GLsizei) h);
\r
482 glMatrixMode(GL_PROJECTION);
\r
485 glOrtho(near, far, near*(GLfloat)h/(GLfloat)w,
\r
486 far*(GLfloat)h/(GLfloat)w, near, far);
\r
488 glOrtho(near*(GLfloat)w/(GLfloat)h,
\r
489 far*(GLfloat)w/(GLfloat)h, near, far, near, far);
\r
490 glMatrixMode(GL_MODELVIEW);
\r
495 * Called when mouse event occurs
\r
496 * @param btn Mouse button
\r
497 * @param state State of mouse button
\r
498 * @param x Mouse x position
\r
499 * @param y Mouse y position
\r
501 void mouse(int btn, int state, int x, int y) {
\r
509 glDisable(GL_LIGHTING);
\r
511 //if (useTexture) {
\r
512 // glEnable(GL_TEXTURE_2D);
\r
516 glTexCoord2f(0.0, 0.0);
\r
517 glVertex3fv(floorVertices[0]);
\r
518 glTexCoord2f(0.0, 16.0);
\r
519 glVertex3fv(floorVertices[1]);
\r
520 glTexCoord2f(16.0, 16.0);
\r
521 glVertex3fv(floorVertices[2]);
\r
522 glTexCoord2f(16.0, 0.0);
\r
523 glVertex3fv(floorVertices[3]);
\r
526 /*if (useTexture) {
\r
527 glDisable(GL_TEXTURE_2D);
\r
530 glEnable(GL_LIGHTING);
\r
537 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
\r
540 0.0, 8.0, 60.0, /* eye is at (0,8,60) */
\r
541 0.0, 8.0, 0.0, /* center is at (0,8,0) */
\r
542 0.0, 1.0, 0.0 /* up is in postivie Y direction */
\r
545 /* Reposition the light source. */
\r
546 lightPosition[0] = 12*cos(lightAngle);
\r
547 lightPosition[1] = lightHeight;
\r
548 lightPosition[2] = 12*sin(lightAngle);
\r
549 lightPosition[3] = 0.0;
\r
552 glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
\r
554 glEnable(GL_BLEND);
\r
555 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
\r
557 glDisable(GL_BLEND);
\r
560 glDisable(GL_LIGHTING);
\r
562 glColor3f(1.0, 0.0, 1.0);
\r
563 glTranslatef(0.0, 8.0, 0.0);
\r
565 glutSolidTeapot(1); // Draw teapot for test
\r
567 glEnable(GL_LIGHTING);
\r
571 glDisable(GL_LIGHTING);
\r
572 glColor3f(1.0, 1.0, 1.0);
\r
574 /* Draw a yellow ball at the light source. */
\r
575 glTranslatef(lightPosition[0], lightPosition[1], lightPosition[2]);
\r
576 glutSolidSphere(1.0, 5, 5);
\r
578 glEnable(GL_LIGHTING);
\r
587 * init function; sets initial OpenGL state
\r
590 glMatrixMode(GL_PROJECTION);
\r
593 60.0, /* field of view in degree */
\r
594 1.0, /* aspect ratio */
\r
599 glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
\r
600 glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
\r
601 glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.1);
\r
602 glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.05);
\r
603 glEnable(GL_LIGHT0);
\r
604 glEnable(GL_LIGHTING);
\r
606 glMatrixMode(GL_MODELVIEW);
\r
611 * @param argc Number of arguments
\r
612 * @param argv Array of arguments
\r
613 * @return Program exit code
\r
615 int main(int argc, char **argv) {
\r
617 strcpy(dataDir, argv[1]);
\r
618 else if(opendir(dirDefault1))
\r
619 strcpy(dataDir, dirDefault1);
\r
620 else if(opendir(dirDefault2))
\r
621 strcpy(dataDir, dirDefault2);
\r
622 else fileErr(dirDefault1);
\r
624 for(int i=0; i<NMESH; i++) meshes[i]=NULL;
\r
625 for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;
\r
627 glutInit(&argc, argv);
\r
629 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
\r
631 glutInitWindowSize(500, 500);
\r
632 glutCreateWindow("Scene Editor");
\r
634 glShadeModel(GL_SMOOTH); // Enables Smooth Shading
\r
635 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
\r
636 glClearDepth(1.0f); // Depth Buffer Setup
\r
637 glEnable(GL_DEPTH_TEST); // Enables Depth Testing
\r
638 glDepthFunc(GL_LEQUAL); // the type
\r
639 glEnable(GL_CULL_FACE);
\r
640 glEnable(GL_TEXTURE_2D);
\r
643 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
\r
645 glutReshapeFunc(windowReshape);
\r
646 glutDisplayFunc(display);
\r
647 glutMouseFunc(mouse);
\r