ECS175
ECS 175 Introduction to Computer Graphics
Spring Quarter 2000
Programming Assignment 1
2D Scan Conversion
Due Date: Thursday, April 20, 2000
In this assignment, you will have to write a basic polygon scan converter forming the basis for the graphics library developed during this course. These are the basic requirements (for details on each, see the following sections):
- Write the foundations of a graphics library that implements the OpenGL API.
- Write a scan-converter capable of rendering convex, flat-shaded polygons and polygon outlines into the frame buffer.
- Provide a user interface to select rendering parameters.
- Write a scanner to read in scene files.
The Application Program Interface
All the assignments of this course together will build a small graphics library whose functionality will be a subset of the industry-standard OpenGL library. To show you one possible way of structuring a library, and to teach using the OpenGL library at the same time, all assignments will require that all parts of your graphics libraries will implement the respective parts of the OpenGL API.
The OpenGL API describes a state machine, in the meaning that a drawing command does not specify all possible drawing parameters to the library (with gazillions of options inside OpenGL, that would be impossible anyway...). Instead, all drawing commands are executed in the context of the current state. The current state includes the current color, the current transformation matrix, the current polygon draw style, etc.
For this assignment, you have to implement the first parts of the current rendering state. Later projects will add more content to the state. These are the settings your library will have to keep track of for now:
- The current viewport
- To be able to render into an area of the screen, OpenGL has to know where this area is and how large it is. All vertex coordinates passed to the OpenGL scan converter lie in the range [-1, 1] in both x and y. The viewport tells OpenGL how to transform these "canonical" coordinates into window coordinates. Coordinates of -1 are mapped to the left or lower edges of the window, respectively, and coordinates of 1 are mapped to the right or upper edges of the window, respectively. The current viewport is set with the glViewport(x, y, width, height) command.
- The current color
- The current color is stored as an (R, G, B) triple of floating-point values, each in the range [0, 1]. The current color is set with the glColor3f(r, g, b) command. The passed color values can not be assumed to lie inside the valid range - your library has to clamp the values.
- The current polygon drawing mode
- The current polygon drawing mode determines whether polygons are drawn as outlines or filled. The possible values of the drawing mode are GL_FILL and GL_LINE. The current drawing mode is set with the glPolygonMode(GL_FRONT_AND_BACK, mode) command. Don't worry about the first parameter - OpenGL can treat the front- and backsides of polygons differently. Your library does not have to. But to make the API compatible, your library should check the value of the first parameter, and generate an error if it is something else than GL_FRONT_AND_BACK.
Rendering Polygons
In the API used for this assignment, a polygon is specified to the graphics library by a code segment like this:
glBegin(GL_POLYGON);
glColor3f(r1, g1, b1);
glVertex2f(x1, y1);
glColor3f(r2, g2, b2);
glVertex2f(x2, y2);
/* ... */
glColor3f(rn, gn, bn);
glVertex2f(xn, yn);
glEnd();
The call to glBegin(GL_POLYGON) tells the library to expect vertex coordinates for a polygon next. The number of vertices of that polygon is not known in advance, but there exists a system-defined upper limit. (For your programs, 128 could be a reasonable limit). The vertices of a polygon are specified with calls to glVertex2f(x, y). Vertices are either specified in clockwise or counter-clockwise orientation around the polygon's boundary. You can assume that all polygons specified are convex.
In OpenGL, calls to glColor3f(r, g, b) can be mixed with calls to glVertex2f(x, y). In later assignments, this will be the way to specify different colors for each vertex of a polygon, to achieve smooth shading (the color of a vertex is the value of the current color at the time the vertex was specified). To render constant-colored polygons, as this assignments asks for, OpenGL uses the color of the first vertex is specified as the color for the complete polygon.
The polygon specification finishes when glEnd() is called. At this point, OpenGL remembers that it has to draw a polygon, and it will connect all the points passed between glBegin() and glEnd() to form a convex polygon. Then, depending on the polygon draw mode, it will either fill the polygon or draw its outline. In this assignment, where polygons are only constant-colored, it will take the color of the first vertex for both filling and outline drawing.
The scan converter you will implement for this assignment will form the basis for the rest of the library. Therefore, you should structure it in a way that makes it easily extendable - you will need it later, and spending a bit more time on design instead of just coding along will save huge amounts of time later.
User Interface
The user interface you will have to write for this assignments has to provide a graphics window to contain the program's output, and an input area in which a user can specify different rendering parameters. (For this assignment, that will only be the polygon drawing mode).
To get you started, a simple user interface based upon the XForms library (version 0.89) will be provided. You can download two versions of a framework program from the course account; the first version will be set up to use the "real" OpenGL library, the second one is set up to use the graphics library you will have to write. You don't have to use these frameworks, but they might help in getting you started.
Scanner
Your program has to be able to read in text files describing the scenes to render. The format of these text files will stick very closely to the API implemented by your graphics library. For this assignment, your scanner program has to be able to parse text files according to the following context-free grammar:
; Floating-point numbers in standard C format
; (also, this happens to be the format that scanf() can read...)
float: [+-]?[0-9]*.[0-9]+
| [+-]?[0-9]*
| [+-]?.[0-9]*
| [+-]?[0-9]*.[0-9]+[eE][0-9]+
| [+-]?[0-9]*+[eE][0-9]+
| [+-]?.[0-9]+[eE][0-9]+
; Color definitions (with RGB values):
color: "color" float "," float "," float
; Vertex definitions (with x,y values):
vertex: "vertex" float "," float
; A command that can appear between a begin and the associated end:
bracketCommand: color
| vertex
; A list of those:
bracketCommandList: bracketCommand
| bracketCommand bracketCommandList
; Polygon definitions:
polygon: "beginPolygon" bracketCommandList "end"
; A command that can appear outside a begin/end bracket:
outerCommand: color
| polygon
; The input file is a (possibly empty) list of those:
input: ; Nothing
| outerCommand input
This grammar will be extended for later assignments, so take care to make the scanner easily extendable.
Brief Summary
To summarize, these are the steps we recommend to do the project:
- Download the GL version of the framework files from the course account (/home/cs175r/samples/project1/GL) to your directory.
- Create a C++ source file for the scanner, let's call it Scanner.cpp. Don't forget to add this file to the makefile as well.
- Write the implementation of the scanner, say the function is called scanFile, into Scanner.cpp.
- Fill in the code that calls the scanner into the redraw function inside the source file Framework_cb.cpp. This function is going to be called whenever the graphics window has to be redrawn for some reason. There are two global variables inside Framework_cb.cpp that are set up by the user interface:
- inputFilename is the name of the file the user selected last.
- polygonMode is the current drawing mode (either GL_LINE or GL_FILL).
Essentially, the only line you have to add to the redraw function is the one that calls the main scanner function, scanFile. Don't forget to add a prototype for that function to the Framework_cb.cpp file!
- If you want to use OpenGL to render the third scene file we provided, you have to explicitly turn off smooth shading (it's the default). To do this, add the line glShadeModel(GL_FLAT) before any other GL commands inside the redraw function.
- As soon as you see correct renderings of the three scene files, you can move on to doing the "real" assignment by downloading the GL-less version of the framework into your directory. This will overwrite Framework_cb.cpp, so make sure to copy all code you filled into that one.
- The GL-less framework can use the same scanner, the only difference is that it doesn't know all the GL commands by itself, so you have to implement them. The framework includes a file OpenGL.cpp that you should put your functions into. The only graphics function that you need (and that you are allowed to use) from your GL functions is plotPixel(int x, int y, float r, float g, float b). It is used to set the color of a single pixel with coordinates (x, y) to the color (r, g, b).
- For all assignments in this course, you will have to exactly implement the OpenGL API. Therefore, your functions have to have the same names and the same types as the original functions. If you are in doubt what the exact type of any GL function is, type man <functionName> to find out.
General Note
This assignment is asking you for a lot of stuff to implement; also, for some stuff that you might never have implemented before. It is very important that you start early on this assignment, and that you spend some time on designing it before you start coding.
Think about what parts you have to implement, and how these parts have to interact with each other. The graphics library you are going to implement is very flexible, and your code has to support this flexibility.