(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 void fileErr(char* fileName) {\r
109     printf("Error reading file: %s\n", fileName);\r
110     printf("If not in the CSSE labs, you will need to include the directory containing\n");\r
111     printf("the models on the command line, or put it in the same folder as the exectutable.");\r
112     exit(1);\r
113 }  \r
114 \r
115 texture* loadTexture(char *fileName) {\r
116     texture* t = malloc(sizeof (texture));\r
117     BITMAPINFO *info;\r
118 \r
119     t->rgbData = LoadDIBitmap(fileName, &info);\r
120     t->height=info->bmiHeader.biHeight;\r
121     t->width=info->bmiHeader.biWidth;\r
122 \r
123     return t;\r
124 }\r
125 \r
126 // The following works for the supplied .x files\r
127 // but probably not for .x files from other sources.\r
128 mesh* loadMesh(char* fileName) {\r
129     mesh* m = malloc(sizeof (mesh));\r
130     FILE* fp = fopen(fileName, "r");\r
131     char line[256] = "";\r
132     int lineBuffSize = 256;\r
133 \r
134     if(fp == NULL) fileErr(fileName);\r
135 \r
136     while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 )\r
137         fgets(line, lineBuffSize, fp);\r
138 \r
139     fscanf(fp, "%d;\n", &(m->nVertices));\r
140     m->vertices = malloc(m->nVertices * sizeof(vertex));\r
141     for(int i=0; i < m->nVertices; i++)\r
142         fscanf(fp, "%f; %f; %f;%*[,;]\n", &(m->vertices[i][0]), &(m->vertices[i][1]), &(m->vertices[i][2]) );\r
143 \r
144     fscanf(fp, "%d;\n", &(m->nTriangles));\r
145     m->triangles = malloc(m->nTriangles * sizeof(triangle));\r
146     for(int i=0; i < m->nTriangles; i++)\r
147         fscanf(fp, "%*d; %d, %d, %d;%*[;,]", m->triangles[i], m->triangles[i]+1, m->triangles[i]+2);\r
148 \r
149     while(strcmp(line,"  MeshNormals {\r\n") != 0 && strcmp(line,"  MeshNormals {\n") != 0)\r
150         fgets(line, lineBuffSize, fp);\r
151 \r
152     fgets(line, lineBuffSize, fp);\r
153     m->normals = malloc(m->nVertices * sizeof(normal));\r
154     for(int i=0; i < m->nVertices; i++)\r
155         fscanf(fp, "%f; %f; %f;%*[;,]\n",\r
156                &(m->normals[i][0]), &(m->normals[i][1]), &(m->normals[i][2]));\r
157 \r
158     while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0)\r
159         fgets(line, lineBuffSize, fp);\r
160 \r
161     fgets(line, lineBuffSize, fp);\r
162     m->texCoords = malloc(m->nVertices * sizeof(texCoord));\r
163     for(int i=0; i < m->nVertices; i++)\r
164         fscanf(fp, "%f;%f;%*[,;]\n", &(m->texCoords[i][0]), &(m->texCoords[i][1]) );\r
165     fclose(fp);\r
166     \r
167     return m;\r
168 }\r
169 \r
170 char dataDir[200];  // Stores the directory name for the meshes and textures.\r
171 \r
172 // getMesh(i) loads mesh[i] if it isn't already loaded.  \r
173 // You must call getMesh(i) at least once before using mesh[i].\r
174 // [You may want to add to this function.]\r
175 void getMesh(int i) { // getMesh(i) loads mesh[i] if it isn't already loaded.  \r
176     char fileName[220];\r
177     if(i>=NMESH || i<0) {\r
178         printf("Error in getMesh - wrong model number");\r
179         exit(1);\r
180     }\r
181     if(meshes[i] != NULL)\r
182         return;\r
183     sprintf(fileName, "%s/model%d.x", dataDir, i+1);\r
184     meshes[i] = loadMesh(fileName);\r
185 }\r
186 \r
187 // getTexture(i) loads texture i if it isn't already loaded.\r
188 // After calling getTexture(i), you can make texture i the current texture using\r
189 //     glBindTexture(GL_TEXTURE_2D, i);    (Use i=0 to return to the default plain texture.)\r
190 // You can then scale the texture via:   (See the textbook, section 8.8.3.)\r
191 //      glMatrixMode(GL_TEXTURE);     \r
192 // You must call getTexture(i) at least once before using texture i.\r
193 void getTexture(int i) { // getTexture(i) loads texture i if it isn't already loaded.\r
194     char fileName[220];\r
195     if(i<1 || i>NTEXTURE) {\r
196         printf("Error in getTexture - wrong texture number");\r
197         exit(1);\r
198     }\r
199     if(textures[i-1] != NULL)\r
200         return;\r
201     sprintf(fileName, "%s/texture%d.bmp", dataDir, i);\r
202 \r
203     textures[i-1] = loadTexture(fileName);\r
204 \r
205     glBindTexture(GL_TEXTURE_2D, i);\r
206 \r
207     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textures[i-1]->width, textures[i-1]->height,\r
208                  0, GL_RGB, GL_UNSIGNED_BYTE, textures[i-1]->rgbData);\r
209     gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, textures[i-1]->width, textures[i-1]->height, GL_RGB, \r
210                       GL_UNSIGNED_BYTE, textures[i-1]->rgbData);\r
211     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);\r
212     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);\r
213     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r
214     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);\r
215 \r
216     glBindTexture(GL_TEXTURE_2D, 0);  // Back to default texture\r
217 }\r
218 \r
219 void processMainEvents(int id) {\r
220   switch (id) {\r
221     case ROTATE_MOVE_CAMERA:\r
222       // Do stuff\r
223       break;\r
224 \r
225     case POSITION_SCALE:\r
226       // Do stuff\r
227       break;\r
228 \r
229     case ROTATION_TEXTURE_SCALE:\r
230       // Do stuff\r
231       break;\r
232 \r
233     case EXIT:\r
234       exit(EXIT_SUCCESS);\r
235 \r
236   }\r
237 }\r
238 \r
239 void processObjectEvents(int id) {\r
240 \r
241 }\r
242 \r
243 void processMaterialEvents(int id) {\r
244   switch (id) {\r
245     case MATERIAL_ALL_RGB:\r
246       // Do stuff\r
247       break;\r
248 \r
249     case MATERIAL_AMBIENT_RGB:\r
250       // Do stuff\r
251       break;\r
252 \r
253     case MATERIAL_DIFFUSE_RGB:\r
254       // Do stuff\r
255       break;\r
256 \r
257     case MATERIAL_SPECULAR_RGB:\r
258       // Do stuff\r
259       break;\r
260 \r
261     case MATERIAL_ALL_ADSS:\r
262       // Do stuff\r
263       break;\r
264 \r
265     case MATERIAL_RED_ADSS:\r
266       // Do stuff\r
267       break;\r
268 \r
269     case MATERIAL_GREEN_ADSS:\r
270       // Do stuff\r
271       break;\r
272 \r
273     case MATERIAL_BLUE_ADSS:\r
274       // Do stuff\r
275       break;\r
276 \r
277   }\r
278 }\r
279 \r
280 void processTextureEvents(int id) {\r
281 \r
282 }\r
283 \r
284 void processGTextureEvents(int id) {\r
285 \r
286 }\r
287 \r
288 void processLightEvents(int id) {\r
289   switch (id) {\r
290     case LIGHT_MOVE_LIGHT_1:\r
291       // Do stuff\r
292       break;\r
293 \r
294     case LIGHT_RGBALL_LIGHT_1:\r
295       // Do stuff\r
296       break;\r
297 \r
298     case LIGHT_MOVE_LIGHT_2:\r
299       // Do stuff\r
300       break;\r
301 \r
302     case LIGHT_RGBALL_LIGHT_2:\r
303       // Do stuff\r
304       break;\r
305 \r
306   }\r
307 }\r
308 \r
309 /**\r
310  * Rounds up numbers, from http://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number\r
311  * @param numToRound Number to round\r
312  * @param multiple Multiple to round up to\r
313  * @return Rounded number\r
314  */\r
315 int roundUp(int numToRound, int multiple) {\r
316   if(multiple == 0) {\r
317     return numToRound;\r
318   }\r
319 \r
320   int remainder = numToRound % multiple;\r
321   if (remainder == 0)\r
322     return numToRound;\r
323   return numToRound + multiple - remainder;\r
324 }\r
325 \r
326 /**\r
327  * Makes a submenu from an array of items, splitting the list into subsubmenus\r
328  * of only 10 items.\r
329  * @param menuEntries Array of menu items\r
330  * @param menuEntriesSize Size of menuEntries\r
331  * @param callback Callback function for this array of menu items\r
332  * @return Reference to menu created\r
333  */\r
334 int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) {\r
335   if ( menuEntriesSize == 0 ) return -1;\r
336 \r
337   int menuNumber = roundUp(menuEntriesSize, 10) / 10;\r
338   int submenuObjects[menuNumber-1];\r
339 \r
340   for( int i = 0; i < menuNumber; i++ ) {\r
341     submenuObjects[i] = glutCreateMenu(callback);\r
342     int startNum = i*11 - (i-1);\r
343     for ( int j = startNum - 1; j < (startNum+9); j++ ) {\r
344       if ( j == menuEntriesSize ) break; // Detect if we've reached the end of the array\r
345       glutAddMenuEntry( menuEntries[j], j + 1 );\r
346     }\r
347   } \r
348 \r
349   int mainMenu = glutCreateMenu(callback);\r
350   for ( int i = 0; i < menuNumber; i++ ) {\r
351     char name[10]; // buffer to hold name\r
352     int startNum = i*11 - (i-1);\r
353     int endNum = startNum + 9;\r
354     if ( i == menuNumber - 1 ) { // We're on the last one\r
355       endNum = startNum + (menuEntriesSize - startNum); // Work out final number\r
356     }\r
357     sprintf(name, "%d-%d", startNum, endNum);\r
358     glutAddSubMenu( name, submenuObjects[i] );\r
359   }\r
360 \r
361   return mainMenu;\r
362 }\r
363 \r
364 void makeMenu() {\r
365   // Construct material menu\r
366   int materialMenu = glutCreateMenu(processMaterialEvents);\r
367   glutAddMenuEntry("All R/G/B", MATERIAL_ALL_RGB);\r
368   glutAddMenuEntry("Ambient R/G/B", MATERIAL_AMBIENT_RGB);\r
369   glutAddMenuEntry("Diffuse R/G/B", MATERIAL_DIFFUSE_RGB);\r
370   glutAddMenuEntry("Specular R/G/B", MATERIAL_SPECULAR_RGB);\r
371   glutAddMenuEntry("All Amb/Diff/Spec/Shine", MATERIAL_ALL_ADSS);\r
372   glutAddMenuEntry("Red Amb/Diff/Spec/Shine", MATERIAL_RED_ADSS);\r
373   glutAddMenuEntry("Green Amb/Diff/Spec/Shine", MATERIAL_GREEN_ADSS);\r
374   glutAddMenuEntry("Blue Amb/Diff/Spec/Shine", MATERIAL_BLUE_ADSS);\r
375 \r
376   // Construct light menu\r
377   int lightMenu = glutCreateMenu(processLightEvents);\r
378   glutAddMenuEntry("Move Light 1", LIGHT_MOVE_LIGHT_1);\r
379   glutAddMenuEntry("R/G/B/All Light 1", LIGHT_RGBALL_LIGHT_1);\r
380   glutAddMenuEntry("Move Light 2", LIGHT_MOVE_LIGHT_2);\r
381   glutAddMenuEntry("R/G/B/All Light 2", LIGHT_RGBALL_LIGHT_2);\r
382 \r
383   // Construct object menu\r
384   int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]);\r
385   int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents );\r
386 \r
387   // Construct texture / ground texture menus\r
388   int textureMenuEntriesSize = sizeof(textureMenuEntries) / sizeof(textureMenuEntries[0]);\r
389   int textureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processTextureEvents );\r
390   int gTextureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processGTextureEvents );\r
391 \r
392   // Construct main menu\r
393   glutCreateMenu(processMainEvents);\r
394   glutAddMenuEntry("Rotate/Move Camera", ROTATE_MOVE_CAMERA);\r
395   glutAddSubMenu("Add object", objectMenu);\r
396   glutAddMenuEntry("Position/Scale", POSITION_SCALE);\r
397   glutAddMenuEntry("Rotation/Texture Scale", ROTATION_TEXTURE_SCALE);\r
398   glutAddSubMenu("Material", materialMenu);\r
399   glutAddSubMenu("Texture", textureMenu);\r
400   glutAddSubMenu("Ground texture", gTextureMenu);\r
401   glutAddSubMenu("Lights", lightMenu);\r
402   glutAddMenuEntry("Exit", EXIT);\r
403 \r
404   // Bind to right mouse button\r
405   glutAttachMenu(GLUT_RIGHT_BUTTON);\r
406 }\r
407 \r
408 void display() {\r
409    // You probably want to change both of the following.\r
410    glClear(GL_COLOR_BUFFER_BIT);\r
411    glFlush();\r
412 }\r
413 \r
414 char *dirDefault1 = "models-textures"; \r
415 char *dirDefault2 = "/cslinux/examples/CITS2231/project-files/models-textures";\r
416 int main(int argc, char **argv) {\r
417 \r
418     if(argc>1)\r
419           strcpy(dataDir, argv[1]);\r
420     else if(opendir(dirDefault1))\r
421           strcpy(dataDir, dirDefault1);\r
422     else if(opendir(dirDefault2))\r
423       strcpy(dataDir, dirDefault2);\r
424     else fileErr(dirDefault1);\r
425 \r
426     for(int i=0; i<NMESH; i++) meshes[i]=NULL;\r
427     for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;\r
428 \r
429     // The following is enough to run the program, but you'll\r
430     // need to change it significantly.\r
431 \r
432     glutInit(&argc, argv);\r
433     glutCreateWindow("Scene Editor");\r
434     glutDisplayFunc(display);\r
435 \r
436     makeMenu();\r
437     glutMainLoop();\r
438 }\r

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