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
113 * Prints out error message when file cannot be read
\r
114 * @param fileName Name of file that could not be read
\r
116 void fileErr(char* fileName) {
\r
117 printf("Error reading file: %s\n", fileName);
\r
118 printf("If not in the CSSE labs, you will need to include the directory containing\n");
\r
119 printf("the models on the command line, or put it in the same folder as the exectutable.");
\r
120 exit(EXIT_FAILURE);
\r
124 * Reads .bmp texture files and converts them to a texture object
\r
125 * @param fileName .bmp texture file
\r
126 * @return texture object
\r
128 texture* loadTexture(char *fileName) {
\r
129 texture* t = malloc(sizeof (texture));
\r
132 t->rgbData = LoadDIBitmap(fileName, &info);
\r
133 t->height=info->bmiHeader.biHeight;
\r
134 t->width=info->bmiHeader.biWidth;
\r
140 * Reads .x files and converts them to a mesh object
\r
141 * @param fileName .x mesh file
\r
142 * @return mesh object
\r
144 mesh* loadMesh(char* fileName) {
\r
145 mesh* m = malloc(sizeof (mesh));
\r
146 FILE* fp = fopen(fileName, "r");
\r
147 char line[256] = "";
\r
148 int lineBuffSize = 256;
\r
150 if(fp == NULL) fileErr(fileName);
\r
152 while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 )
\r
153 fgets(line, lineBuffSize, fp);
\r
155 fscanf(fp, "%d;\n", &(m->nVertices));
\r
156 m->vertices = malloc(m->nVertices * sizeof(vertex));
\r
157 for(int i=0; i < m->nVertices; i++)
\r
158 fscanf(fp, "%f; %f; %f;%*[,;]\n", &(m->vertices[i][0]), &(m->vertices[i][1]), &(m->vertices[i][2]) );
\r
160 fscanf(fp, "%d;\n", &(m->nTriangles));
\r
161 m->triangles = malloc(m->nTriangles * sizeof(triangle));
\r
162 for(int i=0; i < m->nTriangles; i++)
\r
163 fscanf(fp, "%*d; %d, %d, %d;%*[;,]", m->triangles[i], m->triangles[i]+1, m->triangles[i]+2);
\r
165 while(strcmp(line," MeshNormals {\r\n") != 0 && strcmp(line," MeshNormals {\n") != 0)
\r
166 fgets(line, lineBuffSize, fp);
\r
168 fgets(line, lineBuffSize, fp);
\r
169 m->normals = malloc(m->nVertices * sizeof(normal));
\r
170 for(int i=0; i < m->nVertices; i++)
\r
171 fscanf(fp, "%f; %f; %f;%*[;,]\n",
\r
172 &(m->normals[i][0]), &(m->normals[i][1]), &(m->normals[i][2]));
\r
174 while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0)
\r
175 fgets(line, lineBuffSize, fp);
\r
177 fgets(line, lineBuffSize, fp);
\r
178 m->texCoords = malloc(m->nVertices * sizeof(texCoord));
\r
179 for(int i=0; i < m->nVertices; i++)
\r
180 fscanf(fp, "%f;%f;%*[,;]\n", &(m->texCoords[i][0]), &(m->texCoords[i][1]) );
\r
186 // [You may want to add to this function.]
\r
188 * Loads mesh[i] if it isn't already loaded.
\r
189 * You must call getMesh(i) at least once before using mesh[i].
\r
193 void getMesh(int i) { // getMesh(i) loads mesh[i] if it isn't already loaded.
\r
194 char fileName[220];
\r
195 if(i>=NMESH || i<0) {
\r
196 printf("Error in getMesh - wrong model number");
\r
199 if(meshes[i] != NULL)
\r
201 sprintf(fileName, "%s/model%d.x", dataDir, i+1);
\r
202 meshes[i] = loadMesh(fileName);
\r
206 * Loads texture i if it isn't already loaded
\r
208 * After calling getTexture(i), you can make texture i the current texture using
\r
209 * glBindTexture(GL_TEXTURE_2D, i);
\r
210 * Use i=0 to return to the default plain texture.
\r
212 * You can then scale the texture via:
\r
213 * glMatrixMode(GL_TEXTURE);
\r
214 * See the textbook, section 8.8.3.
\r
216 * You must call getTexture(i) at least once before using texture i.
\r
217 * @param i Texture ID
\r
219 void getTexture(int i) {
\r
220 char fileName[220];
\r
221 if(i<1 || i>NTEXTURE) {
\r
222 printf("Error in getTexture - wrong texture number");
\r
225 if(textures[i-1] != NULL)
\r
227 sprintf(fileName, "%s/texture%d.bmp", dataDir, i);
\r
229 textures[i-1] = loadTexture(fileName);
\r
231 glBindTexture(GL_TEXTURE_2D, i);
\r
233 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textures[i-1]->width, textures[i-1]->height,
\r
234 0, GL_RGB, GL_UNSIGNED_BYTE, textures[i-1]->rgbData);
\r
235 gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, textures[i-1]->width, textures[i-1]->height, GL_RGB,
\r
236 GL_UNSIGNED_BYTE, textures[i-1]->rgbData);
\r
237 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
\r
238 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
\r
239 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
\r
240 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
\r
242 glBindTexture(GL_TEXTURE_2D, 0); // Back to default texture
\r
246 * Event hander for main menu events
\r
247 * @param id ID of menu item selected
\r
249 void processMainEvents(int id) {
\r
251 case ROTATE_MOVE_CAMERA:
\r
255 case POSITION_SCALE:
\r
259 case ROTATION_TEXTURE_SCALE:
\r
264 exit(EXIT_SUCCESS);
\r
270 * Event hander for materials menu events
\r
271 * @param id ID of menu item selected
\r
273 void processMaterialEvents(int id) {
\r
275 case MATERIAL_ALL_RGB:
\r
279 case MATERIAL_AMBIENT_RGB:
\r
283 case MATERIAL_DIFFUSE_RGB:
\r
287 case MATERIAL_SPECULAR_RGB:
\r
291 case MATERIAL_ALL_ADSS:
\r
295 case MATERIAL_RED_ADSS:
\r
299 case MATERIAL_GREEN_ADSS:
\r
303 case MATERIAL_BLUE_ADSS:
\r
311 * Event hander for light menu events
\r
312 * @param id ID of menu item selected
\r
314 void processLightEvents(int id) {
\r
316 case LIGHT_MOVE_LIGHT_1:
\r
320 case LIGHT_RGBALL_LIGHT_1:
\r
324 case LIGHT_MOVE_LIGHT_2:
\r
328 case LIGHT_RGBALL_LIGHT_2:
\r
336 * Event hander for object menu events
\r
337 * @param id ID of object selected
\r
339 void processObjectEvents(int id) {
\r
344 * Event hander for texture menu events
\r
345 * @param id ID of texutre selected
\r
347 void processTextureEvents(int id) {
\r
352 * Event hander for ground texture menu events
\r
353 * @param id ID of ground texture selected
\r
355 void processGTextureEvents(int id) {
\r
360 * Rounds up numbers, from http://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
\r
361 * @param numToRound Number to round
\r
362 * @param multiple Multiple to round up to
\r
363 * @return Rounded number
\r
365 int roundUp(int numToRound, int multiple) {
\r
366 if(multiple == 0) {
\r
370 int remainder = numToRound % multiple;
\r
371 if (remainder == 0)
\r
373 return numToRound + multiple - remainder;
\r
377 * Makes a submenu from an array of items, splitting the list into subsubmenus
\r
378 * of only 10 items.
\r
379 * @param menuEntries Array of menu items
\r
380 * @param menuEntriesSize Size of menuEntries
\r
381 * @param callback Callback function for this array of menu items
\r
382 * @return Reference to menu created
\r
384 int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) {
\r
385 if ( menuEntriesSize == 0 ) return -1;
\r
387 int menuNumber = roundUp(menuEntriesSize, 10) / 10;
\r
388 int submenuObjects[menuNumber-1];
\r
390 for( int i = 0; i < menuNumber; i++ ) {
\r
391 submenuObjects[i] = glutCreateMenu(callback);
\r
392 int startNum = i*11 - (i-1);
\r
393 for ( int j = startNum - 1; j < (startNum+9); j++ ) {
\r
394 if ( j == menuEntriesSize ) break; // Detect if we've reached the end of the array
\r
395 glutAddMenuEntry( menuEntries[j], j + 1 );
\r
399 int mainMenu = glutCreateMenu(callback);
\r
400 for ( int i = 0; i < menuNumber; i++ ) {
\r
401 char name[10]; // buffer to hold name
\r
402 int startNum = i*11 - (i-1);
\r
403 int endNum = startNum + 9;
\r
404 if ( i == menuNumber - 1 ) { // We're on the last one
\r
405 endNum = startNum + (menuEntriesSize - startNum); // Work out final number
\r
407 sprintf(name, "%d-%d", startNum, endNum);
\r
408 glutAddSubMenu( name, submenuObjects[i] );
\r
415 * Creates menu for program
\r
418 // Construct material menu
\r
419 int materialMenu = glutCreateMenu(processMaterialEvents);
\r
420 glutAddMenuEntry("All R/G/B", MATERIAL_ALL_RGB);
\r
421 glutAddMenuEntry("Ambient R/G/B", MATERIAL_AMBIENT_RGB);
\r
422 glutAddMenuEntry("Diffuse R/G/B", MATERIAL_DIFFUSE_RGB);
\r
423 glutAddMenuEntry("Specular R/G/B", MATERIAL_SPECULAR_RGB);
\r
424 glutAddMenuEntry("All Amb/Diff/Spec/Shine", MATERIAL_ALL_ADSS);
\r
425 glutAddMenuEntry("Red Amb/Diff/Spec/Shine", MATERIAL_RED_ADSS);
\r
426 glutAddMenuEntry("Green Amb/Diff/Spec/Shine", MATERIAL_GREEN_ADSS);
\r
427 glutAddMenuEntry("Blue Amb/Diff/Spec/Shine", MATERIAL_BLUE_ADSS);
\r
429 // Construct light menu
\r
430 int lightMenu = glutCreateMenu(processLightEvents);
\r
431 glutAddMenuEntry("Move Light 1", LIGHT_MOVE_LIGHT_1);
\r
432 glutAddMenuEntry("R/G/B/All Light 1", LIGHT_RGBALL_LIGHT_1);
\r
433 glutAddMenuEntry("Move Light 2", LIGHT_MOVE_LIGHT_2);
\r
434 glutAddMenuEntry("R/G/B/All Light 2", LIGHT_RGBALL_LIGHT_2);
\r
436 // Construct object menu
\r
437 int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]);
\r
438 int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents );
\r
440 // Construct texture / ground texture menus
\r
441 int textureMenuEntriesSize = sizeof(textureMenuEntries) / sizeof(textureMenuEntries[0]);
\r
442 int textureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processTextureEvents );
\r
443 int gTextureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processGTextureEvents );
\r
445 // Construct main menu
\r
446 glutCreateMenu(processMainEvents);
\r
447 //glutAddMenuEntry("Rotate/Move Camera", ROTATE_MOVE_CAMERA);
\r
448 //glutAddSubMenu("Add object", objectMenu);
\r
449 //glutAddMenuEntry("Position/Scale", POSITION_SCALE);
\r
450 //glutAddMenuEntry("Rotation/Texture Scale", ROTATION_TEXTURE_SCALE);
\r
451 //glutAddSubMenu("Material", materialMenu);
\r
452 //glutAddSubMenu("Texture", textureMenu);
\r
453 //glutAddSubMenu("Ground texture", gTextureMenu);
\r
454 //glutAddSubMenu("Lights", lightMenu);
\r
455 glutAddMenuEntry("Exit", EXIT);
\r
457 // Bind to right mouse button
\r
458 glutAttachMenu(GLUT_RIGHT_BUTTON);
\r
462 * Called when window is resized
\r
463 * @param width New width
\r
464 * @param height New height
\r
466 void windowReshape(int width, int height) {
\r
467 glViewport(0, 0, (GLsizei)width, (GLsizei)height);
\r
468 printf("Width: %d, height: %d\n", width, height);
\r
469 glMatrixMode(GL_PROJECTION);
\r
471 gluPerspective(60 * height % 360, (GLfloat)width / (GLfloat)height, 0.1, 1000.0);
\r
472 glMatrixMode(GL_MODELVIEW);
\r
476 * Called when mouse event occurs
\r
477 * @param btn Mouse button
\r
478 * @param state State of mouse button
\r
479 * @param x Mouse x position
\r
480 * @param y Mouse y position
\r
482 void mouse(int btn, int state, int x, int y) {
\r
490 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
\r
493 glTranslatef(0.0f,0.0f,-10.0f); // Move into the Screen 10.0
\r
495 glutSolidTeapot(1);
\r
502 * @param argc Number of arguments
\r
503 * @param argv Array of arguments
\r
504 * @return Program exit code
\r
506 int main(int argc, char **argv) {
\r
509 strcpy(dataDir, argv[1]);
\r
510 else if(opendir(dirDefault1))
\r
511 strcpy(dataDir, dirDefault1);
\r
512 else if(opendir(dirDefault2))
\r
513 strcpy(dataDir, dirDefault2);
\r
514 else fileErr(dirDefault1);
\r
516 for(int i=0; i<NMESH; i++) meshes[i]=NULL;
\r
517 for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;
\r
519 glutInit(&argc, argv);
\r
521 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
\r
522 glutInitWindowSize(500, 500);
\r
523 glutCreateWindow("Scene Editor");
\r
525 glShadeModel(GL_SMOOTH); // Enables Smooth Shading
\r
526 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
\r
527 glClearDepth(1.0f); // Depth Buffer Setup
\r
528 glEnable(GL_DEPTH_TEST); // Enables Depth Testing
\r
529 glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
\r
531 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
\r
533 glutReshapeFunc(windowReshape);
\r
534 glutDisplayFunc(display);
\r
535 glutMouseFunc(mouse);
\r