(no commit message)
[atyndall/cits2231.git] / scene.c
1 // compile this program using:\r
2 //   gcc -O3 -Wall -std=c99 -o scene scene.c bitmap.c -lglut\r
3 // Or, with cygwin:        (-mno-cygwin is only so the executable runs from windows)\r
4 //   gcc -mno-cygwin -O3 -Wall -std=c99 -o scene scene.c bitmap.c -lglut32 -lglu32 -lopengl32\r
5 // Or, use make via the supplied Makefile:     (For cygwin, install the package for make)\r
6 //   make\r
7 \r
8 #include <stdlib.h>\r
9 #include <stdio.h>\r
10 #include <dirent.h>\r
11 #include <string.h>\r
12 #include <math.h>\r
13 #include <GL/gl.h>\r
14 #include <GL/glut.h>\r
15 \r
16 #include "bitmap.h"\r
17 \r
18 // Type definitions for vertex-coordinates, normals, texture-coordinates, \r
19 // and triangles (via the indices of 3 vertices).\r
20 typedef GLfloat vertex[3];\r
21 typedef GLfloat normal[3];\r
22 typedef GLfloat texCoord[2];\r
23 typedef GLint vertexIndex;\r
24 typedef vertexIndex triangle[3];\r
25 \r
26 // A type for a mesh\r
27 typedef struct {         \r
28     int nVertices;           //  The number of vertices in the mesh\r
29     vertex* vertices;        //  Array with coordinates of vertices\r
30     normal* normals;         //  Array with normals of vertices\r
31     texCoord* texCoords;     //  Array with texture-coordinates of vertices\r
32     int nTriangles;          //  The number of triangles in the mesh\r
33     triangle* triangles;     //  Array of trangles via 3 indices into "vertices"\r
34 } mesh;\r
35 \r
36 #define NMESH 54       // The number of meshes (in the models-textures dir)\r
37 mesh* meshes[NMESH];   // An array of pointers to the meshes - see getMesh\r
38 \r
39 // A type for a 2D texture, with height and width in pixels\r
40 typedef struct {\r
41     int height;\r
42     int width;\r
43     GLubyte *rgbData;   // Array of bytes with the colour data for the texture\r
44 } texture;\r
45 \r
46 #define NTEXTURE 30     // The number of textures (in the models-textures dir)\r
47 texture* textures[NTEXTURE];   // An array of texture pointers - see getTexture\r
48 \r
49 typedef struct {  \r
50     // You'll need to add scale, rotation, material, mesh number, etc.,\r
51     // to this structure\r
52     float x,y,z;\r
53 } SceneObject;\r
54 \r
55 // Menu enum\r
56 enum menu {\r
57   // Main menu\r
58   ROTATE_MOVE_CAMERA,\r
59   POSITION_SCALE,\r
60   ROTATION_TEXTURE_SCALE,\r
61   EXIT,\r
62 \r
63   // Material submenu\r
64   MATERIAL_ALL_RGB,\r
65   MATERIAL_AMBIENT_RGB,\r
66   MATERIAL_DIFFUSE_RGB,\r
67   MATERIAL_SPECULAR_RGB,\r
68   MATERIAL_ALL_ADSS,\r
69   MATERIAL_RED_ADSS,\r
70   MATERIAL_GREEN_ADSS,\r
71   MATERIAL_BLUE_ADSS,\r
72 \r
73   // Light submenu\r
74   LIGHT_MOVE_LIGHT_1,\r
75   LIGHT_RGBALL_LIGHT_1,\r
76   LIGHT_MOVE_LIGHT_2,\r
77   LIGHT_RGBALL_LIGHT_2\r
78 };\r
79 \r
80 // Menu arrays\r
81 const char *textureMenuEntries[NTEXTURE] = {\r
82   "1 Plain", "2 Rust", "3 Concrete", "4 Carpet", "5 Beach Sand",\r
83   "6 Rocky", "7 Brick", "8 Water", "9 Paper", "10 Marble",\r
84   "11 Wood", "12 Scales", "13 Fur", "14 Denim", "15 Hessian",\r
85   "16 Orange Peel", "17 Ice Crystals", "18 Grass", "19 Corrugated Iron", "20 Styrofoam",\r
86   "21 Bubble Wrap", "22 Leather", "23 Camouflage", "24 Asphalt", "25 Scratched Ice",\r
87   "26 Rattan", "27 Snow", "28 Dry Mud", "29 Old Concrete", "30 Leopard Skin"\r
88 };\r
89 \r
90 const char *objectMenuEntries[NMESH] = {\r
91   "1 Thin Dinosaur","2 Big Dog","3 Saddle Dinosaur", "4 Dragon", "5 Cleopatra",\r
92   "6 Bone I", "7 Bone II", "8 Rabbit", "9 Long Dragon", "10 Buddha",\r
93   "11 Sitting Rabbit", "12 Frog", "13 Cow", "14 Monster", "15 Sea Horse",\r
94   "16 Head", "17 Pelican", "18 Horse", "19 Kneeling Angel", "20 Porsche I",\r
95   "21 Truck", "22 Statue of Liberty", "23 Sitting Angel", "24 Metal Part", "25 Car",\r
96   "26 Apatosaurus", "27 Airliner", "28 Motorbike", "29 Dolphin", "30 Spaceman",\r
97   "31 Winnie the Pooh", "32 Shark", "33 Crocodile", "34 Toddler", "35 Fat Dinosaur",\r
98   "36 Chihuahua", "37 Sabre-toothed Tiger", "38 Lioness", "39 Fish", "40 Horse (head down)",\r
99   "41 Horse (head up)", "42 Skull", "43 Fighter Jet I", "44 Toad", "45 Convertible",\r
100   "46 Porsche II", "47 Hare", "48 Vintage Car", "49 Fighter Jet II", "50 Winged Monkey",\r
101   "51 Chef", "52 Parasaurolophus", "53 Rooster", "54 T-rex"\r
102 };\r
103 \r
104 #define MAXOBJECTS 256\r
105 SceneObject sceneObjs[MAXOBJECTS];  // An array with details of the objects in a scene\r
106 int nObjects=0;                     // How many objects there are in the scene currently.\r
107 \r
108 // Directories containing models\r
109 char *dirDefault1 = "models-textures";\r
110 char *dirDefault2 = "/cslinux/examples/CITS2231/project-files/models-textures";\r
111 \r
112 char dataDir[200];  // Stores the directory name for the meshes and textures.\r
113 \r
114 /**\r
115  * Prints out error message when file cannot be read\r
116  * @param fileName Name of file that could not be read\r
117  */\r
118 void fileErr(char* fileName) {\r
119     printf("Error reading file: %s\n", fileName);\r
120     printf("If not in the CSSE labs, you will need to include the directory containing\n");\r
121     printf("the models on the command line, or put it in the same folder as the exectutable.");\r
122     exit(EXIT_FAILURE);\r
123 }  \r
124 \r
125 /**\r
126  * Reads .bmp texture files and converts them to a texture object\r
127  * @param fileName .bmp texture file\r
128  * @return texture object\r
129  */\r
130 texture* loadTexture(char *fileName) {\r
131     texture* t = malloc(sizeof (texture));\r
132     BITMAPINFO *info;\r
133 \r
134     t->rgbData = LoadDIBitmap(fileName, &info);\r
135     t->height=info->bmiHeader.biHeight;\r
136     t->width=info->bmiHeader.biWidth;\r
137 \r
138     return t;\r
139 }\r
140 \r
141 /**\r
142  * Reads .x files and converts them to a mesh object\r
143  * @param fileName .x mesh file\r
144  * @return mesh object\r
145  */\r
146 mesh* loadMesh(char* fileName) {\r
147     mesh* m = malloc(sizeof (mesh));\r
148     FILE* fp = fopen(fileName, "r");\r
149     char line[256] = "";\r
150     int lineBuffSize = 256;\r
151 \r
152     if(fp == NULL) fileErr(fileName);\r
153 \r
154     while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 )\r
155         fgets(line, lineBuffSize, fp);\r
156 \r
157     fscanf(fp, "%d;\n", &(m->nVertices));\r
158     m->vertices = malloc(m->nVertices * sizeof(vertex));\r
159     for(int i=0; i < m->nVertices; i++)\r
160         fscanf(fp, "%f; %f; %f;%*[,;]\n", &(m->vertices[i][0]), &(m->vertices[i][1]), &(m->vertices[i][2]) );\r
161 \r
162     fscanf(fp, "%d;\n", &(m->nTriangles));\r
163     m->triangles = malloc(m->nTriangles * sizeof(triangle));\r
164     for(int i=0; i < m->nTriangles; i++)\r
165         fscanf(fp, "%*d; %d, %d, %d;%*[;,]", m->triangles[i], m->triangles[i]+1, m->triangles[i]+2);\r
166 \r
167     while(strcmp(line,"  MeshNormals {\r\n") != 0 && strcmp(line,"  MeshNormals {\n") != 0)\r
168         fgets(line, lineBuffSize, fp);\r
169 \r
170     fgets(line, lineBuffSize, fp);\r
171     m->normals = malloc(m->nVertices * sizeof(normal));\r
172     for(int i=0; i < m->nVertices; i++)\r
173         fscanf(fp, "%f; %f; %f;%*[;,]\n",\r
174                &(m->normals[i][0]), &(m->normals[i][1]), &(m->normals[i][2]));\r
175 \r
176     while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0)\r
177         fgets(line, lineBuffSize, fp);\r
178 \r
179     fgets(line, lineBuffSize, fp);\r
180     m->texCoords = malloc(m->nVertices * sizeof(texCoord));\r
181     for(int i=0; i < m->nVertices; i++)\r
182         fscanf(fp, "%f;%f;%*[,;]\n", &(m->texCoords[i][0]), &(m->texCoords[i][1]) );\r
183     fclose(fp);\r
184     \r
185     return m;\r
186 }\r
187 \r
188 // [You may want to add to this function.]\r
189 /**\r
190  * Loads mesh[i] if it isn't already loaded.\r
191  * You must call getMesh(i) at least once before using mesh[i].\r
192  *\r
193  * @param i Mesh ID\r
194  */\r
195 void getMesh(int i) { // getMesh(i) loads mesh[i] if it isn't already loaded.  \r
196     char fileName[220];\r
197     if(i>=NMESH || i<0) {\r
198         printf("Error in getMesh - wrong model number");\r
199         exit(1);\r
200     }\r
201     if(meshes[i] != NULL)\r
202         return;\r
203     sprintf(fileName, "%s/model%d.x", dataDir, i+1);\r
204     meshes[i] = loadMesh(fileName);\r
205 }\r
206 \r
207 /**\r
208  * Loads texture i if it isn't already loaded\r
209  *\r
210  * After calling getTexture(i), you can make texture i the current texture using\r
211  *      glBindTexture(GL_TEXTURE_2D, i);\r
212  * Use i=0 to return to the default plain texture.\r
213  *\r
214  * You can then scale the texture via:\r
215  *      glMatrixMode(GL_TEXTURE);\r
216  * See the textbook, section 8.8.3.\r
217  *\r
218  * You must call getTexture(i) at least once before using texture i.\r
219  * @param i Texture ID\r
220  */\r
221 void getTexture(int i) {\r
222     char fileName[220];\r
223     if(i<1 || i>NTEXTURE) {\r
224         printf("Error in getTexture - wrong texture number");\r
225         exit(1);\r
226     }\r
227     if(textures[i-1] != NULL)\r
228         return;\r
229     sprintf(fileName, "%s/texture%d.bmp", dataDir, i);\r
230 \r
231     textures[i-1] = loadTexture(fileName);\r
232 \r
233     glBindTexture(GL_TEXTURE_2D, i);\r
234 \r
235     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textures[i-1]->width, textures[i-1]->height,\r
236                  0, GL_RGB, GL_UNSIGNED_BYTE, textures[i-1]->rgbData);\r
237     gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, textures[i-1]->width, textures[i-1]->height, GL_RGB, \r
238                       GL_UNSIGNED_BYTE, textures[i-1]->rgbData);\r
239     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);\r
240     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);\r
241     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r
242     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);\r
243 \r
244     glBindTexture(GL_TEXTURE_2D, 0);  // Back to default texture\r
245 }\r
246 \r
247 /**\r
248  * Event hander for main menu events\r
249  * @param id ID of menu item selected\r
250  */\r
251 void processMainEvents(int id) {\r
252   switch (id) {\r
253     case ROTATE_MOVE_CAMERA:\r
254       // Do stuff\r
255       break;\r
256 \r
257     case POSITION_SCALE:\r
258       // Do stuff\r
259       break;\r
260 \r
261     case ROTATION_TEXTURE_SCALE:\r
262       // Do stuff\r
263       break;\r
264 \r
265     case EXIT:\r
266       exit(EXIT_SUCCESS);\r
267 \r
268   }\r
269 }\r
270 \r
271 /**\r
272  * Event hander for materials menu events\r
273  * @param id ID of menu item selected\r
274  */\r
275 void processMaterialEvents(int id) {\r
276   switch (id) {\r
277     case MATERIAL_ALL_RGB:\r
278       // Do stuff\r
279       break;\r
280 \r
281     case MATERIAL_AMBIENT_RGB:\r
282       // Do stuff\r
283       break;\r
284 \r
285     case MATERIAL_DIFFUSE_RGB:\r
286       // Do stuff\r
287       break;\r
288 \r
289     case MATERIAL_SPECULAR_RGB:\r
290       // Do stuff\r
291       break;\r
292 \r
293     case MATERIAL_ALL_ADSS:\r
294       // Do stuff\r
295       break;\r
296 \r
297     case MATERIAL_RED_ADSS:\r
298       // Do stuff\r
299       break;\r
300 \r
301     case MATERIAL_GREEN_ADSS:\r
302       // Do stuff\r
303       break;\r
304 \r
305     case MATERIAL_BLUE_ADSS:\r
306       // Do stuff\r
307       break;\r
308 \r
309   }\r
310 }\r
311 \r
312 /**\r
313  * Event hander for light menu events\r
314  * @param id ID of menu item selected\r
315  */\r
316 void processLightEvents(int id) {\r
317   switch (id) {\r
318     case LIGHT_MOVE_LIGHT_1:\r
319       // Do stuff\r
320       break;\r
321 \r
322     case LIGHT_RGBALL_LIGHT_1:\r
323       // Do stuff\r
324       break;\r
325 \r
326     case LIGHT_MOVE_LIGHT_2:\r
327       // Do stuff\r
328       break;\r
329 \r
330     case LIGHT_RGBALL_LIGHT_2:\r
331       // Do stuff\r
332       break;\r
333 \r
334   }\r
335 }\r
336 \r
337 /**\r
338  * Event hander for object menu events\r
339  * @param id ID of object selected\r
340  */\r
341 void processObjectEvents(int id) {\r
342 \r
343 }\r
344 \r
345 /**\r
346  * Event hander for texture menu events\r
347  * @param id ID of texutre selected\r
348  */\r
349 void processTextureEvents(int id) {\r
350 \r
351 }\r
352 \r
353 /**\r
354  * Event hander for ground texture menu events\r
355  * @param id ID of ground texture selected\r
356  */\r
357 void processGTextureEvents(int id) {\r
358 \r
359 }\r
360 \r
361 /**\r
362  * Rounds up numbers, from http://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number\r
363  * @param numToRound Number to round\r
364  * @param multiple Multiple to round up to\r
365  * @return Rounded number\r
366  */\r
367 int roundUp(int numToRound, int multiple) {\r
368   if(multiple == 0) {\r
369     return numToRound;\r
370   }\r
371 \r
372   int remainder = numToRound % multiple;\r
373   if (remainder == 0)\r
374     return numToRound;\r
375   return numToRound + multiple - remainder;\r
376 }\r
377 \r
378 /**\r
379  * Makes a submenu from an array of items, splitting the list into subsubmenus\r
380  * of only 10 items.\r
381  * @param menuEntries Array of menu items\r
382  * @param menuEntriesSize Size of menuEntries\r
383  * @param callback Callback function for this array of menu items\r
384  * @return Reference to menu created\r
385  */\r
386 int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) {\r
387   if ( menuEntriesSize == 0 ) return -1;\r
388 \r
389   int menuNumber = roundUp(menuEntriesSize, 10) / 10;\r
390   int submenuObjects[menuNumber-1];\r
391 \r
392   for( int i = 0; i < menuNumber; i++ ) {\r
393     submenuObjects[i] = glutCreateMenu(callback);\r
394     int startNum = i*11 - (i-1);\r
395     for ( int j = startNum - 1; j < (startNum+9); j++ ) {\r
396       if ( j == menuEntriesSize ) break; // Detect if we've reached the end of the array\r
397       glutAddMenuEntry( menuEntries[j], j + 1 );\r
398     }\r
399   } \r
400 \r
401   int mainMenu = glutCreateMenu(callback);\r
402   for ( int i = 0; i < menuNumber; i++ ) {\r
403     char name[10]; // buffer to hold name\r
404     int startNum = i*11 - (i-1);\r
405     int endNum = startNum + 9;\r
406     if ( i == menuNumber - 1 ) { // We're on the last one\r
407       endNum = startNum + (menuEntriesSize - startNum); // Work out final number\r
408     }\r
409     sprintf(name, "%d-%d", startNum, endNum);\r
410     glutAddSubMenu( name, submenuObjects[i] );\r
411   }\r
412 \r
413   return mainMenu;\r
414 }\r
415 \r
416 /**\r
417  * Creates menu for program\r
418  */\r
419 void makeMenu() {\r
420   // Construct material menu\r
421   int materialMenu = glutCreateMenu(processMaterialEvents);\r
422   glutAddMenuEntry("All R/G/B", MATERIAL_ALL_RGB);\r
423   glutAddMenuEntry("Ambient R/G/B", MATERIAL_AMBIENT_RGB);\r
424   glutAddMenuEntry("Diffuse R/G/B", MATERIAL_DIFFUSE_RGB);\r
425   glutAddMenuEntry("Specular R/G/B", MATERIAL_SPECULAR_RGB);\r
426   glutAddMenuEntry("All Amb/Diff/Spec/Shine", MATERIAL_ALL_ADSS);\r
427   glutAddMenuEntry("Red Amb/Diff/Spec/Shine", MATERIAL_RED_ADSS);\r
428   glutAddMenuEntry("Green Amb/Diff/Spec/Shine", MATERIAL_GREEN_ADSS);\r
429   glutAddMenuEntry("Blue Amb/Diff/Spec/Shine", MATERIAL_BLUE_ADSS);\r
430 \r
431   // Construct light menu\r
432   int lightMenu = glutCreateMenu(processLightEvents);\r
433   glutAddMenuEntry("Move Light 1", LIGHT_MOVE_LIGHT_1);\r
434   glutAddMenuEntry("R/G/B/All Light 1", LIGHT_RGBALL_LIGHT_1);\r
435   glutAddMenuEntry("Move Light 2", LIGHT_MOVE_LIGHT_2);\r
436   glutAddMenuEntry("R/G/B/All Light 2", LIGHT_RGBALL_LIGHT_2);\r
437 \r
438   // Construct object menu\r
439   int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]);\r
440   int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents );\r
441 \r
442   // Construct texture / ground texture menus\r
443   int textureMenuEntriesSize = sizeof(textureMenuEntries) / sizeof(textureMenuEntries[0]);\r
444   int textureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processTextureEvents );\r
445   int gTextureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processGTextureEvents );\r
446 \r
447   // Construct main menu\r
448   glutCreateMenu(processMainEvents);\r
449   //glutAddMenuEntry("Rotate/Move Camera", ROTATE_MOVE_CAMERA);\r
450   //glutAddSubMenu("Add object", objectMenu);\r
451   //glutAddMenuEntry("Position/Scale", POSITION_SCALE);\r
452   //glutAddMenuEntry("Rotation/Texture Scale", ROTATION_TEXTURE_SCALE);\r
453   //glutAddSubMenu("Material", materialMenu);\r
454   //glutAddSubMenu("Texture", textureMenu);\r
455   //glutAddSubMenu("Ground texture", gTextureMenu);\r
456   //glutAddSubMenu("Lights", lightMenu);\r
457   glutAddMenuEntry("Exit", EXIT);\r
458 \r
459   // Bind to right mouse button\r
460   glutAttachMenu(GLUT_RIGHT_BUTTON);\r
461 }\r
462 \r
463 /**\r
464  * Called when window is resized\r
465  * @param w New width\r
466  * @param h New height\r
467  */\r
468 void windowReshape(int w, int h) {\r
469 \r
470 }\r
471 \r
472 /**\r
473  * Called when mouse event occurs\r
474  * @param btn Mouse button\r
475  * @param state State of mouse button\r
476  * @param x Mouse x position\r
477  * @param y Mouse y position\r
478  */\r
479 void mouse(int btn, int state, int x, int y) {\r
480   \r
481 }\r
482 \r
483 /**\r
484  * Display function\r
485  */\r
486 void display() {\r
487    // You probably want to change both of the following.\r
488    glClear(GL_COLOR_BUFFER_BIT);\r
489    glFlush();\r
490 }\r
491 \r
492 /**\r
493  * Main function\r
494  * @param argc Number of arguments\r
495  * @param argv Array of arguments\r
496  * @return Program exit code\r
497  */\r
498 int main(int argc, char **argv) {\r
499 \r
500     if(argc>1)\r
501           strcpy(dataDir, argv[1]);\r
502     else if(opendir(dirDefault1))\r
503           strcpy(dataDir, dirDefault1);\r
504     else if(opendir(dirDefault2))\r
505       strcpy(dataDir, dirDefault2);\r
506     else fileErr(dirDefault1);\r
507 \r
508     for(int i=0; i<NMESH; i++) meshes[i]=NULL;\r
509     for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;\r
510 \r
511     glutInit(&argc, argv);\r
512 \r
513     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);\r
514     glutInitWindowSize(500, 500);\r
515     glutCreateWindow("Scene Editor");\r
516     glutReshapeFunc(windowReshape);\r
517     glutDisplayFunc(display);\r
518     glutMouseFunc(mouse);\r
519     glEnable(GL_DEPTH_TEST); // Enable hidden surface removal\r
520 \r
521     makeMenu();\r
522     glutMainLoop();\r
523 }\r

UCC git Repository :: git.ucc.asn.au