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
14 #include <GL/glut.h>
\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
26 // A type for a mesh
\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
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
39 // A type for a 2D texture, with height and width in pixels
\r
43 GLubyte *rgbData; // Array of bytes with the colour data for the texture
\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
50 // You'll need to add scale, rotation, material, mesh number, etc.,
\r
51 // to this structure
\r
60 ROTATION_TEXTURE_SCALE,
\r
65 MATERIAL_AMBIENT_RGB,
\r
66 MATERIAL_DIFFUSE_RGB,
\r
67 MATERIAL_SPECULAR_RGB,
\r
70 MATERIAL_GREEN_ADSS,
\r
75 LIGHT_RGBALL_LIGHT_1,
\r
77 LIGHT_RGBALL_LIGHT_2
\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
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
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
108 // Directories containing models
\r
109 char *dirDefault1 = "models-textures";
\r
110 char *dirDefault2 = "/cslinux/examples/CITS2231/project-files/models-textures";
\r
112 char dataDir[200]; // Stores the directory name for the meshes and textures.
\r
115 * Prints out error message when file cannot be read
\r
116 * @param fileName Name of file that could not be read
\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
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
130 texture* loadTexture(char *fileName) {
\r
131 texture* t = malloc(sizeof (texture));
\r
134 t->rgbData = LoadDIBitmap(fileName, &info);
\r
135 t->height=info->bmiHeader.biHeight;
\r
136 t->width=info->bmiHeader.biWidth;
\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
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
152 if(fp == NULL) fileErr(fileName);
\r
154 while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 )
\r
155 fgets(line, lineBuffSize, fp);
\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
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
167 while(strcmp(line," MeshNormals {\r\n") != 0 && strcmp(line," MeshNormals {\n") != 0)
\r
168 fgets(line, lineBuffSize, fp);
\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
176 while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0)
\r
177 fgets(line, lineBuffSize, fp);
\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
188 // [You may want to add to this function.]
\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
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
201 if(meshes[i] != NULL)
\r
203 sprintf(fileName, "%s/model%d.x", dataDir, i+1);
\r
204 meshes[i] = loadMesh(fileName);
\r
208 * Loads texture i if it isn't already loaded
\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
214 * You can then scale the texture via:
\r
215 * glMatrixMode(GL_TEXTURE);
\r
216 * See the textbook, section 8.8.3.
\r
218 * You must call getTexture(i) at least once before using texture i.
\r
219 * @param i Texture ID
\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
227 if(textures[i-1] != NULL)
\r
229 sprintf(fileName, "%s/texture%d.bmp", dataDir, i);
\r
231 textures[i-1] = loadTexture(fileName);
\r
233 glBindTexture(GL_TEXTURE_2D, i);
\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
244 glBindTexture(GL_TEXTURE_2D, 0); // Back to default texture
\r
248 * Event hander for main menu events
\r
249 * @param id ID of menu item selected
\r
251 void processMainEvents(int id) {
\r
253 case ROTATE_MOVE_CAMERA:
\r
257 case POSITION_SCALE:
\r
261 case ROTATION_TEXTURE_SCALE:
\r
266 exit(EXIT_SUCCESS);
\r
272 * Event hander for materials menu events
\r
273 * @param id ID of menu item selected
\r
275 void processMaterialEvents(int id) {
\r
277 case MATERIAL_ALL_RGB:
\r
281 case MATERIAL_AMBIENT_RGB:
\r
285 case MATERIAL_DIFFUSE_RGB:
\r
289 case MATERIAL_SPECULAR_RGB:
\r
293 case MATERIAL_ALL_ADSS:
\r
297 case MATERIAL_RED_ADSS:
\r
301 case MATERIAL_GREEN_ADSS:
\r
305 case MATERIAL_BLUE_ADSS:
\r
313 * Event hander for light menu events
\r
314 * @param id ID of menu item selected
\r
316 void processLightEvents(int id) {
\r
318 case LIGHT_MOVE_LIGHT_1:
\r
322 case LIGHT_RGBALL_LIGHT_1:
\r
326 case LIGHT_MOVE_LIGHT_2:
\r
330 case LIGHT_RGBALL_LIGHT_2:
\r
338 * Event hander for object menu events
\r
339 * @param id ID of object selected
\r
341 void processObjectEvents(int id) {
\r
346 * Event hander for texture menu events
\r
347 * @param id ID of texutre selected
\r
349 void processTextureEvents(int id) {
\r
354 * Event hander for ground texture menu events
\r
355 * @param id ID of ground texture selected
\r
357 void processGTextureEvents(int id) {
\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
367 int roundUp(int numToRound, int multiple) {
\r
368 if(multiple == 0) {
\r
372 int remainder = numToRound % multiple;
\r
373 if (remainder == 0)
\r
375 return numToRound + multiple - remainder;
\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
386 int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) {
\r
387 if ( menuEntriesSize == 0 ) return -1;
\r
389 int menuNumber = roundUp(menuEntriesSize, 10) / 10;
\r
390 int submenuObjects[menuNumber-1];
\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
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
409 sprintf(name, "%d-%d", startNum, endNum);
\r
410 glutAddSubMenu( name, submenuObjects[i] );
\r
417 * Creates menu for program
\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
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
438 // Construct object menu
\r
439 int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]);
\r
440 int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents );
\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
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
459 // Bind to right mouse button
\r
460 glutAttachMenu(GLUT_RIGHT_BUTTON);
\r
464 * Called when window is resized
\r
465 * @param w New width
\r
466 * @param h New height
\r
468 void windowReshape(int w, int h) {
\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
479 void mouse(int btn, int state, int x, int y) {
\r
487 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
\r
493 * @param argc Number of arguments
\r
494 * @param argv Array of arguments
\r
495 * @return Program exit code
\r
497 int main(int argc, char **argv) {
\r
500 strcpy(dataDir, argv[1]);
\r
501 else if(opendir(dirDefault1))
\r
502 strcpy(dataDir, dirDefault1);
\r
503 else if(opendir(dirDefault2))
\r
504 strcpy(dataDir, dirDefault2);
\r
505 else fileErr(dirDefault1);
\r
507 for(int i=0; i<NMESH; i++) meshes[i]=NULL;
\r
508 for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;
\r
510 glutInit(&argc, argv);
\r
512 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
\r
513 glutInitWindowSize(500, 500);
\r
514 glutCreateWindow("Scene Editor");
\r
515 glutReshapeFunc(windowReshape);
\r
516 glutDisplayFunc(display);
\r
517 glutMouseFunc(mouse);
\r
518 glEnable(GL_DEPTH_TEST); // Enable hidden surface removal
\r