用Direct3D实现三维漫游文档格式.docx
《用Direct3D实现三维漫游文档格式.docx》由会员分享,可在线阅读,更多相关《用Direct3D实现三维漫游文档格式.docx(29页珍藏版)》请在冰点文库上搜索。
下面介绍一下Direct3DRM当中经常用到的一类函数----回调函数(CallBackfunction),这种函数必须深入理解才能明白Direct3DRM当中的各个调用过程,以后才能自如的添加你自己的代码。
回调函数的特殊性:
●回调函数的创建不是任意的,都应遵循某一特定的结构和参数列表,且必须以这样的参数形式才能被其它函数正确调用。
●回调函数的调用是附属于某一特定的函数,而不能单独使用,且还不能通过跟踪父函数来达到跟踪回调函数的目的,而这样做只能导致回调函数已经执行了。
因此为了调试或检验回调函数体的代码,必须采用调试窗口当中的RuntoCursor的方法来调试,这样才能让调试器跟踪进入回调函数体。
●回调函数的深入理解:
(只针对设备的查找用到的回调函数),因它是一种自动查找函数,故在其父函数找到某一特定的设备或驱动程序后它就会自动调用给定的回调函数,这样就形成一个循环,:
父函数查找设备—回调函数检验设备—回调函数返回—再由父函数查找。
其中当回调函数返回D3DENUMRET_OK则让父函数继续查找形成循环,返回D3DENUMRET_CANCEL则停止查找父函数结束。
●其它形式的回调函数:
最常用于Direct3DRM当中的是Movecallbackfunction(),它于设备查找回调函数的明显区别在于设备查找回调函数只跟随父函数执行一次,而Movecallback()会在接口IDirect3DRMViewport:
:
Move()调用的美一次都要执行各个框架的Movecallback(),这样可以统一管理各个框架的动作,且一个回调函数可以应用于多个框架,同时,它在Direct3DRM当中对动画的应用起着相当重要的作用,即在插值动画与一般的动画当中都必须使用回调函数来逐帧的渲染你设定的每一个动作。
小结:
这里讲述的回调函数只能让你有一个感性的认识,具体认识到具体的程序当中才能体会。
接下来我们将进入代码的添加阶段,并给出详细的注释,你可得仔细看好了。
4.0、窗口框架的生成
我们将利用MFC和DirectXSDK进行建造,这样做的好处是可以结合了它们各自的优点。
同时,大多数的Win32例子均采用SDK编写—晦涩难懂,但代码高效,而MFC却一目了然。
还有我们为了调试的方便,将建立窗口形式的Direct3D应用程序。
这里我们将借助MFC提供的对话框模板加以改进就能实现MFC和DirectXSDK的无缝联接。
具体表现为在对话框类中的InitInstance()中将自己创建的窗口类变为主窗口,则以后对系统的操作将完全转化为对这个自定义窗口的操作,也就任由你摆布了。
具体代码添加过程如下。
初始化基本窗口类模板步骤:
●首先启动VC++6.0,从File下选择New,选择Project下MFCAPPWizard(exe),命名为D3Dtest,除向导第二项全部不选外,接受其它所有缺省值。
●去掉对话框资源,在FileView处去掉D3DtestDlg.cpp和D3DtestDlg.h。
在ResourceView处去掉Dialog栏。
●打开主菜单下Insert,从NewClass下Name栏输入d3dwnd在BaseClass栏选genericCWnd作为基类。
OK
●打开d3dwnd.cpp文件,在View主菜单下选ClassVizard,classname选d3dwnd类,Message分别双击选WM_PAINT、WM_DESTROY、WM_MOVE、WM_SIZE、WM_KEYDOWN默认消息处理函数名,OK。
●打开d3dwnd.cpp创建自定义函数(具体方法是在ClassView列表中d3dwnd处单击右键选addMemberFunction)名为:
voidSetFreshRegion(HWNDwin);
//窗口更新
voidShowInfo();
//将渲染结果放到前台
HRESULTD3DGetSurfDesc(LPDDSURFACEDESClpDDSurfDesc,
LPDIRECTDRAWSURFACElpDDSurf);
voidClearBuffer(LPDIRECTDRAWSURFACElpbuffer);
//初始化面对象
BOOLCreatefaces(intw,inth);
//创建面对象
voidCleanUp();
//释放各个对象
BOOLMyScene();
//初始化场景内容
BOOLCreateDevAndView(LPDIRECTDRAWCLIPPERlpclipper,
intdriver,intwidth,intheight);
BOOLEnumDrivers();
//查询驱动设备
BOOLRenderLoop();
//着色循环
BOOLInitD3D(HWNDwin);
//初始化D3DRM
BOOLCreate3DWnd();
//创建3D窗口
●打开D3Dtest.cpp文件,在View主菜单下选ClassVizard,classname选CD3DtestApp类Message栏分别双击InitInstance、ExitInstance、OnIdle然后OK即可。
这里重载这些函数是进行渲染的需要。
以上建立了基本类模板和主窗口的基本消息处理函数,下面是在各个文件中添加的代码:
为了能够正确的编译并连接,我们将作如下的准备:
工程项目设置:
在Project主菜单下选Settings,在link栏的object/librarymodules处加入如下的.lib文件才能保证程序的正确连接:
dinput.libddraw.libd3dxof.libd3drm.libdxguid.libwinmm.lib
网格准备:
这里用DirectXSDK提供的sphere3.x和triplane.x
图片准备:
背景用DirectXSDK提供的lake.bmp,球体帖图用它提供的tutor.bmp(长宽均满足2的整数次幂)
●首先创建一个名为D3Daid.h的头文件,并加入当前工程当中。
这个文件主要记录程序当中用到的各种自定义结构,这里为了方便将它们综合在一个文件当中。
添加代码如下:
structAid_Value
{
CRectclient_rect;
//自己建立的窗口客户矩形区域
POINTstartpoint;
//窗口客户区的起点(相对整个屏幕)
LPDIRECT3DRMDEVICEdev;
//D3D保留模式设备对象指针
LPDIRECTDRAWlpddraw;
//DDraw对象指针
LPDIRECTDRAWSURFACElpprimary;
//主面对象(大小为800X600)
LPDIRECTDRAWSURFACElpback;
//后台面对象(大小与窗口一样)
LPDIRECT3DRMVIEWPORTview;
//保留模式视点对象指针
LPDIRECT3DRMFRAMEscene;
//场景框架
LPDIRECT3DRMFRAMEcamera;
//照相机框架
GUIDDriverGUID[5];
//可能找到的全局统一标识符结构
charDriverName[5][50];
//可能找到的三维驱动设备名称
D3DVALUEdx,dy,dz;
//照相机的漫游坐标
D3DVALUERotate_rad;
//旋转弧度
intNumDrivers;
//可能找到的驱动设备数目
intCurrDriver;
//当前使用的驱动设备
BOOLbRotate;
//旋转标志(在三维漫游当中使用)
BOOLbMove;
//移动标志(通上)
BOOLbQuit;
//程序结束标志
BOOLbInitialized;
//程序被初始化标志
BOOLbMinimized;
//程序最小化标志
intBPP;
//当前系统使用的颜色位数
};
●打开D3Dtest.h添加代码如下:
#include"
d3dwnd.h"
classCD3DtestApp:
publicCWinApp
public:
d3dwnd*p3Dwnd;
//自定义的窗口对象指针
intfailcount;
//渲染失败数目
};
●打开d3dwnd.h添加代码如下:
#defineINITGUID//预定义COM构件的全局统一标识符
d3drmwin.h"
//D3D保留模式
D3Daid.h"
//自定义结构
●打开D3Dtest.cpp添加代码如下:
这里不用系统产生的InitInstance()函数
BOOLCD3DtestApp:
InitInstance()
p3Dwnd=newd3dwnd;
//分配空间
p3Dwnd->
Create3DWnd();
//创建窗口并初始化D3DRM
m_pMainWnd=p3Dwnd;
//让自定义窗口设为当前线程的主窗口
m_pMainWnd->
ShowWindow(SW_SHOW);
//显示窗口
UpdateWindow();
//更新窗口
failcount=0;
//初始化渲染失败次数
returnTRUE;
}
OnIdle(LONGlCount)
//TODO:
Addyourspecializedcodehereand/orcallthebaseclass
if(!
RenderLoop())//当没有消息处理时就进行渲染
++failcount;
if(failcount>
2)
p3Dwnd->
CleanUp();
//如果失败三次则退出程序
returnTRUE;
//这里不执行系统的空闲函数CWinApp:
OnIdle(lCount);
intCD3DtestApp:
ExitInstance()
deletep3Dwnd;
//退出程序时去掉窗口指针
returnCWinApp:
ExitInstance();
●打开d3dwnd.cpp添加代码如下:
math.h"
#defineMAX_DRIVERS5//最大驱动器数目
#definePI26.28318f//圆周
#defineRotate_stepPI2/100.0f//旋转步长
#defineMove_step0.5f//移动步长
#defineSAFE_RELEASE(x)if(x!
=NULL){x->
Release();
x=NULL;
#defineMSG(str)MessageBox(str,"
ApplicationMessage"
MB_OK)
//预定义两个常用的宏
HWNDhwnd=NULL;
//初始化主窗口句柄
LPDIRECT3DRMlpD3DRM=NULL;
//初始化D3D保留模式对象指针
LPDIRECTDRAWCLIPPERlpDDClipper=NULL;
//初始化裁剪对象指针
Aid_Valuemyvalue;
//自定义结构对象
DWORDBppToDDbd(intbpp)//进行颜色位数匹配
switch(bpp)
{
case1:
returnDDBD_1;
case2:
case4:
returnDDBD_4;
case8:
returnDDBD_8;
case16:
returnDDBD_16;
case24:
returnDDBD_24;
case32:
returnDDBD_32;
default:
return0;
}
//设备查找回调函数
//回调函数将找到的一个匹配的设备驱动器后,进行填充并退出设备查找
//匹配准则是:
与当前的系统的颜色位数一致,一般都使用硬件抽象层
HRESULTWINAPIenumDeviceFunc(GUID*lpGuid,
LPSTRlpDeviceDescription,LPSTRlpDeviceName,
LPD3DDEVICEDESClpHWDesc,LPD3DDEVICEDESClpHELDesc,
LPVOIDlpContext)
LPD3DDEVICEDESClpdesc;
//D3D设备描述结构
int*lpStartDriver=(int*)lpContext;
lpdesc=lpHWDesc->
dcmColorModel?
lpHWDesc:
lpHELDesc;
if(!
lpdesc->
dwDeviceRenderBitDepth&
BppToDDbd(myvalue.BPP))
returnD3DENUMRET_OK;
memcpy(&
myvalue.DriverGUID[myvalue.NumDrivers],lpGuid,sizeof(GUID));
lstrcpy(&
myvalue.DriverName[myvalue.NumDrivers][0],lpDeviceName);
if(lpdesc==lpHWDesc&
&
dcmColorModel!
=D3DCOLOR_MONO)
*lpStartDriver=myvalue.NumDrivers;
myvalue.NumDrivers++;
if(myvalue.NumDrivers==5)
return(D3DENUMRET_CANCEL);
returnD3DENUMRET_OK;
voidUserControl(LPDIRECT3DRMFRAMEframe,void*arg,D3DVALUEdelta)
frame->
GetScene(&
scene);
if(myvalue.bMove)
AddTranslation(D3DRMCOMBINE_AFTER,
myvalue.dx,myvalue.dy,myvalue.dz);
SetPosition(scene,myvalue.dx,myvalue.dy,myvalue.dz);
myvalue.bMove=FALSE;
if(myvalue.bRotate)
AddRotation(D3DRMCOMBINE_REPLACE,
0,1,0,myvalue.Rotate_rad);
myvalue.bRotate=FALSE;
SAFE_RELEASE(scene);
//以上是全局函数和变量的声名,应把它们放在d3dwnd:
d3dwnd()构造函数之前
//以下是自定义函数的实体和各个消息响应函数
//创建窗口并初始化D3D的各种对象
BOOLd3dwnd:
Create3DWnd()
LPCTSTRm_name=AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW,
AfxGetApp()->
LoadCursor(IDC_ARROW),//光标
(HBRUSH)GetStockObject(BLACK_BRUSH),//窗口背景
LoadIcon(IDR_MAINFRAME)//图标
);
//窗口注册
BOOLbret=CreateEx(WS_EX_APPWINDOW,m_name,"
Direct3DRMExample"
WS_VISIBLE|WS_OVERLAPPEDWINDOW,//定义窗口属性
CW_USEDEFAULT,CW_USEDEFAULT,400,400,//窗口大小和起始位置
NULL,NULL,NULL);
//创建窗口
bret)
returnFALSE;
hwnd=GetSafeHwnd();
InitD3D(hwnd))//初始化D3D
MSG("
InitDirect3DFailed!
"
);
OnDestroy();
InitD3D(HWNDwin)
LPDIRECT3DRMTEXTURElptex=NULL;
//背景文理帖图
//注意:
D3D中的文理帖图的长宽都必须是2的整数次幂
HDChdc;
ZeroMemory(&
myvalue,sizeof(myvalue));
//初始化自定义结构对象
hdc=:
GetDC(win);
//取得当前窗口的设备句柄
myvalue.BPP=:
GetDeviceCaps(hdc,BITSPIXEL);
//取得系统的当前颜色位数
ReleaseDC(win,hdc);
SetFreshRegion(win);
//更新窗口位置
EnumDrivers())//查找匹配的驱动设备
returnFALSE;
myvalue.lpddraw->
SetCooperativeLevel(win,DDSCL_NORMAL);
//设置协作等级(这里是窗口协作等级)
Createfaces(myvalue.client_rect.Width(),myvalue.client_rect.Height()))
//根据窗口的大小来建立各种要用到的面对象
Direct3DRMCreate(&
lpD3DRM);
//创建D3D保留模式对象
lpD3DRM->
CreateFrame(NULL,&
myvalue.scene);
//创建场景根框架
CreateFrame(myvalue.scene,&
myvalue.camera);
//创建场景中照相机框架
if(myvalue.scene->
SetOrientation(NULL,D3DVAL(0.0),D3DVAL(0.0),D3DVAL(1.0),
D3DVAL(0.0),D3DVAL(-1.0),D3DVAL(0.0))!
=D3DRM_OK)
//设置场景的方向(默认的方向)
LoadTexture("
lake.bmp"
&
lptex);
//创建背景纹理帖图
myvalue.scene->
SetSceneBackgroundImage(lptex);
//设置背景帖图
/*myvalue.scene->
SetSceneBackgroundRGB(0.5f,0.6f,0.7f)