University of Dublin TRINITY COLLEGE Design of a NPR Interactive 3D Landmark Map of Dublin City Joseph Farrell B.A. (Mod.) Computer Science Final Year Project May 2007 Supervisor: Dr. John Dingliana School of Computer Science and Statistics O’Reilly Institute, Trinity College, Dublin 2, Ireland
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
University of Dublin
TRINITY COLLEGE
Design of a NPR Interactive 3D Landmark Map of Dublin City
Joseph Farrell B.A. (Mod.) Computer Science Final Year Project May 2007
Supervisor: Dr. John Dingliana
School of Computer Science and Statistics
O’Reilly Institute, Trinity College, Dublin 2, Ireland
2
Declaration
I hereby declare that this thesis is, except where otherwise stated, entirely my own work and
that it has not been submitted as an exercise for a degree at any other university.
__________________________ May 8, 2007
Joseph Farrell
3
Acknowledgements
I would like to thank my supervisor, John Dingliana, for providing me with the opportunity to
pursue this project and for his support and guidance throughout the year. I would also like to
thank my family and friends for their continued support and encouragement.
4
Abstract
Non photorealistic rendering (NPR for short) has come to denote computer generated images
and animations that are not a true to life representation of a scene. These renderings represent
the scene in different methods, to convey the key information relevant to the viewer in a
reduced format that is easier to understand. NPR for the most part gives images and models
the impression as though they were completed by hand.
The main goal of this project was to produce an interactive 3D Landmark map of Dublin City.
Various different NPR techniques were used to stylise the buildings and the map creating an
aesthetically pleasing look. These techniques reduced the complexity of the building models
whilst retaining their main characteristics.
The map which was produced has overall less complexity and despite this the landmark
buildings are more recognisable and the map as a whole is more comprehendible. This project
illustrates the value of NPR for the mapping of large geographic areas by applying it to the
key landmarks of Dublin. An example of a hand painted non-photorealistic style map of the
city of St. Petersburg illustrating this, can be seen in Figure 1.
Figure 1: A hand painted NPR style map of Saint Petersburg (Russia) produced by Escape Travel Limited [EST 07]
vn 0.000000 -1.570796 0.000000 vn 0.000000 -1.570796 0.000000 vn 0.000000 -1.570796 0.000000 vn 0.000000 -1.570796 0.000000 vn 0.000000 1.570796 0.000000 vn 0.000000 1.570796 0.000000 vn 0.000000 1.570796 0.000000 vn 0.000000 1.570796 0.000000 # 8 vertex normals g Box01 usemtl Map_Material s 2 f 4/11/4 2/9/2 1/10/1 3/12/3 s 4 f 8/12/8 7/11/7 5/9/5 6/10/6 s 8 f 6/8/6 5/7/5 1/5/1 2/6/2 s 16 f 8/4/8 6/3/6 2/1/2 4/2/4 s 32 f 7/8/7 8/7/8 4/5/4 3/6/3 s 64 f 5/4/5 7/3/7 3/1/3 1/2/1 # 6 faces G
The structure of the OBJ file format can be seen above listed file Cube.obj. It can be seen that
any line preceded by ‘#’ is a comment. Similarly the file lists the cubes vertices (v), texture
vertices (vt) and vertex normal (vn). The shapes box01 sides (s) and faces (f) are then
declared using the Map_Material as declared in the material library (mtllib) called ‘Cube.mtl’
which can be seen below.
# # Wavefront material file # Cube.mtl # newmtl Map_Material Ka 0.5882 0.5882 0.5882 Kd 0.5882 0.5882 0.5882 Ks 0.9 0.9 0.9 illum 2 Ns 4 map_Kd map_quad.bmp
26
The above two files when rendered in OpenGL resulted in the textured cube in Figure 8. It is
relatively easy to see how this file format could be used to define the more complex building
models as seen later in the report.
Figure 8: Textured box which is the rendered output from the file ‘Cube.obj’
27
Chapter 4
4. Implementation This chapter examines in detail the execution and the workings of the design as it was
outlined previously in the previous chapter. The completion of the map, the buildings and the
user interface are discussed. Also the adding of some further features and the optimisation of
the code is examined in detail.
4.1 Map
Initially it was attempted to use a matrix of raster images to represent the base map. It
immediately became apparent that this approach would not be successful as the raster images
lost their quality when zooming in or out. Another approach was tested where the raster
images were swapped with high resolution images when zooming in. This proved difficult to
smoothly implement and was wasteful of resources as the high resolution image dramatically
increased in size.
To avoid the quality loss of the raster images a vector map was required. To produce this
vector map various applications for conversion from a raster to a vector file format were tried,
nevertheless every one produced substandard maps. The only effective approach to achieve a
quality vector map was to manually trace the map.
This was completed in Adobe Illustrator as the AI file (Adobe Illustrators standard file
format) could be easily exported to an OBJ file through 3DS Max. Adobe Illustrator was used
as opposed to 3DS Max for this as it was easier to work in 2D rather than a 3D environment.
Moreover, it was relatively straightforward to export to the OBJ file format which could then
be used in OpenGL. The finished product can be seen in Figure 9 which compares the
resultant vector map to the initial raster map image when zoomed in close. This show the
dramatic improvement in the quality of the image that the selected approach enabled. From a
user point of view this enables zooming to be used to any magnification with the deterioration
of image quality.
28
Figure 9: Left raster base map image distorted due to zooming, Right vector map without any
distortion
Street labels were added to the map using Glut stroke characters. The text for the label and
details of the translations and rotations were stored in a text based file. These details were
then loaded into local memory of the program and the labels were then positioned based on
these. The positioning variables in this text file can be updated and additional labels added
through the positioning application which is discussed further in the positioning section.
After placing the main landmarks on the map some of the other regions appeared to lack
details due to being two dimensional. As a result certain elements of the maps OBJ file were
edited in 3DS Max to create raised blocks to represent irrelevant buildings on the outer edges
of the map. Blending code was a then applied to the map in the application to make these
blocks appear semi-transparent, so as not to take from the maps important landmarks. In
Figure 10 the result of this can be seen more clearly.
29
Figure 10 Screenshot demonstrating the blended blocks of outer city
4.2 Buildings
It was apparent from early research that the classic NPR rendering styles would not achieve
an effective style. As you can seen in figure 11 where some classic NPR shader’s have been
applied to a building model from the Virtual Dublin project [VDUB 05]. These results proved
unsatisfactory as the models are heavily reliant on the detail contained in the textures and this
detail was lost with these NPR techniques. It was made clear from this that this project would
have to focus on a different approach. A decision was made to use a combination of texture
filtering and outlining, this kept the necessary detail from the textures whilst still achieving an
effective NPR style. The following sections delve more deeply into how exactly this was
accomplished.
Figure 11: Example of a heavily texture mapped building model rendered normally with its
texture (left), cel-shaded (centre) and pencil shaded (right)
30
Prior to proceeding with the texture filtering some additional alterations were also made to the
models. The first of these was polygon reduction. This involved the manipulation of the
models using 3DS Max. As these models were intended to be photorealistic some contained
excessive detail. Reducing the polygon count of the models reduced this detail sufficiently.
This alteration both aided the NPR style and the reduction of the size of the model. Also
texture sizes were reduced by 50% using the batch conversion tool of Adobe Photoshop. This
was also needed as these models were initially intended to be photorealistic and the high
resolution of these textures was not necessary for this application.
4.2.1 Texture Filtering
Various different algorithms were applied to the textures of the models to achieve an effective
NPR style. These algorithms were adapted from previous work [IMP 07] and were used for
increasing contrast, increasing brightness and greyscale conversion. An example of the photo-
realistic buildings from which work began can be seen in Figure 12 below.
Figure 12: A photo- realistic rendering of City Hall (File-size: 1.6mb)
Firstly the textures were converted to greyscale and this was achieved by use of the following
algorithm. This code scans through all the picture elements (pixels). Each pixel has a red,
green and blue value and to convert the image to greyscale each pixel value is updated with
an average of the three.
31
// Manipulate the image data converting to greyscal e for ( int pixel=0; pixel < numPixels*bytesInPixel; pixel+=bytesInPixel){ imageData[pixel]=(imageData[pixel]+imageData[pixel +1]
+imageData[pixel+2])/3; imageData[pixel+1]=(imageData[pixel]+imageData[pix el+1] +imageData[pixel+2])/3; imageData[pixel+2]=imageData[pixel]+imageData[pixe l+1] +imageData[pixel+2])/3; } Secondly this algorithm manipulates all the pixels increasing the value of all elements by a set
value. Testing of different values for this variable shown £% giving the optimal output. This
resulted in an increase in brightness of the image from its previous state.
// setup variables for brightness manipulation int light = 35; int Light_transform[256]; for (i=0;i<256;i++){ Light_transform[i]=i+light; if (Light_transform[i]>255) Light_transform[i]=255; if (Light_transform[i]<0) Light_transform[i]=0; } // Manipulate the image data increasing brightness for ( int pixel=0; pixel < numPixels*bytesInPixel; pixel+=by tesInPixel){ imageData[pixel] = Light_transform[imageData[pixe l]]; imageData[pixel+1] = Light_transform[imageData[pi xel+1]]; imageData[pixel+2] = Light_transform[imageData[pi xel+2]]; }
This third algorithm below manipulates the contrast of the texture. The code first sets up a
contrast transformation array based on a contrast variable. The code then scans through all the
pixels updating each element based upon this transform array.
// set up variables for contrast manipulation float contrast = 0.8; int Contrast_transform[256]; for ( int i=0;i<256;i++){ if (i<( int )(128.0f+128.0f*tan(contrast))&&
i>( int )(128.0f-128.0f*tan(contrast))) Contrast_transform[i]=(i-128)/tan(contrast)+128 ; else if (i>=( int )(128.0f+128.0f*tan(contrast))) Contrast_transform[i]=255; else Contrast_transform[i]=0; } // Manipulate the image data increasing contrast for ( int pixel=0; pixel < numPixels*bytesInPixel; pixel+=by tesInPixel){ imageData[pixel] = Contrast_transform[imageData[p ixel]];
• These files are used for the loading of the OBJ buildings models. Thanks to
John Hamill (ISG group TCD) for use of this piece of code.
Buildings.txt and labels.txt
• These two text files store the data and translation details for both the street
labels and the building models.
The remaining folders contain all the building and map models which are stored
in different grouped folders.
46
Appendix B. Application Screenshots
The following is a selection of screenshots of the application in operation:
Figure 21: Application Screenshot 1
Figure 22: Application Screenshot 2
47
Figure 23: Application Screenshot 3
Figure 24: Application Screenshot 4
48
Appendix C. Code Listing
The full source code for this project is included on the enclosed CD. Below is the code listing
for the file ‘main.cpp’, which contains the core of the coding for this application.
An Interactive Computer Generated Landmark Map of D ublin #include <string.h> // String class to hold Label data #include <GL/glut.h> // GlUT and OpenGL #include <GL/glui.h> // GLUI #include <windows.h> // for timeGetTime() for framerate #include <mmsystem.h> #include <stdio.h> // input, output #pragma comment( lib , "ImageLoader.lib") / / ImageLoader Library #include "OBJModel.h" // OBJModel class #include "ImageLoader.h" // ImageLoader class // Variables to hold frame-start and // frame-end times for frame-rate calculations... DWORD frameStartTime, frameEndTime; // Variables required for the mouse interactions float xy_aspect; int last_x, last_y; float rotationX = 0.0, rotationY = 0.0; /** These are the live variables passed into GLUI * **/ int obj_type = 1; int main_window; float scaleX=1.0,scaleY=1.0,scaleZ=1.0; // Variable to store the display state of specific items int show_build = 1; int show_map = 1; int show_text = 1; int show_bare = 0; // Variable function stares int curr_highlight = 0; int curr_find = 0; int last_find; // Top 20 landmarks for find and highlight function s char *string_list[] = {" ", "Art Gallery", "Bank Of Ireland","Berkley Hall", "Busaras","Central Bank", "Christ Church","City Hall", "Civic Offices","Dublin Castle", "Dublin Spire","Hapenny Bridge", "IFSC","Liberty Hall", "Stephens Green","The Custom's House",
49
"The Four Courts","Trinity Campanile", "Trinty Chapel","Trinity Museum"}; // Scaling and other interaction variables are decl ared float scale = 3.0; float view_rotate[16] = { 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0 ,0,1 }; float obj_pos[] = { 0.0, 0.0, 0.0 }; float RotateAngle = 0.0; float TiltAngle = -50.0; // Number of buildings int NumBuilds; // variable for label details const int NumLabels=40; char LabelsName[NumLabels][30]; char llabel[40]; float lrot[NumLabels]; float lx[NumLabels],ly[NumLabels],lz[NumLabels]; // Variables for mouse camera manipualtion int mouseX,mouseY,prevMouseX,prevMouseY; float mouseSensitivity = 0.05; bool cameraMove = false ; bool cameraZoom = false ; // Translation for Find functionality float find_pos[20][2]={{0.,0.0},{-0.151,-3.0},
{-18.5,-0.35},{-14.5,-1.7}, {-18.2,0.349},{-14.75,-1.5}, {-8.4,9.89},{-11.0,3.2}, {1.2,7.95},{-3.65,7.65}, {-6.4,-10.0},{-1.5,7.5}, {-19.95,3.15},{-4.2,0.7}, {-5.9,1.25},{-3.35,0.25}}; // OBJ OBJECTS Arrays COBJModel Buildings1[20]; COBJModel Buildings2[20]; COBJModel Buildings3[20]; COBJModel Buildings4[20]; COBJModel Buildings5[20]; COBJModel Buildings6[20]; COBJModel Buildings7[20]; // OBJ model for general map COBJModel map01; // OBJ model for rivers.. COBJModel map_rivers; /** Pointers to the windows and some of the control s we'll create **/ GLUI *glui; GLUI_Checkbox *checkbox; GLUI_Spinner *spinner; GLUI_RadioGroup *radio; GLUI_Panel *obj_panel; #define RESET_ID 303 /*********** control_cb() ******/ /* GLUI control callback */ void control_cb( int control )
50
{ // Rest the interaction values when if ( control == RESET_ID ) { scale = 3.0; obj_pos[0] = 0.0; obj_pos[1] = 0.0; RotateAngle = 0.0; TiltAngle = -50.0; show_build = 1; show_map = 1; show_text = 1; curr_highlight = 0; curr_find = 0; } } /*********************** FrameStart() ************* ******/ void FrameStart( void ) { // Record frame start time frameStartTime = timeGetTime(); } /********************* FrameEnd() ***************** **/ void FrameEnd( void *font, GLclampf r, GLclampf g, GLclampf b, GLfloat x, GLfloat y) { float elapsedTime; char str[30]; char *ch; GLint matrixMode; // Record frame end time frameEndTime = timeGetTime(); // Calculate the frames per second elapsedTime = (frameEndTime-frameStartTime); elapsedTime = elapsedTime/1000; sprintf(str, "Frames per second: %2.0f", 1.0/elap sedTime); glGetIntegerv(GL_MATRIX_MODE, &matrixMode); // Display the calculated FPS value glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0.0, 1.0, 0.0, 1.0); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glPushAttrib(GL_COLOR_BUFFER_BIT); /* save current colour */ glColor3f(r, g, b); glRasterPos3f(x, y, 0.0); for (ch= str; *ch; ch++) { glutBitmapCharacter(font, ( int )*ch); } glPopAttrib(); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(matrixMode); } /*************** GlutKeyboard() **********/ void GlutKeyboard( unsigned char Key, int x, int y) { // Update variable based upon key inputs switch (Key)
51
{ case 27 : case 'q': case 'Q': exit(0); break ; case 'a': case 'A': TiltAngle+=1; break ; case 'z': case 'Z': TiltAngle-=1; break ; case 's': case 'S': RotateAngle+=1; break ; case 'd': case 'D': RotateAngle-=1; break ; case 'f': case 'F': scale+=0.1; break ; case 'v': case 'V': scale-=0.1; break ; }; glutPostRedisplay(); } /************** SpecialKeys() **********/ void SpecialKeys( int key, int x, int y) { switch (key) { case GLUT_KEY_UP: // to pan up obj_pos[1]+=1.0f; break ; case GLUT_KEY_DOWN: // to pan down obj_pos[1]-=1.0f; break ; case GLUT_KEY_LEFT: // to pan left obj_pos[0]-=1.0f; break ; case GLUT_KEY_RIGHT: // to pan right obj_pos[0]+=1.0f; break ; }; glutPostRedisplay(); }
52
/***************************************** GlutIdle () ***********/ void GlutIdle( void ) { /* According to the GLUT specification, the current window is // Undefined during an idle call-back // So we need to explicitly change it if necessa ry */ if ( glutGetWindow() != main_window ) glutSetWindow(main_window); glutPostRedisplay(); } /************************* MouseClick() **********/ void MouseClick( int button, int state, int x, int y){ // Set camera move to true when left click down if (button == GLUT_LEFT_BUTTON){ switch (state){ case GLUT_DOWN:cameraMove=true ; break ; case GLUT_UP:cameraMove= false ; break ; } } // Set camera zoom to true on right click down if (button == GLUT_RIGHT_BUTTON){ switch (state){ case GLUT_DOWN:cameraZoom=true ; break ; case GLUT_UP:cameraZoom= false ; break ; } } } /********************* GlutMotion() **********/ void MouseMotion( int x, int y){ // If camerMove true edit translation variables
// as determined by the previous mouse position if (cameraMove){ obj_pos[0] += (x - prevMouseX) * mouseSensitivity ; obj_pos[1] -= (y - prevMouseY) * mouseSensitivity ; } // If camerZoom true edit map scaling variable // as determined by the previous mouse posi tion if (cameraZoom){ if (!cameraMove){ scale -= (y - prevMouseY) * mouseSensitivity; } } // Record the previous mouse positions prevMouseX=x; prevMouseY=y; } /******************* MouseMove() **********/ void MouseMove( int x, int y){ // Record the previous mouse positions prevMouseX=x; prevMouseY=y; } /***************** GlutReshape() *************/ void GlutReshape( int x, int y )
// Translate to position rivers correctly glColor3f(0.801, 0.801, 0.901); // Set Colour map_rivers.DrawModel(); // Draw the rivers obj glPopMatrix(); } /********************* DrawLabels() **********/ // Draw Labels as per the previous loaded data from file void DrawLabels() { glPushMatrix(); glColor3f(0, 0, 0); // Set the colour to black glScalef( 0.001, 0.001, 0.001 ); // Scale to set the size of the font for ( int num =0; num<NumLabels ;num++){ glPushMatrix(); // Translate and Rotate to the required position glTranslatef( lx[num], ly[num], lz[num] ); glRotatef( lrot[num], 0.0, 0.0, 1.0 ); for ( int c=0; LabelsName[num][c]!= '\0'; c++) { glLineWidth(2); if (LabelsName[num][c]== '_'){ glutStrokeCharacter(GLUT_STROKE_ROMAN ,' '); } else {
54
glutStrokeCharacter(GLUT_STROKE_ROMAN ,LabelsName[ num][c]); } } glPopMatrix(); } glPopMatrix(); } /************************ LoadBuildings() ********* */ // Load the buildings based on data from files void LoadBuildings() { // Attributes for temporary storage of building dat a FILE *buildfp; // File used to obtain data char *BuildName[30]; // Temp store for specific buildings name char dirname[60]; // Temp store for buildings directory data char BuildFilename[60]; // Temp store for the buildings filenames char LISTTYPE[20]; // Temp store for specific buildings type float bx=0.0,by=0.0,bz=0.0; // Temp store for buildings translations // Open the file containing the Building Data buildfp=fopen("Buildings.txt","r"); // Buffer used to scan through the data in the file char buildingBuffer[256]; fgets(&buildingBuffer[0],250,buildfp); // Read the first line of data // While this line is a comment ( denoted by '#' sy mbol) while (buildingBuffer[0] == '#'){ fgets(&buildingBuffer[0],250,buildfp); // Skip the comments } // Read the number of builds to load from the file sscanf(&buildingBuffer[0],"Number Of Buildings: %d ",&NumBuilds); // While the number of buildings loaded is
// less than number requiring loading loop through following code for ( int bload=0;bload<NumBuilds;bload++){ // Read the next line of data fgets(&buildingBuffer[0],250,buildfp); // While this line is a comment( denoted by '#' sym bol) while (buildingBuffer[0] == '#'){ fgets(&buildingBuffer[0],250,buildfp); // Skip the comments } sscanf(&buildingBuffer[0],"%s %s %s x%f y%f z%f %s \n",&BuildName[0], &dirname[0],&BuildFilename[0],&bx,&by,&bz,&LISTT YPE[0]); if (bload<20){ std::cout<<std::endl<<"
if ((bload>19)&&(bload<40)){ std::cout<<std::endl<<"
Loading Building2 array at positon "<<bload-20<< std::endl; Buildings2[bload-20].LoadCOBJModel2(dirname,Build Filename); Buildings2[bload-20].SetOBJPos(bx,by,bz); Buildings2[bload-20].ModelNumber=bload; Buildings2[bload-20].LockPosition(); Buildings2[bload-20].ReadMTL(TEX_S3TC|TEX_PALETT ED); } if ((bload>39)&&(bload<60)){ std::cout<<std::endl<<"
Loading Building3 array at positon "<<bload-40<< std::endl; Buildings3[bload-40].LoadCOBJModel2(dirname,Build Filename); Buildings3[bload-40].SetOBJPos(bx,by,bz); Buildings3[bload-40].ModelNumber=bload; Buildings3[bload-40].LockPosition(); Buildings3[bload-40].ReadMTL(TEX_S3TC|TEX_PALETT ED); } if ((bload>59)&&(bload<80)){ std::cout<<std::endl<<"
Loading Building4 array at positon "<<bload-60<<s td::endl; Buildings4[bload-60].LoadCOBJModel2(dirname,Build Filename); Buildings4[bload-60].SetOBJPos(bx,by,bz); Buildings4[bload-60].ModelNumber=bload; Buildings4[bload-60].LockPosition(); Buildings4[bload-60].ReadMTL(TEX_S3TC|TEX_PALETT ED); } if ((bload>79)&&(bload<100)){ std::cout<<std::endl<<"
Loading Building5 array at positon "<<bload-80<< std::endl; Buildings5[bload-80].LoadCOBJModel2(dirname,Build Filename); Buildings5[bload-80].SetOBJPos(bx,by,bz); Buildings5[bload-80].ModelNumber=bload; Buildings5[bload-80].LockPosition(); Buildings5[bload-80].ReadMTL(TEX_S3TC|TEX_PALETT ED); } if ((bload>99)&&(bload<120)){ std::cout<<std::endl<<"
Loading Building6 array at positon "<<bload-100 <<std::endl; Buildings6[bload-100].LoadCOBJModel2(dirname,Buil dFilename); Buildings6[bload-100].SetOBJPos(bx,by,bz); Buildings6[bload-100].ModelNumber=bload; Buildings6[bload-100].LockPosition(); Buildings6[bload-100].ReadMTL(TEX_S3TC|TEX_PALET TED); } if ((bload>119)&&(bload<140)){ std::cout<<std::endl<<"
Loading Building7 array at positon "<<bload-12 0<<std::endl; Buildings7[bload-120].LoadCOBJModel2(dirname,Buil dFilename); Buildings7[bload-120].SetOBJPos(bx,by,bz); Buildings7[bload-120].ModelNumber=bload; Buildings7[bload-120].LockPosition(); Buildings7[bload-120].ReadMTL(TEX_S3TC|TEX_PALET TED); } } // close the buildings file fclose(buildfp);
56
} /******************************* LoadLabels() ***** *****/ // Load the Labels based on data from files void LoadLabels() { // load labels for the map FILE *labelfp; labelfp=fopen("Labels.txt","r"); char labelBuffer[256]; fgets(&labelBuffer[0],250,labelfp); while (labelBuffer[0] == '#'){ // Skip Comments fgets(&labelBuffer[0],250,labelfp); } //int NumLabel; //sscanf(&labelBuffer[0],"Number Of Labels: %d",&Nu mLabel); for ( int lload=0;lload<NumLabels;lload++){ fgets(&labelBuffer[0],250,labelfp); while (labelBuffer[0] == '#'){ // Skip Comments fgets(&labelBuffer[0],250,labelfp); } sscanf(&labelBuffer[0],"%s x%f y%f z%f r%f\n",&LabelsName[lload][0],&lx[lload],&ly[lload], &lz[lload],&lrot[lload]); } // close the Labels file fclose(labelfp); } /******************************* LoadMap() ******** **/ void LoadMap() { // Load map OBJ map01.LoadCOBJModel2("Map","map04.obj"); map01.ReadMTL(); // Load river OBJ map_rivers.LoadCOBJModel2("Map","rivers.obj"); map_rivers.ReadMTL(); } /**********************DrawBuildings() **********/ void DrawBuildings() { glRotatef( 90.0f, 1.0, 0.0, 0.0 ); // rotate onto map glScalef( 0.015f, 0.015f, 0.015f); // Scaling of buildings glTranslatef(-435.0f, 10.0f, 102.0f); // translate to so as building positions are correc t glPushMatrix(); glScalef( scaleX, scaleY, scaleZ); // Scaling of buildings glPushAttrib( GL_ENABLE_BIT ); glEnable (GL_LINE_SMOOTH); glEnable (GL_POLYGON_SMOOTH );
57
glEnable(GL_CULL_FACE); glCullFace (GL_FRONT); //Enable front face Culling glPushAttrib( GL_POLYGON_BIT | GL_C URRENT_BIT ); glPolygonMode(GL_FRONT_AND_ BACK, GL_LINE); glLineWidth(2); for ( int cc=0;cc<NumBuilds;cc++){ if (cc==(curr_highlight-1)){ glColor3f(1, 0, 0); // Draw highlighted red silhouette } else { glColor3f(0, 0, 0);
// Assume all other silhouettes black } if (cc<20){ Buildings1[cc].DrawModel(); } if ((cc>19)&&(cc<40)){ Buildings2[cc-20].DrawModel(); } if ((cc>39)&&(cc<60)){ Buildings3[cc-40].DrawModel(); } if ((cc>59)&&(cc<80)){ Buildings4[cc-60].DrawModel(); } if ((cc>79)&&(cc<100)){ Buildings5[cc-80].DrawModel(); } if ((cc>99)&&(cc<120)){ Buildings6[cc-100].DrawModel(); } if ((cc>119)&&(cc<140)){ Buildings7[cc-120].DrawModel(); } } glPopAttrib(); glDisable(GL_CULL_FACE); glColor3f(1, 1, 1); for ( cc=0;cc<NumBuilds;cc++){ if (show_bare==0){ if (cc<20){ Buildings1[cc].DrawModel(); } if ((cc>19)&&(cc<40)){ Buildings2[cc-20].DrawModel(); } if ((cc>39)&&(cc<60)){ Buildings3[cc-40].DrawModel(); } if ((cc>59)&&(cc<80)){ Buildings4[cc-6000].DrawModel(); } if ((cc>79)&&(cc<100)){ Buildings5[cc-80].DrawModel(); } if ((cc>99)&&(cc<120)){ Buildings6[cc-100].DrawModel();
58
} if ((cc>119)&&(cc<140)){ Buildings7[cc-120].DrawModel(); } } if (show_bare==1){ if (cc<20){ Buildings1[cc].DrawModelBare(); } if ((cc>19)&&(cc<40)){ Buildings2[cc-20].DrawModelBare(); } if ((cc>39)&&(cc<60)){ Buildings3[cc-40].DrawModelBare(); } if ((cc>59)&&(cc<80)){ Buildings4[cc-6000].DrawModelBare(); } if ((cc>79)&&(cc<100)){ Buildings5[cc-80].DrawModelBare(); } if ((cc>99)&&(cc<120)){ Buildings6[cc-100].DrawModelBare(); } if ((cc>119)&&(cc<140)){ Buildings7[cc-120].DrawModelBare(); } } } glPopAttrib(); glPopMatrix(); } /********************** GlutDisplay() ************* ****/ void GlutDisplay( void ) { glClearDepth( 1.0 ); glClearColor( 1.0f, 1.0f, 1.0f, 1.0f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_ BIT ); // Start the Frames per second counter ... FrameStart(); glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glFrustum( -xy_aspect*.04, xy_aspect*.04, -.04, .04, .1, 1500.0 ); glMatrixMode( GL_MODELVIEW ); glLoadIdentity(); // Apply Restrictions To trans,rotates.. if (scale>18.0){scale=18.0;} if (scale<0.7){scale=0.7;} if (TiltAngle<-140.0){TiltAngle=-140.0;} if (RotateAngle>70.0){RotateAngle=4.0;} if (RotateAngle<-70.0){RotateAngle=-70.0;} if (obj_pos[0]<-24.0){obj_pos[0]=-24.0;} if (obj_pos[0]>33.0){obj_pos[0]=33.0;} if (obj_pos[1]<-26.0){obj_pos[1]=-26.0;} if (obj_pos[1]>36.0){obj_pos[1]=36.0;}
59
// Apply translations for finding buildings if (curr_find!=last_find){ obj_pos[0]=find_pos[curr_find][0]; obj_pos[1]=find_pos[curr_find][1]; } last_find=curr_find; glTranslatef( 0.0, 0.0, -30.0f ); // Apply the tilt angle to the map glRotatef(TiltAngle,1,0,0); // Rotations via the Glui rotation ball glMultMatrixf( view_rotate ); // Scaling for zooming in and out glScalef( scale, scale, scale ); // Translation for pan up, down, left, right... glTranslatef( -obj_pos[0], -obj_pos[1], obj_pos[2 ] ); // Apply the rotation angle to the map glRotatef(RotateAngle,0,0,1); // If show_map is true draw map.. if ( show_map ){ DrawMap(); } // show text if ( show_text ) { DrawLabels(); } if ( show_build ) { DrawBuildings(); // DrawGenericBuild(); } // Frame counter display code FrameEnd(GLUT_BITMAP_HELVETICA_12, 0.0, 0.0, 0.0, 0.05, 0.95); glFlush(); glutSwapBuffers(); } /*********************** main() ******************* */ void main( int argc, char * argv[]) { /****************************************/ /* Initialize GLUT and create window */ /**************************************** //glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLU T_DOUBLE); glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGBA | GL UT_DEPTH ); glutInitWindowPosition( 20, 20 ); glutInitWindowSize( 900, 700 ); main_window = glutCreateWindow( "Dublin City Map" ); // Set OpenGL parameters glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); LoadMap(); // Load OBJ map files
60
LoadBuildings(); // Load buildings from obj files LoadLabels(); / / Load Labels for street names etc.. glutDisplayFunc( GlutDisplay ); // Main display function GLUI_Master.set_glutReshapeFunc( GlutReshape ); // Function to deal with window resizing GLUI_Master.set_glutKeyboardFunc( GlutKeyboard ); // Function to deal with key press GLUI_Master.set_glutSpecialFunc( SpecialKeys ); // Special key function set to NULL GLUI_Master.set_glutMouseFunc(MouseClick); // Function to deal with mouse click glutMotionFunc(MouseMotion); // Function to deal with mouse motion glutPassiveMotionFunc(MouseMove); // Function to deal with mouse move /****************************************/ /* Here's the GLUI code */ /****************************************/ // Print the Glui Version printf( "GLUI version: %3.2f\n", GLUI_Master.get_ version() ); /*** Create the side sub window ***/ glui = GLUI_Master.create_glui_subwindow( main_wi ndow, GLUI_SUBWINDOW_RIGHT ); // Spacer glui->add_statictext( " " ); // Static Text glui->add_statictext( " CONTROLS" ); // Seperator bar glui->add_separator(); // Pan Up, Down, Left, Right GLUI_Translation *trans_xy = glui->add_translation( "Pan [ML]", GLUI_TRANSLAT ION_XY, obj_pos ); trans_xy->set_speed( .01 ); // Seperator bar glui->add_separator(); // Zoom in, out GLUI_Translation *trans_z = glui->add_translation( "Zoom [f,v,MR]", GLUI_TRAN SLATION_Z, &obj_pos[2] ); trans_z->set_speed( .1 ); // Seperator bar glui->add_separator(); // Rotate Left or Right GLUI_Translation *trans_x = glui->add_translation( "Rotate [s,d]", GLUI_TRANS LATION_X, &RotateAngle ); trans_x->set_speed( .1 ); // Seperator bar glui->add_separator(); // Increase or decrease the tilt angle GLUI_Translation *tansy = glui->add_translation( "Tilt Angle [a,z]", GLUI_T RANSLATION_Y, &TiltAngle ); trans_y->set_speed( .1 );
61
// Seperator bar glui->add_separator(); // Rotation Ball GLUI_Rotation *view_rot = glui->add_rotation( "Ro tation", view_rotate ); view_rot->set_spin( 0.0005 ); // Seperator bar glui->add_separator(); // Spacer glui->add_statictext( "" ); /*** Add Rollout for Options ***/ GLUI_Rollout *options = glui->add_rollout( "Optio ns", true ); glui->add_checkbox_to_panel( options, "Display Ma p", &show_map ); glui->add_checkbox_to_panel( options, "Display Te xt", &show_text ); glui->add_checkbox_to_panel( options, "Display Bu ildings", &show_build ); glui->add_checkbox_to_panel( options, "Draw Bare Models", &show_bare ); // Spacer glui->add_statictext( "" ); GLUI_Spinner *segment_spinner2 = glui->add_spinner( "Scale Buildings Height:", G LUI_SPINNER_FLOAT, &scaleY ); segment_spinner2->set_float_limits( .2f, 4.0 ); // Spacer glui->add_statictext( "" ); /**** Add listbox ****/ GLUI_Listbox *list2 = glui->add_listbox( "Find:", &curr_find ); int j; for ( j=0; j<20; j++ ) list2->add_item( j, string_list[j] ); /**** Add listbox ****/ GLUI_Listbox *list = glui->add_listbox( "Highligh t:", &curr_highlight ); int i; for ( i=0; i<20; i++ ) list->add_item( i, string_list[i] ); // Spacer glui->add_statictext( "" ); // A 'quit' button to terminate program glui->add_button( "Reset", RESET_ID, control_cb); // A 'quit' button to terminate program glui->add_button( "Quit", 0,(GLUI_Update_CB)exit ); /** Link windows to GLUI, and register idle callbac k ****/ glui->set_main_gfx_window( main_window ); /**** We register the idle callback with GLUI, *not * with GLUT **/ GLUI_Master.set_glutIdleFunc( GlutIdle ); /**** Regular GLUT main loop ****/ glutMainLoop(); }
62
Bibliography
[EST 07] “Escape Travel Ltd - NPR map of St. Petersburg” http://www.escapetravel.spb.ru/images/map.jpg Last checked 7 May 02007 [SST 07] “Shrek Sequel - a picture that's worth 20 terabytes”
http://www.sfgate.com/cgi-bin/article.cgi?file=/chronicle/archive/2004/06/21/BUGME78HCO1.DTL Last checked 7 May 2007
[SHB86] “Hairy Brushes” Proceedings of an annual conference on Computer Graphics
Steve Strassman August 1986 [ANS87] “Drawing natural scenery by computer graphics”
T. T. Sasada May 1987
[NPA 07] “NPR architectural example”
http://www.cs.northwestern.edu/academics/courses/special_topics/395-npr/npr/resource.html Last checked 7 may 2007