1 /**
2 * TODO
3 */
4 module dash.graphics.adapters.win32gl;
5 
6 version( Windows ):
7 
8 import dash.core, dash.graphics, dash.utility;
9 
10 import win32.windef, win32.winuser, win32.winbase;
11 import win32.wingdi : PIXELFORMATDESCRIPTOR, SetPixelFormat, SwapBuffers;
12 import derelict.opengl3.gl3, derelict.opengl3.wgl, derelict.opengl3.wglext;
13 
14 enum DWS_FULLSCREEN = WS_POPUP | WS_SYSMENU;
15 enum DWS_WINDOWED = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU;
16 
17 extern( Windows )
18 private LRESULT WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
19 {
20     switch( message )
21     {
22         // On close
23         case WM_CLOSE:
24             DGame.instance.currentState = EngineState.Quit;
25             break;
26         // If key down, send it to input
27         case WM_KEYDOWN:
28             Keyboard.setButtonState( cast(Keyboard.Buttons)wParam, true );
29             break;
30         // If key up, send it to input
31         case WM_KEYUP:
32             Keyboard.setButtonState( cast(Keyboard.Buttons)wParam, false );
33             break;
34         // On right mouse down
35         case WM_RBUTTONDOWN:
36             Mouse.setButtonState( Mouse.Buttons.Right, true );
37             break;
38         // On right mouse up
39         case WM_RBUTTONUP:
40             Mouse.setButtonState( Mouse.Buttons.Right, false );
41             break;
42         // On left mouse down
43         case WM_LBUTTONDOWN:
44             Mouse.setButtonState( Mouse.Buttons.Left, true );
45             break;
46         // On left mouse up
47         case WM_LBUTTONUP:
48             Mouse.setButtonState( Mouse.Buttons.Left, false );
49             break;
50         // On mouse scroll
51         case WM_MOUSEWHEEL:
52             Mouse.setAxisState( Mouse.Axes.ScrollWheel, Mouse.getAxisState( Mouse.Axes.ScrollWheel ) + ( ( cast(int)wParam >> 16 ) / 120 ) );
53             break;
54             // If no change, send to default windows handler
55         default:
56             return DefWindowProc( hWnd, message, wParam, lParam );
57     }
58     return 0;
59 }
60 
61 /**
62 * TODO
63 */
64 final class Win32GL : OpenGL
65 {
66 private:
67     HWND _hWnd;
68     HINSTANCE _hInstance;
69     bool _wasFullscreen;
70     HDC deviceContext;
71     HGLRC renderContext;
72 
73 public:
74     /// TODO
75     mixin( Property!_hWnd );
76     /// TODO
77     mixin( Property!_hInstance );
78     /// TODO
79     static @property Win32GL get() { return cast(Win32GL)Graphics.adapter; }
80 
81     /**
82     * TODO
83     */
84     override void initialize()
85     {
86         // Load opengl functions
87         DerelictGL3.load();
88 
89         // Setup the window
90         screenWidth = GetSystemMetrics( SM_CXSCREEN );
91         screenHeight = GetSystemMetrics( SM_CYSCREEN );
92 
93         hInstance = GetModuleHandle( null );
94 
95         WNDCLASSEX wcex = {
96             WNDCLASSEX.sizeof,
97                 CS_HREDRAW | CS_VREDRAW,// | CS_OWNDC,
98                 &WndProc,
99                 0,
100                 0,
101                 cast(void*)hInstance,
102                 null,
103                 LoadCursor( null, IDC_ARROW ),
104                 cast(HBRUSH)( COLOR_WINDOW + 1 ),
105                 null,
106                 DGame.instance.title.ptr,
107                 null
108         };
109 
110         RegisterClassEx( &wcex );
111 
112         // Setup opengl
113         uint formatCount;
114         int pixelFormat;
115         PIXELFORMATDESCRIPTOR pfd;
116 
117         // Check for OpenGL version
118         openWindow( false );
119 
120         deviceContext = GetDC( hWnd );
121         SetPixelFormat( deviceContext, 1, &pfd );
122         renderContext = wglCreateContext( deviceContext );
123         wglMakeCurrent( deviceContext, renderContext );
124 
125         DerelictGL3.reload();
126 
127         if( DerelictGL3.loadedVersion < GLVersion.GL40 )
128         {
129             fatalf( "Your version of OpenGL is unsupported. Required: GL40 Yours: %s", DerelictGL3.loadedVersion );
130             //throw new Exception( "Unsupported version of OpenGL." );
131             return;
132         }
133 
134         shutdown();
135         openWindow();
136 
137         // Set attributes list
138         const(int)[ 19 ] attributeList = [
139             WGL_SUPPORT_OPENGL_ARB, TRUE,                       // Support for OpenGL rendering
140             WGL_DRAW_TO_WINDOW_ARB, TRUE,                       // Support for rendering window
141             WGL_ACCELERATION_ARB,   WGL_FULL_ACCELERATION_ARB,  // Support for hardware acceleration
142             WGL_COLOR_BITS_ARB,     24,                         // Support for 24bit color
143             WGL_DEPTH_BITS_ARB,     24,                         // Support for 24bit depth buffer
144             WGL_DOUBLE_BUFFER_ARB,  TRUE,                       // Support for double buffer
145             WGL_SWAP_METHOD_ARB,    WGL_SWAP_EXCHANGE_ARB,      // Support for swapping buffers
146             WGL_PIXEL_TYPE_ARB,     WGL_TYPE_RGBA_ARB,          // Support for RGBA pixel type
147             WGL_STENCIL_BITS_ARB,   8,                          // Support for 8 bit stencil buffer
148             0                                                   // Null terminate
149         ];
150 
151         // Set version to 4.0
152         const(int)[ 5 ] versionInfo = [
153             WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
154             WGL_CONTEXT_MINOR_VERSION_ARB, 0,
155             0
156         ];
157 
158         // Get new Device Context
159         deviceContext = GetDC( hWnd );
160 
161         // Query pixel format
162         wglChoosePixelFormatARB( deviceContext, attributeList.ptr, null, 1, &pixelFormat, &formatCount );
163 
164         // Set the pixel format
165         SetPixelFormat( deviceContext, pixelFormat, &pfd );
166 
167         // Create OpenGL rendering context
168         renderContext = wglCreateContextAttribsARB( deviceContext, null, versionInfo.ptr );
169 
170         // Set current context
171         wglMakeCurrent( deviceContext, renderContext );
172 
173         refresh();
174 
175         HANDLE hIcon = LoadImage( null, ( Resources.Textures ~ "/icon.ico" ).ptr, IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE);
176         if( hIcon )
177         {
178             //Change both icons to the same icon handle.
179             SendMessage( hWnd, WM_SETICON, ICON_SMALL, cast(int)hIcon );
180             SendMessage( hWnd, WM_SETICON, ICON_BIG, cast(int)hIcon );
181 
182             //This will ensure that the application icon gets changed too.
183             SendMessage( GetWindow( hWnd, GW_OWNER ), WM_SETICON, ICON_SMALL, cast(int)hIcon );
184             SendMessage( GetWindow( hWnd, GW_OWNER ), WM_SETICON, ICON_BIG, cast(int)hIcon );
185         }
186     }
187 
188     /**
189     * TODO
190     */
191     override void shutdown()
192     {
193         wglMakeCurrent( null, null );
194         wglDeleteContext( renderContext );
195         renderContext = null;
196         ReleaseDC( hWnd, deviceContext );
197         deviceContext = null;
198         closeWindow();
199     }
200 
201     /**
202     * TODO
203     */
204     override void resize()
205     {
206         LONG style = GetWindowLong( hWnd, GWL_STYLE ) & ~( DWS_FULLSCREEN | DWS_WINDOWED );
207 
208         loadProperties();
209 
210         if( fullscreen )
211         {
212             width = screenWidth;
213             height = screenHeight;
214             style |= DWS_FULLSCREEN;
215         }
216         else
217         {
218             style |= DWS_WINDOWED;
219         }
220 
221         if( _wasFullscreen != fullscreen )
222         {
223             SetWindowLong( hWnd, GWL_STYLE, style );
224             SetWindowPos( hWnd, null, ( screenWidth - width ) / 2, ( screenHeight - height ) / 2,
225                           width + ( 2 * GetSystemMetrics( SM_CYBORDER ) ),
226                           height + GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYBORDER ),
227                           SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED );
228         }
229 
230         _wasFullscreen = fullscreen;
231 
232         resizeDefferedRenderBuffer();
233 
234         glViewport( 0, 0, width, height );
235     }
236 
237     /**
238     * TODO
239     */
240     override void refresh()
241     {
242         resize();
243 
244         // Enable back face culling
245         if( backfaceCulling )
246         {
247             glEnable( GL_CULL_FACE );
248             glCullFace( GL_BACK );
249         }
250 
251         // Turn on of off the v sync
252         wglSwapIntervalEXT( vsync );
253     }
254 
255     /**
256     * TODO
257     */
258     override void swapBuffers()
259     {
260         SwapBuffers( deviceContext );
261     }
262 
263     /**
264     * TODO
265     */
266     override void openWindow()
267     {
268         openWindow( true );
269     }
270 
271     /**
272     * TODO
273     */
274     final void openWindow( bool showWindow )
275     {
276         hWnd = CreateWindowEx( 0, DGame.instance.title.ptr, DGame.instance.title.ptr, fullscreen ? DWS_FULLSCREEN : DWS_WINDOWED,
277                                ( screenWidth - width ) / 2, ( screenHeight - height ) / 2, width, height,
278                               null, null, hInstance, null );
279 
280         assert( hWnd );
281 
282         resize();
283 
284         ShowWindow( hWnd, showWindow ? SW_NORMAL : SW_HIDE );
285     }
286 
287     /**
288     * TODO
289     */
290     override void closeWindow()
291     {
292         DestroyWindow( hWnd );
293         hWnd = null;
294     }
295 
296     /**
297     * TODO
298     */
299     override void messageLoop()
300     {
301         MSG msg;
302 
303         // Initialize the message structure.
304         ( cast(byte*)&msg )[ 0 .. msg.sizeof ] = 0;
305 
306         // Handle the windows messages.
307         while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
308         {
309             TranslateMessage( &msg );
310             DispatchMessage( &msg );
311         }
312     }
313 }