用于 SDI Direct3D 开发的 3D 启用视图基类






4.82/5 (18投票s)
2005 年 1 月 26 日
17分钟阅读

174786

5513
CDSSD3DView8 类的定义,这是一个 CView 派生类,用于简化 SDI 环境下的 Direct3D 开发。
目录
引言
本文介绍了 CDSSD3DView8
类,这是一个 CView
派生类,它为 MFC 单文档界面 (SDI) 应用程序提供了 Direct3D 支持。CDSSD3DView8
类旨在用作开发者主要 SDI 视图类的基类,以替代 CView
。
此类目标是提供与熟悉的 Direct3D SDK CD3DApplication
类类似的功能,同时允许用户享受 Visual C++ AppWizard 生成的 SDI 应用程序的易用性。此类还设计为用户无需在 SDI 应用程序中“额外添加”Direct3D 支持。此支持直接内置于视图类本身。
在互联网上搜索,我发现了一些描述如何将 OpenGL 与 SDI 结合使用或如何将 Direct3D 与 MFC 混合使用的示例,但没有提供通用的 CView
派生类,可以作为使用 Visual Studio 的单一文档界面功能开发 Direct3D 应用程序的起点。因此,我决定介绍一个我一直在自己开发中使用的类。
优点和缺点
优点和缺点是使用 SDI 构建 Direct3D 应用程序的直接结果。3D 渲染将比“正常”的 Direct3D SDK 应用程序稍慢。没有全屏支持,因为 SDI 应用程序不运行全屏。也没有适配器和显示模式的枚举。但由于使用 CDSSD3DView8
类构建的 SDI 应用程序将始终使用桌面作为目标,因此无需此类支持。
从积极的方面来看,通过开发 SDI 应用程序,您可以获得 AppWizard 和 ClassWizard 的所有支持,以方便开发。尽管 MFC/SDI 可能不适合游戏开发,但它使游戏工具的开发更加容易。例如,我使用 CDSSD3DView8
类作为我的 DirectX 网格编辑器的主视图的基类,我一直在折腾它。使用 Visual C++ 创建支持对话框、菜单、工具栏、消息处理程序等比使用 DirectX SDK 界面要容易得多。我想象着手动编写这些资源就觉得头皮发麻……
包含内容
CDSSD3DView8
类定义在文件 DSSD3DView8.h 和 DSSD3DView8.cpp 中。
还包含了一个示例程序,该程序渲染了一个简单的立方体(3D 开发的“Hello, world”)并在闪烁灯光下显示,以展示 CDSS3DView8
类如何用作 SDI 应用程序的基类(而非 CView
)。此示例程序的所有源代码都已包含。
此类和包含的示例程序使用 DirectX 8.0。程序使用 Visual C++ 6.0 编写,并在 Windows XP 平台使用 NVIDIA GeForce4 MX440-SE 显卡进行测试。此类并不复杂,因此可以轻松适配到更高版本的 DirectX。此外,MX440-SE 略显老旧,因此在更高级的显卡上运行示例应该没有问题。
要求
本文假设读者对 Direct3D 有基本了解,并了解如何使用 MSSDK DirectX 工具包访问 Direct3D。本文无意作为 Direct3D 入门教程,因为市面上有大量关于该主题的文章和书籍。
如前所述,CDSSD3DView8
类使用 DirectX 8.0。因此,您显然需要安装 DirectX 8.0 SDK 工具包和兼容的 3D 显卡。此外,由于该应用程序是使用 Visual C++ 6.0 构建的,我建议将其作为开发平台。我尚未在任何其他 Visual Studio 环境中对其进行测试。
设置 SDI 项目
要使用 CDSSD3DView8
类创建自己的 SDI 应用程序,只需按照标准的 AppWizard 页面生成 SDI 应用程序的源代码。在选择基类视图类时,您必须选择 CView
。所有其他 SDI 选项完全由用户决定。
应用程序代码创建后,用户需要将 CDSSD3DView8
的头文件和源文件复制到应用程序目录,并对手写生成的视图头文件和源文件进行一些简单的手动更改,以支持 CDSSD3DView8
作为基类。此外,还需要对项目设置进行一些更改。
设置视图头文件
在 AppWizard 生成的视图头文件的开头,您需要添加对 CDSSD3DView8
头文件的引用
#include "DSSD3DView8.h"
接下来,更改视图类的声明,使其派生自 CDSSD3DView8
而非 CView
。CDSSD3DView8
直接派生自 CView
,因此所有 CView
方法和数据仍可供用户使用。此处显示的是示例程序的视图类
class CD3D8SDIView : public CDSSD3DView8
最后,您需要删除 AppWizard 创建的 OnDraw
的声明。CDSSD3DView8
类中包含 OnDraw
的完整实现。其实现细节将在下文讨论。但可以肯定的是,派生视图类将不需要自己的 OnDraw
实现。
设置视图源文件
在生成的 .cpp 文件中还需要进行一些手动更改。其中大部分是简单的查找和替换操作。
前两个更改是针对 ClassWizard 宏的。请注意,一旦完成这两个更改,通过 ClassWizard 添加的任何处理程序都将自动包含对 CDSSD3DView8
中相应成员函数的调用。
IMPLEMENT_DYNCREATE(CD3D8SDIView, CDSSD3DView8) BEGIN_MESSAGE_MAP(CD3D8SDIView, CDSSD3DView8)
接下来,您需要删除 OnDraw
的实现。如上所述,派生自 CDSSD3DView8
的视图类将不需要提供自己的 OnDraw
实现。
最后,您需要查找所有“CView::
”基类调用实例,并将其替换为“CDSSD3DView8::
”。这会将 AppWizard 生成的处理程序重定向到调用 CDSSD3DView8
基类而不是 CView
。
项目设置
最后,您可能需要对您的 Project->Settings 进行一些更改。在 C++ 选项卡下,选中 Preprocessor 类别。如果您尚未在 Tools->Options 中指定 DirectX 8.0 SDK 的路径,则需要在此处添加这些路径。
接下来,在 Project->Settings 下,选择 Link 选项卡,并将“d3dx8.lib d3dxof.lib d3d8.lib dxerr8.lib winmm.lib dxguid.lib”添加到 Object/Library modules 列表中。
这应该可以完成 SDI 项目的设置,以包含 CDSSD3DView8
类。您已准备好开始编码!如果这是一个新应用程序,请生成并运行它。您将看到标准的 SDI 框架、菜单、工具栏和状态栏。但是,客户端区域应该是纯黑色的。这是底层的 CDSS3DView8
类在清除后台缓冲区并将场景呈现到客户端区域。
接下来的几部分将更详细地介绍如何使用 CDSSD3DView8
类提供的简单 API 进行应用程序开发。
代码
本节讨论了 CDSSD3DView8
接口的各个方面,这些接口暴露给应用程序程序员,以及提供底层 Direct3D 支持的一些内部 private
数据和方法。首先,我们列出了暴露给派生类使用的 API。此 API 非常简单,许多部分对于 Direct3D SDK 的 CD3DApplication
类的用户来说会很熟悉。之后,将触及类的“内部结构”……即提供底层 3D 支持的数据成员和方法。
API
这里我们讨论 CDSSD3DView8
接口的较简单方面。此处涵盖了头文件中定义的各种原型,并包含任何额外的澄清说明。
Direct3D API 方法
核心开发人员 API 方法遵循熟悉的 Direct3D SDK 原型。与 SDK 的 CD3DApplication
类中的对应项一样,这些基类实现所做的很少。
virtual HRESULT InitDeviceObjects() { return S_OK; } virtual HRESULT InvalidateDeviceObjects() { return S_OK; } virtual HRESULT RestoreDeviceObjects() { return S_OK; } virtual HRESULT DeleteDeviceObjects() { return S_OK; } virtual HRESULT Render() { return S_OK; } virtual HRESULT FrameMove();
InitDeviceObjects
在获得 Direct3D 设备后调用一次。其对应的 DeleteDeviceObjects
在视图销毁并释放 Direct3D 设备之前调用一次。
InvalidateDeviceObjects
在设备丢失或视图大小调整时调用。RestoreDeviceObject
在设备恢复后调用,并且在由于视图大小调整而重新计算内部数据后调用。
Render
从 CDSSD3DView8
类的 OnDraw
方法中调用,一旦 OnDraw
确定 3D 设备稳定。Render
与 SDK 版本略有不同。OnDraw
将派生类 Render
方法的调用置于 Direct 3D 的 BeginScene
/EndScene
对之间。这之后立即是 Present
调用,将后台缓冲区呈现到视图。这使得派生类的 Render
方法无需担心这些细节。渲染将在稍后更详细地讨论。
最后是 FrameMove
。这是唯一一个不是单行实现的此类方法。FrameMove
在头文件的末尾作为 inline
函数提供。FrameMove
的操作方式与 CD3DApplication
实现略有不同,并且将在稍后更详细地介绍。
在大多数情况下,应用程序不必调用这些基类版本。唯一可能的例外是 FrameMove
,但其实现非常简单,可以包含在派生类的 FrameMove
方法中。
GDI 支持
根据文档,在 BeginScene
/EndScene
对之间调用 Windows GDI 函数是不安全的。为了支持 GDI,已包含两个辅助虚拟函数,它们都接收传递给 OnDraw
的 DeviceContext
virtual void PreRender(CDC *) { } virtual void PostRender(CDC *) { }
PreRender
由 OnDraw
方法在清除 3D 后台缓冲区并调用 BeginScene
之前调用。这允许用户执行任何必要的预 Render
任务。一个例子可能包括建立一个或多个变换矩阵。实际上,尽管此方法列在“GDI 支持”下,但在此处执行任何 GDI 渲染是不明智的,因为客户端区域很快就会被后台缓冲区占用。
PostRender
由 OnDraw
在调用 Direct3D 接口的 EndScene
和 Present
函数之后调用。在这里,可以调用任何 GDI 函数,将其他精灵和其他内容添加到场景中。当调用 PostRender
时,后台缓冲区已移动到视图,Direct3D 不再“拥有”客户端区域。
3D 数据访问
除一个数据成员外,所有数据成员都保持 private
。这确保了粗心的开发者不会意外覆盖主 Direct3D 设备接口或任何其他内部数据。通过“getter”方法访问设备和相关数据。这些方法的注释也包含在内,用于基本解释。稍后将讨论更多详细信息。
public: // 3D DEVICE ACCESS // // Get3DDevice is called to return the IDirect3DDevice8 interface // pointer. This will be NULL if the device hasn't been created // or if Open3D failed creating a 3D device. // // GetDeviceState returns the current 3D device state. This will // be one of the following values (as returned by // TestCooperativeLevel): // // - S_OK if everything is running smoothly // - D3DERR_DEVICELOST if the device is in a lost state // - D3DERR_DEVICENOTRESET if device focus has been regained but // hasn't been reset yet. // // The error cases are handled internally, so callers don't have // to take any specific action. // LPDIRECT3DDEVICE8 Get3DDevice() const { return m_p3DDevice; } HRESULT GetDeviceState() const { return m_hDeviceState; } // MEMBER DATA ACCESS // // GetBackBuffer returns the backbuffer width and height as a 2D // vector. // // GetHalfBackBuffer returns the half-size backbuffer coordinate pair. // // GetViewport returns the current Viewport. // // GetHALCaps returns the HAL capability set for the current 3D // adapter. // // GetREFCaps returns the REF capability set for the current 3D // adapter. // // GetCurrentCaps returns a pointer to either the HAL or REF // capabilities, depending on which was selected when the 3D // Device was created. This will be NULL if the 3D device hasn't // been obtained yet, or if Open3D method failed to create the // device. // const D3DXVECTOR2 & GetBackBuffer() const { return m_ptBackBuffer; } const D3DXVECTOR2 & GetHalfBackBuffer() const { return m_ptHalfBackBuffer; } const D3DVIEWPORT8 & GetViewport() const { return m_Viewport; } const D3DCAPS8 & GetHALCaps() const { return m_capsHAL; } const D3DCAPS8 & GetREFCaps() const { return m_capsREF; } const D3DCAPS8 * GetCurrentCaps() const { return m_pCaps; } protected: // m_cClearColor is the background color we'll apply when we // clear the backbuffer. This can be set by derived classes // to their own color. The default is black. // D3DCOLOR m_cClearColor;
Get3DDevice
是派生类如何访问 Direct3D 接口。与 CD3DApplication
类不同,Direct3D 接口并未暴露。如上所述,设备指针被保留为私有以进行保护。
请注意,GetDeviceState
主要用于参考。如果 3D 设备未处于 S_OK
状态,用户无需采取任何行动。这由 CDSSD3DView8
类自动处理,它会定期尝试重新获取丢失的 3D 设备。
GetHalfBackBuffer
函数可能看起来很奇怪。但我将其用于将鼠标点转换为标准化的(例如,-1.0f 到 1.0f)屏幕坐标,作为 3D 命中测试的第一步。
m_cClearColor
是唯一可供派生类访问的 3D 相关数据成员。这在 OnDraw
清除后台缓冲区时设置背景颜色。示例应用程序在程序开始时使用此项来设置暗灰色背景。默认的 m_cClearColor
是黑色。
调试辅助方法
另外包含两个方法来辅助应用程序调试
// LogDXDebug is called to log some application debugging message // to the TRACE0 output. The function works just like the // printf() statement, allowing variable argument lists. // // LogDXError is called to log an error message based on the // specified HRESULT value. The message logged will be of // the format: <FN>:returned 0x<RESULT> (<STR>). // <FN>,, and <RESULT> are provided by the caller. // <STR> will be filled in by the DX utility method. // protected: void LogDXDebug( const char * szSpec, ... ); void LogDXError( const char * szFn, const char * szAction, HRESULT hResult );
这些函数仅将字符串记录到一个内部缓冲区,然后将其转储到 TRACE0。因此,这些消息将在 Release 版本中禁用。LogDXDebug
的工作方式类似于 printf()
语句,允许可变参数列表。LogDXError
以特定格式记录消息。这两个方法都在基类 CDSSD3DView8
启动时使用,它们生成的输出显示在此处
对我个人而言,我有一个单独的调试线程,将调试和错误信息记录到一组文件中。但该调试器对于这个简单的应用程序来说过于复杂,我也不想让本文充斥过多的无关内容。所以我选择了这种简单的 TRACE0 方法。
ClassWizard 生成的命令处理程序
为了确保正常运行,CDSSD3DView8
处理一组小的 Windows 消息。为了确保正常运行,如果在派生类中覆盖了这些方法中的任何一个,则必须调用这些基类版本。命令处理程序在此定义并简要讨论如下。
// Overrides public: // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CDSSD3DView8) public: virtual void OnInitialUpdate(); protected: virtual void OnDraw(CDC* pDC); //}}AFX_VIRTUAL // Generated message map functions protected: //{{AFX_MSG(CDSSD3DView8) afx_msg void OnDestroy(); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnTimer(UINT nIDEvent); afx_msg BOOL OnEraseBkgnd(CDC* pDC); //}}AFX_MSG DECLARE_MESSAGE_MAP()
OnInitialUpdate
是建立 Direct3D 接口的地方。此实现调用内部方法 Open3D()
来初始化 3D 窗口化环境。Open3D
将稍后详细讨论。
OnDraw
是主要的客户端绘制入口点。这将在稍后详细讨论。
OnDestroy
在视图正在销毁时响应收到的 WM_DESTROY
命令而被调用。此方法调用内部 Close3D
方法,该方法清理 3D 环境。Close3D
将最后调用 InvalidateDeviceObjects
,然后立即调用 DeleteDeviceObjects
。之后,所有计时器将被关闭,Direct3D 接口将被释放。
OnSize
在用户调整视图大小时响应调用。如果已建立 Direct3D 设备(并且如果客户端区域的大小实际上正在改变),OnSize
将调用内部 Reset3D
方法,使用新的视图大小重新建立 3D 环境。
OnTimer
在响应 WM_TIMER
消息时调用。CDSSD3DView8
类管理两个计时器:FrameMove
计时器,如下文所述;以及一个 3D 设备状态计时器。第二个计时器在 3D 设备已建立但已被确定为无效状态时启动。此计时器每半秒触发一次,并调用内部 TestDeviceState
方法来检查设备是否可以重置。此计时器运行直到设备重新建立或视图销毁。
OnEraseBkgnd
是一个空实现,如实现所示
BOOL CDSSD3DView8::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
您的程序无法控制导致客户端区域部分失效的所有路径。一个例子是使用鼠标右键打开一个上下文敏感的弹出菜单。菜单关闭时,Windows 有时会使您的客户端区域无效,如果调用默认的 CView::OnEraseBkgnd
,这可能会导致闪烁。所有这些不受控制的客户端无效路径最终都会导致 OnDraw
,它会调用 Direct3DDevice 的 Clear
函数将后台缓冲区设置为当前 m_ClearColor
。因此,通过不让 CView
处理背景擦除来消除闪烁。
深入探讨
这里我们将更详细地讨论一些代码。FrameMove
已描述,OnDraw
与 Render
的关系也已描述。之后,将检查一些内部 3D 控制方法。
更多关于 FrameMove
FrameMove
与 CD3DApplication
版本略有不同。由于这只是一个视图类,它不控制应用程序中的运行循环。因此,FrameMove
是通过使用内部 Windows 计时器来处理的。
FrameMove
不像 SDK 的 CD3DApplication
中那样自动触发。它必须在派生类中通过调用以下函数手动配置:
BOOL StartFrameTimer(DWORD dwTimeoutMS);
该函数接受毫秒数作为超时值。如果成功,则返回 TRUE
,如果 SetTimer
调用失败,则返回 FALSE
。一旦进行了此调用,计时器将开始运行。正如预期的那样,FrameMove
由该类的 OnTimer
实现调用。因此,包含自己计时器的派生类应确保调用 CDSSD3DView8::OnTimer
以确保 Frame 计时器正确运行。
FrameMove
在头文件中实现为 inline
,如下所示:
inline HRESULT CDSSD3DView8::FrameMove() { Invalidate(FALSE); return S_OK; }
请注意,FrameMove
会使视图无效,这最终会导致调用 OnDraw
,从而调用 Render
。Invalidate
调用指定 bRepaint
标志为 FALSE
。但是,由于 CDSSD3DView8
类会 stub OnEraseBkgnd
(参见上文),如果您意外地将默认的 TRUE
作为 bRepaint
标志传递,您也不会看到任何闪烁。
CDSSD3DView8
类也不维护任何内部时间计数器。派生类的 FrameMove
应调用:
FLOAT GetElapsedTime() const;
以获取自上一帧以来经过的时间。但是,GetElapsedTime
仅返回 StartFrameTimer
中指定的原始毫秒数除以 1000,将其转换为单位为秒的浮点值。如果派生类依赖于精确到毫秒的计时,这可能会导致小的计时错误。
当应用程序不再需要 Frame 计时器时,一个简单的调用将禁用它:
void StopFrameTimer();
此函数在清理期间调用,因此派生类不必担心此细节。
示例应用程序使用 FrameMove
及其支持函数,为场景应用一个随机闪烁的定向光。该光位于观察者的眼睛点。
如果您想要更健壮的 FrameMove
实现,可以尝试在主 CWinApp
派生应用程序类的 OnIdle
方法中添加计时支持。
OnDraw 和 Render
如前所述,派生类无需提供自己的 OnDraw
实现。CDSSD3DView8
类提供了此功能,并在其执行过程中调用虚拟钩子。为了便于解释各种虚拟钩子被调用的位置,此处提供了 OnDraw
的代码:
void CDSSD3DView8::OnDraw(CDC* pDC) { // Test the device state. If we're not good to go, then we have // to exit. TestDeviceState(); if( m_hDeviceState != S_OK ) return; // Allow derived class pre-rendering. PreRender(pDC); // Clear the backbuffer. m_p3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, m_cClearColor, 1.0f, 0 ); // BeginScene... const char * szAction = "BeginScene"; HRESULT hResult = m_p3DDevice->BeginScene(); if( hResult == S_OK ) { szAction = "Render"; hResult = Render(); m_p3DDevice->EndScene(); m_p3DDevice->Present(NULL, NULL, NULL, NULL); } // Log any errors. if( hResult != S_OK ) LogDXError("OnDraw", szAction, hResult); // And now post-rendering. PostRender(pDC); }
首先,OnDraw
调用内部 TestDeviceState
方法。TestDeviceState
测试 3D 设备的当前状态,并在发生意外情况时采取适当的措施。此测试的结果存储在 m_hDeviceState
成员中,可通过 GetDeviceState()
访问。TestDeviceState
的实现非常直接,可以在源文件 DSSD3DView8.cpp 中查看。
在 TestDeviceState
之后,调用 PreRender
。此方法前面已提及,无需进一步说明。
之后,OnDraw
进入 3D 渲染部分。如前所述,请注意 OnDraw
将 Render
调用置于 BeginScene
/EndScene
对之间,并随后调用 Present
。这与 Direct3D SDK 的 CD3DApplication
类实现不同,后者期望用户的 Render
方法处理所有这些细节。如果您对 Render
在 CDSSD3DView8
中的处理方式感到不适,请随时删除 BeginScene
、EndScene
和 Present
的调用,并将它们移至您自己的 Render
方法。此序列包含在 OnDraw
中是为了简化。
OnDraw
最后调用 PostRender
。这已在前面描述过。
3D 设备管理
本节定义了用于管理主 Direct3D 接口以及从中获得的 Direct3DDevice 的内部方法和数据。这些方法和数据都是 private
的,应用程序程序员无法访问。具体的声明在 CDSSD3DView8.h 中,并在此显示。已包含代码注释以提供对此信息的大部分内容的解释。
首先,我们有数据。注释足够具有描述性,因此不再需要进一步解释。
private: ///////////////////////////////////////////////////////////////////// // 3D DEVICE DATA // // m_pD3D is the master 3D interface object. This object provides // the entry point into Direct3D services. // // m_D3DDisplayMode is the current adapter display mode, and is // established as soon as the master 3D interface is created. // // m_capsHAL is the HAL adapter capabilities, as loaded from the // master 3D object. // // m_capsREF is the REF adapter capabilities, as loaded from the // master 3D object. // // m_pCaps will point to the currently active capability set (either // m_capsHAL or m_capsREF). This pointer is established depending // on whether or not the 3D Device was created using a HAL device // (preferable) or a REF device. // // m_d3dPresentParams is the presentation parameter structure used // to initialize the 3D device. This is maintained in case the // device needs reset (e.g. when the window is resized). // // m_p3DDevice is the 3D device interface created for this View. // // m_Viewport is the current viewport. This will be similar to the // local Backbuffer definition, but will be loaded from the 3D // device. // // m_ptBackBuffer is a 2-D point that defines the width (x) and // height (y) of the backbuffer. This will always reflect the // current width and height of the view's client area. // // m_ptHalfBackBuffer is a point that simply holds the backbuffer // width/2 in the X field, height/2 in the Y field. This value // is used when converting a mouse point to model space. // // m_hDeviceState holds the current 3D device state, as returned // by TestCooperativeLevel. If no 3D device exists, then this // will hold D3DERR_INVALIDDEVICE. // // m_bInitDevicesNeeded is a one-shot flag that indicates whether // or not a call to InitDeviceObjects is needed. // LPDIRECT3D8 m_pDirect3D; D3DDISPLAYMODE m_d3dDisplayMode; D3DCAPS8 m_capsHAL; D3DCAPS8 m_capsREF; D3DCAPS8 * m_pCaps; D3DPRESENT_PARAMETERS m_d3dPresentParams; LPDIRECT3DDEVICE8 m_p3DDevice; D3DVIEWPORT8 m_Viewport; D3DXVECTOR2 m_ptBackBuffer; D3DXVECTOR2 m_ptHalfBackBuffer; HRESULT m_hDeviceState; BOOL m_bInitDevicesNeeded;
直接管理 3D 环境的成员函数也是 private
的,应用程序程序员无法访问。它们执行所有“后台”操作来维护 3D 环境,包括启动、运行和关闭。由于它们不是暴露给应用程序开发者的 CDSSD3DView8
正式 API 的一部分,因此读者可以自行研究这些方法的实现。已包含头文件注释,对这些方法进行了简要说明。
// Open3D is called to create the 3D device and perform any one-time // initialization. The function returns S_OK if successful, or // some error code if an error occurs. The result of this test // is stored in m_hDeviceState, which can be accessed via the // GetDeviceState() method. // // Create3DDevice is called to create a 3D device of the requested // type. // // Close3D is called to shut down all 3D objects and the master // 3D device. // // Reset3D is called whenever the size of the view changes or // whenever it has been determined that the 3D device has been // lost. If the reset fails and the device is lost, the function // will start the 1 second timer to keep polling the 3D Device's // Reset method. // // TestDeviceState is called internally to test the 3D device state // and either reset the device or start the reset timer if the // device isn't ready. When the function exits, m_hDeviceState // will hold the current state. // HRESULT Open3D(); HRESULT Create3DDevice(D3DDEVTYPE d3dDevType); void Close3D(); HRESULT Reset3D(); void TestDeviceState(); // StartResetTimer is called whenever a "device lost" state is // detected. If the timer isn't already running, then a new // timer is started. Timeouts are handled in OnTimer, which will // attempt to reset the device if control hasn't been regained. // // StopResetTimer is called to kill any current reset timer. If it's // not running, then the call is ignored. // // m_nResetTimerId is the timer Id value created when the Reset // timer is running. // UINT m_nResetTimerId; void StartResetTimer(); inline void StopResetTimer();
这些函数均由各种 MFC 事件驱动,通过上述六个 ClassWizard 覆盖(从 OnInitialUpdate
到 OnEraseBkgnd
)。因此,如果用户的视图类提供了自己的这些 On...
方法的覆盖,那么调用这些 On...
方法的 CDSSD3DView8
版本至关重要。
MDI 情况如何?
我在一个 MDI 应用程序示例中测试了此类,并且没有遇到任何问题。使用示例代码,我创建了一个 MDI 应用程序,并且能够在 MDI 框架中同时打开多个闪烁的立方体视图。尽管这不是对 MDI 环境中 CDSSD3DView8
类的完整测试(我只是检查了内存泄漏等),但我认为此类也相当适合 MDI 应用程序。
免责声明与许可
大多数类和文件都包含“DSS”前缀。这些代码是我为我一人公司 Donut Shop Software (因此是 DSS 前缀) 开发的。我已从代码中删除了所有版权声明,以便在此文章中发布。除了本网站定义的那些之外,使用此软件没有任何显式或隐式的许可问题。
谢谢
感谢所有花时间阅读本文并尝试使用代码的人。这是我的第一篇文章,所以我很感激您能一直读到这里。玩得开心!
特别感谢 Obliterator 指出了调整视图大小时的一个缺陷。现已更正并更新了下载内容。