/******************************Module*Header*******************************\ * Module Name: glwindow.c * * OpenGL window maintenance. * * Created: 09-Mar-1995 15:10:10 * Author: Gilman Wong [gilmanw] * * Copyright (c) 1995 Microsoft Corporation * \**************************************************************************/ #include #include #include #include #include "global.h" #include "glwindow.h" #include "nff.h" #include "trackbal.h" // Move global stuff into SCENE struct. HGLRC ghrc = (HGLRC) 0; HPALETTE ghpalOld, ghPalette = (HPALETTE) 0; BOOL bUseStatic = FALSE; UINT uiSysPalUse = SYSPAL_STATIC; extern void DoGlStuff(HWND, HDC); extern HGLRC hrcInitGL(HWND, HDC); extern void stateInit(HWND, HDC); extern void vCleanupGL(HGLRC, HDC); extern BOOL bSetupPixelFormat(HDC); extern void CreateRGBPalette(HDC); extern VOID vSetSize(HWND); extern void ForceRedraw(HWND); extern SCENE *OpenScene(LPSTR lpstrFile); CHAR lpstrFile[256]; // Global ambient lights. static GLfloat lightAmbient[4] = {0.4f, 0.4f, 0.4f, 1.0f}; static GLfloat lightAmbientLow[4] = {0.2f, 0.2f, 0.2f, 1.0f}; //!!!move to SCENE structure // Trackball stuff POINT gptWindow; SIZE gszWindow; // already in SCENE float curquat[4], lastquat[4]; LONG glMouseDownX, glMouseDownY; BOOL gbLeftMouse = FALSE; BOOL gbSpinning = FALSE; float zoom = 1.0f; //!!!move to SCENE structure typedef enum enumPOLYDRAW { POLYDRAW_FILLED = 0, POLYDRAW_LINES = 1, POLYDRAW_POINTS = 2 } POLYDRAW; typedef enum enumSHADE { SHADE_SMOOTH = 0, SHADE_FLAT = 1 } SHADE; typedef enum enumLIGHT { LIGHT_OFF = 0, LIGHT_INFINITE = 1, LIGHT_LOCAL = 2 } LIGHT; SHADE gShadeMode = SHADE_SMOOTH; POLYDRAW gPolyDrawMode = POLYDRAW_FILLED; LIGHT gLightMode = LIGHT_INFINITE; /******************************Public*Routine******************************\ * MyCreateGLWindow * * Setup the windows. * * History: * 15-Dec-1994 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ HWND MyCreateGLWindow(HINSTANCE hInstance, LPTSTR lpstr) { WNDCLASS wc; RECT rcl; HWND hwnd; lstrcpy(lpstrFile, lpstr); // Create the OpenGL window. wc.style = CS_OWNDC; wc.lpfnWndProc = GLWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "GLWClass"; RegisterClass(&wc); hwnd = CreateWindow( "GLWClass", "OpenGL stuff", WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, //CW_USEDEFAULT, //CW_USEDEFAULT, 300, 0, 100, 100, NULL, NULL, hInstance, NULL ); //!!!hack -- not really the right coordinates gptWindow.x = 0; gptWindow.y = 300; gszWindow.cx = 100; gszWindow.cy = 100; if (hwnd) { ShowWindow(hwnd, SW_NORMAL); UpdateWindow(hwnd); } SetTimer(hwnd, 1, 1, NULL); return hwnd; } /******************************Public*Routine******************************\ * GLWndProc * * WndProc for the OpenGL window. * * History: * 15-Dec-1994 -by- Gilman Wong [gilmanw] * Wrote it. \**************************************************************************/ long FAR PASCAL GLWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { long lRet = 0; RECT rcl; HDC hdc; PAINTSTRUCT ps; SCENE *scene; if (message != WM_CREATE) scene = (SCENE *) GetWindowLong(hwnd, GWL_USERDATA); // Process window message. switch (message) { case WM_CREATE: //LBprintf("WM_CREATE"); if(hdc = GetDC(hwnd)) { if (ghrc == (HGLRC) 0) ghrc = hrcInitGL(hwnd, hdc); scene = OpenScene(lpstrFile); if (scene) { SetWindowLong(hwnd, GWL_USERDATA, (LONG) scene); rcl.left = 0; rcl.top = 0; rcl.right = scene->szWindow.cx; rcl.bottom = scene->szWindow.cy; AdjustWindowRect(&rcl, WS_OVERLAPPEDWINDOW, 0); SetWindowPos(hwnd, NULL, 0, 0, rcl.right, rcl.bottom, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS); stateInit(hwnd, hdc); } else lRet = -1; ReleaseDC(hwnd,hdc); } break; case WM_MOVE: gptWindow.x = (int) LOWORD(lParam); gptWindow.y = (int) HIWORD(lParam); break; case WM_SIZE: gszWindow.cx = LOWORD(lParam); gszWindow.cy = HIWORD(lParam); scene->szWindow.cx = LOWORD(lParam); scene->szWindow.cy = HIWORD(lParam); vSetSize(hwnd); ForceRedraw(hwnd); break; case WM_PAINT: //LBprintf("WM_PAINT"); hdc = BeginPaint( hwnd, &ps ); DoGlStuff( hwnd, hdc ); EndPaint( hwnd, &ps ); break; case WM_PALETTECHANGED: //LBprintf("WM_PALETTECHANGED"); if (hwnd != (HWND) wParam) { if (hdc = GetDC(hwnd)) { UnrealizeObject(ghPalette); SelectPalette(hdc, ghPalette, TRUE); if (RealizePalette(hdc) != GDI_ERROR) lRet = 1; } } break; case WM_QUERYNEWPALETTE: //LBprintf("WM_QUERYNEWPALETTE"); if (hdc = GetDC(hwnd)) { UnrealizeObject(ghPalette); SelectPalette(hdc, ghPalette, FALSE); if (RealizePalette(hdc) != GDI_ERROR) lRet = 1; } break; case WM_KEYDOWN: switch (wParam) { case VK_ESCAPE: // is quick exit PostMessage(hwnd, WM_DESTROY, 0, 0); break; case VK_UP: zoom *= .8f; if (zoom < 1.0f && zoom > .8f) zoom = 1.0f; LBprintf("Zoom = %f", zoom); vSetSize(hwnd); break; case VK_DOWN: zoom *= 1.25f; if (zoom > 1.0f && zoom < 1.25f) zoom = 1.0f; LBprintf("Zoom = %f", zoom); vSetSize(hwnd); break; default: break; } break; case WM_CHAR: switch(wParam) { case 's': case 'S': gShadeMode = (gShadeMode + 1) % 2; glShadeModel( gShadeMode == SHADE_FLAT ? GL_FLAT : GL_SMOOTH ); LBprintf("glShadeModel(%s)", gShadeMode == SHADE_FLAT ? "GL_FLAT" : "GL_SMOOTH"); break; case 'p': case 'P': gPolyDrawMode = (gPolyDrawMode + 1) % 3; switch (gPolyDrawMode) { case POLYDRAW_POINTS: glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); break; case POLYDRAW_LINES: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); break; case POLYDRAW_FILLED: default: glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; } LBprintf("glPolygonMode(%s)", gPolyDrawMode == POLYDRAW_POINTS ? "GL_POINT" : gPolyDrawMode == POLYDRAW_LINES ? "GL_LINE" : "GL_FILL"); break; case 'l': case 'L': gLightMode = (gLightMode + 1) % 3; if ( gLightMode != LIGHT_OFF ) { int i; glEnable(GL_NORMALIZE); glEnable(GL_LIGHTING); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, gLightMode == LIGHT_LOCAL ? GL_TRUE : GL_FALSE); for (i = 0; i < scene->Lights.count; i++) { glCallList(scene->Lights.listBase + i); glEnable(GL_LIGHT0 + i); } /* If no other lights, turn on ambient on higher */ if (!scene->Lights.count) glLightModelfv(GL_LIGHT_MODEL_AMBIENT, &lightAmbient); else glLightModelfv(GL_LIGHT_MODEL_AMBIENT, &lightAmbientLow); } else { glDisable(GL_NORMALIZE); glDisable(GL_LIGHTING); } LBprintf("Light = %s", gLightMode == LIGHT_OFF ? "disabled" : gLightMode == LIGHT_INFINITE ? "infinite" : "local"); break; default: break; } ForceRedraw(hwnd); break; case WM_LBUTTONDOWN: SetCapture(hwnd); glMouseDownX = LOWORD(lParam); glMouseDownY = HIWORD(lParam); gbLeftMouse = TRUE; ForceRedraw(hwnd); break; case WM_LBUTTONUP: ReleaseCapture(); gbLeftMouse = FALSE; ForceRedraw(hwnd); break; case WM_TIMER: hdc = GetDC(hwnd); DoGlStuff( hwnd, hdc ); ReleaseDC(hwnd, hdc); break; case WM_DESTROY: KillTimer(hwnd, 1); hdc = GetDC(hwnd); vCleanupGL(ghrc, hdc); ReleaseDC(hwnd, hdc); PostQuitMessage( 0 ); break; default: lRet = DefWindowProc(hwnd, message, wParam, lParam); break; } return lRet; } unsigned char threeto8[8] = { 0, 0111>>1, 0222>>1, 0333>>1, 0444>>1, 0555>>1, 0666>>1, 0377 }; unsigned char twoto8[4] = { 0, 0x55, 0xaa, 0xff }; unsigned char oneto8[2] = { 0, 255 }; unsigned char ComponentFromIndex(i, nbits, shift) { unsigned char val; val = i >> shift; switch (nbits) { case 1: val &= 0x1; return oneto8[val]; case 2: val &= 0x3; return twoto8[val]; case 3: val &= 0x7; return threeto8[val]; default: return 0; } } void CreateRGBPalette(HDC hdc) { PIXELFORMATDESCRIPTOR pfd, *ppfd; LOGPALETTE *pPal; int n, i; ppfd = &pfd; n = GetPixelFormat(hdc); DescribePixelFormat(hdc, n, sizeof(PIXELFORMATDESCRIPTOR), ppfd); if (ppfd->dwFlags & PFD_NEED_PALETTE) { n = 1 << ppfd->cColorBits; pPal = (PLOGPALETTE)LocalAlloc(LMEM_FIXED, sizeof(LOGPALETTE) + n * sizeof(PALETTEENTRY)); pPal->palVersion = 0x300; pPal->palNumEntries = n; for (i=0; ipalPalEntry[i].peRed = ComponentFromIndex(i, ppfd->cRedBits, ppfd->cRedShift); pPal->palPalEntry[i].peGreen = ComponentFromIndex(i, ppfd->cGreenBits, ppfd->cGreenShift); pPal->palPalEntry[i].peBlue = ComponentFromIndex(i, ppfd->cBlueBits, ppfd->cBlueShift); pPal->palPalEntry[i].peFlags = (i == 0 || i == 255) ? 0 : PC_NOCOLLAPSE; } ghPalette = CreatePalette(pPal); LocalFree(pPal); if (ppfd->dwFlags & PFD_NEED_SYSTEM_PALETTE) { uiSysPalUse = SetSystemPaletteUse(hdc, SYSPAL_NOSTATIC); bUseStatic = TRUE; } ghpalOld = SelectPalette(hdc, ghPalette, FALSE); n = RealizePalette(hdc); UnrealizeObject(ghPalette); n = RealizePalette(hdc); } } BOOL bSetupPixelFormat(HDC hdc) { PIXELFORMATDESCRIPTOR pfd, *ppfd; int pixelformat; ppfd = &pfd; memset(ppfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR); ppfd->nVersion = 1; ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; ppfd->dwLayerMask = PFD_MAIN_PLANE; ppfd->iPixelType = PFD_TYPE_RGBA; ppfd->cColorBits = 24; ppfd->cDepthBits = 16; ppfd->cAccumBits = 0; ppfd->cStencilBits = 0; pixelformat = ChoosePixelFormat(hdc, ppfd); if ( (pixelformat = ChoosePixelFormat(hdc, ppfd)) == 0 ) { MessageBox(NULL, "ChoosePixelFormat failed", "Error", MB_OK); return FALSE; } if (SetPixelFormat(hdc, pixelformat, ppfd) == FALSE) { MessageBox(NULL, "SetPixelFormat failed", "Error", MB_OK); return FALSE; } CreateRGBPalette(hdc); return TRUE; } HGLRC hrcInitGL(HWND hwnd, HDC hdc) { HGLRC hrc; /* Create a Rendering Context */ bSetupPixelFormat( hdc ); hrc = wglCreateContext( hdc ); /* Make it Current */ wglMakeCurrent( hdc, hrc ); return hrc; } void stateInit(HWND hwnd, HDC hdc) { GLint i; SCENE *scene = (SCENE *) GetWindowLong(hwnd, GWL_USERDATA); //!!! pass instead glDrawBuffer(GL_BACK); /* Set the clear color */ glClearColor( scene->rgbaClear.r, scene->rgbaClear.g, scene->rgbaClear.b, scene->rgbaClear.a ); /* Turn on z-buffer */ glEnable(GL_DEPTH_TEST); /* Turn on backface culling */ glFrontFace(GL_CCW); //glEnable(GL_CULL_FACE); /* Shading */ glShadeModel( gShadeMode == SHADE_FLAT ? GL_FLAT : GL_SMOOTH ); /* Polygon draw mode */ switch (gPolyDrawMode) { case POLYDRAW_POINTS: glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); break; case POLYDRAW_LINES: glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); break; case POLYDRAW_FILLED: default: glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); break; } /* Turn on the lights */ if ( gLightMode != LIGHT_OFF ) { glEnable(GL_NORMALIZE); glEnable(GL_LIGHTING); glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE); glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, gLightMode == LIGHT_LOCAL ? GL_TRUE : GL_FALSE); for (i = 0; i < scene->Lights.count; i++) { glCallList(scene->Lights.listBase + i); glEnable(GL_LIGHT0 + i); } /* If no other lights, turn on ambient on higher */ if (!scene->Lights.count) glLightModelfv(GL_LIGHT_MODEL_AMBIENT, &lightAmbient); else glLightModelfv(GL_LIGHT_MODEL_AMBIENT, &lightAmbientLow); } else { glDisable(GL_NORMALIZE); glDisable(GL_LIGHTING); } /* Setup viewport */ vSetSize(hwnd); /* Initialize trackball */ trackball(curquat, 0.0, 0.0, 0.0, 0.0); } VOID vSetSize(HWND hwnd) { SCENE *scene = (SCENE *) GetWindowLong(hwnd, GWL_USERDATA); //!!! pass instead MyXYZ xyzZoomFrom; GLfloat localPerspective = scene->AspectRatio; /* Adjust aspect ratio to window size */ localPerspective *= ((GLfloat) scene->szWindow.cx / (GLfloat) scene->szWindow.cy); /* Compute the vector from xyzAt to xyzFrom */ xyzZoomFrom.x = scene->xyzFrom.x - scene->xyzAt.x; xyzZoomFrom.y = scene->xyzFrom.y - scene->xyzAt.y; xyzZoomFrom.z = scene->xyzFrom.z - scene->xyzAt.z; /* Scale by the zoom factor */ xyzZoomFrom.x *= zoom; xyzZoomFrom.y *= zoom; xyzZoomFrom.z *= zoom; /* Compute new xyzFrom */ xyzZoomFrom.x += scene->xyzAt.x; xyzZoomFrom.y += scene->xyzAt.y; xyzZoomFrom.z += scene->xyzAt.x; /* Set up viewport extents */ glViewport(0, 0, scene->szWindow.cx, scene->szWindow.cy); /* Set up the projection matrix */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); //gluPerspective(scene->ViewAngle, scene->AspectRatio, // scene->Hither, scene->Yon); gluPerspective(scene->ViewAngle, localPerspective, scene->Hither, scene->Yon * zoom); /* Set up the model matrix */ glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //gluLookAt(scene->xyzFrom.x,scene->xyzFrom.y,scene->xyzFrom.z, // scene->xyzAt.x,scene->xyzAt.y,scene->xyzAt.z, // scene->xyzUp.x,scene->xyzUp.y,scene->xyzUp.z); gluLookAt(xyzZoomFrom.x,xyzZoomFrom.y,xyzZoomFrom.z, scene->xyzAt.x,scene->xyzAt.y,scene->xyzAt.z, scene->xyzUp.x,scene->xyzUp.y,scene->xyzUp.z); } void vCleanupGL(HGLRC hrc, HDC hdc) { //if (ghPalette) // DeleteObject(SelectObject(ghdcMem, ghpalOld)); /* Destroy our context */ wglDeleteContext( hrc ); if (bUseStatic) SetSystemPaletteUse(hdc, uiSysPalUse); } void ForceRedraw(HWND hwnd) { MSG msg; if (!PeekMessage(&msg, hwnd, WM_PAINT, WM_PAINT, PM_NOREMOVE) ) { InvalidateRect(hwnd, NULL, FALSE); } } void DoGlStuff( HWND hwnd, HDC hdc ) { SCENE *scene = (SCENE *) GetWindowLong(hwnd, GWL_USERDATA); //!!! pass instead USHORT usMouseCurX, usMouseCurY; POINT pt; float matRot[4][4]; if (gbLeftMouse) { if (GetCursorPos(&pt)) { // Subtract current window origin to convert to window coordinates. pt.x -= gptWindow.x; pt.y -= gptWindow.y; // If mouse has moved since button was pressed, change quaternion. if (pt.x != glMouseDownX || pt.y != glMouseDownY) { //trackball(lastquat, // 2.0*(gszWindow.cx-glMouseDownX)/gszWindow.cx-1.0, // 2.0*glMouseDownY/gszWindow.cy-1.0, // 2.0*(gszWindow.cx-pt.x)/gszWindow.cx-1.0, // 2.0*pt.y/gszWindow.cy-1.0); trackball(lastquat, 2.0*(glMouseDownX)/gszWindow.cx-1.0, 2.0*(gszWindow.cy-glMouseDownY)/gszWindow.cy-1.0, 2.0*(pt.x)/gszWindow.cx-1.0, 2.0*(gszWindow.cy-pt.y)/gszWindow.cy-1.0); gbSpinning = TRUE; } else gbSpinning = FALSE; glMouseDownX = pt.x; glMouseDownY = pt.y; } } glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glPushMatrix(); if (gbSpinning) add_quats(lastquat, curquat, curquat); build_rotmatrix(matRot, curquat); glMultMatrixf(&(matRot[0][0])); if (scene->Objects.count) { //LBprintf("call display list"); glCallList(scene->Objects.listBase); } glPopMatrix(); SwapBuffers(hdc); } SCENE *OpenScene(LPSTR lpstrFile) { SCENE *scene = (SCENE *) NULL; CHAR *pchExt; // Find the extension. pchExt = lpstrFile; while ((*pchExt != '.') && (*pchExt != '\0')) pchExt++; if (*pchExt == '.') { // Use extension as the key to call appropriate parser. if (!lstrcmpi(pchExt, ".nff")) scene = NffOpenScene(lpstrFile); else if (!lstrcmpi(pchExt, ".obj")) scene = ObjOpenScene(lpstrFile); else LBprintf("Unknown extension: %s", pchExt); } return scene; }