August 15, 2005
The chapter 3 programming exercises in Angel suggest, firstly,
creating a simple drawing program that uses the mouse to create
simple shapes such as line segments, rectangles and triangles.
Use menus to select modes and to allow the user to change the
drawing color.
This seems pretty ambitious to my trepiditious
self. Squarely facing the problem I emulate the great artists
and learn by first borrowing from others. I borrowed this from
Michael Glass at
Valparaiso University, which claims to be somewhere in Indiana.
I love this guy's approach with the skeleton code.
How it does it
The attached OpenGL/GLUT skeleton program (also
available from the class web page) contains an array of pixels
called canvas. Each pixel in the array contains three bytes of type
rgb_pixel for the three colors. The program sets up OpenGL so that
it uses OpenGL pixel mode to draw the entire canvas from this pixel
array.
Note: Functions include
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); ll
The parts marked with question marks allow you
to insert your own code. The screen is initialized, the viewing
camera is set for 2D orthogonal, and callbacks are installed for
you. If you update the pixel array, it will automatically appear on
the screen.
The array is one dimensional, but you can index the pixel array
using the LOC macro. The x and y coordinates range from 0 to
screenWidth and screenHeight respectively. The following illustrates
setting a single pixel element at x0 and y0
to pure white:
canvas[LOC(x0,y0)].r =
255;
canvas[LOC(x0,y0)].g = 255;
canvas[LOC(x0,y0)].b = 255;
Alternatively you could have a single variable
that contains pure white:
rgb_pixel white = {255,
255, 255};
...
canvas[LOC(x0,y0)] = white;
At the beginning of the program, immediately
after InitGL(), set the entire
canvas to light blue with
for(i = 0; i < screenWidth*screenHeight;
i++) {
canvas[i] = bgcolor;.
In the mouse-click left-button handler, you
keep track of triangles as they are being drawn. First click sets
the first point for the first triangle. Next sets the second point
and draws a line. Third click set the third point (finishing the
triangle), and draws two more lines, then colors the triangle. The
triangle is saved away in a data structure, and subsequent clicks
start the next triangle.
In keyboard E key handler, you remove the last-added triangle and
call a routine to
re-draw the entire stack of triangles. The triangles are re-drawn in
the order they
were entered.
In the mouse-click right-button-down handler, you set a flag saying
that a triangle
is being dragged (assuming the coordinates of the click are within a
triangle—you
need to scan the stack from most recent to oldest triangle to find
out). You also save these coordinates. In the button-up handler you
clear the flag, so the triangle
will not be dragged any more.
In the mouse-move handler, you see whether a triangle is being
dragged. If so, you
compute the difference between the current mouse coordinates and the
previous
coordinates (originally saved saved on mouse-down, updated as you
move) and
use the delta-x and delta-y differences to move the whole triangle.
Since triangles
can be on top of each other, this means redrawing the whole shebang.
A warning about y coordinates: it is usual for drawing programs to
have y increase
from 0 at the bottom of the screen to its maximum value at the top.
It is also usual
for mouse locations to be counted from 0 at the top of the screen,
and increasing
going down. This means that the mouse ym coordinate and the
screen-drawing y
coordinates are related by y = screenHeight −ym.
/*
CS 365 Lab 1 OpenGL Template by Michael Glass
* Look for ??? to see where your code goes!
* (Cribbed and modified from one used at Utah, written by Kristi
Potter) */
#include <GL/glut.h>
#include <stdio.h>
/*------------------------------------------
* Standard functions and callbacks
*------------------------------------------*/
/* Initialize GL (defined below) */
void initGL();
/* Called while program is idle */
void idle();
/* Put your draw functions here */
void redraw();
/* All functions for display, calls redraw
*/
void display();
/* Called when a key is pressed */
void keyEvent(unsigned char
key,int x,int y);
/* Called when mouse button pressed */
void mouse(int button,int
state,int x,int y);
/* Called when the mouse moves */
void mouseMotion(int x,int
y);
/*----------------------------------------------------------
* USER DATA TYPES
*----------------------------------------------------------*/
/* The location of a single point */
typedef struct {
int x, y;
} point;
/* Struct to hold a single pixel (as three
color bytes) */
typedef struct
{
unsigned char r, g, b;
/* use 256 colors */
} rgb_pixel;
/*--------------------------------------
* USER FUNCTION FORWARD DECLARATIONS
*--------------------------------------*/
void draw_line(point p0,
point p1);
void fill_triangle(point a, point
b, point c);
/*-----------------------------------------------------------
* GLOBAL VARIABLES
*-----------------------------------------------------------*/
/* Dimensions of the screen */
#define SCREENWIDTH 600
#define SCREENHEIGHT 500
const int screenWidth = SCREENWIDTH;
const int screenHeight =
SCREENHEIGHT;
/*-------------------------------------
* USER GLOBAL VARIABLES */
const rgb_pixel bgcolor =
{210, 210, 255}; /* Light blue */
/* ??? other user global variables, such
as */
rgb_pixel black = {0, 0, 0};
/*-------------------------------------------------
* one-dimensional Pixel buffer array representing the whole
picture
* The 2D LOC macro converts (x,y) to an index into this buffer
*/
rgb_pixel canvas[SCREENWIDTH * SCREENHEIGHT];
#define LOC(X,Y) ((Y)*screenWidth +
(X))
/*---------------------------------------------------
* MAIN PROGRAM
* draws model, calls phantom code */
int main(int argc, char **argv)
{
int i;
/* Initialize glut */
glutInit(&argc,argv);
/* Initialize GL (display
cycle, mouse and keyboard)
Note that display calls redraw*/
initGL();
/* Makes each pixel the
background color. */
for(i = 0; i <
screenWidth*screenHeight; i++) {
canvas[i] = bgcolor;
}
// event loop starts here
glutMainLoop();
return 0;
}
/*----------------------------------------------------------------------
* init */
void initGL() {
// use red/green/blue, double buffered
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB
);
// Set Main Graphics Window
Size
glutInitWindowSize(screenWidth, screenHeight);
// Set the main Graphics Window Position.
glutInitWindowPosition(200,100);
// the window name is an
unsigned int that works like a handle
glutCreateWindow("Howdy Pardner");
// initializes callbacks for glut
glutDisplayFunc(display);
glutMotionFunc(mouseMotion);
glutIdleFunc(idle);
glutMouseFunc(mouse);
glutKeyboardFunc(keyEvent);
}
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// Idle
// Sets up the idle function for glui stuff.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void idle() {
// Redraw the contents of the display window
glutPostRedisplay();
}
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// redraw
// Called to refresh and draw onto the screen.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void redraw() {
//glPushMatrix();
/* Put all drawing
stuff here.
* For this assignment
we just dump our pixel map to the window */
/* Rows of pixel data
are all packed together, no word-alignment */
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/* Draws the canvas array to
the image buffer. */
glDrawPixels((GLsizei)screenWidth, (GLsizei)screenHeight,
GL_RGB,
GL_UNSIGNED_BYTE, canvas);
// glPopMatrix();
}
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// display
// Sets and refreshes the window and swaps buffers.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void display() {
// Clear the buffers
glClear(GL_COLOR_BUFFER_BIT );
//Load the projection matrix
glMatrixMode( GL_PROJECTION );
// Set the projection matrix to the identity
matrix
glLoadIdentity();
/* Simplest Orthographic 2D projection */
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
redraw();
// Swap the double buffers
glutSwapBuffers();
// Flush all drawing to the
screen
glFlush();
}
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// keyEvent
// Registers specific input characters from the keyboard.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void keyEvent(unsigned char
key,int x,int y) {
switch (key) {
// Quit
case 'Q':
case 'q':
exit(0);
break;
default:
//do nothing on the default
break;
}
/* ??? Add other keyboard events to the above
switch as needed */
glutPostRedisplay();
}
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// mouse
// Registers when the mouse buttons are pressed.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
int i = 0;
int triangle[3][2];
void mouse(int
button,int state,int
x,int y) {
if(button == GLUT_LEFT_BUTTON && state
== GLUT_DOWN) {
/* ??? Here is where you accumulate
triangle points
* and draw a triangle
when you get all three points.*/
if(i
< 3) {
++i;
}
else {
i = 0;
canvas[LOC(x0,y0)] =
black;glColor3f(1.0,1.0,0.0);
glBegin(GL_TRIANGLES);
glVertex2iv(triangle[0]);
glVertex2iv(triangle[1]);
glVertex2iv(triangle[2]);
glEnd();
}
}
if(button == GLUT_LEFT_BUTTON && state
== GLUT_UP) {
}
if(button == GLUT_RIGHT_BUTTON &&
state == GLUT_DOWN) {
/* ??? Make a note that this
button is down */
}
if(button == GLUT_RIGHT_BUTTON &&
state == GLUT_UP) {
/* ??? Make a note that this
button is back up */
}
}
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
// mouseMotion
// Registers when the mouse is moved.
//+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+_+
void mouseMotion(int x,int
y)
{
if ((x < 0) | (x >= screenWidth) | (y
< 0) | (y >= screenHeight)) return;
/* ??? Here insert code to
move triangle if (x,y) is within
* a triangle and the proper mouse button is down*/
}
|