1 /*
2  * Star field screensaver
3  *
4  * Copyright 2011 Carlo Bramini
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 #define WIN32_LEAN_AND_MEAN
22 #include <windows.h>
23 #include <tchar.h>
24 #include <scrnsave.h>
25 
26 #include <stdlib.h>
27 #include <time.h>
28 #include <math.h>
29 
30 #include <GL/gl.h>
31 #include <GL/glu.h>
32 
33 #include "resource.h"
34 #include "settings.h"
35 
36 #define FAR_PLANE       -80.0f
37 #define NEAR_PLANE      3.0f
38 #define GAP             0.0f
39 #define FIELD_WIDTH     50.f
40 #define FIELD_HEIGHT    45.f
41 #define FIELD_DEPTH     (NEAR_PLANE - FAR_PLANE + GAP)
42 
43 #define STAR_RED        0.f
44 #define STAR_GREEN      0.f
45 #define STAR_BLUE       0.10f
46 #define STAR_TAIL       0.9f
47 
48 typedef struct {
49     float x1;
50     float y1;
51     float x2;
52     float y2;
53     float z;
54 } VERTEX;
55 
56 static VERTEX  Vertex[MAX_STARS];
57 static HGLRC   hRC;        // Permanent Rendering Context
58 static HDC     hDC;        // Private GDI Device Context
59 static float   fAngle;
60 static GLuint  glStarTex;
61 
62 // Light position
63 static GLfloat g_light_position[4] = {
64     0.0f, 0.0f, 3.0f, 1.0f
65 };
66 
67 static PIXELFORMATDESCRIPTOR pfd=       // Pixel Format Descriptor
68 {
69     sizeof(PIXELFORMATDESCRIPTOR),      // Size Of This Pixel Format Descriptor
70     1,                                  // Version Number (?)
71     PFD_DRAW_TO_WINDOW |                // Format Must Support Window
72     PFD_SUPPORT_OPENGL |                // Format Must Support OpenGL
73     PFD_DOUBLEBUFFER,                   // Must Support Double Buffering
74     PFD_TYPE_RGBA,                      // Request An RGBA Format
75     16,                                 // Select A 16Bit Color Depth
76     0, 0, 0, 0, 0, 0,                   // Color Bits Ignored (?)
77     0,                                  // No Alpha Buffer
78     0,                                  // Shift Bit Ignored (?)
79     0,                                  // No Accumulation Buffer
80     0, 0, 0, 0,                         // Accumulation Bits Ignored (?)
81     16,                                 // 16Bit Z-Buffer (Depth Buffer)
82     0,                                  // No Stencil Buffer
83     0,                                  // No Auxiliary Buffer (?)
84     PFD_MAIN_PLANE,                     // Main Drawing Layer
85     0,                                  // Reserved (?)
86     0, 0, 0                             // Layer Masks Ignored (?)
87 };
88 
89 static HBITMAP CreateStarBitmap(HWND hWnd, HDC hDC)
90 {
91     BITMAPINFO bi;
92     LPBYTE     lpBits;
93     LPBYTE    *lppBits;
94     HBITMAP    hTextBmp, hFileBmp;
95     HDC        hTextDC, hFileDC;
96     HGDIOBJ    hOldText, hOldFile;
97     UINT       i;
98     DWORD     *Ptr32;
99     BITMAP     bm;
100     HINSTANCE  hInstance;
101 
102     // Get instance for loading the texture
103     hInstance = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
104 
105     // Load the texture
106     hFileBmp = (HBITMAP)
107               LoadImage(
108                     hInstance,
109                     MAKEINTRESOURCE(IDB_STAR),
110                     IMAGE_BITMAP,
111                     0, 0,
112                     LR_CREATEDIBSECTION | LR_DEFAULTSIZE
113               );
114 
115     // Get texture specs
116     GetObject(hFileBmp, sizeof(BITMAP), &bm);
117 
118     // Allocate new 32 bit texture
119     ZeroMemory(&bi, sizeof(bi));
120 
121     bi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
122     bi.bmiHeader.biWidth       = bm.bmWidth;
123     bi.bmiHeader.biHeight      = -bm.bmHeight;
124     bi.bmiHeader.biPlanes      = 1;
125     bi.bmiHeader.biBitCount    = 32;
126     bi.bmiHeader.biCompression = BI_RGB;
127 
128     // Makes GCC happy ;-|
129     lppBits = &lpBits;
130 
131     hTextBmp = CreateDIBSection(hDC,
132                                 (BITMAPINFO*)&bi,
133                                 DIB_RGB_COLORS,
134                                 (void**)lppBits,
135                                 NULL,
136                                 0);
137 
138     // Save new texture specs
139 //    GetObject(hTextBmp, sizeof(BITMAP), &bmStarTex);
140 //    bmStarTex.bmBits = lpBits;
141 
142     // Copy 24 bit texture in 32 texture
143     hTextDC = CreateCompatibleDC(hDC);
144     hFileDC = CreateCompatibleDC(hDC);
145 
146     hOldText = SelectObject(hTextDC, hTextBmp);
147     hOldFile = SelectObject(hFileDC, hFileBmp);
148 
149     BitBlt(hTextDC, 0, 0, bm.bmWidth, bm.bmHeight, hFileDC, 0, 0, SRCCOPY);
150 
151     SelectObject(hTextDC, hOldText);
152     SelectObject(hFileDC, hOldFile);
153 
154     DeleteDC(hTextDC);
155     DeleteDC(hFileDC);
156 
157     // Delete 24 bit texture
158     DeleteObject(hFileBmp);
159 
160     GetObject(hTextBmp, sizeof(BITMAP), &bm);
161 
162     // Apply ALPHA channel to new texture
163     for (Ptr32=(DWORD *)lpBits, i=0; i < (UINT)(bm.bmWidth * bm.bmHeight); i++)
164     {
165         DWORD Color = Ptr32[i] & 0x00FFFFFF;
166         DWORD Alpha = Color & 0xFF;
167 
168         Color |= Alpha << 24;
169 
170         Ptr32[i] = Color;
171     }
172 
173     return hTextBmp;
174 }
175 
176 static void InitGL(HBITMAP hStarTex)
177 {
178     BITMAP       bm;
179     unsigned int i;
180     float        xp, yp, zp;
181 
182     // set the GL clear color - use when the color buffer is cleared
183     glClearColor(STAR_RED, STAR_GREEN, STAR_BLUE, STAR_TAIL);
184 
185     if (Settings.bSmoothShading)
186         // set the shading model to 'smooth'
187         glShadeModel( GL_SMOOTH );
188     else
189         // set the shading model to 'flat'
190         glShadeModel( GL_FLAT );
191 
192     // set GL to render front of polygons
193     glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
194     // enable depth test
195     glDisable( GL_DEPTH_TEST );
196 
197     // enable lighting
198     glEnable( GL_LIGHTING );
199     // enable lighting for front
200     glLightModeli( GL_FRONT, GL_TRUE );
201     // material have diffuse and ambient lighting
202     glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );
203     // enable color
204     glEnable( GL_COLOR_MATERIAL );
205     // enable light 0
206     glEnable( GL_LIGHT0 );
207 
208     // set light attenuation
209     glLightf( GL_LIGHT0, GL_CONSTANT_ATTENUATION,  0.01f); //0.01f );
210     glLightf( GL_LIGHT0, GL_LINEAR_ATTENUATION,    0.01f); //0.2f );
211     glLightf( GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.005f); //0.001f );
212 
213     // clear the color buffer once
214     glClear( GL_COLOR_BUFFER_BIT );
215 
216     // randomly generate
217     srand( time( NULL ) );
218 
219     // Initialize *ALL* stars vertexes (not just programmed ones).
220     for (i = 0; i < MAX_STARS; i++)
221     {
222         xp = ( (float) rand() / RAND_MAX - .5f ) * FIELD_WIDTH;
223         yp = ( (float) rand() / RAND_MAX - .5f ) * FIELD_HEIGHT;
224         zp = ( (float) rand() / RAND_MAX       ) * FIELD_DEPTH + FAR_PLANE;
225 
226         Vertex[i].x1 = -1.f + xp;
227         Vertex[i].y1 = -1.f + yp;
228         Vertex[i].x2 =  1.f + xp;
229         Vertex[i].y2 =  1.f + yp;
230         Vertex[i].z  = zp;
231     }
232 
233     glGenTextures(1, &glStarTex);           // Create One Texture
234 
235     // Create Linear Filtered Texture
236     glBindTexture(GL_TEXTURE_2D, glStarTex);
237 
238     if (Settings.bEnableFiltering)
239     {
240         glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
241         glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
242     } else {
243         glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
244         glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
245     }
246 
247     // Get Texture properties
248     GetObject(hStarTex, sizeof(BITMAP), &bm);
249 
250     // Create texture as a mipmap
251 #if 0
252     glTexImage2D(GL_TEXTURE_2D, 0, 4, bm.bmWidth, bm.bmHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, bm.bmBits);
253 #else
254     gluBuild2DMipmaps(GL_TEXTURE_2D, 4, bm.bmWidth, bm.bmHeight, GL_RGBA, GL_UNSIGNED_BYTE, bm.bmBits);
255 #endif
256 
257     // Disable Texture Mapping (background smoothing)
258     glDisable(GL_TEXTURE_2D);
259 
260     if (Settings.bFinePerspective)
261         // Really Fast Perspective Calculations
262         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
263     else
264         // Really Nice Perspective Calculations
265         glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
266 
267     // enable blending
268     glEnable( GL_BLEND );
269 }
270 
271 static void
272 render(void)
273 {
274     unsigned int i;
275     float        fSpin;
276     float        fSpeed;
277     float        xp, yp;
278 
279     // Initialize current speed
280     fSpeed = (float)Settings.uiSpeed / 100.f;
281 
282     glEnable(GL_TEXTURE_2D);                // Enable Texture Mapping
283 
284     glBlendFunc(GL_SRC_ALPHA,GL_ONE);           // Set The Blending Function For Translucency
285 
286     switch (Settings.uiRotation) {
287     case ROTATION_LINEAR:
288         fAngle += fSpeed;
289         glRotatef( fAngle, 0.0f, 0.0f, 1.0f );
290         break;
291 
292     case ROTATION_PERIODIC:
293         fAngle += fSpeed / 75.f;
294         fSpin = (float)(50. * cos(fAngle));
295         glRotatef( fSpin, 0.0f, 0.0f, 1.0f );
296         break;
297     }
298 
299     glColor3ub(255, 255, 255);
300 
301     glBegin(GL_QUADS);              // Begin Drawing The Textured Quad
302 
303     // Draw the stars
304     for (i = 0; i < Settings.uiNumStars; i++)
305     {
306         glTexCoord2f(0.0f, 0.0f); glVertex3f(Vertex[i].x1, Vertex[i].y1, Vertex[i].z);
307         glTexCoord2f(1.0f, 0.0f); glVertex3f(Vertex[i].x2, Vertex[i].y1, Vertex[i].z);
308         glTexCoord2f(1.0f, 1.0f); glVertex3f(Vertex[i].x2, Vertex[i].y2, Vertex[i].z);
309         glTexCoord2f(0.0f, 1.0f); glVertex3f(Vertex[i].x1, Vertex[i].y2, Vertex[i].z);
310 
311         // increment z
312         Vertex[i].z += fSpeed;
313 
314         // check to see if passed view
315         if( Vertex[i].z > NEAR_PLANE + GAP ||
316             Vertex[i].z < FAR_PLANE )
317         {
318             xp = ( (float) rand() / RAND_MAX - .5f ) * FIELD_WIDTH;
319             yp = ( (float) rand() / RAND_MAX - .5f ) * FIELD_HEIGHT;
320 
321             Vertex[i].x1 = -1.f + xp;
322             Vertex[i].y1 = -1.f + yp;
323             Vertex[i].x2 =  1.f + xp;
324             Vertex[i].y2 =  1.f + yp;
325             Vertex[i].z  = FAR_PLANE;
326         }
327     }
328 
329     glEnd();                    // Done Drawing The Textured Quad
330 
331     glDisable(GL_TEXTURE_2D);               // Enable Texture Mapping
332 }
333 
334 static LRESULT CALLBACK
335 OnCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
336 {
337     GLuint  PixelFormat;
338     HBITMAP hStarTex;
339 
340     LoadSettings();
341 
342     // Gets A Device Context For The Window
343     hDC = GetDC(hWnd);
344 
345     // Finds The Closest Match To The Pixel Format We Set Above
346     PixelFormat = ChoosePixelFormat(hDC, &pfd);
347 
348     // No Matching Pixel Format?
349     if (!PixelFormat)
350     {
351         MessageBox(0, _T("Can't Find A Suitable PixelFormat."), _T("Error"),MB_OK | MB_ICONERROR);
352 
353         // This Sends A 'Message' Telling The Program To Quit
354         PostQuitMessage(0);
355         return 0;
356     }
357 
358     // Can We Set The Pixel Mode?
359     if (!SetPixelFormat(hDC, PixelFormat, &pfd))
360     {
361         MessageBox(0, _TEXT("Can't Set The PixelFormat."), _T("Error"), MB_OK | MB_ICONERROR);
362 
363         // This Sends A 'Message' Telling The Program To Quit
364         PostQuitMessage(0);
365         return 0;
366     }
367 
368     // Grab A Rendering Context
369     hRC = wglCreateContext(hDC);
370 
371     // Did We Get One?
372     if (!hRC)
373     {
374         MessageBox(0, _T("Can't Create A GL Rendering Context."), _T("Error"), MB_OK | MB_ICONERROR);
375 
376         // This Sends A 'Message' Telling The Program To Quit
377         PostQuitMessage(0);
378         return 0;
379     }
380 
381     // Can We Make The RC Active?
382     if (!wglMakeCurrent(hDC, hRC))
383     {
384         MessageBox(0, _T("Can't Activate GLRC."), _TEXT("Error"), MB_OK | MB_ICONERROR);
385 
386         // This Sends A 'Message' Telling The Program To Quit
387         PostQuitMessage(0);
388         return 0;
389     }
390 
391     // Load star texture
392     hStarTex = CreateStarBitmap(hWnd, hDC);
393 
394     // Initialize The GL Screen Using Screen Info
395     InitGL(hStarTex);
396 
397     // Delete GDI object for texture
398     DeleteObject(hStarTex);
399 
400     // Update screen every 10ms
401     SetTimer(hWnd, 1, 10, NULL);
402 
403     // Initialize spinning angle
404     fAngle = 0.f;
405 
406     return 0L;
407 }
408 
409 static LRESULT CALLBACK
410 OnDestroy(HWND hWnd, WPARAM wParam, LPARAM lParam)
411 {
412     // Delete update timer
413     KillTimer(hWnd, 1);
414 
415     // Disable Fullscreen Mode
416     ChangeDisplaySettings(NULL, 0);
417 
418     // Make The DC Current
419     wglMakeCurrent(hDC, NULL);
420 
421     // Kill The RC
422     wglDeleteContext(hRC);
423 
424     // Free The DC
425     ReleaseDC(hWnd, hDC);
426 
427 #ifdef _DEBUG_SSTARS
428     PostQuitMessage(0);
429 #endif
430 
431     return 0L;
432 }
433 
434 static LRESULT CALLBACK
435 OnPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
436 {
437     if (Settings.bDoBlending)
438     {
439         glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
440 
441         // disable lighting
442         glDisable( GL_LIGHTING );
443 
444         // blend in a polygon
445         glColor4f(STAR_RED, STAR_GREEN, STAR_BLUE, STAR_TAIL);
446 
447         // Restore both model view and projection to identity
448         glMatrixMode(GL_MODELVIEW);
449         glPushMatrix();
450         glLoadIdentity();
451 
452         glMatrixMode(GL_PROJECTION);
453         glPushMatrix();
454         glLoadIdentity();
455 
456         // Blur the background
457         glBegin( GL_QUADS );
458             glVertex3f( -1.0f, -1.0f, -1.0f );
459             glVertex3f( -1.0f,  1.0f, -1.0f );
460             glVertex3f(  1.0f,  1.0f, -1.0f );
461             glVertex3f(  1.0f, -1.0f, -1.0f );
462         glEnd();
463 
464         // Recover previous matrix
465         glPopMatrix();
466         glMatrixMode(GL_MODELVIEW);
467         glPopMatrix();
468 
469         // enable lighting
470         glEnable( GL_LIGHTING );
471     } else {
472         glClear(GL_COLOR_BUFFER_BIT);
473     }
474 
475     // save the current matrix state, so transformation will
476     // not persist across displayFunc calls, since we
477     // will do a glPopMatrix() at the end of this function
478     glPushMatrix();
479 
480     // render the scene
481     render();
482 
483     // restore the matrix state
484     glPopMatrix();
485 
486     // flush the buffer
487     glFlush();
488 
489     // swap the double buffer
490     SwapBuffers(hDC);
491 
492     // Clear redraw event
493     ValidateRect(hWnd, NULL);
494 
495     return 0L;
496 }
497 
498 static LRESULT CALLBACK
499 OnSize(HWND hWnd, WPARAM wParam, LPARAM lParam)
500 {
501     GLsizei w = LOWORD(lParam);
502     GLsizei h = HIWORD(lParam);
503 
504     // map the view port to the entire client area
505     glViewport(0, 0, w, h);
506 
507     // set the matrix mode to projection matrix
508     glMatrixMode(GL_PROJECTION);
509 
510     // load the identity matrix
511     glLoadIdentity();
512 
513     // set the perspective matrix
514     gluPerspective( 64.0, (GLdouble) w / (GLdouble)h, .1, 300.0 );
515 
516     // set the matrix mode to the modelview matrix
517     glMatrixMode(GL_MODELVIEW);
518 
519     // load the identity matrix into the modelview matrix
520     glLoadIdentity();
521 
522     // set the 'camera'
523     gluLookAt( 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 2.0, 3.0 );
524 
525     // set the position of the light
526     glLightfv( GL_LIGHT0, GL_POSITION, g_light_position );
527 
528     return 0L;
529 }
530 
531 LRESULT CALLBACK
532 ScreenSaverProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
533 {
534     switch (uMsg) {
535     case WM_CREATE:
536         return OnCreate(hWnd, wParam, lParam);
537 
538     case WM_TIMER:
539         InvalidateRect(hWnd, NULL, FALSE);
540         return 0L;
541 
542     case WM_DESTROY:
543         return OnDestroy(hWnd, wParam, lParam);
544 
545     case WM_PAINT:
546         return OnPaint(hWnd, wParam, lParam);
547 
548     case WM_SIZE: // Resizing The Screen
549         return OnSize(hWnd, wParam, lParam);
550     }
551 
552 #ifdef _DEBUG_SSTARS
553     return DefWindowProc(hWnd, uMsg, wParam, lParam);
554 #else
555     return DefScreenSaverProc(hWnd, uMsg, wParam, lParam);
556 #endif
557 }
558 
559 #ifdef _DEBUG_SSTARS
560 
561 ATOM InitApp(HINSTANCE hInstance, LPCTSTR szClassName)
562 {
563     WNDCLASS wc;
564 
565     ZeroMemory(&wc, sizeof(wc));
566 
567     wc.style         = CS_HREDRAW | CS_VREDRAW;
568     wc.lpfnWndProc   = ScreenSaverProc;
569     wc.cbClsExtra    = 0;
570     wc.cbWndExtra    = 0;
571     wc.hInstance     = hInstance;
572     wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
573     wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
574     wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
575     wc.lpszMenuName  = NULL;
576     wc.lpszClassName = szClassName;
577 
578     return RegisterClass(&wc);
579 }
580 
581 HWND InitInstance(HINSTANCE hInstance, int nCmdShow)
582 {
583     HWND  hWnd;
584     TCHAR szClass[] = _T("CLASS");
585     TCHAR szTitle[] = _T("TITLE");
586 
587     InitApp(hInstance, szClass);
588 
589     hWnd = CreateWindow(
590             szClass,
591             szTitle,
592             WS_OVERLAPPEDWINDOW,
593             CW_USEDEFAULT,
594             CW_USEDEFAULT,
595             192*3, //CW_USEDEFAULT,
596             108*3, //CW_USEDEFAULT,
597             NULL,
598             NULL,
599             hInstance,
600             NULL);
601 
602     if (hWnd)
603     {
604         ShowWindow(hWnd, nCmdShow);
605         UpdateWindow(hWnd);
606     }
607 
608     return hWnd;
609 }
610 
611 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
612 {
613     MSG     msg;
614     HWND    hWnd;
615     HWND    hDlg;
616 
617     RegisterDialogClasses(hInstance);
618 
619     hWnd = InitInstance(hInstance, nShowCmd);
620 
621     hDlg = CreateDialog(hInstance, MAKEINTRESOURCE(DLG_SCRNSAVECONFIGURE), NULL, ScreenSaverConfigureDialog);
622     ShowWindow(hDlg, SW_SHOW);
623     UpdateWindow(hDlg);
624 
625     for (;;)
626     {
627         if (GetMessage(&msg, NULL, 0, 0) <= 0)
628             break;
629 
630         TranslateMessage(&msg);
631         DispatchMessage(&msg);
632     }
633 
634     return 0;
635 }
636 #endif
637