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 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
115 texture* loadTexture(char *fileName) {
\r
116 texture* t = malloc(sizeof (texture));
\r
119 t->rgbData = LoadDIBitmap(fileName, &info);
\r
120 t->height=info->bmiHeader.biHeight;
\r
121 t->width=info->bmiHeader.biWidth;
\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
134 if(fp == NULL) fileErr(fileName);
\r
136 while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 )
\r
137 fgets(line, lineBuffSize, fp);
\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
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
149 while(strcmp(line," MeshNormals {\r\n") != 0 && strcmp(line," MeshNormals {\n") != 0)
\r
150 fgets(line, lineBuffSize, fp);
\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
158 while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0)
\r
159 fgets(line, lineBuffSize, fp);
\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
170 char dataDir[200]; // Stores the directory name for the meshes and textures.
\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
181 if(meshes[i] != NULL)
\r
183 sprintf(fileName, "%s/model%d.x", dataDir, i+1);
\r
184 meshes[i] = loadMesh(fileName);
\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
199 if(textures[i-1] != NULL)
\r
201 sprintf(fileName, "%s/texture%d.bmp", dataDir, i);
\r
203 textures[i-1] = loadTexture(fileName);
\r
205 glBindTexture(GL_TEXTURE_2D, i);
\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
216 glBindTexture(GL_TEXTURE_2D, 0); // Back to default texture
\r
219 void processMainEvents(int id) {
\r
221 case ROTATE_MOVE_CAMERA:
\r
225 case POSITION_SCALE:
\r
229 case ROTATION_TEXTURE_SCALE:
\r
234 exit(EXIT_SUCCESS);
\r
239 void processObjectEvents(int id) {
\r
243 void processMaterialEvents(int id) {
\r
245 case MATERIAL_ALL_RGB:
\r
249 case MATERIAL_AMBIENT_RGB:
\r
253 case MATERIAL_DIFFUSE_RGB:
\r
257 case MATERIAL_SPECULAR_RGB:
\r
261 case MATERIAL_ALL_ADSS:
\r
265 case MATERIAL_RED_ADSS:
\r
269 case MATERIAL_GREEN_ADSS:
\r
273 case MATERIAL_BLUE_ADSS:
\r
280 void processTextureEvents(int id) {
\r
284 void processGTextureEvents(int id) {
\r
288 void processLightEvents(int id) {
\r
290 case LIGHT_MOVE_LIGHT_1:
\r
294 case LIGHT_RGBALL_LIGHT_1:
\r
298 case LIGHT_MOVE_LIGHT_2:
\r
302 case LIGHT_RGBALL_LIGHT_2:
\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
315 int roundUp(int numToRound, int multiple) {
\r
316 if(multiple == 0) {
\r
320 int remainder = numToRound % multiple;
\r
321 if (remainder == 0)
\r
323 return numToRound + multiple - remainder;
\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
334 int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) {
\r
335 if ( menuEntriesSize == 0 ) return -1;
\r
337 int menuNumber = roundUp(menuEntriesSize, 10) / 10;
\r
338 int submenuObjects[menuNumber-1];
\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
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
357 sprintf(name, "%d-%d", startNum, endNum);
\r
358 glutAddSubMenu( name, submenuObjects[i] );
\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
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
383 // Construct object menu
\r
384 int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]);
\r
385 int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents );
\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
392 // Construct main menu
\r
393 int mainMenu = 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
404 // Bind to right mouse button
\r
405 glutAttachMenu(GLUT_RIGHT_BUTTON);
\r
409 // You probably want to change both of the following.
\r
410 glClear(GL_COLOR_BUFFER_BIT);
\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
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
426 for(int i=0; i<NMESH; i++) meshes[i]=NULL;
\r
427 for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;
\r
429 // The following is enough to run the program, but you'll
\r
430 // need to change it significantly.
\r
432 glutInit(&argc, argv);
\r
433 glutCreateWindow("Scene Editor");
\r
434 glutDisplayFunc(display);
\r