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
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
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
66 texture* loadTexture(char *fileName) {
\r
67 texture* t = malloc(sizeof (texture));
\r
70 t->rgbData = LoadDIBitmap(fileName, &info);
\r
71 t->height=info->bmiHeader.biHeight;
\r
72 t->width=info->bmiHeader.biWidth;
\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
85 if(fp == NULL) fileErr(fileName);
\r
87 while(strcmp(line,"Mesh {\r\n") != 0 && strcmp(line,"Mesh {\n") != 0 )
\r
88 fgets(line, lineBuffSize, fp);
\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
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
100 while(strcmp(line," MeshNormals {\r\n") != 0 && strcmp(line," MeshNormals {\n") != 0)
\r
101 fgets(line, lineBuffSize, fp);
\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
109 while(strcmp(line,"MeshTextureCoords {\r\n") != 0 && strcmp(line,"MeshTextureCoords {\n") != 0)
\r
110 fgets(line, lineBuffSize, fp);
\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
121 char dataDir[200]; // Stores the directory name for the meshes and textures.
\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
132 if(meshes[i] != NULL)
\r
134 sprintf(fileName, "%s/model%d.x", dataDir, i+1);
\r
135 meshes[i] = loadMesh(fileName);
\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
150 if(textures[i-1] != NULL)
\r
152 sprintf(fileName, "%s/texture%d.bmp", dataDir, i);
\r
154 textures[i-1] = loadTexture(fileName);
\r
156 glBindTexture(GL_TEXTURE_2D, i);
\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
167 glBindTexture(GL_TEXTURE_2D, 0); // Back to default texture
\r
173 ROTATE_MOVE_CAMERA,
\r
175 ROTATION_TEXTURE_SCALE,
\r
178 // Material submenu
\r
180 MATERIAL_AMBIENT_RGB,
\r
181 MATERIAL_DIFFUSE_RGB,
\r
182 MATERIAL_SPECULAR_RGB,
\r
185 MATERIAL_GREEN_ADSS,
\r
186 MATERIAL_BLUE_ADSS,
\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
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
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
221 void processMainEvents(int id) {
\r
229 void processObjectEvents(int id) {
\r
233 void processMaterialEvents(int id) {
\r
237 void processTextureEvents(int id) {
\r
241 void processGTextureEvents(int id) {
\r
245 void processLightEvents(int id) {
\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
255 int roundUp(int numToRound, int multiple) {
\r
256 if(multiple == 0) {
\r
260 int remainder = numToRound % multiple;
\r
261 if (remainder == 0)
\r
263 return numToRound + multiple - remainder;
\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
274 int makeSubmenuFromArray( const char *menuEntries[], unsigned int menuEntriesSize, void *callback ) {
\r
275 if ( menuEntriesSize == 0 ) return -1;
\r
277 int menuNumber = roundUp(menuEntriesSize, 10) / 10;
\r
278 int submenuObjects[menuNumber-1];
\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
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
297 sprintf(name, "%d-%d", startNum, endNum);
\r
298 glutAddSubMenu( name, submenuObjects[i] );
\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
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
323 // Construct object menu
\r
324 int objectMenuEntriesSize = sizeof(objectMenuEntries) / sizeof(objectMenuEntries[0]);
\r
325 int objectMenu = makeSubmenuFromArray( objectMenuEntries, objectMenuEntriesSize, processObjectEvents );
\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
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
344 // Bind to right mouse button
\r
345 glutAttachMenu(GLUT_RIGHT_BUTTON);
\r
349 // You probably want to change both of the following.
\r
350 glClear(GL_COLOR_BUFFER_BIT);
\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
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
366 for(int i=0; i<NMESH; i++) meshes[i]=NULL;
\r
367 for(int i=0; i<NTEXTURE; i++) textures[i]=NULL;
\r
369 // The following is enough to run the program, but you'll
\r
370 // need to change it significantly.
\r
372 glutInit(&argc, argv);
\r
373 glutCreateWindow("Scene Editor");
\r
374 glutDisplayFunc(display);
\r