Hosting a C++ D3D engine in C# Winforms
Application specific codeNow CD3Dmanager by itself doesn’t actually do anything useful. We can derive from this class and override its virtual member functions with code specific to our application. The following code snippet defines a renderer that draws some dynamically updated text using a ID3Dfont.
class CD3DTestRender : public CD3Dmanager { … };
HRESULT CD3DTestRender::OnUpdate(double dTime, double dElapsedTime)
{
m_Time = dTime;
m_FPS = 1.0 / dElapsedTime;
return S_OK;
}
HRESULT CD3DTestRender::OnRender(LPDIRECT3DDEVICE9 pd3dDevice)
{
HRESULT hr;
// Clear the render target and the zbuffer
ATTEMPT(pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff000000, 1.0f, 0) );
// Render the scene
if(SUCCEEDED( pd3dDevice->BeginScene()))
{
RECT rc;
TCHAR szInfo[256];
_stprintf(szInfo,_T("Time: %f\nFPS: %f\nRes: %d %d\nMState:0x%x\nMXY:%d %d"),
m_Time,m_FPS,m_uiWidth,m_uiHeight,m_dwMouseButtonStates,m_iMouseX,m_iMouseY);
SetRect(&rc, 10, 10, 0, 0 );
m_pFont->DrawText( NULL, szInfo, -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 0.0f, 0.0f, 1.0f ));
ATTEMPT(pd3dDevice->EndScene());
ATTEMPT(pd3dDevice->Present( NULL, NULL, NULL, NULL ));
}
Sleep(10);
return S_OK;
}
HRESULT CD3DTestRender::OnInitDeviceObjects(LPDIRECT3DDEVICE9 pd3dDevice)
{
m_pd3dDevice = pd3dDevice;
return D3DXCreateFont(m_pd3dDevice, // D3D device
12, // Height
0, // Width
FW_REGULAR, // Weight
1, // MipLevels, 0 = autogen mipmaps
FALSE, // Italic
DEFAULT_CHARSET, // CharSet
OUT_DEFAULT_PRECIS, // OutputPrecision
DEFAULT_QUALITY, // Quality
DEFAULT_PITCH | FF_DONTCARE, // PitchAndFamily
L"Courier", // pFaceName
&m_pFont); // ppFont
}
HRESULT CD3DTestRender::OnRestoreDeviceObjects()
{
return m_pFont->OnResetDevice();
}
HRESULT CD3DTestRender::OnInvalidateDeviceObjects()
{
return m_pFont->OnLostDevice();
}
HRESULT CD3DTestRender::OnDeleteDeviceObjects()
{
SAFE_RELEASE(m_pFont);
return S_OK;
}
Why expose the derived class in a Win32 DLL?Now comes the task of exposing our derived class in a Win32 DLL. Why can’t we just stick all of this code into our C++/CLI DLL? Well we can, C++/CLI is real C++ code, just with some additions and a few not so obvious restrictions (like no inline assembly, no variable argument lists,..). When importing your derived class code into a C++/CLI project, be aware that your class will be compiled as managed code, and there might be performance penalties (as for how much, that is up for debate). At some point I attempted to import a large C++ code base into a C++/CLI project, and came across a number of problems with the acceptance of my own code and the code of third parties as valid C++/CLI code. I am sure there are ways around these problems, but for this article I elected to wrap my C++ code into an unmanaged Win32 DLL, for use by the C++/CLI DLL. This Win32 DLL will now run in an unmanaged environment and not suffer any performance penalties. Given the simplicity of the given CD3Dmanager and CTestRenderer classes, they could have been placed directly in the C++/CLI project, but for illustrative purposes I am using this Win32 DLL. To accomplish this, create a new Win32 DLL project in Visual Studio, and add the code for our CD3Dmanager and CTestRenderer classes. To expose our classes and their public member functions from the DLL, we can prepend this define to our class declarations: #ifdef _EXPORTING #define CLASS_DECLSPEC __declspec(dllexport) #else #define CLASS_DECLSPEC __declspec(dllimport) #endifLike so: class CLASS_DECLSPEC CD3Dmanager … class CLASS_DECLSPEC CD3DTestRender : public CD3Dmanager …Where _EXPORTING is defined when building the DLL, and not defined when using the DLL. |
|