C++ OpenGL 3D GameTutorial 1: Making the window with win32 API study notes


Video address icon-default.png?t=N7T8https://www.youtube.com/watch?v=jHcz22MDPeE&list=PLv8DnRaQOs5-MR-zbP1QUdq5FL0FWqVzg

 1. Entry function

         First look at the entry function main code:

#include<OGL3D/Game/OGame.h>

int main()
{
	OGame game;
	game.Run();

	return 0;
}

        Here is a question about C++ syntax, here is OGame game; this line of statement has actually constructed an OGame object, and its name is game. If you want to use the new keyword like in C#, it should be like this :

OGame* game = new OGame();
game->Run();

        Should it be understood this way? The new keyword actually means to open a new memory. What is returned is the pointer of this memory, so the value is actually assigned to OGame*, a pointer to the OGame class.

        New in C# should have the same meaning, but the way it is written makes developers think that it is assigned to an object instead of a pointer. Well, that should be the case.

 2. OGame category

        Paste the code first:

        OGame.h

#pragma once
#include<memory>

class OWindow;
class OGame
{
public:
	OGame();
	~OGame();

	void Run();
	void Quit();
protected:
	bool m_isRunning = true;
	std::unique_ptr<OWindow> m_display;
};

         OGame.cpp

#include<OGL3D/Game/OGame.h>
#include<OGL3D/Window/OWindow.h>
#include<Windows.h>

OGame::OGame()
{
	m_display = std::unique_ptr<OWindow>(new OWindow());
}

OGame::~OGame()
{
}

void OGame::Run()
{
	MSG msg;
	while (m_isRunning && !m_display->isClosed())
	{
		if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		Sleep(1);
	}
}

void OGame::Quit()
{
	m_isRunning = false;
}

        Let’s take a look at the std::unique_ptr smart pointer first:

std::unique_ptr<OWindow> m_display;

        This line of code defines a smart pointer named m_display pointing to an OWindow type object, which requires

#include<memory>;

        It is said that this thing can intelligently release memory. Haha, let’s understand it this way first and see the effect in the future.

        There is also a GetMessage function similar to PeekMessage. The difference between the two is that if no message is returned, PeekMessage will return a null value, while GetMessage will make the program sleep. Also, GetMessage will definitely delete the message after retrieving the message from the message queue, but PeekMessage can decide whether to remove the message through the last parameter. GetMessage is understood as "taking away" the message, and PeekMessage is understood as "peeping" at the message, not necessarily "taking away". Here we set the last parameter of PeekMessage to PM_REMOVE, which is the situation of "taking it away".

        The TranslateMessage function is used to convert keyboard messages into character messages and re-deliver the converted new messages to the message queue of the calling thread.

        For example, when we tap a character key on the keyboard, the system will generate WM_KEYDOWN and WM_KEYUP messages. The additional parameters of these two messages (wParam and lParam) contain information such as virtual key codes and scan codes, and we often need to get the ASCII code of a certain character in the program. The TranslateMessage function can convert the WM_KEYDOWN and WM_ KEYUP messages The combination is converted into a WM_CHAR message (the wParam additional parameter of the message contains the ASCII code of the character), and the converted new message is delivered to the message queue of the calling thread. Note that the TranslateMessage function does not modify the original message.

        DispatchMessage is a bit complicated to explain online, and it looks confusing. My understanding is that this function sends the message to the function corresponding to the lpfnWndProc attribute in the window object. The specific distribution mechanism will be studied slowly, but there is only one window in our program. , there is no chance if I send it wrongly, there is nothing to say.

3. OWindow class

        Paste the code first:

        OWindow.h

#include<Windows.h>
class OWindow
{
public:
		OWindow();
		~OWindow();

		void OnDestroy();
		bool isClosed();
private:
	HWND m_handle = nullptr;
};

        OWindow.cpp

#include<OGL3D/Window/OWindow.h>
#include <Windows.h>
#include<assert.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_DESTROY:
	{
		OWindow* window = (OWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
		window->OnDestroy();
		break;
	}
	default:
		return DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return NULL;
}

OWindow::OWindow()
{
	WNDCLASSEX wc = {};
	wc.cbSize = sizeof(WNDCLASSEX);
	wc.lpszClassName = L"OGL3DWindow";
	wc.lpfnWndProc = WndProc;

	assert(RegisterClassEx(&wc));

	RECT rc = { 0,0,1024,768 };
	AdjustWindowRect(&rc, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, false);

	m_handle = CreateWindowEx(NULL, L"OGL3DWindow", L"Parcode | OpenGL 3D Game", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, CW_USEDEFAULT, CW_USEDEFAULT,
		rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, NULL, NULL);

	assert(m_handle);

	SetWindowLongPtr(m_handle, GWLP_USERDATA, (LONG_PTR)this);

	ShowWindow(m_handle, SW_SHOW);
	UpdateWindow(m_handle);
}

OWindow::~OWindow()
{
	DestroyWindow(m_handle);
}

void OWindow::OnDestroy()
{
	m_handle = nullptr;
}

bool OWindow::isClosed()
{
	return !m_handle;
}

        Why add L in front of the string? Represents an ANSI string converted into a unicode string so that each character takes up two bytes.

        RegisterClassEx seems to be used to register windows. It probably adds the window pointer to a list to facilitate traversal and search. RegisterClassEx is replaced by RegisterClassExA or RegisterClassExW according to the compilation environment. If you use RegisterClassExA to register a window class, the application notifies the system that the message returned by the window of the registered class uses the text and character parameters of the ANSI character set; if you use RegisterClassExW to register the window class, the application needs the system to pass the text parameters of the message in Unicode. . That is to say, in the current environment, RegisterClassEx should be equivalent to RegisterClassExW, which requires that the message string must be a unicode string. This shows the necessity of adding the letter L in front of the message string mentioned earlier.

        The SetWindowLongPtr function is used to set the extended style or additional data of a window. This function is compatible with 32-bit and 64-bit versions of Windows systems. Here, the pointer to the OWindow object is converted into the LONG_PRT type and saved in the window data offset GWLP_USERDATA. Well, I don’t quite understand. In short, the pointer to the OWindow object is saved, and can be taken out when needed in the future, and used to access the OWindow object. We can see from the WinProc method how the GetWindowLongPtr function corresponding to the SetWindowLongPtr function takes the value and calls the method:

OWindow* window = (OWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
window->OnDestroy();

Guess you like

Origin blog.csdn.net/ttod/article/details/135382120