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 glMatrixMode(GL_PROJECTION);
\r
471 gluPerspective(60, (GLfloat)width / (GLfloat)height, 0.1, 1000.0);
\r
473 /*glViewport(0, 0, w, h);
\r
474 glMatrixMode(GL_PROJECTION); /* switch matrix mode
\r
477 gluOrtho2D(-2.0, 2.0, -2.0 * (GLfloat) h / (GLfloat) w, 2.0 * (GLfloat) h / (GLfloat) w);
\r
479 gluOrtho2D(-2.0 * (GLfloat) w / (GLfloat) h, 2.0 * (GLfloat) w / (GLfloat) h, -2.0, 2.0);
\r
480 glMatrixMode(GL_MODELVIEW); /* return to modelview mode */
\r
484 * Called when mouse event occurs
\r
485 * @param btn Mouse button
\r
486 * @param state State of mouse button
\r
487 * @param x Mouse x position
\r
488 * @param y Mouse y position
\r
490 void mouse(int btn, int state, int x, int y) {
\r
498 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
\r
501 glTranslatef(0.0f,0.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0
\r
504 glColor3f(1.0f,0.0f,0.0f); // Set The Color To Red
\r
505 glVertex3f(-1.0f, 1.0f, 0.0f); // Top left
\r
506 glVertex3f( 1.0f, 1.0f, 0.0f); // Top Right
\r
507 glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right
\r
508 glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left
\r
516 * @param argc Number of arguments
\r
517 * @param argv Array of arguments
\r
518 * @return Program exit code
\r
520 int main(int argc, char **argv) {
\r
523 strcpy(dataDir, argv[1]);
\r
524 else if(opendir(dirDefault1))
\r
525 strcpy(dataDir, dirDefault1);
\r
526 else if(opendir(dirDefault2))
\r
527 strcpy(dataDir, dirDefault2);
\r
528 else fileErr(dirDefault1);
\r
530 for(int i=0; i<NMESH; i++) meshes[i]=NULL;
\r
531 for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;
\r
533 glutInit(&argc, argv);
\r
535 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
\r
536 glutInitWindowSize(500, 500);
\r
537 glutCreateWindow("Scene Editor");
\r
539 glShadeModel(GL_SMOOTH); // Enables Smooth Shading
\r
540 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background
\r
541 glClearDepth(1.0f); // Depth Buffer Setup
\r
542 glEnable(GL_DEPTH_TEST); // Enables Depth Testing
\r
543 glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
\r
545 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
\r
547 glutReshapeFunc(windowReshape);
\r
548 glutDisplayFunc(display);
\r
549 glutMouseFunc(mouse);
\r