65.9K
CodeProject 正在变化。 阅读更多。
Home

3DHelper

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.61/5 (9投票s)

2009年2月17日

CPOL

3分钟阅读

viewsIcon

62124

downloadIcon

1238

用于显示 3D 数据的辅助类

3DHelperDemo

引言

是否曾遇到过显示与 3D 相关的数据的问题?例如,在 Windows 对话框窗口中显示 3D 曲线?不喜欢实现整个 DirectX/OpenGL 类层次结构?
有一个解决方案:3DHelper 类!

可以在我的 主页 上找到更多工具。

背景  

如果您熟悉 3D 编程,可能已经使用过免费/非免费的库来执行将 3D 对象渲染到屏幕上的任务。
这种方法的一个很大(在我看来!)的缺点是:您的代码变得越来越复杂,难以维护甚至难以理解。更不用说可移植性了。

只需要将一些文件包含到您的项目中,即可执行所有必要的数学运算以“获得 3D”听起来太好了,不像是真的?现在它实现了!

解决方案

本文中描述的 API 的目的是简单地帮助您将 3D 数据投影到您的 2D 窗口中。
给定一个 3D 点(例如,作为 nurbs 曲面的一部分的点、一个 3D 函数值等),API 会转换此点并返回一对坐标,表示您窗口中的一个点。

而且最好的是 - 没有涉及复杂的 OpenGL/DirectX。这是一个纯粹的软件解决方案。

使用代码 

为了让 API 帮助您,它需要执行一些基本步骤

  1. 将类型为 CMF3DHelper 的变量添加到您的项目中。
  2. 在启动时(以及每次调整大小事件后)调用您的变量的 CMF3DHelper::Initialize() 函数。(参见 1.)
    Initialize() 函数需要参数
    • 指向您的窗口的设备上下文 (DC) 的指针(用于获取窗口大小)
    • 代表您数据的“立方体”,由两个向量表示:vminvmax
      您的所有数据点都应位于该立方体内。想象一下类似一个边界立方体,覆盖您的所有数据点。
      2D 示例: 您需要显示形式为 y=sin(x) 的曲线,其中 x 在 [-PI] 和 [+PI] 之间。该函数的结果在 [-1] 到 [+1] 的范围内。
      因此,您必须使用两个角定义边界框(2D!)
      (-1, -PI),即左上角,
      (+1, +PI) 类似于右下角。
    • 一个标志 (bSupportTrackBall) 告诉 API 是否执行由鼠标控制的旋转。
  3. 对于要计算的每个数据点,调用 RenderPoint() 函数。它返回您的 3D 数据点对应的屏幕(=窗口)坐标。

最好检查 3DHelperDemoSimple 项目。我将代码减少到最少(无检查代码)以阐明这些步骤。
打开 3DHelperDemoSimple 项目,然后选择 OnInitDialog 方法。

只有以下几行被添加到向导生成的代码中:

    // Initialize the 3D system
    initialize(); 

initialize 函数本身在下面几行实现。
执行的步骤

  1. 初始化 API。
  2. 启动更新计时器,用于刷新鼠标(=旋转)信息。在我们的例子中,整个场景将每 25 毫秒重绘一次。
// Set up initial 3D system
void CMy3DHelperDemoSimpleDlg::initialize()
{
	CWnd *pTarget = GetDlgItem(IDC_STATIC_PLACEHOLDER);
	m_c3DHelper.Initialize(pTarget->GetDC(), NeHe::Vector(-2.0f, -2.0f, -2.0f), 
		NeHe::Vector(2.0f,2.0f,2.0f), TRUE);

	// Timer used to refresh "trackball" information
	SetTimer(1, 25, NULL);
}

由计时器触发,每 25 毫秒将进入 OnTimer 函数。
在内部,将更新轨迹球位置 (UpdateTrackBall,这再次需要当前 DC 作为参数),并且将重绘场景 (redraw())。

void CMy3DHelperDemoSimpleDlg::OnTimer(UINT nIDEvent) 
{
	// Timer is used to refresh trackball information
	if(nIDEvent == 1) {
		KillTimer(nIDEvent); // Prevent timer "overlapping"
		m_c3DHelper.UpdateTrackBall
			(GetDlgItem(IDC_STATIC_PLACEHOLDER)->GetDC());
		redraw();
		SetTimer(1, 25, NULL); // Start timer for next redrawing round
	}
	CDialog::OnTimer(nIDEvent);
}

Redraw 再次很简单。只需逐点获取您的数据点,为每个点调用 RenderPoint() 并使用例如 SetPixel() 函数显示结果。

3DHelperDemo 项目实现了更复杂的示例。

兴趣点 

我决定不亲自实现整个数学运算 (认为那不是一个独特的任务...)。因此,我从 NeHe(一个与 OpenGL 相关的 GREAT 站点,我得到了 5 分!)中获取了一些矩阵/向量代码。

为了更好地了解 OpenGL 的实现方式,我仔细研究了免费的 mesa 实现。对于每个对 3D 实现感兴趣的人来说,这都是必须的!

注释

欢迎任何反馈!

SoftwareHive 上查看更多工具和更新。

历史

  • 2009/02/14 - 初始发布
© . All rights reserved.