本文分为两部分:
(1)在Duilib窗口内创建win32子窗口
(2)在子窗口内使用OpenGL导入3DS模型
在MFC中使用OpenGL导入3DS模型的说明见另一篇文章:VS2015中MFC界面使用OpenGL导入3DS模型(C++完整代码和源文件)
做了个简单的Duilib界面,并在子窗口内导入了DC10.3DS模型,控制模型自动缩放和旋转,效果如下所示,本篇文章将针对第4步和第5步进行详细记录,文章最后附有完整代码和源文件。
Interface.xml文件完整代码如下,只在界面中绘制了一个按钮,其中
button_off.png 和 button_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>
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>
新建时选择win32-》控制台应用程序,项目名称为 Win32Project1_DuilibTest ,其余默认,新建完成,如下图所示,编译运行一下,以便生成Debug文件夹。
将xml和png文件放进Debug-》skin文件夹内,如下图所示
在【源文件】右键,使用【类向导】【添加类】,类命名为【CInterfaceWnd】,基类为【WindowImplBase】,如下图所示,完成添加。
此时提示WindowImplBase不是类名或结构名,是因为还没有配置Duilib环境,下面开始配置。
(1)将Duilib库文件放在项目根目录下【lib】文件夹中,如下图所示
(2)在项目右键【属性】-》【配置属性】-》【VC++目录】-》编辑【库目录】-》添加新航【$(solutiondir)lib】-》【确定】-》【确定】,完成库目录的添加,如下图所示
(3)在项目右键【属性】-》【连接器】-》【输入】-》【附加依赖项】-》添加【$(SolutionDir)\lib\DuiLib_ud.lib】-》【确定】-》【确定】,如下图所示
(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环境配置。
(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)
{
}
(1)同样方法添加新类【COpenGLWnd】,基类同样为【WindowImplBase】
(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按钮,弹出子窗口如下(深蓝色区域),至此子窗口创建完成
(1)将opengl所需要的文件复制到项目文件夹下,共三个文件夹:bin(存放.dll文件)、include(存放.h文件)、lib(存放.lib文件)
(注:GLU.h和GL.h文件在系统内,不需要额外添加处理,如系统找不到,那么GL的lib、dll、h文件也同glut相关文件一样处理)
以下配置图片直接使用的MFC下配置OpenGL的截图,不更新了
(2)配置环境。在项目右击-》【属性】-》【配置属性】-》【VC++目录】-》编辑【包含目录】-》添加【$(solutiondir)include】-》确定-》编辑【库目录】-》添加【$(solutiondir)lib】-》确定-》【确定】
(注:确保所配置环境的属性与当前运行环境一致,如下图所示)
(3) 在【stdafx.h】中加入OpenGL头文件
//OpenGL
#include
#include
配置完成,编译运行成功
(1)在【源文件】右键,使用【类向导】添加新类C3DSModel,如下图所示,并且在C3DSModel.h和C3DSModel.cpp中添加【#include “stdafx.h”】头文件**
(2)打开DC10.cpp文件,将除函数外的结构体、数组等复制到C3DSModel.h头文件中,并为函数添加声明,如下图所示
(3)将其余函数复制到C3DSModel.cpp文件中,并在函数名前加入【C3DSModel::】,如下图所示
编译通过,运行成功,但此时对话窗口并没有开始绘制3DS模型(文章后面贴出完整代码)
采用上述同样的方法添加新类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按钮开始绘制
虽然导入了3DS模型,但是还存在以下几点问题未进行编写:
以上问题,将后续发文详细说明。
本文完整程序代码下载地址:https://download.csdn.net/download/u012293114/12104631