f9df3c82a42f345abf0964399cae38116c7ecd1c
[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 #define MAXOBJECTS 256\r
56 SceneObject sceneObjs[MAXOBJECTS];  // An array with details of the objects in a scene\r
57 int nObjects=0;                     // How many objects there are in the scene currently.\r
58 \r
59 void fileErr(char* fileName) {\r
60     printf("Error reading file: %s\n", fileName);\r
61     printf("If not in the CSSE labs, you will need to include the directory containing\n");\r
62     printf("the models on the command line, or put it in the same folder as the exectutable.");\r
63     exit(1);\r
64 }  \r
65 \r
66 texture* loadTexture(char *fileName) {\r
67     texture* t = malloc(sizeof (texture));\r
68     BITMAPINFO *info;\r
69 \r
70     t->rgbData = LoadDIBitmap(fileName, &info);\r
71     t->height=info->bmiHeader.biHeight;\r
72     t->width=info->bmiHeader.biWidth;\r
73 \r
74     return t;\r
75 }\r
76 \r
77 // The following works for the supplied .x files\r
78 // but probably not for .x files from other sources.\r
79 mesh* loadMesh(char* fileName) {\r
80     mesh* m = malloc(sizeof (mesh));\r
81     FILE* fp = fopen(fileName, "r");\r
82     char line[256] = "";\r
83     int lineBuffSize = 256;\r
84 \r
85     if(fp == NULL) fileErr(fileName);\r
86 \r
87     while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 )\r
88         fgets(line, lineBuffSize, fp);\r
89 \r
90     fscanf(fp, "%d;\n", &(m->nVertices));\r
91     m->vertices = malloc(m->nVertices * sizeof(vertex));\r
92     for(int i=0; i < m->nVertices; i++)\r
93         fscanf(fp, "%f; %f; %f;%*[,;]\n", &(m->vertices[i][0]), &(m->vertices[i][1]), &(m->vertices[i][2]) );\r
94 \r
95     fscanf(fp, "%d;\n", &(m->nTriangles));\r
96     m->triangles = malloc(m->nTriangles * sizeof(triangle));\r
97     for(int i=0; i < m->nTriangles; i++)\r
98         fscanf(fp, "%*d; %d, %d, %d;%*[;,]", m->triangles[i], m->triangles[i]+1, m->triangles[i]+2);\r
99 \r
100     while(strcmp(line,"  MeshNormals {\r\n") != 0 && strcmp(line,"  MeshNormals {\n") != 0)\r
101         fgets(line, lineBuffSize, fp);\r
102 \r
103     fgets(line, lineBuffSize, fp);\r
104     m->normals = malloc(m->nVertices * sizeof(normal));\r
105     for(int i=0; i < m->nVertices; i++)\r
106         fscanf(fp, "%f; %f; %f;%*[;,]\n",\r
107                &(m->normals[i][0]), &(m->normals[i][1]), &(m->normals[i][2]));\r
108 \r
109     while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0)\r
110         fgets(line, lineBuffSize, fp);\r
111 \r
112     fgets(line, lineBuffSize, fp);\r
113     m->texCoords = malloc(m->nVertices * sizeof(texCoord));\r
114     for(int i=0; i < m->nVertices; i++)\r
115         fscanf(fp, "%f;%f;%*[,;]\n", &(m->texCoords[i][0]), &(m->texCoords[i][1]) );\r
116     fclose(fp);\r
117     \r
118     return m;\r
119 }\r
120 \r
121 char dataDir[200];  // Stores the directory name for the meshes and textures.\r
122 \r
123 // getMesh(i) loads mesh[i] if it isn't already loaded.  \r
124 // You must call getMesh(i) at least once before using mesh[i].\r
125 // [You may want to add to this function.]\r
126 void getMesh(int i) { // getMesh(i) loads mesh[i] if it isn't already loaded.  \r
127     char fileName[220];\r
128     if(i>=NMESH || i<0) {\r
129         printf("Error in getMesh - wrong model number");\r
130         exit(1);\r
131     }\r
132     if(meshes[i] != NULL)\r
133         return;\r
134     sprintf(fileName, "%s/model%d.x", dataDir, i+1);\r
135     meshes[i] = loadMesh(fileName);\r
136 }\r
137 \r
138 // getTexture(i) loads texture i if it isn't already loaded.\r
139 // After calling getTexture(i), you can make texture i the current texture using\r
140 //     glBindTexture(GL_TEXTURE_2D, i);    (Use i=0 to return to the default plain texture.)\r
141 // You can then scale the texture via:   (See the textbook, section 8.8.3.)\r
142 //      glMatrixMode(GL_TEXTURE);     \r
143 // You must call getTexture(i) at least once before using texture i.\r
144 void getTexture(int i) { // getTexture(i) loads texture i if it isn't already loaded.\r
145     char fileName[220];\r
146     if(i<1 || i>NTEXTURE) {\r
147         printf("Error in getTexture - wrong texture number");\r
148         exit(1);\r
149     }\r
150     if(textures[i-1] != NULL)\r
151         return;\r
152     sprintf(fileName, "%s/texture%d.bmp", dataDir, i);\r
153 \r
154     textures[i-1] = loadTexture(fileName);\r
155 \r
156     glBindTexture(GL_TEXTURE_2D, i);\r
157 \r
158     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textures[i-1]->width, textures[i-1]->height,\r
159                  0, GL_RGB, GL_UNSIGNED_BYTE, textures[i-1]->rgbData);\r
160     gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, textures[i-1]->width, textures[i-1]->height, GL_RGB, \r
161                       GL_UNSIGNED_BYTE, textures[i-1]->rgbData);\r
162     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);\r
163     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);\r
164     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);\r
165     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);\r
166 \r
167     glBindTexture(GL_TEXTURE_2D, 0);  // Back to default texture\r
168 }\r
169 \r
170 // Menu enum\r
171 enum menu {\r
172   // Main menu\r
173   ROTATE_MOVE_CAMERA,\r
174   POSITION_SCALE,\r
175   ROTATION_TEXTURE_SCALE,\r
176   EXIT,\r
177 \r
178   // Material submenu\r
179   MATERIAL_ALL_RGB,\r
180   MATERIAL_AMBIENT_RGB,\r
181   MATERIAL_DIFFUSE_RGB,\r
182   MATERIAL_SPECULAR_RGB,\r
183   MATERIAL_ALL_ADSS,\r
184   MATERIAL_RED_ADSS,\r
185   MATERIAL_GREEN_ADSS,\r
186   MATERIAL_BLUE_ADSS,\r
187 \r
188   // Light submenu\r
189   LIGHT_MOVE_LIGHT_1,\r
190   LIGHT_RGBALL_LIGHT_1,\r
191   LIGHT_MOVE_LIGHT_2,\r
192   LIGHT_RGBALL_LIGHT_2\r
193 };\r
194 \r
195 // Menu arrays\r
196 const char *textureMenuEntries[NTEXTURE] = {\r
197   "1 Plain", "2 Rust", "3 Concrete", "4 Carpet", "5 Beach Sand", \r
198   "6 Rocky", "7 Brick", "8 Water", "9 Paper", "10 Marble", \r
199   "11 Wood", "12 Scales", "13 Fur", "14 Denim", "15 Hessian",\r
200   "16 Orange Peel", "17 Ice Crystals", "18 Grass", "19 Corrugated Iron", "20 Styrofoam",\r
201   "21 Bubble Wrap", "22 Leather", "23 Camouflage", "24 Asphalt", "25 Scratched Ice",\r
202   "26 Rattan", "27 Snow", "28 Dry Mud", "29 Old Concrete", "30 Leopard Skin"\r
203 };\r
204 \r
205 const char *objectMenuEntries[NMESH] = {\r
206   "1 Thin Dinosaur","2 Big Dog","3 Saddle Dinosaur", "4 Dragon", "5 Cleopatra", \r
207   "6 Bone I", "7 Bone II", "8 Rabbit", "9 Long Dragon", "10 Buddha", \r
208   "11 Sitting Rabbit", "12 Frog", "13 Cow", "14 Monster", "15 Sea Horse", \r
209   "16 Head", "17 Pelican", "18 Horse", "19 Kneeling Angel", "20 Porsche I", \r
210   "21 Truck", "22 Statue of Liberty", "23 Sitting Angel", "24 Metal Part", "25 Car", \r
211   "26 Apatosaurus", "27 Airliner", "28 Motorbike", "29 Dolphin", "30 Spaceman", \r
212   "31 Winnie the Pooh", "32 Shark", "33 Crocodile", "34 Toddler", "35 Fat Dinosaur", \r
213   "36 Chihuahua", "37 Sabre-toothed Tiger", "38 Lioness", "39 Fish", "40 Horse (head down)", \r
214   "41 Horse (head up)", "42 Skull", "43 Fighter Jet I", "44 Toad", "45 Convertible", \r
215   "46 Porsche II", "47 Hare", "48 Vintage Car", "49 Fighter Jet II", "50 Winged Monkey", \r
216   "51 Chef", "52 Parasaurolophus", "53 Rooster", "54 T-rex"\r
217 };\r
218 \r
219 \r
220 \r
221 void processMainEvents(int id) {\r
222   switch (id) {\r
223     case EXIT:\r
224       exit(0);\r
225 \r
226   }\r
227 }\r
228 \r
229 void processObjectEvents(int id) {\r
230 \r
231 }\r
232 \r
233 void processMaterialEvents(int id) {\r
234 \r
235 }\r
236 \r
237 void processTextureEvents(int id) {\r
238 \r
239 }\r
240 \r
241 void processGTextureEvents(int id) {\r
242 \r
243 }\r
244 \r
245 void processLightEvents(int id) {\r
246 \r
247 }\r
248 \r
249 /**\r
250  * Rounds up numbers, from http://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number\r
251  * @param numToRound Number to round\r
252  * @param multiple Multiple to round up to\r
253  * @return Rounded number\r
254  */\r
255 int roundUp(int numToRound, int multiple) {\r
256   if(multiple == 0) {\r
257     return numToRound;\r
258   }\r
259 \r
260   int remainder = numToRound % multiple;\r
261   if (remainder == 0)\r
262     return numToRound;\r
263   return numToRound + multiple - remainder;\r
264 }\r
265 \r
266 /**\r
267  * Makes a submenu from an array of items, splitting the list into subsubmenus\r
268  * of only 10 items.\r
269  * @param menuEntries Array of menu items\r
270  * @param menuEntriesSize Size of menuEntries\r
271  * @param callback Callback function for this array of menu items\r
272  * @return Reference to menu created\r
273  */\r
274 int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) {\r
275   if ( menuEntriesSize == 0 ) return -1;\r
276 \r
277   int menuNumber = roundUp(menuEntriesSize, 10) / 10;\r
278   int submenuObjects[menuNumber-1];\r
279 \r
280   for( int i = 0; i < menuNumber; i++ ) {\r
281     submenuObjects[i] = glutCreateMenu(callback);\r
282     int startNum = i*11 - (i-1);\r
283     for ( int j = startNum - 1; j < (startNum+9); j++ ) {\r
284       if ( j == menuEntriesSize ) break; // Detect if we've reached the end of the array\r
285       glutAddMenuEntry( menuEntries[j], j + 1 );\r
286     }\r
287   } \r
288 \r
289   int mainMenu = glutCreateMenu(callback);\r
290   for ( int i = 0; i < menuNumber; i++ ) {\r
291     char name[10]; // buffer to hold name\r
292     int startNum = i*11 - (i-1);\r
293     int endNum = startNum + 9;\r
294     if ( i == menuNumber - 1 ) { // We're on the last one\r
295       endNum = startNum + (menuEntriesSize - startNum); // Work out final number\r
296     }\r
297     sprintf(name, "%d-%d", startNum, endNum);\r
298     glutAddSubMenu( name, submenuObjects[i] );\r
299   }\r
300 \r
301   return mainMenu;\r
302 }\r
303 \r
304 void makeMenu() {\r
305   // Construct material menu\r
306   int materialMenu = glutCreateMenu(processMaterialEvents);\r
307   glutAddMenuEntry("All R/G/B", MATERIAL_ALL_RGB);\r
308   glutAddMenuEntry("Ambient R/G/B", MATERIAL_AMBIENT_RGB);\r
309   glutAddMenuEntry("Diffuse R/G/B", MATERIAL_DIFFUSE_RGB);\r
310   glutAddMenuEntry("Specular R/G/B", MATERIAL_SPECULAR_RGB);\r
311   glutAddMenuEntry("All Amb/Diff/Spec/Shine", MATERIAL_ALL_ADSS);\r
312   glutAddMenuEntry("Red Amb/Diff/Spec/Shine", MATERIAL_RED_ADSS);\r
313   glutAddMenuEntry("Green Amb/Diff/Spec/Shine", MATERIAL_GREEN_ADSS);\r
314   glutAddMenuEntry("Blue Amb/Diff/Spec/Shine", MATERIAL_BLUE_ADSS);\r
315 \r
316   // Construct light menu\r
317   int lightMenu = glutCreateMenu(processLightEvents);\r
318   glutAddMenuEntry("Move Light 1", LIGHT_MOVE_LIGHT_1);\r
319   glutAddMenuEntry("R/G/B/All Light 1", LIGHT_RGBALL_LIGHT_1);\r
320   glutAddMenuEntry("Move Light 2", LIGHT_MOVE_LIGHT_2);\r
321   glutAddMenuEntry("R/G/B/All Light 2", LIGHT_RGBALL_LIGHT_2);\r
322 \r
323   // Construct object menu\r
324   int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]);\r
325   int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents );\r
326 \r
327   // Construct texture / ground texture menus\r
328   int textureMenuEntriesSize = sizeof(textureMenuEntries) / sizeof(textureMenuEntries[0]);\r
329   int textureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processTextureEvents );\r
330   int gTextureMenu = makeSubmenuFromArray( textureMenuEntries, textureMenuEntriesSize, processGTextureEvents );\r
331 \r
332   // Construct main menu\r
333   int mainMenu = glutCreateMenu(processMainEvents);\r
334   glutAddMenuEntry("Rotate/Move Camera", ROTATE_MOVE_CAMERA);\r
335   glutAddSubMenu("Add object", objectMenu);\r
336   glutAddMenuEntry("Position/Scale", POSITION_SCALE);\r
337   glutAddMenuEntry("Rotation/Texture Scale", ROTATION_TEXTURE_SCALE);\r
338   glutAddSubMenu("Material", materialMenu);\r
339   glutAddSubMenu("Texture", textureMenu);\r
340   glutAddSubMenu("Ground texture", gTextureMenu);\r
341   glutAddSubMenu("Lights", lightMenu);\r
342   glutAddMenuEntry("Exit", EXIT);\r
343 \r
344   // Bind to right mouse button\r
345   glutAttachMenu(GLUT_RIGHT_BUTTON);\r
346 }\r
347 \r
348 void display() {\r
349    // You probably want to change both of the following.\r
350    glClear(GL_COLOR_BUFFER_BIT);\r
351    glFlush();\r
352 }\r
353 \r
354 char *dirDefault1 = "models-textures"; \r
355 char *dirDefault2 = "/cslinux/examples/CITS2231/project-files/models-textures";\r
356 int main(int argc, char **argv) {\r
357 \r
358     if(argc>1)\r
359           strcpy(dataDir, argv[1]);\r
360     else if(opendir(dirDefault1))\r
361           strcpy(dataDir, dirDefault1);\r
362     else if(opendir(dirDefault2))\r
363       strcpy(dataDir, dirDefault2);\r
364     else fileErr(dirDefault1);\r
365 \r
366     for(int i=0; i<NMESH; i++) meshes[i]=NULL;\r
367     for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;\r
368 \r
369     // The following is enough to run the program, but you'll\r
370     // need to change it significantly.\r
371 \r
372     glutInit(&argc, argv);\r
373     glutCreateWindow("Scene Editor");\r
374     glutDisplayFunc(display);\r
375 \r
376     makeMenu();\r
377     glutMainLoop();\r
378 }\r

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