//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// /* glapp.c - Simple OpenGL shell There are several options allowed on the command line. They are: -height : what window/screen height do you want to use? -width : what window/screen width do you want to use? -bpp : what color depth do you want to use? -window : create a rendering window rather than full-screen -fov : use a field of view other than 90 degrees */ #include "stdafx.h" #pragma warning(disable:4305) #pragma warning(disable:4244) #include #include #include #include #include #include #include "matsysapp.h" #include "cmdlib.h" #include "mathlib/mathlib.h" #include "materialsystem/imaterialproxyfactory.h" #include "filesystem.h" #include "materialsystem/imaterialproxy.h" #include "materialsystem/MaterialSystem_Config.h" #include "tier0/icommandline.h" #include "filesystem_tools.h" #include "materialsystem/imesh.h" #include "vstdlib/cvar.h" static int g_nCapture = 0; #define OSR2_BUILD_NUMBER 1111 #define M_PI 3.14159265358979323846 // matches value in gcc v2 math.h SpewRetval_t MatSysAppSpewFunc( SpewType_t type, char const *pMsg ) { printf( "%s", pMsg ); OutputDebugString( pMsg ); if( type == SPEW_ASSERT ) return SPEW_DEBUGGER; else if( type == SPEW_ERROR ) return SPEW_ABORT; else return SPEW_CONTINUE; } class MatSysAppMaterialProxyFactory : public IMaterialProxyFactory { public: virtual IMaterialProxy *CreateProxy( const char *proxyName ) { CreateInterfaceFn clientFactory = Sys_GetFactoryThis(); if( !clientFactory ) { return NULL; } // allocate exactly enough memory for the versioned name on the stack. char proxyVersionedName[1024]; strcpy( proxyVersionedName, proxyName ); strcat( proxyVersionedName, IMATERIAL_PROXY_INTERFACE_VERSION ); IMaterialProxy *materialProxy; materialProxy = ( IMaterialProxy * )clientFactory( proxyVersionedName, NULL ); if( !materialProxy ) { return NULL; } return materialProxy; } virtual void DeleteProxy( IMaterialProxy *pProxy ) { if( pProxy ) { pProxy->Release(); } } }; MatSysAppMaterialProxyFactory g_MatSysAppMaterialProxyFactory; MaterialSystemApp g_MaterialSystemApp; float fAngle = 0.0f; char *szFSDesc[] = { "Windowed", "Full Screen" }; extern "C" unsigned int g_Time; unsigned int g_Time = 0; int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { return g_MaterialSystemApp.WinMain(hInstance, hPrevInstance, szCmdLine, iCmdShow); } BOOL isdigits( char *s ) { int i; for (i = 0; s[i]; i++) { if ((s[i] > '9') || (s[i] < '0')) { return FALSE; } } return TRUE; } LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam) { return g_MaterialSystemApp.WndProc(hwnd, iMsg, wParam, lParam); } // This function builds a list the screen resolutions supported by the display driver static void BuildModeList(screen_res_t* &pResolutions, int &iResCount) { DEVMODE dm; int mode; mode = 0; while(EnumDisplaySettings(NULL, mode, &dm)) { mode++; } pResolutions = (screen_res_t *)malloc(sizeof(screen_res_t)*mode); mode = 0; while(EnumDisplaySettings(NULL, mode, &dm)) { pResolutions[mode].width = dm.dmPelsWidth; pResolutions[mode].height = dm.dmPelsHeight; pResolutions[mode].bpp = dm.dmBitsPerPel; pResolutions[mode].flags = dm.dmDisplayFlags; pResolutions[mode].frequency = dm.dmDisplayFrequency; mode++; } iResCount = mode; } bool Sys_Error(const char *pMsg, ...) { va_list marker; char msg[4096]; va_start(marker, pMsg); vsprintf(msg, pMsg, marker); va_end(marker); MessageBox(NULL, msg, "FATAL ERROR", MB_OK); g_MaterialSystemApp.Term(); exit(1); return false; } void con_Printf(const char *pMsg, ...) { char msg[2048]; va_list marker; va_start(marker, pMsg); vsprintf(msg, pMsg, marker); va_end(marker); OutputDebugString(msg); } bool MSA_IsKeyDown(char key) { return !!(GetAsyncKeyState(key) & 0x8000); } bool MSA_IsMouseButtonDown( int button ) { if( button == MSA_BUTTON_LEFT ) return !!(GetAsyncKeyState(VK_LBUTTON) & 0x8000); else return !!(GetAsyncKeyState(VK_RBUTTON) & 0x8000); } void MSA_Sleep(unsigned long count) { if(count > 0) Sleep(count); } static void MaterialSystem_Error( char *fmt, ... ) { char str[4096]; va_list marker; va_start(marker, fmt); vsprintf(str, fmt, marker); va_end(marker); Sys_Error(str); } static void MaterialSystem_Warning( char *fmt, ... ) { } void InitMaterialSystemConfig(MaterialSystem_Config_t *pConfig) { memset( pConfig, 0, sizeof(*pConfig) ); // pConfig->screenGamma = 2.2f; // pConfig->texGamma = 2.2; // pConfig->overbright = 2; pConfig->bAllowCheats = false; // pConfig->bLinearFrameBuffer = false; pConfig->skipMipLevels = 0; // pConfig->lightScale = 1.0f; pConfig->bFilterLightmaps = true; pConfig->bFilterTextures = true; pConfig->bMipMapTextures = true; pConfig->nShowMipLevels = 0; pConfig->bReverseDepth = false; pConfig->bCompressedTextures = true; // pConfig->bBumpmap = true; pConfig->bShowSpecular = true; pConfig->bShowDiffuse = true; // pConfig->maxFrameLatency = 1; pConfig->bDrawFlat = false; // pConfig->bLightingOnly = false; pConfig->bSoftwareLighting = false; pConfig->bEditMode = false; // No, we're not in WorldCraft. // pConfig->m_bForceTrilinear = false; pConfig->m_nForceAnisotropicLevel = 0; // pConfig->m_bForceBilinear = false; } /* ==================== CalcFov ==================== */ float CalcFov (float fov_x, float width, float height) { float a; float x; if (fov_x < 1 || fov_x > 179) fov_x = 90; // error, set to 90 x = width/tan(fov_x/360*M_PI); a = atan (height/x); a = a*360/M_PI; return a; } MaterialSystemApp::MaterialSystemApp() { Clear(); } MaterialSystemApp::~MaterialSystemApp() { Term(); } void MaterialSystemApp::Term() { int i; // Free the command line holder memory if (m_argc > 0) { // Free in reverse order of allocation for (i = (m_argc-1); i >= 0; i--) { free(m_argv[i]); } // Free the parameter "pockets" free(m_argv); } // Free the memory that holds the video resolution list if (m_pResolutions) free(m_pResolutions); if (m_hDC) { if (!ReleaseDC((HWND)m_hWnd, (HDC)m_hDC)) { MessageBox(NULL, "ShutdownOpenGL - ReleaseDC failed\n", "ERROR", MB_OK); } m_hDC = NULL; } if (m_bFullScreen) { ChangeDisplaySettings( 0, 0 ); } Clear(); } void MaterialSystemApp::Clear() { m_pMaterialSystem = NULL; m_hMaterialSystemInst = 0; m_hInstance = 0; m_iCmdShow = 0; m_hWnd = 0; m_hDC = 0; m_bActive = false; m_bFullScreen = false; m_width = m_height = 0; m_centerx = m_centery = 0; m_bpp = 0; m_bChangeBPP = false; m_bAllowSoft = 0; g_nCapture = 0; m_szCmdLine = 0; m_argc = 0; m_argv = 0; m_glnWidth = 0; m_glnHeight = 0; m_gldAspect = 0; m_NearClip = m_FarClip = 0; m_fov = 90; m_pResolutions = 0; m_iResCount = 0; m_iVidMode = 0; } int MaterialSystemApp::WinMain(void *hInstance, void *hPrevInstance, char *szCmdLine, int iCmdShow) { MSG msg; HDC hdc; memset(&msg,0,sizeof(msg)); CommandLine()->CreateCmdLine( Plat_GetCommandLine() ); // Not changable by user m_hInstance = hInstance; m_iCmdShow = iCmdShow; m_pResolutions = 0; m_NearClip = 8.0f; m_FarClip = 28400.0f; // User definable m_fov = 90.0f; m_bAllowSoft = FALSE; m_bFullScreen = TRUE; // Get the current display device info hdc = GetDC( NULL ); m_DevInfo.bpp = GetDeviceCaps(hdc, BITSPIXEL); m_DevInfo.width = GetSystemMetrics(SM_CXSCREEN); m_DevInfo.height = GetSystemMetrics(SM_CYSCREEN); ReleaseDC(NULL, hdc); // Parse the command line if there is one m_argc = 0; if (strlen(szCmdLine) > 0) { m_szCmdLine = szCmdLine; GetParameters(); } // Default to 640 pixels wide m_width = FindNumParameter("-width", 640); m_height = FindNumParameter("-height", 480); m_bpp = FindNumParameter("-bpp", 32); m_fov = FindNumParameter("-fov", 90); // Check for windowed rendering m_bFullScreen = FALSE; if (FindParameter("-fullscreen")) { m_bFullScreen = TRUE; } // Build up the video mode list BuildModeList(m_pResolutions, m_iResCount); // Create the main program window, start up OpenGL and create our viewport if (CreateMainWindow( m_width, m_height, m_bpp, m_bFullScreen) != TRUE) { ChangeDisplaySettings(0, 0); MessageBox(NULL, "Unable to create main window.\nProgram will now end.", "FATAL ERROR", MB_OK); Term(); return 0; } // Turn the cursor off for full-screen mode if (m_bFullScreen == TRUE) { // Probably want to do this all the time anyway ShowCursor(FALSE); } // We're live now m_bActive = TRUE; // Define this funciton to init your app AppInit(); RECT rect; GetWindowRect( (HWND)m_hWnd, &rect ); m_centerx = ( rect.left + rect.right ) / 2; m_centery = ( rect.top + rect.bottom ) / 2; // Begin the main program loop while (m_bActive == TRUE) { while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage (&msg); DispatchMessage (&msg); } if (m_pMaterialSystem) { RenderScene(); } } if (m_bFullScreen == TRUE) { ShowCursor(TRUE); } // Release the parameter and video resolution lists Term(); // Tell the app to cleanup. AppExit(); return msg.wParam; } long MaterialSystemApp::WndProc(void *inhwnd, long iMsg, long wParam, long lParam) { if(inhwnd != m_hWnd) { return DefWindowProc((HWND)inhwnd, iMsg, wParam, lParam); } HWND hwnd = (HWND)inhwnd; switch (iMsg) { case WM_CHAR: switch(wParam) { case VK_ESCAPE: SendMessage(hwnd, WM_CLOSE, 0, 0); break; } AppChar( wParam ); break; case WM_KEYDOWN: AppKey( wParam, true ); break; case WM_KEYUP: AppKey( wParam, false ); break; case WM_ACTIVATE: if ((LOWORD(wParam) != WA_INACTIVE) && ((HWND)lParam == NULL)) { ShowWindow(hwnd, SW_RESTORE); SetForegroundWindow(hwnd); } else { if (m_bFullScreen) { ShowWindow(hwnd, SW_MINIMIZE); } } return 0; case WM_SETFOCUS: if(g_bCaptureOnFocus) { MouseCapture(); } break; case WM_KILLFOCUS: if(g_bCaptureOnFocus) { MouseRelease(); } break; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: { if(!g_bCaptureOnFocus) { g_nCapture++; MouseCapture(); } } break; case WM_LBUTTONUP: case WM_RBUTTONUP: { if(!g_bCaptureOnFocus) { g_nCapture--; MouseRelease(); } } break; case WM_CLOSE: Term(); m_bActive = FALSE; break; case WM_DESTROY: PostQuitMessage (0); return 0; } return DefWindowProc (hwnd, iMsg, wParam, lParam); } bool MaterialSystemApp::InitMaterialSystem() { RECT rect; // Init libraries. MathLib_Init( true, true, true, 2.2f, 2.2f, 0.0f, 2.0f ); SpewOutputFunc( MatSysAppSpewFunc ); if ((m_hDC = GetDC((HWND)m_hWnd)) == NULL) { ChangeDisplaySettings(0, 0); MessageBox(NULL, "GetDC on main window failed", "FATAL ERROR", MB_OK); return FALSE; } // Load the material system DLL and get its interface. char *pDLLName = "MaterialSystem.dll"; m_hMaterialSystemInst = LoadLibrary( pDLLName ); if( !m_hMaterialSystemInst ) { return Sys_Error( "Can't load MaterialSystem.dll\n" ); } CreateInterfaceFn clientFactory = Sys_GetFactory( pDLLName ); if ( clientFactory ) { m_pMaterialSystem = (IMaterialSystem *)clientFactory( MATERIAL_SYSTEM_INTERFACE_VERSION, NULL ); if ( !m_pMaterialSystem ) { return Sys_Error( "Could not get the material system interface from materialsystem.dll" ); } } else { return Sys_Error( "Could not find factory interface in library MaterialSystem.dll" ); } const char *pPath = CommandLine()->ParmValue("-game"); if(!pPath) { // If they didn't specify -game on the command line, use VPROJECT. CmdLib_InitFileSystem( "." ); pPath = "."; } else { CmdLib_InitFileSystem( pPath ); } // BUGBUG: Can't figure out why this is broken. EXECUTABLE_PATH should already be there // but it isn't so the shader system is computing texture memory on each run... g_pFullFileSystem->AddSearchPath( "U:\\main\\game\\bin", "EXECUTABLE_PATH", PATH_ADD_TO_TAIL ); const char *pShaderDLL = FindParameterArg("-shaderdll"); char defaultShaderDLL[256]; if(!pShaderDLL) { strcpy(defaultShaderDLL, "shaderapidx9.dll"); pShaderDLL = defaultShaderDLL; } if(!m_pMaterialSystem->Init(pShaderDLL, &g_MatSysAppMaterialProxyFactory, CmdLib_GetFileSystemFactory())) return Sys_Error("IMaterialSystem::Init failed"); MaterialSystem_Config_t config; InitMaterialSystemConfig(&config); config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true ); config.SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, true ); if(!m_pMaterialSystem->SetMode(m_hWnd, config)) return Sys_Error("IMaterialSystem::SetMode failed"); m_pMaterialSystem->OverrideConfig(config, false); GetClientRect((HWND)m_hWnd, &rect); m_glnWidth= rect.right; m_glnHeight = rect.bottom; m_gldAspect = (float)m_glnWidth / m_glnHeight; GetWindowRect( (HWND)m_hWnd, &rect ); m_centerx = (rect.left + rect.right) / 2; m_centery = (rect.top + rect.bottom) / 2; return true; } bool MaterialSystemApp::CreateMainWindow(int width, int height, int bpp, bool fullscreen) { HWND hwnd; WNDCLASSEX wndclass; DWORD dwStyle, dwExStyle; int x, y, sx, sy, ex, ey, ty; if ((hwnd = FindWindow(g_szAppName, g_szAppName)) != NULL) { SetForegroundWindow(hwnd); return 0; } wndclass.cbSize = sizeof (wndclass); wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; wndclass.lpfnWndProc = ::WndProc; wndclass.cbClsExtra = 0; wndclass.cbWndExtra = 0; wndclass.hInstance = (HINSTANCE)m_hInstance; wndclass.hIcon = 0; wndclass.hCursor = LoadCursor (NULL, IDC_ARROW); wndclass.hbrBackground = (HBRUSH)COLOR_GRAYTEXT; wndclass.lpszMenuName = NULL; wndclass.lpszClassName = g_szAppName; wndclass.hIconSm = 0; if (!RegisterClassEx (&wndclass)) { MessageBox(NULL, "Window class registration failed.", "FATAL ERROR", MB_OK); return FALSE; } if (fullscreen) { dwExStyle = WS_EX_TOPMOST; dwStyle = WS_POPUP | WS_VISIBLE; x = y = 0; sx = m_width; sy = m_height; } else { dwExStyle = 0; //dwStyle = WS_CAPTION | WS_SYSMENU | WS_THICKFRAME; // Use this if you want a "normal" window dwStyle = WS_CAPTION; ex = GetSystemMetrics(SM_CXEDGE); ey = GetSystemMetrics(SM_CYEDGE); ty = GetSystemMetrics(SM_CYSIZE); // Center the window on the screen x = (m_DevInfo.width / 2) - ((m_width+(2*ex)) / 2); y = (m_DevInfo.height / 2) - ((m_height+(2*ey)+ty) / 2); sx = m_width+(2*ex); sy = m_height+(2*ey)+ty; /* Check to be sure the requested window size fits on the screen and adjust each dimension to fit if the requested size does not fit. */ if (sx >= m_DevInfo.width) { x = 0; sx = m_DevInfo.width-(2*ex); } if (sy >= m_DevInfo.height) { y = 0; sy = m_DevInfo.height-((2*ey)+ty); } } if ((hwnd = CreateWindowEx (dwExStyle, g_szAppName, // window class name g_szAppName, // window caption dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, // window style x, // initial x position y, // initial y position sx, // initial x size sy, // initial y size NULL, // parent window handle NULL, // window menu handle (HINSTANCE)m_hInstance, // program instance handle NULL)) // creation parameters == NULL) { ChangeDisplaySettings(0, 0); MessageBox(NULL, "Window creation failed.", "FATAL ERROR", MB_OK); return FALSE; } m_hWnd = hwnd; if (!InitMaterialSystem()) { m_hWnd = NULL; return FALSE; } ShowWindow((HWND)m_hWnd, m_iCmdShow); UpdateWindow((HWND)m_hWnd); SetForegroundWindow((HWND)m_hWnd); SetFocus((HWND)m_hWnd); return TRUE; } void MaterialSystemApp::RenderScene() { if(!m_pMaterialSystem) return; static DWORD lastTime = 0; POINT cursorPoint; float deltax = 0, deltay = 0, frametime; DWORD newTime = GetTickCount(); DWORD deltaTime = newTime - lastTime; if ( deltaTime > 1000 ) deltaTime = 0; lastTime = newTime; frametime = (float) ((double)deltaTime * 0.001); g_Time = newTime; if ( g_nCapture ) { GetCursorPos( &cursorPoint ); SetCursorPos( m_centerx, m_centery ); deltax = (cursorPoint.x - m_centerx) * 0.1f; deltay = (cursorPoint.y - m_centery) * -0.1f; } else { deltax = deltay = 0; } CMatRenderContextPtr pRenderContext(m_pMaterialSystem); m_pMaterialSystem->BeginFrame(deltaTime); pRenderContext->ClearBuffers(true, true); pRenderContext->Viewport( 0, 0, m_width, m_height ); pRenderContext->MatrixMode(MATERIAL_PROJECTION); pRenderContext->LoadIdentity(); pRenderContext->PerspectiveX(m_fov, m_gldAspect, m_NearClip, m_FarClip); pRenderContext->MatrixMode(MATERIAL_VIEW); pRenderContext->LoadIdentity(); pRenderContext->MatrixMode(MATERIAL_MODEL); pRenderContext->LoadIdentity(); AppRender( frametime, deltax, deltay ); m_pMaterialSystem->SwapBuffers(); m_pMaterialSystem->EndFrame(); } void MaterialSystemApp::MouseCapture() { SetCapture( (HWND)m_hWnd ); ShowCursor(FALSE); SetCursorPos( m_centerx, m_centery ); } void MaterialSystemApp::MouseRelease() { ShowCursor(TRUE); ReleaseCapture(); SetCursorPos( m_centerx, m_centery ); } void MaterialSystemApp::GetParameters() { int count; char *s, *tstring; // Make a copy of the command line to count the parameters - strtok is destructive tstring = (char *)malloc(sizeof(char)*(strlen(m_szCmdLine)+1)); strcpy(tstring, m_szCmdLine); // Count the parameters s = strtok(tstring, " "); count = 1; while (strtok(NULL, " ") != NULL) { count++; } free(tstring); // Allocate "pockets" for the parameters m_argv = (char **)malloc(sizeof(char*)*(count+1)); // Copy first parameter into the "pockets" m_argc = 0; s = strtok(m_szCmdLine, " "); m_argv[m_argc] = (char *)malloc(sizeof(char)*(strlen(s)+1)); strcpy(m_argv[m_argc], s); m_argc++; // Copy the rest of the parameters do { // get the next token s = strtok(NULL, " "); if (s != NULL) { // add it to the list m_argv[m_argc] = (char *)malloc(sizeof(char)*(strlen(s)+1)); strcpy(m_argv[m_argc], s); m_argc++; } } while (s != NULL); } int MaterialSystemApp::FindNumParameter(const char *s, int defaultVal) { int i; for (i = 0; i < (m_argc-1); i++) { if (stricmp(m_argv[i], s) == 0) { if (isdigits(m_argv[i+1])) { return(atoi(m_argv[i+1])); } else { return defaultVal; } } } return defaultVal; } bool MaterialSystemApp::FindParameter(const char *s) { int i; for (i = 0; i < m_argc; i++) { if (stricmp(m_argv[i], s) == 0) { return true; } } return false; } const char *MaterialSystemApp::FindParameterArg( const char *s ) { int i; for (i = 0; i < m_argc; i++) { if (stricmp(m_argv[i], s) == 0) { if( (i+1) < m_argc ) return m_argv[i+1]; else return ""; } } return NULL; } void MaterialSystemApp::SetTitleText(const char *fmt, ...) { char str[4096]; va_list marker; va_start(marker, fmt); vsprintf(str, fmt, marker); va_end(marker); ::SetWindowText((HWND)m_hWnd, str); } void MaterialSystemApp::MakeWindowTopmost() { ::SetWindowPos((HWND)m_hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE); } void MaterialSystemApp::AppShutdown() { SendMessage( (HWND)m_hWnd, WM_CLOSE, 0, 0 ); } void MaterialSystemApp::QuitNextFrame() { PostMessage( (HWND)m_hWnd, WM_CLOSE, 0, 0 ); }