|
|
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <gl\glaux.h>
#define PI ((float)3.14159265358979323846)
#define WIDTH 512
#define HEIGHT 512
#define TESS_MIN 5
#define TESS_MAX 100
int tessLevel = TESS_MAX / 2; // corresponds to # of rings/sections in object
int tessInc = 5;
typedef struct { float fX, fY, fZ; float fNx, fNy, fNz; DWORD dwColor; } VERTEX;
typedef struct { int iV1; int iV2; int iV3; } TRIANGLE;
enum { OBJECT_TYPE_SPHERE = 0, OBJECT_TYPE_TORUS, OBJECT_TYPE_CYLINDER };
class OBJECT { public: OBJECT( int rings, int sections ); ~OBJECT( ); int VertexCount() { return nVerts; } int TriangleCount() { return nTris; } VERTEX *VertexData() { return pVertData; } TRIANGLE *TriangleData() { return pTriData; } int NumRings() { return nRings; } int NumSections() { return nSections; }
protected: int iType; // object type
int nVerts, nTris; int nRings, nSections; VERTEX *pVertData; TRIANGLE *pTriData; };
class SPHERE : public OBJECT { public: SPHERE( int rings, int sections );
private: void GenerateData( float fRadius ); int CalcNVertices(); int CalcNTriangles(); };
enum { DRAW_METHOD_VERTEX_ARRAY = 0, DRAW_METHOD_DREE, DRAW_METHOD_TRIANGLES, DRAW_METHOD_TRISTRIPS, NUM_DRAW_METHODS };
// Draw method names
char *pszListType[NUM_DRAW_METHODS] = { "Vertex Array", "DrawRangeElements", "Direct Triangles", "Direct Strips" };
class DRAW_CONTROLLER { public: DRAW_CONTROLLER::DRAW_CONTROLLER(); DRAW_CONTROLLER::~DRAW_CONTROLLER(); void InitGL(); void CycleDrawMethod(); void ToggleDisplayListDraw() {bDisplayList = !bDisplayList; } void ToggleLighting() {bLighting = !bLighting; SetLighting(); } void Draw(); OBJECT *GetDrawObject() {return pObject;} void SetDrawObject( OBJECT *pObj, int objectType ); char *GetDrawMethodName();
private: int iDrawMethod; BOOL bDisplayList; BOOL bLighting; BOOL bDREE; // if DrawRangeElements extension available or not
BOOL bDREE_Disabled; // DREE can be temporarily disabled if not
// practical to use (e.g. high tesselation)
int nDREE_MaxVertices; int nDREE_MaxIndices; PFNGLDRAWRANGEELEMENTSWINPROC pfnDrawRangeElementsWIN; OBJECT *pObject; // Draw object
int iObjectType; GLint dlList[NUM_DRAW_METHODS]; char **pszDrawMethodNames; char szNameBuf[80];
void SetLighting(); void NewList(); void DeleteLists(); void DeleteList(int iList); void DrawObject();
// Draw methods
void DrawVertexArray (); void DrawRangeElements(); void DrawTriangles(); void DrawStrips();
void Vertex(int iVert); void CheckDREE_Existence(); BOOL CheckDREE_Usable(); };
class SCENE { public: SCENE(); ~SCENE(); void Draw(); void NewObject( int tessLevel ); DRAW_CONTROLLER drawController; private: GLfloat fXr, fYr, fZr; GLfloat fDXr, fDYr, fDZr; };
enum { TIMER_METHOD_SINGLE = 0, // single-shot timer
TIMER_METHOD_AVERAGE, // average time for past n results
};
#define MAX_RESULTS 10
class TIMER { public: TIMER( int timerMethodArg ); void Start() { dwMillis = GetTickCount(); } BOOL Stop( int numItems, float *fRate ); void Reset(); private: int timerMethod; int nItems; int nTotalItems; // total # triangles accumulated
DWORD dwMillis; DWORD dwTotalMillis; DWORD updateInterval; // interval between timer updates
// These variables are for result averaging
float fResults[MAX_RESULTS]; int nResults; // current # of results
int nMaxResults; // max # of results for averaging
int iOldestResult; // index of oldest result
float fSummedResults; // current sum of results
};
// Global objects
SCENE *scene; SPHERE *sphere; TIMER timer( TIMER_METHOD_AVERAGE );
#define RGB_COLOR(red, green, blue) \
(((DWORD)(BYTE)(red) << 0) | \ ((DWORD)(BYTE)(green) << 8) | \ ((DWORD)(BYTE)(blue) << 16)) #define FRANDOM(x) (((float)rand() / RAND_MAX) * (x))
#define DROT 10.0f
BOOL fSingle = FALSE;
/****** OBJECT *******************************************************/
OBJECT::OBJECT( int rings, int sections ) : nRings( rings ), nSections( sections ) { pTriData = NULL; pVertData = NULL; }
OBJECT::~OBJECT() { // These ptrs alloc'd in inheriting classes...
if( pVertData ) free( pVertData ); if( pTriData ) free( pTriData ); }
/****** SPHERE *******************************************************/
SPHERE::SPHERE( int rings, int sections ) : OBJECT( rings, sections ) { iType = OBJECT_TYPE_SPHERE;
nVerts = CalcNVertices(); nTris = CalcNTriangles();
// Allocate memory for the sphere data (freed by the base OBJECT class)
// Vertex data
pVertData = (VERTEX *) malloc( nVerts * sizeof(VERTEX) ); assert( pVertData != NULL );
// Triangle indices
pTriData = (TRIANGLE *) malloc( nTris * sizeof(TRIANGLE) ); assert( pTriData != NULL );
GenerateData(1.0f); }
int SPHERE::CalcNVertices() { return (((nRings)+1)*(nSections)+2); }
int SPHERE::CalcNTriangles() { return (((nRings)+1)*(nSections)*2); }
void SPHERE::GenerateData( float fRadius ) { float fTheta, fPhi; /* Angles used to sweep around sphere */ float fDTheta, fDPhi; /* Angle between each section and ring */ float fX, fY, fZ, fV, fRSinTheta; /* Temporary variables */ int i, j, n, m; /* counters */ VERTEX *pvtx = pVertData; TRIANGLE *ptri = pTriData;
/*
* Generate vertices at the top and bottom points. */ pvtx[0].fX = 0.0f; pvtx[0].fY = fRadius; pvtx[0].fZ = 0.0f; pvtx[0].fNx = 0.0f; pvtx[0].fNy = 1.0f; pvtx[0].fNz = 0.0f; pvtx[0].dwColor = RGB_COLOR(0, 0, 255); pvtx[nVerts - 1].fX = 0.0f; pvtx[nVerts - 1].fY = -fRadius; pvtx[nVerts - 1].fZ = 0.0f; pvtx[nVerts - 1].fNx = 0.0f; pvtx[nVerts - 1].fNy = -1.0f; pvtx[nVerts - 1].fNz = 0.0f; pvtx[nVerts - 1].dwColor = RGB_COLOR(0, 255, 0);
/*
* Generate vertex points for rings */ fDTheta = PI / (float) (nRings + 2); fDPhi = 2.0f * PI / (float) nSections; n = 1; /* vertex being generated, begins at 1 to skip top point */ fTheta = fDTheta;
for (i = 0; i <= nRings; i++) { fY = (float)(fRadius * cos(fTheta)); /* y is the same for each ring */ fV = fTheta / PI; /* v is the same for each ring */ fRSinTheta = (float)(fRadius * sin(fTheta)); fPhi = 0.0f; for (j = 0; j < nSections; j++) { fX = (float)(fRSinTheta * sin(fPhi)); fZ = (float)(fRSinTheta * cos(fPhi)); pvtx[n].fX = fX; pvtx[n].fZ = fZ; pvtx[n].fY = fY; pvtx[n].fNx = fX / fRadius; pvtx[n].fNy = fY / fRadius; pvtx[n].fNz = fZ / fRadius; if (n & 1) { pvtx[n].dwColor = RGB_COLOR(0, 0, 255); } else { pvtx[n].dwColor = RGB_COLOR(0, 255, 0); } fPhi += fDPhi; n++; } fTheta += fDTheta; }
/*
* Generate triangles for top and bottom caps. */ for (i = 0; i < nSections; i++) { ptri[i].iV1 = 0; ptri[i].iV2 = i + 1; ptri[i].iV3 = 1 + ((i + 1) % nSections); ptri[nTris - nSections + i].iV1 = nVerts - 1; ptri[nTris - nSections + i].iV2 = nVerts - 2 - i; ptri[nTris - nSections + i].iV3 = nVerts - 2 - ((1 + i) % nSections); }
/*
* Generate triangles for the rings */ m = 1; /* first vertex in current ring, begins at 1 to skip top point*/ n = nSections; /* triangle being generated, skip the top cap */ for (i = 0; i < nRings; i++) { for (j = 0; j < nSections; j++) { ptri[n].iV1 = m + j; ptri[n].iV2 = m + nSections + j; ptri[n].iV3 = m + nSections + ((j + 1) % nSections); ptri[n + 1].iV1 = ptri[n].iV1; ptri[n + 1].iV2 = ptri[n].iV3; ptri[n + 1].iV3 = m + ((j + 1) % nSections); n += 2; } m += nSections; } }
/****** DRAW_CONTROLLER *********************************************/
DRAW_CONTROLLER::DRAW_CONTROLLER( ) { iDrawMethod = 0; bDisplayList = FALSE; bLighting = TRUE;
// check out if DREE exists
CheckDREE_Existence();
pszDrawMethodNames = pszListType;
// Set display list indices to 0 (so they will be filled later)
for( int i = 0; i < NUM_DRAW_METHODS; i++ ) dlList[i] = 0;
// Init GL state
InitGL(); }
DRAW_CONTROLLER::~DRAW_CONTROLLER( ) { DeleteLists(); }
void DRAW_CONTROLLER::DeleteLists() { for( int i = 0; i < NUM_DRAW_METHODS; i ++ ) DeleteList( i ); }
void DRAW_CONTROLLER::DeleteList( int iList ) { if( dlList[iList] ) { glDeleteLists( dlList[iList], 1 ); dlList[iList] = 0; } }
void DRAW_CONTROLLER::CheckDREE_Existence() { // Check for DrawRangeElements extension
pfnDrawRangeElementsWIN = (PFNGLDRAWRANGEELEMENTSWINPROC) wglGetProcAddress("glDrawRangeElementsWIN"); if (pfnDrawRangeElementsWIN == NULL) { bDREE = FALSE; bDREE_Disabled = TRUE; return; }
// Extension exists - find out its limits
bDREE = TRUE; glGetIntegerv( GL_MAX_ELEMENTS_VERTICES_WIN, &nDREE_MaxVertices ); glGetIntegerv( GL_MAX_ELEMENTS_INDICES_WIN, &nDREE_MaxIndices ); }
BOOL DRAW_CONTROLLER::CheckDREE_Usable( ) { // Cancel DrawRangeElements if vertex structure makes it impractical :
// - Vertex range too great to fit in call, or
// - Too many indices
// (I add 1 here to account for top or bottom vertices in a batch)
if( ( (2*pObject->NumSections() + 1) > nDREE_MaxVertices ) || ( (2*pObject->NumSections()*3) > nDREE_MaxIndices ) ) bDREE_Disabled = TRUE; else bDREE_Disabled = FALSE; return !bDREE_Disabled; }
char * DRAW_CONTROLLER::GetDrawMethodName() { // Returns name of current drawing method. If in dlist mode, then this is
// prepended to the name.
sprintf(szNameBuf, "%s%s", bDisplayList ? "Display List " : "", pszDrawMethodNames[iDrawMethod] ); return szNameBuf; }
void DRAW_CONTROLLER::CycleDrawMethod() { iDrawMethod++; if( bDREE_Disabled && (iDrawMethod == DRAW_METHOD_DREE) ) iDrawMethod++;
if( iDrawMethod >= NUM_DRAW_METHODS ) iDrawMethod = 0; }
void DRAW_CONTROLLER::NewList( ) { // Create new list for the current draw method
if( ! dlList[iDrawMethod] ) { dlList[iDrawMethod] = glGenLists(1); } glNewList(dlList[iDrawMethod], GL_COMPILE); DrawObject(); glEndList(); }
void DRAW_CONTROLLER::SetDrawObject( OBJECT *pObj, int objectType ) { iObjectType = objectType; pObject = pObj;
// Delete all current lists
DeleteLists();
// Check if DREE can be used - and if not go on to next method if
// current method is DREE
if( !CheckDREE_Usable() && (iDrawMethod == DRAW_METHOD_DREE) ) CycleDrawMethod(); }
void DRAW_CONTROLLER::Draw() { // Draws display list or immediate mode
// Display list draw
if( bDisplayList ) { // Create dlist if necessary
if( !dlList[iDrawMethod] ) NewList(); glCallList( dlList[iDrawMethod] ); return; }
// Immediate mode draw
DrawObject(); }
void DRAW_CONTROLLER::DrawObject() { // Issues draw commands
switch( iDrawMethod ) { case DRAW_METHOD_VERTEX_ARRAY : DrawVertexArray(); break; case DRAW_METHOD_DREE : DrawRangeElements(); break; case DRAW_METHOD_TRIANGLES : DrawTriangles(); break; case DRAW_METHOD_TRISTRIPS : DrawStrips(); break; } }
void DRAW_CONTROLLER::DrawVertexArray() { glDrawElements(GL_TRIANGLES, pObject->TriangleCount()*3, GL_UNSIGNED_INT, pObject->TriangleData() ); }
void DRAW_CONTROLLER::DrawRangeElements() { GLint *pTriIndices; GLint nVerts, nTris; GLenum type = GL_UNSIGNED_INT;
nVerts = pObject->VertexCount(); nTris = pObject->TriangleCount(); pTriIndices = (int *) pObject->TriangleData();
// Check for trivial case requiring no batching
if( (nVerts <= nDREE_MaxVertices) && (nTris*3 <= nDREE_MaxIndices) ) { pfnDrawRangeElementsWIN(GL_TRIANGLES, 0, nVerts-1, nTris*3, type, pTriIndices ); return; }
// Have to batch : Since the vertex ordering of the sphere is along rings,
// we will batch by groups of rings, according to the vertex index ranges
// allowed by the DrawRangeElements call.
//
// Need some more variables:
GLuint start, end; GLsizei count; GLuint nRingsPerBatch, nTrisPerBatch, nElemsPerBatch, nVerticesPerBatch, elemsLeft; int sections = pObject->NumSections(); int rings = pObject->NumRings();
nRingsPerBatch = nDREE_MaxVertices / (sections); nTrisPerBatch = (nRingsPerBatch-1)*sections*2; nElemsPerBatch = nTrisPerBatch*3; nVerticesPerBatch = nRingsPerBatch*sections; elemsLeft = nTris*3; // Special case first batch with top vertex
start = 0; end = nVerticesPerBatch - sections; count = nElemsPerBatch - (sections*3); // top row only has half the tris of
// a 'normal' row
pfnDrawRangeElementsWIN(GL_TRIANGLES, start, end, count, type, pTriIndices);
// Batch groups of rings around sphere
pTriIndices += count; start = (end - sections + 1); elemsLeft -= count; while( elemsLeft >= nElemsPerBatch ) { pfnDrawRangeElementsWIN(GL_TRIANGLES, start, start + nVerticesPerBatch - 1, nElemsPerBatch, type, pTriIndices);
start += (nVerticesPerBatch - sections); pTriIndices += nElemsPerBatch; elemsLeft -= nElemsPerBatch; }
// Do last batch, including bottom vertex
if( elemsLeft ) { pfnDrawRangeElementsWIN(GL_TRIANGLES, start, nVerts-1, elemsLeft, type, pTriIndices); } }
void DRAW_CONTROLLER::Vertex(int iVert) { VERTEX *pvtx;
pvtx = pObject->VertexData() + iVert; glColor3ubv((GLubyte *)&pvtx->dwColor); glNormal3fv(&pvtx->fNx); glVertex3fv(&pvtx->fX); }
void DRAW_CONTROLLER::DrawTriangles() { int iVert, *pidx; glBegin(GL_TRIANGLES); pidx = (int *) pObject->TriangleData(); for (iVert = 0; iVert < pObject->TriangleCount()*3; iVert++) { Vertex(*pidx++); } glEnd(); }
void DRAW_CONTROLLER::DrawStrips() { int iIdxBase; int iRing, iSection; int sections = pObject->NumSections(); int rings = pObject->NumRings();
// Triangle fans for top and bottom caps
glBegin(GL_TRIANGLE_FAN); Vertex(0); iIdxBase = 1; for (iSection = 0; iSection <= sections; iSection++) { Vertex(iIdxBase+(iSection % sections)); } glEnd();
glBegin(GL_TRIANGLE_FAN);
Vertex(pObject->VertexCount() - 1); iIdxBase = pObject->VertexCount() - sections - 1; for (iSection = sections; iSection >= 0 ; iSection--) { Vertex(iIdxBase+(iSection % sections)); }
glEnd();
// Triangle strips for each ring
iIdxBase = 1; for (iRing = 0; iRing < rings; iRing++) { glBegin(GL_TRIANGLE_STRIP); for (iSection = 0; iSection <= sections; iSection++) { Vertex(iIdxBase+(iSection % sections)); Vertex(iIdxBase+(iSection % sections)+sections); }
glEnd();
iIdxBase += sections; } }
void DRAW_CONTROLLER::InitGL(void) { float fv4[4]; int iv1[1]; int i; fv4[0] = 0.05f; fv4[1] = 0.05f; fv4[2] = 0.05f; fv4[3] = 1.0f; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fv4); fv4[0] = 0.0f; fv4[1] = 1.0f; fv4[2] = 1.0f; fv4[3] = 0.0f; glLightfv(GL_LIGHT0, GL_POSITION, fv4); fv4[0] = 0.9f; fv4[1] = 0.9f; fv4[2] = 0.9f; fv4[3] = 1.0f; glLightfv(GL_LIGHT0, GL_DIFFUSE, fv4); glEnable(GL_LIGHT0); glEnable(GL_LIGHTING); fv4[0] = 0.6f; fv4[1] = 0.6f; fv4[2] = 0.6f; fv4[3] = 1.0f; glMaterialfv(GL_FRONT, GL_SPECULAR, fv4); iv1[0] = 40; glMaterialiv(GL_FRONT, GL_SHININESS, iv1); glEnable(GL_CULL_FACE);
glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); glEnable(GL_COLOR_MATERIAL);
glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45, 1, .01, 15); gluLookAt(0, 0, 10, 0, 0, 0, 0, 1, 0); glMatrixMode(GL_MODELVIEW);
SetLighting(); }
void DRAW_CONTROLLER::SetLighting(void) { if( bLighting ) { glEnable( GL_LIGHTING ); } else { glDisable( GL_LIGHTING ); } }
/****** SCENE *******************************************************/
SCENE::SCENE() { srand(time(NULL));
// Create sphere draw object for the scene
NewObject( tessLevel ); }
void SCENE::NewObject( int tessLevel ) { // Only one object allowed for now - delete any previous object
if( sphere ) delete sphere;
// Create new sphere for the scene
sphere = new SPHERE( tessLevel, tessLevel ); assert( sphere != NULL );
// Inform DRAW_CONTROLLER about new object
drawController.SetDrawObject( sphere, OBJECT_TYPE_SPHERE );
// Initialize array pointer data
VERTEX *pVertData = sphere->VertexData(); glVertexPointer(3, GL_FLOAT, sizeof(VERTEX), &pVertData->fX); glNormalPointer(GL_FLOAT, sizeof(VERTEX), &pVertData->fNx); glColorPointer(3, GL_UNSIGNED_BYTE, sizeof(VERTEX), &pVertData->dwColor);
// Init scene rotation and motion
fXr = 0.0f; fYr = 0.0f; fZr = 0.0f; fDXr = DROT - FRANDOM(2 * DROT); fDYr = DROT - FRANDOM(2 * DROT); fDZr = DROT - FRANDOM(2 * DROT); }
SCENE::~SCENE() { // Delete any scene objects
if( sphere ) delete sphere; }
void SCENE::Draw() { glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glRotatef(fXr, 1.0f, 0.0f, 0.0f); glRotatef(fYr, 0.0f, 1.0f, 0.0f); glRotatef(fZr, 0.0f, 0.0f, 1.0f);
drawController.Draw();
// next rotation...
fXr += fDXr; fYr += fDYr; fZr += fDZr; }
/****** TIMER *******************************************************/
TIMER::TIMER( int timerMethodArg ) : timerMethod( timerMethodArg ) { updateInterval = 2000; // in milliseconds
Reset(); }
void TIMER::Reset() { dwTotalMillis = 0; nTotalItems = 0;
// Parameters for result averaging
nResults = 0; nMaxResults = MAX_RESULTS; // number of most recent results to average
fSummedResults = 0.0f; iOldestResult = 0; // index of oldest result
}
BOOL TIMER::Stop( int numItems, float *fRate ) { dwMillis = GetTickCount()-dwMillis; dwTotalMillis += dwMillis; nTotalItems += numItems;
// If total elapsed time is greater than the update interval, send back
// timing information
if (dwTotalMillis > updateInterval ) { float fItemsPerSecond; int iNewResult;
fItemsPerSecond = (float) nTotalItems*1000.0f/dwTotalMillis;
switch( timerMethod ) {
case TIMER_METHOD_AVERAGE :
// Average last n results (they are kept in a circular buffer)
if( nResults < nMaxResults ) { // Haven't filled the buffer yet
iNewResult = nResults; nResults++; } else { // Full buffer : replace oldest entry with new value
fSummedResults -= fResults[iOldestResult]; iNewResult = iOldestResult; iOldestResult = (iOldestResult == (nMaxResults - 1)) ? 0 : (iOldestResult + 1); }
// Add new result, maintain sum to simplify calculations
fResults[iNewResult] = fItemsPerSecond; fSummedResults += fItemsPerSecond;
// average the result
fItemsPerSecond = fSummedResults / (float) nResults;
break; }
// Set running totals back to 0
dwTotalMillis = 0; nTotalItems = 0;
*fRate = fItemsPerSecond; return TRUE; } else return FALSE; // no new information yet
}
/********************************************************************/
void Reset() { // Called when display changes
// Remove any timing info from title bar, and reset the timer
SetWindowText(auxGetHWND(), scene->drawController.GetDrawMethodName() ); timer.Reset(); }
void NewTessLevel( int tessLevel ) { static int oldTessLevel = 0; // to avoid unnecessary work
// retesselate scene's object
if( tessLevel == oldTessLevel ) return; scene->NewObject( tessLevel );
Reset();
oldTessLevel = tessLevel; }
void Redraw(void) { DRAW_CONTROLLER *pDrawControl = &scene->drawController; float trianglesPerSecond; timer.Start();
// Draw the scene
scene->Draw();
if (fSingle) glFlush(); else auxSwapBuffers(); // Print timing information if Stop returns TRUE
if( timer.Stop( pDrawControl->GetDrawObject()->TriangleCount(), &trianglesPerSecond ) ) { char szMsg[80]; sprintf(szMsg, "%s: %.0lf tri/sec", pDrawControl->GetDrawMethodName(), trianglesPerSecond );
// Print timing info in title bar
SetWindowText(auxGetHWND(), szMsg); } }
void Reshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); Reset(); }
void Keyd(void) { scene->drawController.ToggleDisplayListDraw(); Reset(); }
void Keyl(void) { scene->drawController.ToggleLighting(); Reset(); }
void KeySPACE(void) { scene->drawController.CycleDrawMethod(); Reset(); } void KeyUp(void) { // increase tesselation
tessLevel += tessInc; if( tessLevel > TESS_MAX ) tessLevel = TESS_MAX; NewTessLevel( tessLevel ); }
void KeyDown(void) { // decrease tesselation
tessLevel -= tessInc; if( tessLevel < TESS_MIN ) tessLevel = TESS_MIN; NewTessLevel( tessLevel ); }
void __cdecl main(int argc, char **argv) { GLenum eMode; while (--argc > 0) { argv++;
if (!strcmp(*argv, "-sb")) fSingle = TRUE; } auxInitPosition(10, 10, WIDTH, HEIGHT); eMode = AUX_RGB; if (!fSingle) { eMode |= AUX_DOUBLE; } auxInitDisplayMode(eMode); auxInitWindow("Vertex Array/Direct Comparison");
auxReshapeFunc(Reshape); auxIdleFunc(Redraw);
auxKeyFunc(AUX_l, Keyl); auxKeyFunc(AUX_d, Keyd); auxKeyFunc(AUX_SPACE, KeySPACE); auxKeyFunc(AUX_UP, KeyUp); auxKeyFunc(AUX_DOWN, KeyDown);
// Create scene, with object(s)
scene = new SCENE;
// Start drawing
auxMainLoop(Redraw);
// Party's over
delete scene; }
|