Im quite new at Directx, and I wanted to test a simple direct2d application that draws a circle in a window. I am using Visual Studio Professional. When ever I run my program called D2D Test Engine with Local Windows Debugger, i get these errors:
Exception thrown at 0x0140EB06 in D2D Test Engine.exe: 0xC0000005: Access violation reading location 0x00000008.
Exception thrown at 0x0140EB06 in D2D Test Engine.exe: 0xC0000005: Access violation reading location 0x00000008.
Exception thrown at 0x0140EB06 in D2D Test Engine.exe: 0xC0000005: Access violation reading location 0x00000008.
I can't figure out what is wrong with my program.
Here is my code:
#include <Windows.h>
#include "Graphics.h"
Graphics* graphics;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
if (uMsg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
graphics->BeginDraw();
graphics->ClearScreen(0.0f, 0.0f, 0.5f);
graphics->DrawShape(100, 100, 50, 1.0f, 0.0, 0.0, 1.0);
graphics->EndDraw();
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmd, int nCmdShow) {
WNDCLASSEX windowclass;
ZeroMemory(&windowclass, sizeof(WNDCLASSEX));
windowclass.cbSize = sizeof(WNDCLASSEX);
windowclass.hbrBackground = (HBRUSH) COLOR_WINDOW;
windowclass.hInstance = hInstance;
windowclass.lpfnWndProc = WindowProc;
windowclass.lpszClassName = "MainWindow";
windowclass.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&windowclass);
RECT rect = { 0, 0, 800, 600 };
AdjustWindowRectEx(&rect, WS_EX_OVERLAPPEDWINDOW, false, WS_EX_OVERLAPPEDWINDOW);
HWND windowHandle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "MainWindow", "D2D Test Engine", WS_OVERLAPPEDWINDOW, 0, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, 0);
if (!windowHandle) {
return -1;
}
graphics = new Graphics();
if (graphics->Init(windowHandle) == false) {
delete graphics;
return -1;
}
ShowWindow(windowHandle, nCmdShow);
MSG message;
while (GetMessage(&message, NULL, 0, 0)) {
DispatchMessage(&message);
}
return 0;
}
next file:
#pragma once
#include <Windows.h>
#include <d2d1.h>
class Graphics
{
ID2D1Factory* factory;
ID2D1HwndRenderTarget* renderTarget;
public:
Graphics();
virtual ~Graphics();
bool Init(HWND windowHandle);
void BeginDraw() {renderTarget->BeginDraw();}
void EndDraw() {renderTarget->EndDraw();}
void ClearScreen(float r, float g, float b);
void DrawShape(float x, float y, float radius, float r, float g, float b, float a);
};
next file:
#include "Graphics.h"
#include <d2d1.h>
#include <Windows.h>
Graphics::Graphics()
{
factory = NULL;
renderTarget = NULL;
}
Graphics::~Graphics()
{
if (factory) {
factory->Release();
}
if (renderTarget) {
renderTarget->Release();
}
}
bool Graphics::Init(HWND windowHandle) {
HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &factory);
if (res != S_OK) {
return false;
}
RECT rect;
GetClientRect(windowHandle, &rect);
res = factory->CreateHwndRenderTarget(D2D1::RenderTargetProperties(), D2D1::HwndRenderTargetProperties(windowHandle, D2D1::SizeU(rect.right, rect.bottom)), &renderTarget);
if (res != S_OK) {
return false;
}
return true;
}
void Graphics::ClearScreen(float r, float g, float b) {
renderTarget->Clear(D2D1::ColorF(r, g, b));
}
void Graphics::DrawShape(float x, float y, float radius, float r, float g, float b, float a) {
ID2D1SolidColorBrush* brush;
renderTarget->CreateSolidColorBrush(D2D1::ColorF(r, g, b, a), &brush);
renderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(x, y), radius, radius), brush, 3.0f);
brush->Release();
}
When i dont call BeginDraw(), ClearScreen, DrawShape, and EndDraw a window comes up and it runs fine, so i think it has to do with them.
I figured out the answer. I am pretty new at this directx stuff, and i was using a tutorial to build this. what I needed to do was put all the functions i was calling in a if statement. here is what i did:
if (uMsg == WM_PAINT) {
graphics->BeginDraw();
graphics->ClearScreen(0.0f, 0.0f, 0.5f);
graphics->DrawShape(100, 100, 50, 1.0f, 0.0, 0.0, 1.0);
graphics->EndDraw();
}
sorry for all the trouble, im new to this.
Seems, that you have problem with WM_CREATE message.
This message is send when CreateWindowEx called.
Graphics* graphics = NULL; // it's better to zero pointer in C++.
Simple solution - set graphics to null and check it in WindowProc function.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_DESTROY) {
PostQuitMessage(0);
return 0;
}
if (graphics) {
graphics->BeginDraw();
graphics->ClearScreen(0.0f, 0.0f, 0.5f);
graphics->DrawShape(100, 100, 50, 1.0f, 0.0, 0.0, 1.0);
graphics->EndDraw();
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Related
When I call GLClear without swapping the buffer, it is causing memory leak.
I am not sure if this is a driver issue or if I am doing something wrong.
If anyone can give it a try or have any idea, please tell me.
TL;DR, this is the code summarized. You can find all the code after this block
static bool CloseFlag = false;
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
switch(uMsg)
{
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
//Handle input....
return 0;
case WM_CLOSE:
CloseFlag = true;
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND CreateOpenGLWindow(char* title, int x, int y, int width, int height,
BYTE type, DWORD flags)
{
//Create window (with double buffer) and register it...
}
int main ()
{
hWnd = CreateOpenGLWindow("minimal", 0, 0, 256, 256, PFD_TYPE_RGBA, 0);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
gladLoadWGL(hDC)
gladLoadGL()
ShowWindow(hWnd, SW_SHOW);
while(!CloseFlag)
{
//Handles input
while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
glClear(GL_COLOR_BUFFER_BIT);
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, hDC);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
return 0;
}
Here's a minimum code for reproducing it that uses Win32.
#ifndef UNICODE
#define UNICODE
#define _UNICODE
#endif
#include <windows.h> /* must include this before GL/gl.h */
#include "glad/glad.h"
#include "glad/glad_wgl.h"
#include "wglext.h"
#include <string>
static bool CloseFlag = false;
void display(HDC hDC)
{
/* rotate a triangle around */
glClear(GL_COLOR_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2i(0, 1);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2i(-1, -1);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2i(1, -1);
glEnd();
glFlush();
SwapBuffers(hDC);
}
void JustClear()
{
glClear(GL_COLOR_BUFFER_BIT);
}
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static PAINTSTRUCT ps;
switch(uMsg)
{
case WM_SIZE:
glViewport(0, 0, LOWORD(lParam), HIWORD(lParam));
PostMessage(hWnd, WM_PAINT, 0, 0);
return 0;
case WM_CHAR:
switch (wParam)
{
case 27: /* ESC key */
//PostQuitMessage(0);
CloseFlag = true;
break;
}
return 0;
case WM_CLOSE:
CloseFlag = true;
//PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
HWND CreateOpenGLWindow(char* title, int x, int y, int width, int height,
BYTE type, DWORD flags)
{
int pf;
HDC hDC;
HWND hWnd;
WNDCLASS wc;
PIXELFORMATDESCRIPTOR pfd;
static HINSTANCE hInstance = 0;
/* only register the window class once - use hInstance as a flag. */
if (!hInstance) {
hInstance = GetModuleHandle(NULL);
wc.style = CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = L"OpenGL";
if (!RegisterClass(&wc)) {
return NULL;
}
}
std::wstring className = L"OpenGL";
hWnd = CreateWindow(className.c_str(), className.c_str(), WS_OVERLAPPEDWINDOW |
WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
x, y, width, height, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
return NULL;
}
hDC = GetDC(hWnd);
/* there is no guarantee that the contents of the stack that become
the pfd are zeroed, therefore _make sure_ to clear these bits. */
memset(&pfd, 0, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | flags;
pfd.iPixelType = type;
pfd.cColorBits = 32;
pf = ChoosePixelFormat(hDC, &pfd);
if (pf == 0) {
return 0;
}
if (SetPixelFormat(hDC, pf, &pfd) == FALSE) {
return 0;
}
DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
ReleaseDC(hWnd, hDC);
return hWnd;
}
int main ()
{
HDC hDC; /* device context */
HGLRC hRC; /* opengl context */
HWND hWnd; /* window */
MSG msg; /* message */
hWnd = CreateOpenGLWindow("minimal", 0, 0, 256, 256, PFD_TYPE_RGBA, 0);
if (hWnd == NULL)
exit(1);
hDC = GetDC(hWnd);
hRC = wglCreateContext(hDC);
wglMakeCurrent(hDC, hRC);
if(!gladLoadWGL(hDC))
{
return -1;
}
if (!gladLoadGL()) //Load Glad
{
return -1;
}
ShowWindow(hWnd, SW_SHOW);
while(!CloseFlag)
{
//Handles input
while(PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
//Render
//display(hDC);
JustClear();
}
wglMakeCurrent(NULL, NULL);
ReleaseDC(hWnd, hDC);
wglDeleteContext(hRC);
DestroyWindow(hWnd);
return 0;
}
wglext.h is from here
Here's the OpenGL glad config
/*
OpenGL loader generated by glad 0.1.36 on Fri Oct 28 11:12:34 2022.
Language/Generator: C/C++
Specification: gl
APIs: gl=3.3
Profile: compatibility
Extensions:
Loader: True
Local files: False
Omit khrplatform: False
Reproducible: False
Commandline:
--profile="compatibility" --api="gl=3.3" --generator="c" --spec="gl" --extensions=""
Online:
https://glad.dav1d.de/#profile=compatibility&language=c&specification=gl&loader=on&api=gl%3D3.3
*/
and Glad wgl 1.0 with all the extensions
and here's the CMake I used
cmake_minimum_required(VERSION 3.14)
set (CMAKE_CXX_STANDARD 11)
# For Clang to do parsing
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# set the project name
project(Example)
add_executable(EXAMPLE_EXE ${CMAKE_CURRENT_LIST_DIR}/Src/main.cpp)
add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/External/glad_v0.1.36")
find_package(OpenGL REQUIRED)
target_include_directories(EXAMPLE_EXE PUBLIC "${CMAKE_CURRENT_LIST_DIR}/External/wglExt")
target_link_libraries(EXAMPLE_EXE PUBLIC OpenGL::GL glad)
I have a windowed Direct2D app and added a statusbar to the window from common controls:
InitCommonControls();
HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
hWnd, (HMENU)ID_STATUSBAR, GetModuleHandle(NULL), NULL);
The statusbar is showing up just fine, but as soon as I activate the BeginDraw()&EndDRaw() functions in my message loop, the statusbar is painted over, despite the fact I defined the height of the renderTarget when initialising it
res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
res = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(windowHandle, rectRender),
&renderTarget);
I also created a resize function
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
renderTarget->Resize(rectRender);
InvalidateRect(windowHandle, NULL, FALSE);
and called in in WM_SIZING and WM_SIZE:
case WM_SIZE:
case WM_SIZING:
hStatus = GetDlgItem(hWnd, ID_STATUSBAR);
gfx->Resize(hWnd, hStatus);
SendMessage(hStatus, WM_SIZE, 0, 0);
Doesn't BeginDraw() respect the dimensions of the rendertarget and just take the entire window? And if so, should I consider using layers or is there something wrong in my code?
EDIT: I received some downvotes for this question. If there's something wrong with my post, do let me know and I'll try to improve. I'm still fresh in the win32 world, but I've learned a lot from this platform. I would love to contribute with interesting questions and answers, but a simple -1 doesn't give me a clue what to improve. I've read 2 evenings about the subject on MSDN and various forums but didn't see what I do wrong. I tried be as complete as possible by writing an complete example code that illustrates the issue.
For reference the entire code
#include <windows.h>
#include <CommCtrl.h>
#include <d2d1.h>
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "d2d1.lib")
#define ID_STATUSBAR 1000
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class Graphics
{
ID2D1Factory* factory;
ID2D1HwndRenderTarget* renderTarget;
ID2D1SolidColorBrush* brush;
public:
Graphics()
{
factory = NULL;
renderTarget = NULL;
brush = NULL;
}
~Graphics()
{
if (factory) factory->Release();
if (renderTarget) renderTarget->Release();
if (brush) brush->Release();
}
bool Init(HWND windowHandle, HWND statusHandle);
void BeginDraw() { renderTarget->BeginDraw(); }
void EndDraw() { renderTarget->EndDraw(); }
void Resize(HWND windowHandle, HWND statusHandle);
void DrawCircle(float x, float y, float r);
};
HINSTANCE hInstance;
Graphics* gfx;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
InitCommonControls();
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = TEXT("mainwindow");
RegisterClass(&wc);
HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, TEXT("mainwindow"),
TEXT("MainWindow"), WS_OVERLAPPEDWINDOW, 100, 100, 800, 600,
NULL, NULL, hInstance, NULL);
if (!hWnd) return -1;
HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
hWnd, (HMENU)ID_STATUSBAR, GetModuleHandle(NULL), NULL);
gfx = new Graphics;
if (!gfx->Init(hWnd, hStatus))
{
delete gfx;
return -1;
}
ShowWindow(hWnd, nCmdShow);
MSG message{ 0 };
bool runGame = true;
while (runGame)
{
while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
if (message.message == WM_QUIT)
runGame = false;
}
gfx->BeginDraw();
gfx->DrawCircle(400.0f, 100.0f, 100.0f);
gfx->DrawCircle(400.0f, 300.0f, 100.0f);
gfx->DrawCircle(400.0f, 500.0f, 100.0f);
gfx->EndDraw();
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HWND hStatus;
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SIZE:
case WM_SIZING:
hStatus = GetDlgItem(hWnd, ID_STATUSBAR);
gfx->Resize(hWnd, hStatus);
SendMessage(hStatus, WM_SIZE, 0, 0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
bool Graphics::Init(HWND windowHandle, HWND statusHandle)
{
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
if (res != S_OK) return false;
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
res = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(windowHandle, rectRender),
&renderTarget);
if (res != S_OK) return false;
res = renderTarget->CreateSolidColorBrush(D2D1::ColorF(1, 0, 0, 0), &brush);
if (res != S_OK) return false;
return true;
}
void Graphics::Resize(HWND windowHandle, HWND statusHandle)
{
if (renderTarget != NULL)
{
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
renderTarget->Resize(rectRender);
}
}
void Graphics::DrawCircle(float x, float y, float r)
{
brush->SetColor(D2D1::ColorF(1.0f, 0.0f, 0.0f, 1.0f));
renderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(x, y), r, r), brush, 1.0f);
}
You can add WS_CLIPCHILDREN window style to your MainWindow:
HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, TEXT("mainwindow"),
TEXT("MainWindow"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 100, 100, 800, 600,
NULL, NULL, hInstance, NULL);
Alternatively, you can create another child window (sibling to status bar) and use that for your Direct2D target.
I'm trying to make a fullscreen transparent window on the desktop that i can render on with opengl.
I don't want to overflow the screen but so far that seems to be the only way i can. It's a pretty dirty hack imo and would hope someone knows of a professional solution.
Here's the code:
// libs needed to compile: opengl32 gdi32 dwmapi
#include <windows.h>
#include <GL/gl.h>
#include <dwmapi.h>
HDC hDC;
HGLRC hRC;
HWND hWnd;
bool running=true;
int w=GetSystemMetrics(SM_CXSCREEN),h=GetSystemMetrics(SM_CYSCREEN);
void CreateContext(){
PIXELFORMATDESCRIPTOR pfd; hDC=GetDC(hWnd);
SetPixelFormat(hDC,ChoosePixelFormat(hDC,&pfd),&pfd);
hRC=wglCreateContext(hDC); wglMakeCurrent(hDC,hRC);
}
void EnableTransparency(){DWM_BLURBEHIND b={DWM_BB_ENABLE|DWM_BB_BLURREGION,TRUE,CreateRectRgn(0,0,-1,-1)}; DwmEnableBlurBehindWindow(hWnd,&b);}
LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
switch(uMsg){
case WM_CLOSE: running=false; return 0;
case WM_KEYDOWN: if(wParam==VK_ESCAPE){running=false;} return 0;
}return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
void CreateWin(){
WNDCLASS wc={};
wc.lpfnWndProc=WndProc;
wc.hInstance=GetModuleHandle(NULL);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.lpszClassName="OpenGL";
RegisterClass(&wc);
hWnd = CreateWindow(wc.lpszClassName,"Title",WS_POPUP,0,-1,w,h+1,0,0,wc.hInstance,0); // increasing height by 1 pixel
ShowWindow(hWnd,SW_SHOW);
EnableTransparency();
CreateContext();
}
void PumpMessages(){MSG msg; while(PeekMessage(&msg,NULL,0,0,PM_REMOVE))DispatchMessage(&msg);}
int main(){
CreateWin();
glViewport(0,0,w,h); // the visible screen area (excluding h+1 overflow)
while(running){
PumpMessages();
glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT); // fill solid black
glBegin(GL_TRIANGLES); // transparent triangular window
glColor4f(1,0,0,0.5f); glVertex3f( 0, 1,0); // red (center)
glColor4f(0,1,0,0.5f); glVertex3f(-1,-1,0); // green (left)
glColor4f(0,0,1,0.5f); glVertex3f( 1,-1,0); // blue (right)
glEnd();
SwapBuffers(hDC);
}
return 0;
}
When running your code, I don't actually get any transparency, so I can't understand why you are needing to overflow the screen. But here's how I changed your code to work for me, and without overflowing the screen.
// libs needed to compile: opengl32 gdi32 dwmapi
#include <windows.h>
#include <GL/gl.h>
#include <dwmapi.h>
HDC hDC;
HGLRC hRC;
HWND hWnd;
bool running = true;
int w = GetSystemMetrics(SM_CXSCREEN), h = GetSystemMetrics(SM_CYSCREEN);
void CreateContext() {
PIXELFORMATDESCRIPTOR pfd; hDC = GetDC(hWnd);
SetPixelFormat(hDC, ChoosePixelFormat(hDC, &pfd), &pfd);
hRC = wglCreateContext(hDC); wglMakeCurrent(hDC, hRC);
}
void EnableTransparency() {
SetLayeredWindowAttributes(hWnd, NULL, NULL, NULL);
const MARGINS margins = { -1 };
DwmExtendFrameIntoClientArea(hWnd, &margins);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CLOSE: running = false; return 0;
case WM_KEYDOWN: if (wParam == VK_ESCAPE) { running = false; } return 0;
}return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void CreateWin() {
WNDCLASS wc = {};
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = "OpenGL";
RegisterClass(&wc);
hWnd = CreateWindowEx(WS_EX_LAYERED, wc.lpszClassName, "Title", WS_POPUP, 0, 0, w, h, 0, 0, wc.hInstance, 0);
ShowWindow(hWnd, SW_SHOW);
CreateContext();
EnableTransparency();
}
void PumpMessages() { MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))DispatchMessage(&msg); }
int main() {
CreateWin();
glViewport(0, 0, w, h); // the visible screen area
while (running) {
PumpMessages();
glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); // fill solid black
glBegin(GL_TRIANGLES); // transparent triangular window
glColor4f(1, 0, 0, 0.5f); glVertex3f(0, 1, 0); // red (center)
glColor4f(0, 1, 0, 0.5f); glVertex3f(-1, -1, 0); // green (left)
glColor4f(0, 0, 1, 0.5f); glVertex3f(1, -1, 0); // blue (right)
glEnd();
SwapBuffers(hDC);
}
return 0;
}
Checking error codes has been left out. Essentially, we use a window with the layered style to get transparency. We call SetLayeredWindowAttributes with no flags, because we are not using any of these layered window features, but we need to call the function for the window to become visible.
I'm very new to Direct2D programming and have been following a tutorial. I've adapted the example given in the tutorial to a slightly more complicated program that bounces a ball off the boundaries of the window.
My main program (main.cpp):
#include "Graphics.h"
Graphics* graphics;
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// Exit handler
if (uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmd, int nCmdShow)
{
WNDCLASSEX windowClass;
SecureZeroMemory(&windowClass, sizeof(WNDCLASSEX));
// Set up window
windowClass.cbSize = sizeof(WNDCLASSEX);
windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
windowClass.hInstance = hInstance;
windowClass.lpfnWndProc = WindowProc;
windowClass.lpszClassName = "MainWindow";
windowClass.style = CS_HREDRAW | CS_VREDRAW;
// Register window class and handle
RegisterClassEx(&windowClass);
RECT rect = { 0, 0, 800, 600 };
AdjustWindowRectEx(&rect, WS_OVERLAPPEDWINDOW, false, WS_EX_OVERLAPPEDWINDOW);
HWND windowHandle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "MainWindow", "Test Window", WS_OVERLAPPEDWINDOW, 100, 100,
rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, 0);
if (!windowHandle)
return -1;
graphics = new Graphics();
if (!graphics->Init(windowHandle))
{
delete graphics;
return -1;
}
ShowWindow(windowHandle, nCmdShow);
// Message loop
float x = 51.0, xSpeed = 5.0f, y = 0.0, ySpeed = 5.0f;
MSG message;
message.message = WM_NULL;
while (message.message != WM_QUIT)
{
if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
DispatchMessage(&message);
else
{
// Ball physics
//xSpeed += 0.6f;
x += xSpeed;
ySpeed += 0.2f;
y += ySpeed;
if (y > rect.bottom - 50)
{
ySpeed = -ySpeed;
}
if (x > rect.right - 50)
{
xSpeed = -xSpeed;
}
else if (x < 50)
{
xSpeed = -xSpeed;
}
// Redraw ball
graphics->beginDraw();
graphics->clearScreen(0.0f, 0.0f, 0.5f);
graphics->drawCircle(x, y, 50.0f, 1.0f, 1.0f, 1.0f, 1.0f);
graphics->endDraw();
}
}
delete graphics;
return 0;
}
My header file (Graphics.h):
#pragma once
#include <Windows.h>
#include <d2d1.h>
class Graphics
{
ID2D1Factory* factory;
ID2D1HwndRenderTarget* renderTarget;
ID2D1SolidColorBrush* brush;
public:
Graphics();
~Graphics();
bool Init(HWND windowHandle);
void beginDraw() { renderTarget->BeginDraw(); }
void endDraw() { renderTarget->EndDraw(); }
void clearScreen(float r, float g, float b);
void drawCircle(float x, float y, float radius, float r, float g, float b, float a);
};
My graphics functions (Graphics.cpp):
#include "Graphics.h"
#define CHECKRES if (res != S_OK) return false
Graphics::Graphics()
{
factory = NULL;
renderTarget = NULL;
brush = NULL;
}
Graphics::~Graphics()
{
if (factory)
factory->Release();
if (renderTarget)
renderTarget->Release();
if (brush)
brush->Release();
}
bool Graphics::Init(HWND windowHandle)
{
HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
CHECKRES;
RECT rect;
GetClientRect(windowHandle, &rect);
res = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(windowHandle, D2D1::SizeU(rect.right, rect.bottom)),
&renderTarget
);
CHECKRES;
res = renderTarget->CreateSolidColorBrush(D2D1::ColorF(0, 0, 0, 0), &brush);
CHECKRES;
return true;
}
void Graphics::clearScreen(float r, float g, float b)
{
renderTarget->Clear(D2D1::ColorF(r, g, b));
}
void Graphics::drawCircle(float x, float y, float radius, float r, float g, float b, float a)
{
brush->SetColor(D2D1::ColorF(r, g, b, a));
renderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(x, y), radius, radius), brush, 3.0f);
}
While this program does work fine, there is some minor tearing on the ball's bounce. I have seen this question which lead me to this MSDN article. Despite reading the article, I do still not fully understand how to implement double buffering, to hopefully reduce tearing. Can someone provide a concise example and explanation of the ID2D1RenderTarget::CreateCompatibleRenderTarget, as this high level Windows programming is quite different from what I'm used to?
Check article here. ID2D1HwndRenderTarget objects are double buffered by nature and drawing is done to the offscreen buffer first and when drawing ends it will be blitted to the screen.
I have followed a tutorial (i am newbie in opengl) and for some reason the triangle didn't show... I would appreciate any suggestions:).
It compiles with no warnings, glClearColor works if I change it, but no triangle is displayed.
#include "GphEditorWindow.h"
#include "gl\GL.h"
#include "gl\GLU.h"
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR plCmdLine, int nCmdShow)
{
POINT windowPos = { 100, 100 };
POINT windowSize = { 800, 600 };
if (!editorWindow.Init(hInstance, windowPos, windowSize))
{
MessageBox(NULL, "EditorWindow init failed", "Graphite Editor", MB_OK | MB_ICONERROR);
return ERROR_APP_INIT_FAILURE;
}
else
{
return editorWindow.Run();
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return editorWindow.WndProc(hWnd, message, wParam, lParam);
}
LRESULT EditorWindow::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SIZE:
RECT rect;
GetClientRect(hWnd, &rect);
workspaceWth = rect.right - rect.left;
workspaceHgh = rect.bottom - rect.top;
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return 0L;
}
LRESULT EditorWindowGL::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
long result = EditorWindow::WndProc(hWnd, message, wParam, lParam);
switch (message)
{
case WM_CREATE:
if (!InitWGL(hWnd))
{
MessageBox(NULL, "Rendering Context Creation Failed!", "Graphite Editor", MB_OK | MB_ICONERROR);
return ERROR_APP_INIT_FAILURE;
}
SceneSetup(false);
ShowTitleBarInfo(hWnd);
break;
case WM_DESTROY:
DeleteWGL();
break;
case WM_SIZE:
SceneSetup(false);
break;
case WM_PAINT:
DrawScene();
ValidateRect(hWnd, NULL);
break;
}
return result;
}
bool EditorWindow::Init(HINSTANCE editorHandler, POINT editorWindowPos, POINT editorWindowSize)
{
char editorWindowName[] = "Graphite Editor";
WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)::WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = editorHandler;
wc.hIcon = NULL;
wc.hIconSm = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = editorWindowName;
if (RegisterClassExA(&wc) == 0)
{
DWORD blah = GetLastError();
return false;
}
editorWindowHandler = CreateWindow(editorWindowName, editorWindowName, WS_OVERLAPPEDWINDOW, editorWindowPos.x, editorWindowPos.y, editorWindowSize.x, editorWindowSize.y, NULL, NULL, editorHandler, NULL);
if (editorWindowHandler == NULL)
return false;
ShowWindow(editorWindowHandler, SW_SHOW);
UpdateWindow(editorWindowHandler);
return true;
}
WPARAM EditorWindow::Run()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
bool EditorWindowGL::PixelFormatSet(HDC dcHandler) const
{
PIXELFORMATDESCRIPTOR pixelFormatDesc;
ZeroMemory(&pixelFormatDesc, sizeof(pixelFormatDesc));
pixelFormatDesc.nVersion = 1;
pixelFormatDesc.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
pixelFormatDesc.iPixelType = PFD_TYPE_RGBA;
pixelFormatDesc.cColorBits = 32;
pixelFormatDesc.cDepthBits = 32;
pixelFormatDesc.iLayerType = PFD_MAIN_PLANE;
int pixelFormat = ChoosePixelFormat(dcHandler, &pixelFormatDesc);
if (pixelFormat == 0)
return false;
if (!SetPixelFormat(dcHandler, pixelFormat, &pixelFormatDesc))
return false;
return true;
}
bool EditorWindowGL::InitWGL(HWND windowHandler)
{
dcHandler = ::GetDC(windowHandler);
if (!PixelFormatSet(dcHandler))
return false;
rcHandler = wglCreateContext(dcHandler);
if (rcHandler == NULL)
return false;
if (!wglMakeCurrent(dcHandler, rcHandler))
return false;
return true;
}
void EditorWindowGL::DeleteWGL()
{
wglMakeCurrent(NULL, NULL);
wglDeleteContext(rcHandler);
::ReleaseDC(editorWindowHandler, dcHandler);
}
void EditorWindowGL::ShowTitleBarInfo(HWND windowHandler)
{
char buffer[256];
GetWindowText(windowHandler, buffer, 256);
const GLubyte* version = glGetString(GL_VERSION);
strcat_s(buffer, " | OpenGL");
strcat_s(buffer, (char*)version);
const GLubyte* vendor = glGetString(GL_VENDOR);
strcat_s(buffer, " | ");
strcat_s(buffer, (char*)vendor);
const GLubyte* gpu = glGetString(GL_RENDERER);
strcat_s(buffer, " | "); strcat_s(buffer, (char*)gpu);
SetWindowText(windowHandler, buffer);
}
void EditorWindowGL::SceneSetup(bool isometric)
{
glViewport(0, 0, workspaceWth, workspaceHgh);
glMatrixMode(GL_PROJECTION);
glLoadIdentity;
float aspect = workspaceHgh / (float)workspaceWth;
if (!isometric)
glFrustum(-1.0f, 1.0f, aspect * -1.0f, aspect * 1.0f, 1.0f, 10.0f);
else
glOrtho(-1.0f, 1.0f, aspect * -1.0f, aspect * 1.0f, 1.0f, 10.0f);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_DEPTH_TEST);
}
void EditorWindowGL::DrawScene()
{
const float x0 = 1.0f;
const float y0 = 1.0f;
const float z0 = 1.0f;
glClearColor(0.125f, 0.125f, 0.125f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -3.0f);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glBegin(GL_TRIANGLES);
glVertex3f(-x0, -y0, 0.0f);
glVertex3f(x0, -y0, 1.0f);
glVertex3f(x0, y0, 0.0f);
glEnd();
SwapBuffers(dcHandler);
}