2025-11-28 00:35:46 +09:00

1007 lines
31 KiB
C++

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft Corporation. All rights reserved
#include "stdafx.h"
static const PCWSTR sc_fontName = L"Calibri";
static const float sc_fontSize = 20.0f;
static const D2D1_RECT_F sc_textInfoBox = {10, 10, 350, 200 };
static const float sc_textInfoBoxInset = 10;
static const UINT sc_defaultNumSquares = 16;
static const UINT sc_minNumSquares = 1;
static const UINT sc_maxNumSquares = 1024;
static const float sc_boardWidth = 900.0f;
static const float sc_rotationSpeed = 3.0f;
static const float sc_loupeInset = 20.0f;
static const float sc_maxZoom = 15.0f;
static const float sc_minZoom = 1.0f;
static const float sc_zoomStep = 1.5f;
static const float sc_zoomSubStep = 1.1f;
static const float sc_strokeWidth = 1.0f;
// This determines that maximum texture size we will
// generate for our realizations.
static const UINT sc_maxRealizationDimension = 2000;
/******************************************************************
* *
* WinMain *
* *
* Application entrypoint *
* *
******************************************************************/
int WINAPI WinMain(
HINSTANCE /*hInstance*/,
HINSTANCE /*hPrevInstance*/,
LPSTR /*lpCmdLine*/,
int /*nCmdShow*/
)
{
// Ignore the return value because we want to continue running even in the
// unlikely event that HeapSetInformation fails.
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
if (SUCCEEDED(CoInitialize(NULL)))
{
{
DemoApp app;
if (SUCCEEDED(app.Initialize()))
{
app.RunMessageLoop();
}
}
CoUninitialize();
}
return 0;
}
/******************************************************************
* *
* DemoApp::DemoApp constructor *
* *
* Initialize member data *
* *
******************************************************************/
DemoApp::DemoApp() :
m_hwnd(NULL),
m_antialiasMode(m_antialiasMode),
m_useRealizations(false),
m_updateRealization(true),
m_drawStroke(true),
m_autoGeometryRegen(true),
m_paused(false),
m_pausedTime(0),
m_numSquares(sc_defaultNumSquares),
m_targetZoomFactor(1.0f),
m_currentZoomFactor(1.0f),
m_pD2DFactory(NULL),
m_pWICFactory(NULL),
m_pDWriteFactory(NULL),
m_pRT(NULL),
m_pTextFormat(NULL),
m_pSolidColorBrush(NULL),
m_pRealization(NULL),
m_pGeometry(NULL)
{
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
m_timeDelta = -time.QuadPart;
m_mousePos = D2D1::Point2F(0.0f, 0.0f);
}
/******************************************************************
* *
* DemoApp::~DemoApp destructor *
* *
* Tear down resources *
* *
******************************************************************/
DemoApp::~DemoApp()
{
SafeRelease(&m_pD2DFactory);
SafeRelease(&m_pWICFactory);
SafeRelease(&m_pDWriteFactory);
SafeRelease(&m_pRT);
SafeRelease(&m_pTextFormat);
SafeRelease(&m_pSolidColorBrush);
SafeRelease(&m_pRealization);
SafeRelease(&m_pGeometry);
}
/******************************************************************
* *
* DemoApp::Initialize *
* *
* Create application window and device-independent resources *
* *
******************************************************************/
HRESULT DemoApp::Initialize()
{
HRESULT hr;
hr = CreateDeviceIndependentResources();
if (SUCCEEDED(hr))
{
//register window class
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = DemoApp::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = HINST_THISCOMPONENT;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"D2DDemoApp";
RegisterClassEx(&wcex);
// Create the application window.
//
// Because the CreateWindow function takes its size in pixels, we
// obtain the system DPI and use it to scale the window size.
FLOAT dpiX, dpiY;
m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);
m_hwnd = CreateWindow(
L"D2DDemoApp",
L"D2D Demo App",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
NULL,
NULL,
HINST_THISCOMPONENT,
this
);
hr = m_hwnd ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
ShowWindow(m_hwnd, SW_SHOWNORMAL);
UpdateWindow(m_hwnd);
}
}
return hr;
}
/******************************************************************
* *
* DemoApp::CreateDeviceIndependentResources *
* *
* This method is used to create resources which are not bound *
* to any device. Their lifetime effectively extends for the *
* duration of the app. These resources include the D2D, *
* DWrite, and WIC factories; and a DWrite Text Format object *
* (used for identifying particular font characteristics) and *
* a D2D geometry. *
* *
******************************************************************/
HRESULT DemoApp::CreateDeviceIndependentResources()
{
HRESULT hr = S_OK;
// Create the Direct2D factory.
hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
if (SUCCEEDED(hr))
{
// Create a WIC factory.
hr = CoCreateInstance(
CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_IWICImagingFactory,
reinterpret_cast<void **>(&m_pWICFactory)
);
}
if (SUCCEEDED(hr))
{
// Create a DirectWrite factory.
hr = DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(m_pDWriteFactory),
reinterpret_cast<IUnknown **>(&m_pDWriteFactory)
);
}
if (SUCCEEDED(hr))
{
// Create a DirectWrite text format object.
hr = m_pDWriteFactory->CreateTextFormat(
sc_fontName,
NULL,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
static_cast<FLOAT>(sc_fontSize),
L"", //locale
&m_pTextFormat
);
}
return hr;
}
/******************************************************************
* *
* DemoApp::CreateDeviceResources *
* *
* This method creates resources which are bound to a particular *
* D3D device. It's all centralized here, in case the resources *
* need to be recreated in case of D3D device loss (eg. display *
* change, remoting, removal of video card, etc). *
* *
******************************************************************/
HRESULT DemoApp::CreateDeviceResources()
{
HRESULT hr = S_OK;
if (!m_pRT)
{
IGeometryRealizationFactory *pRealizationFactory = NULL;
RECT rc;
GetClientRect(m_hwnd, &rc);
D2D1_SIZE_U size = D2D1::SizeU(
rc.right - rc.left,
rc.bottom - rc.top
);
// Create a Direct2D render target.
hr = m_pD2DFactory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(m_hwnd, size),
&m_pRT
);
if (SUCCEEDED(hr))
{
// Create brushes.
hr = m_pRT->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::White),
&m_pSolidColorBrush
);
}
if (SUCCEEDED(hr))
{
hr = CreateGeometryRealizationFactory(
m_pRT,
sc_maxRealizationDimension,
&pRealizationFactory
);
}
if (SUCCEEDED(hr))
{
hr = pRealizationFactory->CreateGeometryRealization(&m_pRealization);
}
if (SUCCEEDED(hr))
{
m_updateRealization = true;
}
SafeRelease(&pRealizationFactory);
}
return hr;
}
/******************************************************************
* *
* DemoApp::DiscardDeviceResources *
* *
* Discard device-specific resources which need to be recreated *
* when a Direct3D device is lost. *
* *
******************************************************************/
void DemoApp::DiscardDeviceResources()
{
SafeRelease(&m_pRT);
SafeRelease(&m_pSolidColorBrush);
SafeRelease(&m_pRealization);
}
/******************************************************************
* *
* DemoApp::DiscardGeometryData *
* *
******************************************************************/
void DemoApp::DiscardGeometryData()
{
SafeRelease(&m_pGeometry);
m_updateRealization = true;
}
/******************************************************************
* *
* DemoApp::RunMessageLoop *
* *
* Main window message loop *
* *
******************************************************************/
void DemoApp::RunMessageLoop()
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
HRESULT DemoApp::CreateGeometries()
{
HRESULT hr = S_OK;
if (!m_pGeometry)
{
IGeometryRealizationFactory *pRealizationFactory = NULL;
IGeometryRealization *pRealization = NULL;
ID2D1TransformedGeometry *pGeometry = NULL;
ID2D1PathGeometry *pPathGeometry = NULL;
ID2D1GeometrySink *pSink = NULL;
float squareWidth = 0.9f * sc_boardWidth / m_numSquares;
// Create the path geometry.
hr = m_pD2DFactory->CreatePathGeometry(&pPathGeometry);
if (SUCCEEDED(hr))
{
// Write to the path geometry using the geometry sink to
// create an hour glass shape.
hr = pPathGeometry->Open(&pSink);
}
if (SUCCEEDED(hr))
{
pSink->SetFillMode(D2D1_FILL_MODE_ALTERNATE);
pSink->BeginFigure(
D2D1::Point2F(0, 0),
D2D1_FIGURE_BEGIN_FILLED
);
pSink->AddLine(D2D1::Point2F(1.0f, 0));
pSink->AddBezier(
D2D1::BezierSegment(
D2D1::Point2F(0.75f, 0.25f),
D2D1::Point2F(0.75f, 0.75f),
D2D1::Point2F(1.0f, 1.0f))
);
pSink->AddLine(D2D1::Point2F(0, 1.0f));
pSink->AddBezier(
D2D1::BezierSegment(
D2D1::Point2F(0.25f, 0.75f),
D2D1::Point2F(0.25f, 0.25f),
D2D1::Point2F(0, 0))
);
pSink->EndFigure(D2D1_FIGURE_END_CLOSED);
hr = pSink->Close();
}
if (SUCCEEDED(hr))
{
D2D1_MATRIX_3X2_F scale = D2D1::Matrix3x2F::Scale(squareWidth, squareWidth);
D2D1_MATRIX_3X2_F translation = D2D1::Matrix3x2F::Translation(-squareWidth / 2, -squareWidth / 2);
hr = m_pD2DFactory->CreateTransformedGeometry(
pPathGeometry,
scale * translation,
&pGeometry
);
}
if (SUCCEEDED(hr))
{
// Transfer the reference.
m_pGeometry = pGeometry;
pGeometry = NULL;
}
SafeRelease(&pRealizationFactory);
SafeRelease(&pRealization);
SafeRelease(&pGeometry);
SafeRelease(&pPathGeometry);
SafeRelease(&pSink);
}
return hr;
}
HRESULT DemoApp::RenderMainContent(float time)
{
HRESULT hr = S_OK;
static DWORD dwTimeStart = 0;
static DWORD dwTimeLast = 0;
D2D1_SIZE_F rtSize = m_pRT->GetSize();
float squareWidth = sc_boardWidth / m_numSquares;
m_pRT->SetAntialiasMode(m_antialiasMode);
m_pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black));
D2D1_MATRIX_3X2_F currentTransform;
m_pRT->GetTransform(&currentTransform);
D2D1_MATRIX_3X2_F worldTransform =
D2D1::Matrix3x2F::Translation(
0.5f*(rtSize.width - squareWidth*m_numSquares),
0.5f*(rtSize.height - squareWidth*m_numSquares)
) * currentTransform;
for (UINT i = 0; SUCCEEDED(hr) && (i < m_numSquares); ++i)
{
for (UINT j = 0; SUCCEEDED(hr) && (j < m_numSquares); ++j)
{
float dx = i+0.5f-0.5f*m_numSquares;
float dy = j+0.5f-0.5f*m_numSquares;
float length = sqrtf(2)*m_numSquares;
//
// The intensity variable determines the color and speed of rotation of the
// realization instance. We choose a function that is rotationaly
// symmetric about the center of the grid, which produces a nice
// effect.
//
float intensity =
0.5f * (1+sinf( (0.2f * time + 10.0f * sqrtf(static_cast<float>(dx*dx+dy*dy))/length)));
D2D1_MATRIX_3X2_F rotateTransform =
D2D1::Matrix3x2F::Rotation(
(intensity * sc_rotationSpeed * time * 360.0f) * ((float)M_PI / 180.0f)
);
D2D1_MATRIX_3X2_F newWorldTransform =
rotateTransform *
D2D1::Matrix3x2F::Translation(
(i+0.5f)*squareWidth,
(j+0.5f)*squareWidth
) * worldTransform;
if (m_updateRealization)
{
//
// Note: It would actually be a little simpler to generate our
// realizations prior to entering RenderMainContent. We instead
// generate the realizations based on the top-left primitive in
// the grid, so we can illustrate the fact that realizations
// appear identical to their unrealized counter-parts when the
// exact same world transform is supplied. Only the top left
// realization will look identical, though, as shifting or
// rotating an AA realization can introduce fuzziness.
//
// Realizations are regenerated every frame, so to
// demonstrate that the realization geometry produces identical
// results, you actually need to pause (<space>), which forces
// a regeneration.
//
hr = m_pRealization->Update(
m_pGeometry,
static_cast<REALIZATION_CREATION_OPTIONS>(
REALIZATION_CREATION_OPTIONS_ANTI_ALIASED |
REALIZATION_CREATION_OPTIONS_ALIASED |
REALIZATION_CREATION_OPTIONS_FILLED |
REALIZATION_CREATION_OPTIONS_STROKED |
REALIZATION_CREATION_OPTIONS_UNREALIZED
),
&newWorldTransform,
sc_strokeWidth,
NULL //pIStrokeStyle
);
if (SUCCEEDED(hr))
{
m_updateRealization = false;
}
}
if (SUCCEEDED(hr))
{
m_pRT->SetTransform(newWorldTransform);
m_pSolidColorBrush->SetColor(
D2D1::ColorF(
0.0f,
intensity,
1.0f - intensity
));
hr = m_pRealization->Fill(
m_pRT,
m_pSolidColorBrush,
m_useRealizations ?
REALIZATION_RENDER_MODE_DEFAULT :
REALIZATION_RENDER_MODE_FORCE_UNREALIZED
);
if (SUCCEEDED(hr) && m_drawStroke)
{
m_pSolidColorBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White));
hr = m_pRealization->Draw(
m_pRT,
m_pSolidColorBrush,
m_useRealizations ?
REALIZATION_RENDER_MODE_DEFAULT :
REALIZATION_RENDER_MODE_FORCE_UNREALIZED
);
}
}
}
}
return hr;
}
/******************************************************************
* *
* DemoApp::OnRender *
* *
* Called whenever the application needs to display the client *
* window. This method draws the main content (a 2D array of *
* spinning geometries) and some perf statistics. *
* *
* Note that this function will not render anything if the window *
* is occluded (e.g. when the screen is locked). *
* Also, this function will automatically discard device-specific *
* resources if the D3D device disappears during function *
* invocation, and will recreate the resources the next time it's *
* invoked. *
* *
******************************************************************/
HRESULT DemoApp::OnRender()
{
HRESULT hr = S_OK;
LARGE_INTEGER time;
LARGE_INTEGER frequency;
QueryPerformanceCounter(&time);
QueryPerformanceFrequency(&frequency);
float floatTime;
hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
if (!m_paused)
{
floatTime = static_cast<float>(time.QuadPart + m_timeDelta)/static_cast<float>(frequency.QuadPart);
}
else
{
floatTime = static_cast<float>(m_pausedTime + m_timeDelta)/static_cast<float>(frequency.QuadPart);
}
m_times.Add(time.QuadPart);
if (m_currentZoomFactor < m_targetZoomFactor)
{
m_currentZoomFactor *= sc_zoomSubStep;
if (m_currentZoomFactor > m_targetZoomFactor)
{
m_currentZoomFactor = m_targetZoomFactor;
if (m_autoGeometryRegen)
{
m_updateRealization = true;
}
}
}
else if (m_currentZoomFactor > m_targetZoomFactor)
{
m_currentZoomFactor /= sc_zoomSubStep;
if (m_currentZoomFactor < m_targetZoomFactor)
{
m_currentZoomFactor = m_targetZoomFactor;
if (m_autoGeometryRegen)
{
m_updateRealization = true;
}
}
}
m_pRT->SetTransform(
D2D1::Matrix3x2F::Scale(
m_currentZoomFactor,
m_currentZoomFactor,
m_mousePos)
);
hr = CreateGeometries();
if (SUCCEEDED(hr) && m_pRT && !(m_pRT->CheckWindowState() & D2D1_WINDOW_STATE_OCCLUDED))
{
m_pRT->BeginDraw();
hr = RenderMainContent(floatTime);
if (SUCCEEDED(hr))
{
hr = RenderTextInfo();
if (SUCCEEDED(hr))
{
hr = m_pRT->EndDraw();
if (SUCCEEDED(hr))
{
if (hr == D2DERR_RECREATE_TARGET)
{
DiscardDeviceResources();
}
}
}
}
}
}
return hr;
}
/******************************************************************
* *
* DemoApp::RenderTextInfo *
* *
* Draw the stats text (AA type, fps, etc...). *
* *
******************************************************************/
HRESULT DemoApp::RenderTextInfo()
{
HRESULT hr = S_OK;
WCHAR textBuffer[400];
LARGE_INTEGER frequency;
float fps = 0.0f;
float primsPerSecond = 0.0f;
QueryPerformanceFrequency(&frequency);
UINT numPrimitives = m_numSquares * m_numSquares;
if (m_drawStroke)
{
numPrimitives *= 2;
}
if (m_times.GetCount() > 0)
{
fps = (m_times.GetCount()-1) * frequency.QuadPart /
static_cast<float>((m_times.GetLast() - m_times.GetFirst()));
primsPerSecond = fps * numPrimitives;
}
hr = StringCchPrintf(
textBuffer,
400,
L"%s\n"
L"%s\n"
L"%s\n"
L"# primitives: %d x %d%s = %d\n"
L"Fps: %.2f\n"
L"Primitives / sec : %.0f\n",
m_antialiasMode == D2D1_ANTIALIAS_MODE_ALIASED ?
L"Aliased" : L"PerPrimitive",
m_useRealizations ?
L"Realized" : L"Unrealized",
m_autoGeometryRegen?
L"Auto Realization Regeneration" : L"No Auto Realization Regeneration",
m_numSquares,
m_numSquares,
m_drawStroke ? L" x 2" : L"",
numPrimitives,
fps,
primsPerSecond
);
if (SUCCEEDED(hr))
{
m_pRT->SetTransform(D2D1::Matrix3x2F::Identity());
m_pSolidColorBrush->SetColor(D2D1::ColorF(0.0f, 0.0f, 0.0f, 0.5f));
m_pRT->FillRoundedRectangle(
D2D1::RoundedRect(
sc_textInfoBox,
sc_textInfoBoxInset,
sc_textInfoBoxInset),
m_pSolidColorBrush
);
m_pSolidColorBrush->SetColor(D2D1::ColorF(D2D1::ColorF::White));
m_pRT->DrawText(
textBuffer,
static_cast<UINT>(wcsnlen(textBuffer, ARRAYSIZE(textBuffer))),
m_pTextFormat,
D2D1::RectF(
sc_textInfoBox.left + sc_textInfoBoxInset,
sc_textInfoBox.top + sc_textInfoBoxInset,
sc_textInfoBox.right - sc_textInfoBoxInset,
sc_textInfoBox.bottom - sc_textInfoBoxInset),
m_pSolidColorBrush,
D2D1_DRAW_TEXT_OPTIONS_NONE
);
}
return hr;
}
/******************************************************************
* *
* DemoApp::OnResize *
* *
* If the application receives a WM_SIZE message, this method *
* resizes the render target appropriately. *
* *
******************************************************************/
void DemoApp::OnResize(UINT width, UINT height)
{
if (m_pRT)
{
D2D1_SIZE_U size;
size.width = width;
size.height = height;
// Note: This method can fail, but it's okay to ignore the
// error here -- it will be repeated on the next call to
// EndDraw.
m_pRT->Resize(size);
}
}
/******************************************************************
* *
* DemoApp::OnKeyDown *
* *
******************************************************************/
void DemoApp::OnKeyDown(SHORT vkey)
{
switch (vkey)
{
case 'A':
m_antialiasMode =
(m_antialiasMode == D2D1_ANTIALIAS_MODE_ALIASED) ?
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE :
D2D1_ANTIALIAS_MODE_ALIASED;
break;
case 'R':
m_useRealizations = !m_useRealizations;
break;
case 'G':
m_autoGeometryRegen = !m_autoGeometryRegen;
break;
case 'S':
m_drawStroke = !m_drawStroke;
break;
case VK_SPACE:
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
if (!m_paused)
{
m_pausedTime = time.QuadPart;
}
else
{
m_timeDelta += (m_pausedTime - time.QuadPart);
}
m_paused = !m_paused;
m_updateRealization = true;
break;
case VK_UP:
m_numSquares = min(m_numSquares * 2, sc_maxNumSquares);
// Regenerate the geometries.
DiscardGeometryData();
break;
case VK_DOWN:
m_numSquares = max(m_numSquares / 2, sc_minNumSquares);
// Regenerate the geometries.
DiscardGeometryData();
break;
default:
break;
}
}
/******************************************************************
* *
* OnMouseMove *
* *
******************************************************************/
void DemoApp::OnMouseMove(LPARAM lParam)
{
float dpiX = 96.0f;
float dpiY = 96.0f;
if (m_pRT)
{
m_pRT->GetDpi(&dpiX, &dpiY);
}
m_mousePos = D2D1::Point2F(
LOWORD(lParam) * 96.0f / dpiX,
HIWORD(lParam) * 96.0f / dpiY
);
}
/******************************************************************
* *
* DemoApp::OnWheel *
* *
******************************************************************/
void DemoApp::OnWheel(WPARAM wParam)
{
m_targetZoomFactor *=
pow(sc_zoomStep, GET_WHEEL_DELTA_WPARAM(wParam) / 120.0f);
m_targetZoomFactor = min(
max(m_targetZoomFactor, sc_minZoom),
sc_maxZoom
);
}
/******************************************************************
* *
* DemoApp::WndProc *
* *
* Window message handler *
* *
******************************************************************/
LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
DemoApp *pDemoApp = (DemoApp *)pcs->lpCreateParams;
::SetWindowLongPtrW(
hwnd,
GWLP_USERDATA,
reinterpret_cast<LONG_PTR>(pDemoApp)
);
result = 1;
}
else
{
DemoApp *pDemoApp = reinterpret_cast<DemoApp *>(
::GetWindowLongPtrW(
hwnd,
GWLP_USERDATA
));
bool wasHandled = false;
if (pDemoApp)
{
switch (message)
{
case WM_SIZE:
{
UINT width = LOWORD(lParam);
UINT height = HIWORD(lParam);
pDemoApp->OnResize(width, height);
}
result = 0;
wasHandled = true;
break;
case WM_PAINT:
case WM_DISPLAYCHANGE:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
pDemoApp->OnRender();
EndPaint(hwnd, &ps);
InvalidateRect(hwnd, NULL, FALSE);
}
result = 0;
wasHandled = true;
break;
case WM_KEYDOWN:
{
pDemoApp->OnKeyDown(static_cast<SHORT>(wParam));
}
result = 0;
wasHandled = true;
break;
case WM_MOUSEMOVE:
{
pDemoApp->OnMouseMove(lParam);
}
result = 0;
wasHandled = true;
break;
case WM_MOUSEWHEEL:
{
pDemoApp->OnWheel(wParam);
}
result = 0;
wasHandled = true;
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
result = 1;
wasHandled = true;
break;
}
}
if (!wasHandled)
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
}
return result;
}