VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)

本文分为两部分:
(1)在Duilib窗口内创建win32子窗口
(2)在子窗口内使用OpenGL导入3DS模型

在MFC中使用OpenGL导入3DS模型的说明见另一篇文章:VS2015中MFC界面使用OpenGL导入3DS模型(C++完整代码和源文件)

0、实现思路

Created with Raphaël 2.2.0 开始(本文所建模型名称为DC10) 第1步:在SolidWorks中绘制三维模型,输出DC10.STL格式文件 第2步:将.STL文件导入3DSMax中确立纹理坐标,输出DC10.3DS格式文件 第3步:将DC10.3DS 文件再导入Deep Exploration中进行三维模型材质 、纹理的二次 处理,或直接输出DC10.cpp格式文件 第4步:新建win32控制台应用程序Win32Project1_DuilibTest,创建Duilib窗口以及子窗口 第5步:将DC10.cpp文件加入到Duilib项目中,使用OpenGL库函数在子窗口内进行渲染 结束

做了个简单的Duilib界面,并在子窗口内导入了DC10.3DS模型,控制模型自动缩放和旋转,效果如下所示,本篇文章将针对第4步和第5步进行详细记录,文章最后附有完整代码和源文件。
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第1张图片

1、创建父窗口xml界面文件:Interface.xml

Interface.xml文件完整代码如下,只在界面中绘制了一个按钮,其中
button_off.pngbutton_on.png 是预设的按钮背景图片


<Window size="800,480" caption="0,0,0,0">
	<Font name="微软雅黑" size="14" bold="false" italic="false" />
	<Default name="Button" value="height="60" width="71" normalimage="file='button_off.png'" pushedimage="file='button_on.png'" " />
	<VerticalLayout name="HJDemo" bkcolor="#FFE9EEF2">
		<Button name="Button_1" text="OpenGL" float="true" pos="600,100,0,0" font="0" pushedtextcolor="#FFFFFFFF" />
	VerticalLayout>
Window>

2、创建子窗口xml界面文件:SubInterface.xml

SubInterface.xml文件完整代码如下,空的界面


<Window size="460,460" caption="0,0,0,0">
	<Font name="微软雅黑" size="14" bold="false" italic="false" />
	<VerticalLayout name="HJDemo_sub" bkcolor="#FF008080">
		
	VerticalLayout>
Window>

3、新建【win32控制台应用程序】

新建时选择win32-》控制台应用程序,项目名称为 Win32Project1_DuilibTest ,其余默认,新建完成,如下图所示,编译运行一下,以便生成Debug文件夹。
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第2张图片

4、在项目根目录下的Debug文件夹内新建skin文件夹

xmlpng文件放进Debug-》skin文件夹内,如下图所示
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第3张图片

6、添加Duilib窗口类

在【源文件】右键,使用【类向导】【添加类】,类命名为【CInterfaceWnd】,基类为【WindowImplBase】,如下图所示,完成添加。
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第4张图片
此时提示WindowImplBase不是类名或结构名,是因为还没有配置Duilib环境,下面开始配置。

6、配置Duilib环境

(1)将Duilib库文件放在项目根目录下【lib】文件夹中,如下图所示
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第5张图片
(2)在项目右键【属性】-》【配置属性】-》【VC++目录】-》编辑【库目录】-》添加新航【$(solutiondir)lib】-》【确定】-》【确定】,完成库目录的添加,如下图所示
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第6张图片
(3)在项目右键【属性】-》【连接器】-》【输入】-》【附加依赖项】-》添加【$(SolutionDir)\lib\DuiLib_ud.lib】-》【确定】-》【确定】,如下图所示
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第7张图片
(4)将Duilib文件夹放在项目根目录下,在stdafx.h头文件中加入Duilib头文件

//Duilib
#include 
#include "../Duilib/DuiLib/UIlib.h"
#include "../Duilib/DuiLib/Core/UIControl.h"
#include "..\Duilib\DuiLib\Utils\WinImplBase.h"
using namespace DuiLib;
#ifdef _DEBUG
#pragma comment (lib, "duilib_ud.lib")
#else
#pragma comment (lib, "duilib_u.lib")
#endif

//CString头文件
#include 

(5)在【InterfaceWnd.h】文件中引入【#include “stdafx.h”】头文件(可删除自动添加的WinImplBase.h头文件引用,因为已经在stdafx.h中引用了),【InterfaceWnd.h】代码此时如下。

#pragma once
#include "stdafx.h"

class CInterfaceWnd :
	public WindowImplBase
{
public:
	CInterfaceWnd();
	~CInterfaceWnd();
};

(6)在项目属性\CC++\预处理器\预处理器定义\里添加 _WINDOWS
(7)在项目属性\链接\系统 里选择 窗口 (/SUBSYSTEM:WINDOWS)
编译运行通过,至此完成Duilib环境配置。

7、创建Duilib窗口

(1)Win32Project1_DuilibTest.cpp源文件

#include "stdafx.h"
#include "InterfaceWnd.h"

int APIENTRY _tWinMain(HINSTANCE hInstance,
	HINSTANCE hPrevInstance,
	LPTSTR    lpCmdLine,
	int       nCmdShow)
{
	CPaintManagerUI::SetInstance(hInstance);// 加载XML的时候,需要使用该句柄去定位EXE的路径,才能加载XML的路径

	CInterfaceWnd* wnd = new CInterfaceWnd(); // 生成对象
	wnd->Create(NULL, NULL, UI_WNDSTYLE_DIALOG, 0); // 创建DLG窗口
	wnd->CenterWindow(); // 窗口居中
	wnd->ShowModal();
	CPaintManagerUI::MessageLoop(); // 消息循环

	delete wnd; // 删除对象
	return 0;
}

(2)InterfaceWnd.h源文件

#pragma once
#include "stdafx.h"

class CInterfaceWnd :
	public WindowImplBase
{
public:
	CInterfaceWnd();
	~CInterfaceWnd();

	DUI_DECLARE_MESSAGE_MAP()

	virtual CDuiString GetSkinFolder() { return _T("skin"); };
	virtual CDuiString GetSkinFile() { return _T("Interface.xml"); };
	virtual LPCTSTR GetWindowClassName(void) const { return _T("InterfaceWnd"); };

	//Duilib通用窗口类封装
	virtual void InitWindow();
	virtual void OnClick(TNotifyUI& msg);
	HWND hWnd;

public:
	CPaintManagerUI* GetPaintManagerUI() { return &m_PaintManager; }

private:
	CPaintManagerUI* m_pParent;
	bool m_bZoomed;
	CPaintManagerUI* m_pPaintManager;
};

(3)InterfaceWnd.cpp源文件

#include "stdafx.h"
#include "InterfaceWnd.h"

DUI_BEGIN_MESSAGE_MAP(CInterfaceWnd, WindowImplBase)    //将不同的消息映射到对应的响应函数里面
DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK, OnClick)
DUI_END_MESSAGE_MAP()

CInterfaceWnd::CInterfaceWnd()
{
}

CInterfaceWnd::~CInterfaceWnd()
{
}

void CInterfaceWnd::InitWindow()//初始化窗口
{
	hWnd = m_PaintManager.GetPaintWindow();
}

void CInterfaceWnd::OnClick(TNotifyUI &msg)
{
}

编译运行成功,弹出如下界面
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第8张图片

8、创建Duilib子窗口

(1)同样方法添加新类【COpenGLWnd】,基类同样为【WindowImplBase】
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第9张图片
(2)OpenGLWnd.h源文件

#pragma once
#include "stdafx.h"

class COpenGLWnd :
	public WindowImplBase
{
public:
	COpenGLWnd();
	~COpenGLWnd();
	void Init(HWND hWndParent);
	void SetPos(HWND hWndParent, int X, int Y, int cx, int cy);
	virtual CDuiString GetSkinFolder() { return _T("skin"); };
	virtual CDuiString GetSkinFile() { return _T("SubInterface.xml"); };
	virtual LPCTSTR GetWindowClassName(void) const { return _T("OpenGLWnd"); };

	DUI_DECLARE_MESSAGE_MAP()
};

(3)OpenGLWnd.cpp源文件

#include "stdafx.h"
#include "OpenGLWnd.h"

DUI_BEGIN_MESSAGE_MAP(COpenGLWnd, WindowImplBase)    //将不同的消息映射到对应的响应函数里面
DUI_END_MESSAGE_MAP()

COpenGLWnd::COpenGLWnd()
{
}


COpenGLWnd::~COpenGLWnd()
{
}

void COpenGLWnd::Init(HWND hWndParent) //void Init(HWND hWndParent,POINT ptPos)
{
	Create(hWndParent, _T("CWin32UI"), UI_WNDSTYLE_DIALOG, 0);
}

void COpenGLWnd::SetPos(HWND hWndParent, int X, int Y, int cx, int cy)
{
	::SetWindowPos(*this, NULL, X, Y, cx, cy, SWP_SHOWWINDOW);
}

(4)InterfaceWnd.cpp更新为

#include "stdafx.h"
#include "InterfaceWnd.h"

DUI_BEGIN_MESSAGE_MAP(CInterfaceWnd, WindowImplBase)    //将不同的消息映射到对应的响应函数里面
DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK, OnClick)
DUI_END_MESSAGE_MAP()

CInterfaceWnd::CInterfaceWnd()
{
	// 构造一个新的COpenGL对象
	m_pOpenGLWnd = new COpenGLWnd();
}

CInterfaceWnd::~CInterfaceWnd()
{
	delete m_pOpenGLWnd;
	m_pOpenGLWnd = NULL;
}

void CInterfaceWnd::InitWindow()//初始化窗口
{
	hWnd = m_PaintManager.GetPaintWindow();
}

void CInterfaceWnd::OnClick(TNotifyUI &msg)
{
	if (msg.pSender->GetName() == _T("Button_1")) // 绘制按钮
	{
		if (m_pOpenGLWnd != NULL)
		{
			m_pOpenGLWnd->Init(*this);
			m_pOpenGLWnd->SetPos(hWnd, 380, 180, 460, 460); //300*300是子窗口的大小,但是opengl的绘图区域大小需要在xml里的windows属性size="300,300"设置
		}
	}
}

编译运行通过,弹出Duilib窗口界面,点击OpenGL按钮,弹出子窗口如下(深蓝色区域),至此子窗口创建完成
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第10张图片

9、配置OpenGL文件

(1)将opengl所需要的文件复制到项目文件夹下,共三个文件夹:bin(存放.dll文件)、include(存放.h文件)、lib(存放.lib文件)

  • 【bin】文件夹放入:【glut.dll】和【glut32.dll】
  • 【include】文件夹内新建【GL】文件夹,【GL】文件夹内包含:【GLAUX.H】和【glut.h】
  • 【lib】文件夹放入:【GLAUX.LIB】、【glut.lib】和【glut32.lib】

(注:GLU.h和GL.h文件在系统内,不需要额外添加处理,如系统找不到,那么GL的lib、dll、h文件也同glut相关文件一样处理)
以下配置图片直接使用的MFC下配置OpenGL的截图,不更新了
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第11张图片
(2)配置环境。在项目右击-》【属性】-》【配置属性】-》【VC++目录】-》编辑【包含目录】-》添加【$(solutiondir)include】-》确定-》编辑【库目录】-》添加【$(solutiondir)lib】-》确定-》【确定】
(注:确保所配置环境的属性与当前运行环境一致,如下图所示)
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第12张图片
(3) 在【stdafx.h】中加入OpenGL头文件

//OpenGL
#include 
#include 

配置完成,编译运行成功

10、将模型.cpp文件导入Duilib项目

(1)在【源文件】右键,使用【类向导】添加新类C3DSModel,如下图所示,并且在C3DSModel.h和C3DSModel.cpp中添加【#include “stdafx.h”】头文件**
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第13张图片
(2)打开DC10.cpp文件,将除函数外的结构体、数组等复制到C3DSModel.h头文件中,并为函数添加声明,如下图所示
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第14张图片
(3)将其余函数复制到C3DSModel.cpp文件中,并在函数名前加入【C3DSModel::】,如下图所示
VS2015中Duilib界面使用OpenGL导入3DS模型(C++完整代码和源文件)_第15张图片
编译通过,运行成功,但此时对话窗口并没有开始绘制3DS模型(文章后面贴出完整代码)

11、添加Duilib子窗口类COpenGLWnd

采用上述同样的方法添加新类COpenGLWnd
(1)OpenGLWnd.h源文件

#pragma once
#include "stdafx.h"
#include "C3DSModel.h"

class COpenGLWnd :
	public WindowImplBase
{
public:
	COpenGLWnd();
	~COpenGLWnd();
	void SetPos(HWND hWndParent, int X, int Y, int cx, int cy);
	virtual CDuiString GetSkinFolder() { return _T("skin"); };
	virtual CDuiString GetSkinFile() { return _T("SubInterface.xml"); };
	virtual LPCTSTR GetWindowClassName(void) const { return _T("OpenGLWnd"); };

	DUI_DECLARE_MESSAGE_MAP()

		////////////////////////////////////////////////////////////////
public:
	//添加的成员函数与成员变量
	int MySetPixelFormat(HDC hdc);
	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandled);
	void OnPaint();
	HDC hdc;
	HGLRC hglrc;
	GLfloat step, s;
	////////////////////////////////////////////////////////////////

	C3DSModel* m_p3DSModel;
	GLuint lid = 0;
};

(2)OpenGLWnd.cpp源文件

#include "stdafx.h"
#include "OpenGLWnd.h"

DUI_BEGIN_MESSAGE_MAP(COpenGLWnd, WindowImplBase)    //将不同的消息映射到对应的响应函数里面
DUI_END_MESSAGE_MAP()

COpenGLWnd::COpenGLWnd()
{
	m_p3DSModel = new C3DSModel();
	//给成员变量赋初值
	step = 0.0;
	s = 0.1;
}


COpenGLWnd::~COpenGLWnd()
{
	delete m_p3DSModel;
	m_p3DSModel = NULL;
}

void COpenGLWnd::SetPos(HWND hWndParent, int X, int Y, int cx, int cy)
{
	//子窗口相对于屏幕的位置
	::SetWindowPos(*this, NULL, X, Y, cx, cy, SWP_SHOWWINDOW);
}

////////////////////////////////////////////////////////////////////////////
// 设置像素格式函数
int COpenGLWnd::MySetPixelFormat(HDC hdc)
{
	PIXELFORMATDESCRIPTOR pfd = {
		sizeof(PIXELFORMATDESCRIPTOR),    // pfd结构的大小 
		1,                                // 版本号 
		PFD_DRAW_TO_WINDOW |              // 支持在窗口中绘图 
		PFD_SUPPORT_OPENGL |              // 支持 OpenGL 
		PFD_DOUBLEBUFFER,                 // 双缓存模式 
		PFD_TYPE_RGBA,                    // RGBA 颜色模式 
		24,                               // 24 位颜色深度 
		0, 0, 0, 0, 0, 0,                 // 忽略颜色位 
		0,                                // 没有非透明度缓存 
		0,                                // 忽略移位位 
		0,                                // 无累加缓存 
		0, 0, 0, 0,                       // 忽略累加位 
		32,                               // 32 位深度缓存     
		0,                                // 无模板缓存 
		0,                                // 无辅助缓存 
		PFD_MAIN_PLANE,                   // 主层 
		0,                                // 保留 
		0, 0, 0                           // 忽略层,可见性和损毁掩模 
	};

	int  iPixelFormat;

	// 为设备描述表得到最匹配的像素格式 
	if ((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
	{
		//MessageBox(_T("ChoosePixelFormat Failed"), NULL, MB_OK);
		return 0;
	}

	// 设置最匹配的像素格式为当前的像素格式 
	if (SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
	{
		//MessageBox(_T("SetPixelFormat Failed"), NULL, MB_OK);
		return 0;
	}

	return 1;
}

LRESULT COpenGLWnd::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	if (WindowImplBase::OnCreate(uMsg, wParam, lParam, bHandled) == -1)
		return -1;

	// TODO:  在此添加您专用的创建代码

	// 设置当前的绘图像素格式
	MySetPixelFormat(::GetDC(m_hWnd));
	// 创建绘图描述表
	hdc = ::GetDC(m_hWnd);
	// 创建渲染描述表
	hglrc = wglCreateContext(hdc);
	// 使绘图描述表为当前调用现程的当前绘图描述表 
	wglMakeCurrent(hdc, hglrc);

	return 0;
}

void COpenGLWnd::OnPaint()
{
	//CPaintDC dc(this); // device context for painting
	// TODO: 在此处添加消息处理程序代码

	//调用OpenGL绘图函数进行图形绘制
	glEnable(GL_TEXTURE_2D); // 启用纹理映射
	glShadeModel(GL_SMOOTH); // 启用阴影平滑
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // 黑色背景
	glClearDepth(1.0f); // 设置深度缓存
	glEnable(GL_DEPTH_TEST); // 启用深度测试
	glDepthFunc(GL_LEQUAL); // 所作深度测试的类型
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 真正精细的透视修正
													   //光源
	GLfloat LightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f }; // 环境光参数
	GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; // 漫射光参数
	GLfloat LightPosition[] = { 12.0f, 10.0f, -10.0f, 1.0f }; // 光源位置
	glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); // 设置环境光
	glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); // 设置漫射光
	glLightfv(GL_LIGHT1, GL_POSITION, LightPosition); // 设置光源位置
	glEnable(GL_LIGHT1); // 启用一号光源
	glEnable(GL_LIGHTING);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	//清除颜色缓存和深度缓存

	s += 0.0004;
	if (s>1.5)
		s = 0.1;
	step = step + 0.05;
	if (step > 360.0)
		step = step - 360.0;
	glPushMatrix();
	glScalef(s, s, s);//void glScalef(GLfloatx, GLfloaty, GLfloatz);x,y,z分别为模型在x,y,z轴方向的缩放比。
	glRotatef(step, 1.0, 0.0, 0.0);
	glRotatef(step, 0.0, 1.0, 0.0);
	glRotatef(step, 0.0, 0.0, 1.0);
	//DrawColorBox();
	if (lid == 0)
	{
		lid = m_p3DSModel->Gen3DObjectList();
	}
	glCallList(lid);

	glPopMatrix();
	glFlush();

	SwapBuffers(hdc);

	// Do not call CWnd::OnPaint() for painting messages
}

(3)OpenGLWnd.cpp源文件更新如下

#include "stdafx.h"
#include "InterfaceWnd.h"

DUI_BEGIN_MESSAGE_MAP(CInterfaceWnd, WindowImplBase)    //将不同的消息映射到对应的响应函数里面
DUI_ON_MSGTYPE(DUI_MSGTYPE_CLICK, OnClick)
DUI_END_MESSAGE_MAP()

CInterfaceWnd::CInterfaceWnd()
{
	// 构造一个新的COpenGL对象
	m_pOpenGLWnd = new COpenGLWnd();
}

CInterfaceWnd::~CInterfaceWnd()
{
	delete m_pOpenGLWnd;
	m_pOpenGLWnd = NULL;
}

void CInterfaceWnd::InitWindow()//初始化窗口
{
	hWnd = m_PaintManager.GetPaintWindow();
}

void CInterfaceWnd::OnClick(TNotifyUI &msg)
{
	if (msg.pSender->GetName() == _T("Button_1")) // 绘制按钮
	{
		if (m_pOpenGLWnd != NULL)
		{
			m_pOpenGLWnd->Create(hWnd, _T("CWin32UI"), UI_WNDSTYLE_DIALOG, 0);
			m_pOpenGLWnd->SetPos(hWnd, 380, 180, 460, 460); //300*300是子窗口的大小,但是opengl的绘图区域大小需要在xml里的windows属性size="300,300"设置
			while (1)
			{
				m_pOpenGLWnd->OnPaint();
			}
		}
	}
}

编译运行通过,至此实现在Duilib子窗口内导入3DS模型,并旋转和缩放,点击OpenGL按钮开始绘制

12、存在问题

虽然导入了3DS模型,但是还存在以下几点问题未进行编写:

  1. 子窗口内绘制模型时,父窗口消息将无法再响应,需采用并发线程,建立两条消息队列
  2. 在父窗口Interface.xml文件中将windows的属性caption="0,0,0,0"改为caption=“0,0,0,60”,即可实现窗口拖动(在界面最上方按住鼠标左键拖动),但此时子窗口并不会随之移动,因为子窗口在创建时setPos是相对屏幕位置设置的,偶两种解决思路:
    (1)创建Duilib自定义控件,效果类似Button、Option这种控件,封装进Duilib库,以后可以直接调用
    (2)子窗口位置由【相对屏幕】更改为【相对程序】位置,父窗口位置一旦发生改变,子窗口位置随之改变

以上问题,将后续发文详细说明。
本文完整程序代码下载地址:https://download.csdn.net/download/u012293114/12104631

你可能感兴趣的:(MFC和Duilib,OpenGL)