/* * Star field screensaver * * Copyright 2011 Carlo Bramini * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define WIN32_LEAN_AND_MEAN #include #include #include #include #include #include #include #include #include "resource.h" #include "settings.h" #define FAR_PLANE -80.0f #define NEAR_PLANE 3.0f #define GAP 0.0f #define FIELD_WIDTH 50.f #define FIELD_HEIGHT 45.f #define FIELD_DEPTH (NEAR_PLANE - FAR_PLANE + GAP) #define STAR_RED 0.f #define STAR_GREEN 0.f #define STAR_BLUE 0.10f #define STAR_TAIL 0.9f typedef struct { float x1; float y1; float x2; float y2; float z; } VERTEX; static VERTEX Vertex[MAX_STARS]; static HGLRC hRC; // Permanent Rendering Context static HDC hDC; // Private GDI Device Context static float fAngle; static GLuint glStarTex; // Light position static GLfloat g_light_position[4] = { 0.0f, 0.0f, 3.0f, 1.0f }; static PIXELFORMATDESCRIPTOR pfd= // Pixel Format Descriptor { sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor 1, // Version Number (?) PFD_DRAW_TO_WINDOW | // Format Must Support Window PFD_SUPPORT_OPENGL | // Format Must Support OpenGL PFD_DOUBLEBUFFER, // Must Support Double Buffering PFD_TYPE_RGBA, // Request An RGBA Format 16, // Select A 16Bit Color Depth 0, 0, 0, 0, 0, 0, // Color Bits Ignored (?) 0, // No Alpha Buffer 0, // Shift Bit Ignored (?) 0, // No Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits Ignored (?) 16, // 16Bit Z-Buffer (Depth Buffer) 0, // No Stencil Buffer 0, // No Auxiliary Buffer (?) PFD_MAIN_PLANE, // Main Drawing Layer 0, // Reserved (?) 0, 0, 0 // Layer Masks Ignored (?) }; static HBITMAP CreateStarBitmap(HWND hWnd, HDC hDC) { BITMAPINFO bi; LPBYTE lpBits; LPBYTE *lppBits; HBITMAP hTextBmp, hFileBmp; HDC hTextDC, hFileDC; HGDIOBJ hOldText, hOldFile; UINT i; DWORD *Ptr32; BITMAP bm; HINSTANCE hInstance; // Get instance for loading the texture hInstance = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); // Load the texture hFileBmp = (HBITMAP) LoadImage( hInstance, MAKEINTRESOURCE(IDB_STAR), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE ); // Get texture specs GetObject(hFileBmp, sizeof(BITMAP), &bm); // Allocate new 32 bit texture ZeroMemory(&bi, sizeof(bi)); bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = bm.bmWidth; bi.bmiHeader.biHeight = -bm.bmHeight; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 32; bi.bmiHeader.biCompression = BI_RGB; // Makes GCC happy ;-| lppBits = &lpBits; hTextBmp = CreateDIBSection(hDC, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)lppBits, NULL, 0); // Save new texture specs // GetObject(hTextBmp, sizeof(BITMAP), &bmStarTex); // bmStarTex.bmBits = lpBits; // Copy 24 bit texture in 32 texture hTextDC = CreateCompatibleDC(hDC); hFileDC = CreateCompatibleDC(hDC); hOldText = SelectObject(hTextDC, hTextBmp); hOldFile = SelectObject(hFileDC, hFileBmp); BitBlt(hTextDC, 0, 0, bm.bmWidth, bm.bmHeight, hFileDC, 0, 0, SRCCOPY); SelectObject(hTextDC, hOldText); SelectObject(hFileDC, hOldFile); DeleteDC(hTextDC); DeleteDC(hFileDC); // Delete 24 bit texture DeleteObject(hFileBmp); GetObject(hTextBmp, sizeof(BITMAP), &bm); // Apply ALPHA channel to new texture for (Ptr32=(DWORD *)lpBits, i=0; i < (UINT)(bm.bmWidth * bm.bmHeight); i++) { DWORD Color = Ptr32[i] & 0x00FFFFFF; DWORD Alpha = Color & 0xFF; Color |= Alpha << 24; Ptr32[i] = Color; } return hTextBmp; } static void InitGL(HBITMAP hStarTex) { BITMAP bm; unsigned int i; float xp, yp, zp; // set the GL clear color - use when the color buffer is cleared glClearColor(STAR_RED, STAR_GREEN, STAR_BLUE, STAR_TAIL); if (Settings.bSmoothShading) // set the shading model to 'smooth' glShadeModel( GL_SMOOTH ); else // set the shading model to 'flat' glShadeModel( GL_FLAT ); // set GL to render front of polygons glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); // enable depth test glDisable( GL_DEPTH_TEST ); // enable lighting glEnable( GL_LIGHTING ); // enable lighting for front glLightModeli( GL_FRONT, GL_TRUE ); // material have diffuse and ambient lighting glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); // enable color glEnable( GL_COLOR_MATERIAL ); // enable light 0 glEnable( GL_LIGHT0 ); // set light attenuation glLightf( GL_LIGHT0, GL_CONSTANT_ATTENUATION, 0.01f); //0.01f ); glLightf( GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.01f); //0.2f ); glLightf( GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.005f); //0.001f ); // clear the color buffer once glClear( GL_COLOR_BUFFER_BIT ); // randomly generate srand( time( NULL ) ); // Initialize *ALL* stars vertexes (not just programmed ones). for (i = 0; i < MAX_STARS; i++) { xp = ( (float) rand() / RAND_MAX - .5f ) * FIELD_WIDTH; yp = ( (float) rand() / RAND_MAX - .5f ) * FIELD_HEIGHT; zp = ( (float) rand() / RAND_MAX ) * FIELD_DEPTH + FAR_PLANE; Vertex[i].x1 = -1.f + xp; Vertex[i].y1 = -1.f + yp; Vertex[i].x2 = 1.f + xp; Vertex[i].y2 = 1.f + yp; Vertex[i].z = zp; } glGenTextures(1, &glStarTex); // Create One Texture // Create Linear Filtered Texture glBindTexture(GL_TEXTURE_2D, glStarTex); if (Settings.bEnableFiltering) { glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); } else { glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); } // Get Texture properties GetObject(hStarTex, sizeof(BITMAP), &bm); // Create texture as a mipmap #if 0 glTexImage2D(GL_TEXTURE_2D, 0, 4, bm.bmWidth, bm.bmHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, bm.bmBits); #else gluBuild2DMipmaps(GL_TEXTURE_2D, 4, bm.bmWidth, bm.bmHeight, GL_RGBA, GL_UNSIGNED_BYTE, bm.bmBits); #endif // Disable Texture Mapping (background smoothing) glDisable(GL_TEXTURE_2D); if (Settings.bFinePerspective) // Really Fast Perspective Calculations glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); else // Really Nice Perspective Calculations glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // enable blending glEnable( GL_BLEND ); } static void render(void) { unsigned int i; float fSpin; float fSpeed; float xp, yp; // Initialize current speed fSpeed = (float)Settings.uiSpeed / 100.f; glEnable(GL_TEXTURE_2D); // Enable Texture Mapping glBlendFunc(GL_SRC_ALPHA,GL_ONE); // Set The Blending Function For Translucency switch (Settings.uiRotation) { case ROTATION_LINEAR: fAngle += fSpeed; glRotatef( fAngle, 0.0f, 0.0f, 1.0f ); break; case ROTATION_PERIODIC: fAngle += fSpeed / 75.f; fSpin = (float)(50. * cos(fAngle)); glRotatef( fSpin, 0.0f, 0.0f, 1.0f ); break; } glColor3ub(255, 255, 255); glBegin(GL_QUADS); // Begin Drawing The Textured Quad // Draw the stars for (i = 0; i < Settings.uiNumStars; i++) { glTexCoord2f(0.0f, 0.0f); glVertex3f(Vertex[i].x1, Vertex[i].y1, Vertex[i].z); glTexCoord2f(1.0f, 0.0f); glVertex3f(Vertex[i].x2, Vertex[i].y1, Vertex[i].z); glTexCoord2f(1.0f, 1.0f); glVertex3f(Vertex[i].x2, Vertex[i].y2, Vertex[i].z); glTexCoord2f(0.0f, 1.0f); glVertex3f(Vertex[i].x1, Vertex[i].y2, Vertex[i].z); // increment z Vertex[i].z += fSpeed; // check to see if passed view if( Vertex[i].z > NEAR_PLANE + GAP || Vertex[i].z < FAR_PLANE ) { xp = ( (float) rand() / RAND_MAX - .5f ) * FIELD_WIDTH; yp = ( (float) rand() / RAND_MAX - .5f ) * FIELD_HEIGHT; Vertex[i].x1 = -1.f + xp; Vertex[i].y1 = -1.f + yp; Vertex[i].x2 = 1.f + xp; Vertex[i].y2 = 1.f + yp; Vertex[i].z = FAR_PLANE; } } glEnd(); // Done Drawing The Textured Quad glDisable(GL_TEXTURE_2D); // Enable Texture Mapping } static LRESULT CALLBACK OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) { GLuint PixelFormat; HBITMAP hStarTex; LoadSettings(); // Gets A Device Context For The Window hDC = GetDC(hWnd); // Finds The Closest Match To The Pixel Format We Set Above PixelFormat = ChoosePixelFormat(hDC, &pfd); // No Matching Pixel Format? if (!PixelFormat) { MessageBox(0, _T("Can't Find A Suitable PixelFormat."), _T("Error"),MB_OK | MB_ICONERROR); // This Sends A 'Message' Telling The Program To Quit PostQuitMessage(0); return 0; } // Can We Set The Pixel Mode? if (!SetPixelFormat(hDC, PixelFormat, &pfd)) { MessageBox(0, _TEXT("Can't Set The PixelFormat."), _T("Error"), MB_OK | MB_ICONERROR); // This Sends A 'Message' Telling The Program To Quit PostQuitMessage(0); return 0; } // Grab A Rendering Context hRC = wglCreateContext(hDC); // Did We Get One? if (!hRC) { MessageBox(0, _T("Can't Create A GL Rendering Context."), _T("Error"), MB_OK | MB_ICONERROR); // This Sends A 'Message' Telling The Program To Quit PostQuitMessage(0); return 0; } // Can We Make The RC Active? if (!wglMakeCurrent(hDC, hRC)) { MessageBox(0, _T("Can't Activate GLRC."), _TEXT("Error"), MB_OK | MB_ICONERROR); // This Sends A 'Message' Telling The Program To Quit PostQuitMessage(0); return 0; } // Load star texture hStarTex = CreateStarBitmap(hWnd, hDC); // Initialize The GL Screen Using Screen Info InitGL(hStarTex); // Delete GDI object for texture DeleteObject(hStarTex); // Update screen every 10ms SetTimer(hWnd, 1, 10, NULL); // Initialize spinning angle fAngle = 0.f; return 0L; } static LRESULT CALLBACK OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam) { // Delete update timer KillTimer(hWnd, 1); // Disable Fullscreen Mode ChangeDisplaySettings(NULL, 0); // Make The DC Current wglMakeCurrent(hDC, NULL); // Kill The RC wglDeleteContext(hRC); // Free The DC ReleaseDC(hWnd, hDC); #ifdef _DEBUG_SSTARS PostQuitMessage(0); #endif return 0L; } static LRESULT CALLBACK OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam) { if (Settings.bDoBlending) { glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); // disable lighting glDisable( GL_LIGHTING ); // blend in a polygon glColor4f(STAR_RED, STAR_GREEN, STAR_BLUE, STAR_TAIL); // Restore both model view and projection to identity glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); // Blur the background glBegin( GL_QUADS ); glVertex3f( -1.0f, -1.0f, -1.0f ); glVertex3f( -1.0f, 1.0f, -1.0f ); glVertex3f( 1.0f, 1.0f, -1.0f ); glVertex3f( 1.0f, -1.0f, -1.0f ); glEnd(); // Recover previous matrix glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); // enable lighting glEnable( GL_LIGHTING ); } else { glClear(GL_COLOR_BUFFER_BIT); } // save the current matrix state, so transformation will // not persist across displayFunc calls, since we // will do a glPopMatrix() at the end of this function glPushMatrix(); // render the scene render(); // restore the matrix state glPopMatrix(); // flush the buffer glFlush(); // swap the double buffer SwapBuffers(hDC); // Clear redraw event ValidateRect(hWnd, NULL); return 0L; } static LRESULT CALLBACK OnSize(HWND hWnd, WPARAM wParam, LPARAM lParam) { GLsizei w = LOWORD(lParam); GLsizei h = HIWORD(lParam); // map the view port to the entire client area glViewport(0, 0, w, h); // set the matrix mode to projection matrix glMatrixMode(GL_PROJECTION); // load the identity matrix glLoadIdentity(); // set the perspective matrix gluPerspective( 64.0, (GLdouble) w / (GLdouble)h, .1, 300.0 ); // set the matrix mode to the modelview matrix glMatrixMode(GL_MODELVIEW); // load the identity matrix into the modelview matrix glLoadIdentity(); // set the 'camera' gluLookAt( 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 2.0, 3.0 ); // set the position of the light glLightfv( GL_LIGHT0, GL_POSITION, g_light_position ); return 0L; } LRESULT CALLBACK ScreenSaverProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: return OnCreate(hWnd, wParam, lParam); case WM_TIMER: InvalidateRect(hWnd, NULL, FALSE); return 0L; case WM_DESTROY: return OnDestroy(hWnd, wParam, lParam); case WM_PAINT: return OnPaint(hWnd, wParam, lParam); case WM_SIZE: // Resizing The Screen return OnSize(hWnd, wParam, lParam); } #ifdef _DEBUG_SSTARS return DefWindowProc(hWnd, uMsg, wParam, lParam); #else return DefScreenSaverProc(hWnd, uMsg, wParam, lParam); #endif } #ifdef _DEBUG_SSTARS ATOM InitApp(HINSTANCE hInstance, LPCTSTR szClassName) { WNDCLASS wc; ZeroMemory(&wc, sizeof(wc)); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = ScreenSaverProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = szClassName; return RegisterClass(&wc); } HWND InitInstance(HINSTANCE hInstance, int nCmdShow) { HWND hWnd; TCHAR szClass[] = _T("CLASS"); TCHAR szTitle[] = _T("TITLE"); InitApp(hInstance, szClass); hWnd = CreateWindow( szClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 192*3, //CW_USEDEFAULT, 108*3, //CW_USEDEFAULT, NULL, NULL, hInstance, NULL); if (hWnd) { ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); } return hWnd; } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { MSG msg; HWND hWnd; HWND hDlg; RegisterDialogClasses(hInstance); hWnd = InitInstance(hInstance, nShowCmd); hDlg = CreateDialog(hInstance, MAKEINTRESOURCE(DLG_SCRNSAVECONFIGURE), NULL, ScreenSaverConfigureDialog); ShowWindow(hDlg, SW_SHOW); UpdateWindow(hDlg); for (;;) { if (GetMessage(&msg, NULL, 0, 0) <= 0) break; TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } #endif