Example 04

Example 04

This OpenGL program allows a user to pick (select) a pawn and then select a new location to move the pawn object.

ex_04/board.png

Source Code

board.cc:

// Peter Bui
// CSE 40166 Computer Graphics (Fall 2010)
// Example 4: pawn board
 
#include <cmath>
#include <cstdio>
#include <cstdlib>

#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

// Pawn data structure ---------------------------------------------------------

struct pawn {
    size_t row;
    size_t column;
    int state;
};

// Constants -------------------------------------------------------------------

enum PAWN_STATE {
    PAWN_STATE_UNSELECTED,
    PAWN_STATE_SELECTED,
    PAWN_STATE_MAX,
};

enum DISPLAY_LISTS {
    DL_CELL,
    DL_PAWN,
    DL_BOX,
    DL_MAX
};

static const GLfloat CellColors[][3] = {
    {0.0, 0.0, 0.0},
    {1.0, 1.0, 1.0}
};

static const GLfloat PawnColors[][3] = {
    {1.0, 0.0, 0.0},
    {0.0, 1.0, 0.0},
    {0.0, 0.0, 1.0}
};

#define SBSIZE 128

// Global variables ------------------------------------------------------------

static GLuint DisplayListsBase;

static size_t WindowWidth  = 640;
static size_t WindowHeight = 480;

static size_t BoardRows    = 8;
static size_t BoardColumns = 8;

static struct pawn Pawn = { 0, 0, PAWN_STATE_UNSELECTED };
static GLuint PawnName  = (BoardRows * BoardColumns) + 1;

static GLuint SelectBuffer[SBSIZE];
static GLuint PickLimit = 5;
static GLuint CursorX, CursorY;
static GLenum Mode = GL_RENDER;

static GLuint FrameRate = 60;

// Draw cell -------------------------------------------------------------------

void
draw_cell() 
{
    glBegin(GL_QUADS); {
        glVertex2f(0.0, 0.0);
        glVertex2f(0.0, 1.0);
        glVertex2f(1.0, 1.0);
        glVertex2f(1.0, 0.0);
    } glEnd();
}

// Draw pawn -------------------------------------------------------------------

void
draw_pawn()
{
    glBegin(GL_QUADS); {
        glVertex2f(-0.5, -0.5);
        glVertex2f( 0.5, -0.5);
        glVertex2f( 0.5,  0.0);
        glVertex2f(-0.5,  0.0);
    } glEnd();

    glBegin(GL_TRIANGLE_FAN); {
        double ox = 0.0, oy = 0.25, radius = 0.25;
        int triangles = 32;

        glVertex2f(ox, oy);
        for (int i = 0; i <= triangles; i++) {
            double angle = i * 2.0 * M_PI / triangles;
            glVertex2f(ox + radius * cos(angle), oy + radius * sin(angle));
        }
    } glEnd();
}

// Draw box --------------------------------------------------------------------

void
draw_box() 
{
    glPushAttrib(GL_LINE_BIT);
    glLineWidth(4.0);
    glBegin(GL_LINE_LOOP); {
        glVertex2f(0.0, 0.0);
        glVertex2f(0.0, 1.0);
        glVertex2f(1.0, 1.0);
        glVertex2f(1.0, 0.0);
    } glEnd();
    glPopAttrib();
}

// Draw scene ------------------------------------------------------------------

void
draw_scene()
{
    size_t cc = CursorX * BoardColumns / WindowWidth;
    size_t cr = (WindowHeight - CursorY) * BoardRows / WindowHeight;

    for (size_t r = 0; r < BoardRows; r++) {
        glPushName(r);
        for (size_t c = 0; c < BoardColumns; c++) {
            glPushName(c);
            glPushMatrix();
            glColor3fv(CellColors[(c + (r % 2)) % 2]);
            glTranslatef(c, r, 0.0);
            glCallList(DisplayListsBase + DL_CELL);
            
            if (Pawn.row == r && Pawn.column == c) {
                glPushName(PawnName);
                glPushMatrix();
                glColor3fv(PawnColors[Pawn.state]);
                glTranslatef(0.5, 0.5, 0.0);
                glScalef(0.5, 0.5, 1.0);
                glCallList(DisplayListsBase + DL_PAWN);
                glPopMatrix();
                glPopName();
            }

            if (Pawn.state == PAWN_STATE_SELECTED) {
                if (cc == c && cr == r) {
                    glColor3fv(PawnColors[Pawn.state]);
                    glCallList(DisplayListsBase + DL_BOX);
                }
            }
            glPopMatrix();
            glPopName();
        }
        glPopName();
    }
}

// Initialize scene ------------------------------------------------------------

void
init_scene()
{
    glClearColor(1.0, 1.0, 1.0, 1.0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, BoardRows, 0, BoardColumns);
    glMatrixMode(GL_MODELVIEW);    
}

// Make display lists ----------------------------------------------------------

void
make_display_lists()
{
    DisplayListsBase = glGenLists(DL_MAX);

    glNewList(DisplayListsBase + DL_CELL, GL_COMPILE); {
        glPushMatrix();
        draw_cell();
        glPopMatrix();
    } glEndList();
    
    glNewList(DisplayListsBase + DL_PAWN, GL_COMPILE); {
        glPushMatrix();
        draw_pawn();
        glPopMatrix();
    } glEndList();
    
    glNewList(DisplayListsBase + DL_BOX, GL_COMPILE); {
        glPushMatrix();
        draw_box();
        glPopMatrix();
    } glEndList();
}

// Picking functions -----------------------------------------------------------

void
start_picking()
{
    GLint viewport[4];

    glSelectBuffer(SBSIZE, SelectBuffer);
    glGetIntegerv(GL_VIEWPORT, viewport);
    glRenderMode(GL_SELECT);
    glInitNames();

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluPickMatrix(CursorX, viewport[3] - CursorY, PickLimit, PickLimit, viewport);
    gluOrtho2D(0, BoardRows, 0, BoardColumns);

    glMatrixMode(GL_MODELVIEW);
}

void
process_hits(GLint hits, GLuint *buffer)
{
    GLuint names;
    GLuint *bp = buffer;

    for (GLint i = 0; i < hits; i++) {
        names = *bp;
        bp += 3;

        if (*(bp + (names - 1)) == PawnName) {
            Pawn.state = (Pawn.state + 1) % PAWN_STATE_MAX;
        } else {
            if (Pawn.state == PAWN_STATE_SELECTED) {
                Pawn.row    = *(bp);
                Pawn.column = *(bp + 1);
            }
        }
        bp += names;
    }
}

void
stop_picking()
{
    GLint hits;
    
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    
    glFlush();

    hits = glRenderMode(GL_RENDER);
    if (hits > 0)
        process_hits(hits, SelectBuffer);
    
    Mode = GL_RENDER;
}


// Display callback ------------------------------------------------------------

void
display()
{
    glClear(GL_COLOR_BUFFER_BIT);

    if (Mode == GL_SELECT)
        start_picking();

    draw_scene();
    
    if (Mode == GL_SELECT)
        stop_picking();
    else
        glutSwapBuffers();
}

// Reshape callback ------------------------------------------------------------

void
reshape(GLsizei nw, GLsizei nh)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, BoardRows, 0, BoardColumns);

    glMatrixMode(GL_MODELVIEW);    

    glViewport(0, 0, nw, nh);

    WindowWidth  = nw;
    WindowHeight = nh;
}

// Mouse callback --------------------------------------------------------------

void 
mouse(int button, int state, int x, int y)
{
    if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
        Mode = GL_SELECT;
        CursorX = x;
        CursorY = y;
    }
}

// Passive Motion callback ------------------------------------------------------

void
passive_motion(int x, int y)
{
    CursorX = x;
    CursorY = y;
}

// Keyboard callback ------------------------------------------------------------

void
keyboard(unsigned char key, int x, int y)
{
    if (key == 'q' || key == 'Q')
        exit(EXIT_SUCCESS);
}

// Timer callback ---------------------------------------------------------------

void
timer(int value)
{
    glutPostRedisplay();
    glutTimerFunc(1000/FrameRate, timer, value);
}

// Main execution ---------------------------------------------------------------

int
main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
    glutInitWindowSize(WindowWidth, WindowHeight);
    glutCreateWindow("board");
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(mouse);
    glutPassiveMotionFunc(passive_motion);
    glutKeyboardFunc(keyboard);
    glutTimerFunc(1000/FrameRate, timer, 0);

    init_scene();
    make_display_lists();

    glutMainLoop();

    return (EXIT_SUCCESS);
}

// vim: sts=4 sw=4 ts=8 ft=cpp


Files