Handmade Hero » Forums » Code » How to get my first opengl window up and running
boagz57
Jason
22 posts
#14802 How to get my first opengl window up and running
3 months, 2 weeks ago Edited by Jason on April 3, 2018, 12:11 p.m. Reason: Initial post

So I'm trying to start a separate application which will run on windows 10. I'm not very far into the handmade hero series yet so I skipped ahead to the opengl stuff to try and get opengl running within my app since I will not be using software rendering. I'm able to get a window on screen, however I can't get a color to show up within the window after initializing opengl. I'm still pretty new to windows programming and the process is so weird that I have a hard time figuring out exactly what I'm doing wrong. Any help would be appreciated. My current code is as follows:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#include <Windows.h>
#include <gl/gl.h>
#include <cassert>

#define local_persist static
#define local_func static
#define global_variable static

namespace Win64
{
    local_func void
    ProcessPendingMessages()
    {
        MSG Message;
        while(GetMessageA(&Message, 0, 0, 0))
        {
            switch(Message.message)
            {
                case WM_QUIT:
                {
                } break;
            
                case WM_SYSKEYDOWN:
                case WM_SYSKEYUP:
                case WM_KEYDOWN:
                case WM_KEYUP:
                {
                    uint32 VKCode = (uint32)Message.wParam;

                    if(VKCode == 'W')
                    {
                    }
                    else if(VKCode == 'A')
                    {
                    }
                    else if(VKCode == 'S')
                    {
                    }
                    else if(VKCode == 'D')
                    {
                    }
                    else if(VKCode == 'Q')
                    {
                    }
                    else if(VKCode == 'E')
                    {
                    }
                    else if(VKCode == VK_UP)
                    {
                    }
                    else if(VKCode == VK_LEFT)
                    {
                    }
                    else if(VKCode == VK_DOWN)
                    {
                    }
                    else if(VKCode == VK_RIGHT)
                    {
                    }
                    else if(VKCode == VK_ESCAPE)
                    {
                    }
                    else if(VKCode == VK_SPACE)
                    {
                    }
                }

                default:
                {
                    TranslateMessage(&Message);
                    DispatchMessageA(&Message);
                } break;
            }
        }
    };

    LRESULT CALLBACK
    ProgramWindowCallback(HWND WindowHandle, UINT Message, WPARAM wParam, LPARAM lParam)
    {
        LRESULT Result{0};

        switch(Message)
        {
            case WM_SIZE:
            {
                OutputDebugStringA("WM_SIZE\n");
            }break;

            case WM_DESTROY:
            {
                OutputDebugStringA("WM_DESTROY\n");
            }break;

            case WM_CLOSE:
            {
                OutputDebugStringA("WM_CLOSE\n");
            }break;

            case WM_ACTIVATEAPP:
            {
                OutputDebugStringA("WM_ACTIVATEAPP\n");
            }break;

            case WM_PAINT:
            {
                PAINTSTRUCT Paint;
                RECT ClientRect;
                GetClientRect(WindowHandle, &ClientRect);

                HDC DeviceContext = BeginPaint(WindowHandle, &Paint);

                LONG Width = ClientRect.right - ClientRect.left;
                LONG Height = ClientRect.bottom - ClientRect.top;

                glViewport(0, 0, Width, Height);
                glClearColor(1.0f, 0.0f, 1.0f, 0.0f);
                glClear(GL_COLOR_BUFFER_BIT);
                SwapBuffers(DeviceContext);

                EndPaint(WindowHandle, &Paint);
            }break;

            default:
            {
                Result = DefWindowProc(WindowHandle, Message, wParam, lParam);
            }break;
        }

        return Result;
    };
}

int CALLBACK WinMain(HINSTANCE CurrentProgramInstance, HINSTANCE PrevInstance, LPSTR CommandLine, int ShowCode)
{
    WNDCLASS WindowProperties{};

    //TODO: Check if OWNDC/HREDRAW/VEDRAW matter
    WindowProperties.style = CS_OWNDC|CS_HREDRAW|CS_VREDRAW;
    WindowProperties.lpfnWndProc = Win64::ProgramWindowCallback;
    WindowProperties.hInstance = CurrentProgramInstance;
    WindowProperties.lpszClassName = "MemoWindowClass";

    if(RegisterClass(&WindowProperties))
    {
        HWND Window = CreateWindowEx(0, WindowProperties.lpszClassName, "Memo", WS_OVERLAPPEDWINDOW|WS_VISIBLE, 
                                     CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, CurrentProgramInstance, 0);

        if(Window)
        {
            HDC WindowDeviceContext = GetDC(Window);

            { //Init OpenGL
                HGLRC OpenGLRenderingContext = wglCreateContext(WindowDeviceContext);

                PIXELFORMATDESCRIPTOR DesiredPixelFormat{};
                DesiredPixelFormat.nSize = sizeof(DesiredPixelFormat);
                DesiredPixelFormat.nVersion = 1;
                DesiredPixelFormat.iPixelType = PFD_TYPE_RGBA;
                DesiredPixelFormat.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
                DesiredPixelFormat.cColorBits = 32;
                DesiredPixelFormat.cAlphaBits = 8;
                DesiredPixelFormat.iLayerType = PFD_MAIN_PLANE;

                int SuggestedPixelFormatIndex = ChoosePixelFormat(WindowDeviceContext, &DesiredPixelFormat);
                PIXELFORMATDESCRIPTOR SuggestedPixelFormat{};
                DescribePixelFormat(WindowDeviceContext, SuggestedPixelFormatIndex, sizeof(SuggestedPixelFormat), &SuggestedPixelFormat);

                if (SetPixelFormat(WindowDeviceContext, SuggestedPixelFormatIndex, &SuggestedPixelFormat))
                {
                    if (wglMakeCurrent(WindowDeviceContext, OpenGLRenderingContext))
                    {
                        //Success!
                    }
                    else
                    {
                        //Log error
                        assert(1 == 0);
                    }
                }
                else
                {
                    //Log error
                    assert(1 == 0);
                }
            }

            Win64::ProcessPendingMessages();
        }
    }

    return 0;
}

Finalspace
108 posts / 1 project

I am finalspace and do programming since more than 25 years, started on C64 and got serious with borland delphi. Nowadays i use C/C++ only.

#14808 How to get my first opengl window up and running
3 months, 2 weeks ago Edited by Finalspace on April 3, 2018, 3:51 p.m.

boagz57
So I'm trying to start a separate application which will run on windows 10. I'm not very far into the handmade hero series yet so I skipped ahead to the opengl stuff to try and get opengl running within my app since I will not be using software rendering. I'm able to get a window on screen, however I can't get a color to show up within the window after initializing opengl. I'm still pretty new to windows programming and the process is so weird that I have a hard time figuring out exactly what I'm doing wrong. Any help would be appreciated. My current code is as follows:

1
...



There are a few stuff which is missing:

- You didnt specify any depth bits in the pixel format descriptor
- You descripe the pixel format before setting it (Correct weird way is: Choose, Set, Descripe)
- You didn´t show the window ShowWindow() + UpdateWindow()
- That WM_PAINT thingy looks totally weird, EndPaint may overwrite your result? -> Consider putting the rendering directly after you processed the messages. Otherwise you have to post a WM_PAINT message to update your drawing. I never do any rendering inside WM_PAINT.
mmozeiko
Mārtiņš Možeiko
1720 posts / 1 project
#14809 How to get my first opengl window up and running
3 months, 2 weeks ago Edited by Mārtiņš Možeiko on April 3, 2018, 5:38 p.m.

@boagz57
The error is that you are creating OpenGL context (wglCreateContext) before setting pixel format (SetPixelFormat). Pixel format must be set before. To easier debug this situation always check the return values/codes. In your code you are not checking if OpenGLRenderingContext handle is valid after creating it - you would see that it is not.

Also you should use default WindowProc in WM_CLOSE, otherwise your window cannot be closed.
And put PostQuitMessage call inside WM_DESTROY, so main loop receives WM_QUIT message. Otherwise your executable will run forever without any window visible.

Btw here's a small example from me how to set up modern GL context on Windows: https://git.handmade.network/snippets/17

@Finalspace

1) That is normal. No depth buffer is perfectly fine configuration for OpenGL.
2) No need to describe, that's just an extra thing you can do, but not necessary. But everything will work without it.
3) No need for ShowWindow, because WS_VISIBLE style flag will create window that is visible. And UpdateWindow is not needed, windows will call it for you when it is created. It is needed only in some very special cases.
4) WM_PAINT rendering is fine. Especially if you want to conserve battery. Unfortunetaly it will introduce extra lags. Because of vsync for every SwapBuffers. So if there are a lot of WM_PAINT messages getting delivered (like other windows overlapping/moving on top), then your rendering will slow down a lot. It's better to set a flag in WM_PAINT that "re-rendering required" and do rendering outside after processing all messages in current iteration (first call GetMessage to block, then PeekMessage to drain the loop).
boagz57
Jason
22 posts
#14810 How to get my first opengl window up and running
3 months, 2 weeks ago

@mmozeiko Awesome, that fixed it. I'll definitely be using your code reference. Thank you so much man. Went above and beyond.