#include #include #include #include #include #include #include #include #include #include #include "mtk.hxx" #include "objects.hxx" #include "wave.hxx" #define WIDTH 256 #define HEIGHT 256 float fViewDist = 10.0f; float fTrans = 0.0f; //#define FTRANSINC 0.05f #define FTRANSINC 0.02f float fTransInc = -FTRANSINC; BOOL bDestAlpha; #define TESS_MIN 5 #define TESS_MAX 100 //int tessLevel = TESS_MAX / 2; // corresponds to # of rings/sections in object int tessLevel = 30; // corresponds to # of rings/sections in object int tessInc = 5; class DRAW_CONTROLLER { public: DRAW_CONTROLLER::DRAW_CONTROLLER(); DRAW_CONTROLLER::~DRAW_CONTROLLER() {}; void InitGL(); void ToggleLighting() {bLighting = !bLighting; SetLighting(); } void SetLighting(); void SetDrawObject( OBJECT *pObj, int objectType ); void DrawObject(); void NextRotation(); //mf : this may move // Object rotation GLfloat fXr, fYr, fZr; GLfloat fDXr, fDYr, fDZr; private: void DrawVertexArray(); BOOL bLighting; OBJECT *pObject; // Draw object int iObjectType; }; class SCENE { public: SCENE(); ~SCENE(); void Draw(); void DrawMembrane(); void NewObject( int tessLevel ); DRAW_CONTROLLER drawController; private: void DrawUsingAlphaBuffer(); void DrawNormally(); // Scene rotation GLfloat fXr, fYr, fZr; GLfloat fDXr, fDYr, fDZr; }; // Global objects SCENE *scene; SPHERE *sphere; AVG_UPDATE_TIMER timer( 2.0f, 4 ); #define RGB_COLOR(red, green, blue) \ (((DWORD)(BYTE)(red) << 0) | \ ((DWORD)(BYTE)(green) << 8) | \ ((DWORD)(BYTE)(blue) << 16)) #define RGBA_COLOR(red, green, blue, alpha) \ (((DWORD)(BYTE)(red) << 0) | \ ((DWORD)(BYTE)(green) << 8) | \ ((DWORD)(BYTE)(blue) << 16) | \ ((DWORD)(BYTE)(alpha) << 24)) #define FRANDOM(x) (((float)rand() / RAND_MAX) * (x)) #define DROT 10.0f BOOL fSingleBuf = FALSE; // forwards static void SetGLState(); // volatile state /****** DRAW_CONTROLLER *********************************************/ DRAW_CONTROLLER::DRAW_CONTROLLER( ) { bLighting = TRUE; // Init GL state InitGL(); // Init object 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); } void DRAW_CONTROLLER::SetDrawObject( OBJECT *pObj, int objectType ) { iObjectType = objectType; pObject = pObj; } void DRAW_CONTROLLER::DrawObject() { // Set object position, rotation, and draw it glPushMatrix(); #if 0 glTranslatef( 0.0f, 0.0f, -0.8f ); #else glTranslatef( 0.0f, 0.0f, fTrans ); #endif glRotatef(fXr, 1.0f, 0.0f, 0.0f); glRotatef(fYr, 0.0f, 1.0f, 0.0f); glRotatef(fZr, 0.0f, 0.0f, 1.0f); DrawVertexArray(); glPopMatrix(); } void DRAW_CONTROLLER::DrawVertexArray() { glDrawElements(GL_TRIANGLES, pObject->TriangleCount()*3, GL_UNSIGNED_INT, pObject->TriangleData() ); } void DRAW_CONTROLLER::NextRotation() { // increment object rotation fXr += fDXr; fYr += fDYr; fZr += fDZr; } 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, fViewDist, 0, 0, 0, 0, 1, 0); glMatrixMode(GL_MODELVIEW); glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); 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 float fAlpha = 0.5f; sphere = new SPHERE( tessLevel, tessLevel, fAlpha ); assert( sphere != NULL ); // Inform DRAW_CONTROLLER about new object drawController.SetDrawObject( sphere, OBJECT_TYPE_SPHERE ); // Initialize array pointer data SetGLState(); VERTEX *pVertData = sphere->VertexData(); glVertexPointer(3, GL_FLOAT, sizeof(VERTEX), &pVertData->fX); glNormalPointer(GL_FLOAT, sizeof(VERTEX), &pVertData->fNx); // Color pointer data is dependent on state... SetGLState(); // 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; } /******************************Public*Routine******************************\ * Draw * * Draw the scene using src or dst alpha blending. * \**************************************************************************/ void SCENE::Draw() { if( bDestAlpha ) DrawUsingAlphaBuffer(); else DrawNormally(); // Inrement object rotation drawController.NextRotation(); if( fTrans < -1.2f ) { fTransInc = FTRANSINC; } else if( fTrans > 0.5f ) { fTransInc = -FTRANSINC; } fTrans += fTransInc; } /**************************************************************************\ * DrawUsingAlphaBuffer * * The destination alpha buffer is used to optimize drawing. The optimization * is based on the fact that the objects cover a small area compared to the * entire window. Blending, which is slow, only occurs over the area occupied * by the object. Without the alpha buffer, blending must occur over the entire * window. \**************************************************************************/ void SCENE::DrawUsingAlphaBuffer() { glClear( GL_DEPTH_BUFFER_BIT ); glLoadIdentity(); glEnable( GL_DEPTH_TEST ); glDepthFunc( GL_ALWAYS ); DrawMembrane(); // Draw object's alpha values only on first pass glDepthFunc( GL_LEQUAL ); //#define TURN_OFF_LIGHTING 1 #if TURN_OFF_LIGHTING //mf: this makes it slower.. not sure why glDisable( GL_LIGHTING ); #endif glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE ); drawController.DrawObject(); glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); #if TURN_OFF_LIGHTING glEnable( GL_LIGHTING ); #endif // set up dst alpha blending for next pass glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA ); glEnable( GL_BLEND ); glDisable( GL_DEPTH_TEST ); // Draw object normally on second pass drawController.DrawObject(); glDisable( GL_BLEND ); } /**************************************************************************\ * DrawNormally * * The scene is drawn without using destination alpha buffer. The objects are * drawn first, then the membrane is blended in afterwards. \**************************************************************************/ void SCENE::DrawNormally() { glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT ); glLoadIdentity(); drawController.DrawObject(); // Blend the membrane with the objects already drawn glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glEnable( GL_BLEND ); DrawMembrane(); glDisable( GL_BLEND ); } void SCENE::DrawMembrane() { float fSize = fViewDist; glDisable( GL_LIGHTING ); glColor4f( 0.0f, 0.5f, 0.9f, 0.5f ); glBegin( GL_QUADS ); glVertex3f( fSize, fSize, 0.0f ); glVertex3f( -fSize, fSize, 0.0f ); glVertex3f( -fSize, -fSize, 0.0f ); glVertex3f( fSize, -fSize, 0.0f ); glEnd(); // Reinstate the current lighting model drawController.SetLighting(); } /********************************************************************/ void Reset() { // Called when display changes // Remove any timing info from title bar, and reset the timer SetWindowText(auxGetHWND(), "" ); timer.Reset(); timer.Start(); } 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 Draw(void) { DRAW_CONTROLLER *pDrawControl = &scene->drawController; float updatesPerSecond; // Draw the scene scene->Draw(); if (fSingleBuf) glFlush(); else auxSwapBuffers(); // Print timing information if Stop returns TRUE if( timer.Update( 1, &updatesPerSecond ) ) { char szMsg[80]; sprintf(szMsg, "%s: %.2lf frames/sec", "", updatesPerSecond ); // Print timing info in title bar SetWindowText(auxGetHWND(), szMsg); } } static void SetGLState() { VERTEX *pVertData = sphere->VertexData(); // Set vertex array pointers glVertexPointer(3, GL_FLOAT, sizeof(VERTEX), &pVertData->fX); glNormalPointer(GL_FLOAT, sizeof(VERTEX), &pVertData->fNx); // If using destination alpha blending, the object is drawn with alpha=1.0, // so specify 3-valued rgb colors (a defaults to 1.0). Otherwise, // the object is drawn using 4-valued rgba colors int colorDataSize = bDestAlpha ? 3 : 4; glColorPointer( colorDataSize, GL_UNSIGNED_BYTE, sizeof(VERTEX), &pVertData->dwColor); // Set other GL state if( bDestAlpha ) { glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE ); } else { glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE ); glClearColor( 0.0f, 0.5f, 0.9f, 0.0f ); glEnable( GL_DEPTH_TEST ); glDepthFunc( GL_LEQUAL ); } } void Reshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); Reset(); } void Keya(void) { bDestAlpha = !bDestAlpha; SetGLState(); Reset(); } void Keyd(void) { } void Keyl(void) { scene->drawController.ToggleLighting(); Reset(); } void KeySPACE(void) { } 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")) fSingleBuf = TRUE; } auxInitPosition(10, 10, WIDTH, HEIGHT); // eMode = AUX_RGB | AUX_DEPTH16 | AUX_ALPHA; //mf: ??!! had to choose 32-bitz - don't know why, z-planes seem tight enough eMode = AUX_RGB | AUX_DEPTH24 | AUX_ALPHA; if (!fSingleBuf) { eMode |= AUX_DOUBLE; } auxInitDisplayMode(eMode); auxInitWindow("Insane in the Membrane"); auxReshapeFunc(Reshape); auxIdleFunc(Draw); auxKeyFunc(AUX_a, Keya); auxKeyFunc(AUX_l, Keyl); auxKeyFunc(AUX_d, Keyd); auxKeyFunc(AUX_SPACE, KeySPACE); auxKeyFunc(AUX_UP, KeyUp); auxKeyFunc(AUX_DOWN, KeyDown); bDestAlpha = TRUE; // Create scene, with object(s) scene = new SCENE; // Start drawing timer.Start(); auxMainLoop(Draw); // Party's over delete scene; }